gitoria
All repositories: gitoria
5.1 KB
// components/tickets.hl — THE TICKETS of a repo (gitoria#10, gitoria#16): `/tickets` on <slug>.<domain>, its own// route, rendered on the SERVER with its list: the repo comes from the request's host, the tickets from// tickets.worldapi.org (tickets.hl — hl:fetch answers at once, so the list is in the first HTML and in every// hl:web navigation). The repo must be connected to a tickets project first (gitoria#18, /settings). A named user// opens a ticket AS THEMSELVES (the repo's key + their ident id, tickets.hl); a ticket opened here reaches every open// tickets page of the repo (push `ticketOpened`).parent './main.hl'import { siteName } from '../project.hl'import RepoHead from './repohead.hl'import { repoAt, pageTitle } from '../repos.hl'import { loadTickets, openTicket } from '../tickets.hl'import { slugOfHost, infoOfSession, userOfSession } from '../users.hl'host = nullsession = nullslug = slugOfHost(host)repo = repoAt(slug)hasRepo = repo != nullme = infoOfSession(session)canCreate = repo != null && repo.ticketsOn && me != null && me.namedneedsLogin = repo != null && repo.ticketsOn && me == nullnotConnected = repo != null && repo.ticketsOffloaded = repo != null && repo.ticketsOn ? loadTickets(slug) : nullticketsErrorShown = loaded != null && loaded.error != nullticketsError = loaded != null && loaded.error != null ? loaded.error : ''tickets = loaded != null && loaded.tickets != null ? loaded.tickets : []ticketsTruncated = loaded != null && loaded.truncated == trueticketsHref = loaded != null && loaded.url != null ? loaded.url : ''ticketsShown = tickets.length > 0ticketsNone = loaded != null && loaded.error == null && tickets.length == 0ticketError = ''ticketNotice = ''hasTicketNotice = false__title = pageTitle(siteName, slug, 'Tickets')View {repoPage {RepoHead { }if (hasRepo) {repoTickets { id = "tickets"if (notConnected) { p { id = "ticketsnotconnected" class = "empty" "This repository is not connected to a tickets project. The owner connects it under " a { id = "tosettings" href = "/settings" "Settings" } "." } }if (ticketsErrorShown) { p { id = "ticketserror" class = "message" ticketsError } }if (canCreate) {details { id = "newticket"summary { "New ticket" }form { id = "newticketform"on submit(e) {e.preventDefault()emit openIt(e)}label { "Subject"input { id = "ticketsubject" name = "subject" required = "required" maxlength = "200" autocomplete = "off" }}label { "Text (Markdown, optional)"textarea { id = "ticketsummary" name = "summary" rows = "5" }}button { id = "ticketsave" type = "submit" "Open ticket" }p { id = "newticketerror" class = "message" ticketError }}}}if (needsLogin) { p { id = "ticketsloginhint" class = "muted" "Log in with ident (top right) to open a ticket." } }if (hasTicketNotice) { p { id = "ticketnotice" class = "notice" ticketNotice } }if (ticketsNone) { p { id = "noticketsyet" class = "empty" "No ticket yet in this project." } }if (ticketsShown) {ol { id = "ticketlist"for (t of tickets) {li {a { class = "number" href = t.url "#" t.number }a { class = "subject" href = t.url t.subject }span { class = "state" t.state }time { t.updated }}}}p { class = "muted" "Open a ticket to read and answer it in " a { id = "ticketsall" href = ticketsHref "tickets.worldapi.org" } "." }if (ticketsTruncated) { p { id = "ticketstruncated" class = "muted" "Only the latest tickets are listed here." } }}}}}}on openIt(e) {let f = e.target.elementslet r = emit server gitoriaOpenTicket(slug, f.subject.value, f.summary.value)if (r == null || r.error != null) {ticketError = r != null ? r.error : 'could not open the ticket'return null}ticketError = ''f.subject.value = ''f.summary.value = ''ticketNotice = 'Opened ticket #' + r.ticket.number + '.'hasTicketNotice = trueaddTicket(r.ticket)}addTicket = (row) => {let next = [row]for (x of tickets) { if (x.number != row.number) { next.push(x) } }tickets = nextticketsShown = trueticketsNone = false}// a ticket opened here by anyone: every open tickets page of that repo followson client ticketOpened(slugOf, row) {if (!hasRepo || slugOf != slug) { return null }addTicket(row)}// this session's tabs follow a login / name / logout (the shell's faces push them)on client gitoriaSignedIn(tag, info) {canCreate = repo != null && repo.ticketsOn && info.namedneedsLogin = false}on client gitoriaSignedOut(tag) {canCreate = falseneedsLogin = repo != null && repo.ticketsOn}on server gitoriaOpenTicket(slugOf, subject, summary, session) {if (repoAt(slugOf) == null) { return { error = 'no such repository' } }let u = userOfSession(session)if (u == null) { return { error = 'log in with ident (top right) to open a ticket' } }if (u.name == '') { return { error = 'choose a display name first (top of the page)' } }let r = openTicket(slugOf, u.identity, subject, summary)if (r.error != null) { return { error = r.error } }emit client ticketOpened(slugOf, r.ticket)return r}
Branches
- mainmain branch
Latest commits
- e85eaf01gitoria: 069 round 2 — hybriel 1a096ad3 not adopted (Markdown SSR still grows); browser gate waits for the server-side logout before restartmre
- 09ce4f3fgitoria: mission 069 re-vendor hybriel 8efba065 stopped (big SSR pages grow + slow down); lambda audit clean; old vendor keptmre
- 3dc43108antcolony#40: mission references point to the moved missionsmre
- 8d9450fdantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
- 205d5fe4gitoria: Hybriel master ff51cf46; ssh keys/tokens no double rows (session sync); gates follow #20mre
- 9b27cb26gitoria#21: installable app (manifest, service worker, offline start page), own iconmre
- 68dcb603deploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- e2deed6dgitoria#20: "Add code" only on the Code page of an empty repository, no collapsiblemre
- 8bb97ffddeploy.sh: never send .git or .gitignore to Byrodinmre
- fd981932State of 2026-09-27; bin/ no longer tracked (Hybriel commit is in README)mre
- 4a2d7125initial commitmre