gitoriaLog in with ident

tickets

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commite9d5c618e9d5c618mission 010 (code order) 2/4: one lib/ file per topic — store.hl split into projects / tickets (+ relations) / events / tickets-helpers, util.hl shared helpers (env, storage dir, URLs, sorts, Vienna time), the function routes out of project.hl into lib/api.hl (thin; auth/filters/Accept in api-helpers.hl), invite + member-removal logic out of the faces/routes into invites.hl / tickets.hl; project.hl is the map. /login/callback gets req + the session store by reference. gate 249/0, connect 60/0, real-data reads identicalmree9d5c618/lib/tickets-helpers.hl

4.0 KB

  1. // lib/tickets-helpers.hl — what tickets.hl, events.hl and the pages look up about tickets: the state names, the
  2. // small shapes a page shows (a related ticket's ref, the relation view, the list's filter links). Statics only;
  3. // the central logic (who may do what, the writes) is lib/tickets.hl.
  4. import { ticketHref, ticketApiHref } from './util.hl'
  5. import { projectSlugOf, projectRecords } from './projects.hl'
  6. // THE STATES, in the order a ticket usually walks them; the state's name is also its URL form.
  7. // The OLD names are still accepted (the scheduler and the tools use them): stateOf() maps them.
  8. static states = ['open' 'progress' 'pending' 'review' 'reopened' 'done' 'canceled']
  9. static stateAliases = { 'in progress' = 'progress' 'awaiting creator' = 'review' 'confirmed' = 'done' 'rejected' = 'reopened' 'on hold' = 'pending' }
  10. // the states a person moves a ticket to: the inbox ones
  11. static inboxStates = ['pending' 'review']
  12. // the state a name stands for (a new name, an old one, or its URL form with '-' / '+'), or null
  13. static stateOf = (name) => {
  14. if (name == null || hlTypeName(name) != 'String') { return null }
  15. let s = name.replaceAll('-', ' ').replaceAll('+', ' ').trim().toLowerCase()
  16. if (states.includes(s)) { return s }
  17. let a = stateAliases[s]
  18. return a != null ? a : null
  19. }
  20. static slugOf = (state) => { return state == null ? '' : state.replaceAll(' ', '-') }
  21. static stateOfSlug = (slug) => { return stateOf(slug) }
  22. // ---- relations, as a page shows them (ticket #4) ------------------------------------------------
  23. // what a relation shows of a ticket (no relations of its own — no recursion)
  24. static refOf = (t) => {
  25. let slug = slugOf(t.state)
  26. let pslug = projectSlugOf(t)
  27. let href = ticketHref(pslug, t.number)
  28. let apiHref = ticketApiHref(pslug, t.number)
  29. let key = pslug + '#' + t.number
  30. return { id = t.id project = pslug number = t.number ref = '#' + t.number key = key subject = t.subject state = t.state stateSlug = slug href = href apiHref = apiHref }
  31. }
  32. static keyOf = (t) => { return projectSlugOf(t) + '#' + t.number }
  33. // what the ticket PAGE shows of a row's relations (SSR members and the `ticketsRelated` push)
  34. static noRef = { id = '' project = '' number = 0 ref = '' key = '' subject = '' state = '' stateSlug = '' href = '/' apiHref = '' }
  35. static relationViewOf = (row) => {
  36. let total = row.children.length
  37. let done = 0
  38. for (c of row.children) { if (c.state == 'done') { done = done + 1 } }
  39. let note = ''
  40. if (total > 0) { note = done == total ? 'all children done (' + total + ')' : done + ' of ' + total + ' children done' }
  41. let hasParent = row.parent != null
  42. let hasChildren = total > 0
  43. let hasBlockedBy = row.blockedBy.length > 0
  44. let hasBlocks = row.blocks.length > 0
  45. return { id = row.id parent = hasParent ? row.parent : noRef hasParent = hasParent children = row.children hasChildren = hasChildren
  46. childNote = note childClass = row.allChildrenConfirmed ? 'done' : '' allChildrenConfirmed = row.allChildrenConfirmed
  47. blockedBy = row.blockedBy hasBlockedBy = hasBlockedBy blocks = row.blocks hasBlocks = hasBlocks
  48. hasRelations = hasParent || hasChildren || hasBlockedBy || hasBlocks }
  49. }
  50. // ---- the list page's filter links -------------------------------------------------------------
  51. // [{ label, href, cls }] — `cls` is 'selected' on the one in effect. The href keeps the OTHER filter.
  52. static projectLinks = (project, state) => {
  53. let tail = state != null ? '/state/' + slugOf(state) : ''
  54. let out = [{ label = 'all projects' href = state != null ? tail : '/' cls = project == null ? 'selected' : '' }]
  55. for (p of projectRecords()) {
  56. out.push({ label = p.title href = '/project/' + p.slug + tail cls = p.slug == project ? 'selected' : '' })
  57. }
  58. return out
  59. }
  60. static stateLinks = (project, state) => {
  61. let head = project != null ? '/project/' + project : ''
  62. let out = [{ label = 'all states' href = project != null ? head : '/' cls = state == null ? 'selected' : '' }]
  63. for (s of states) {
  64. out.push({ label = s href = head + '/state/' + slugOf(s) cls = s == state ? 'selected ' + slugOf(s) : slugOf(s) })
  65. }
  66. return out
  67. }

Branches

Latest commits

  • e9d5c618mission 010 (code order) 2/4: one lib/ file per topic — store.hl split into projects / tickets (+ relations) / events / tickets-helpers, util.hl shared helpers (env, storage dir, URLs, sorts, Vienna time), the function routes out of project.hl into lib/api.hl (thin; auth/filters/Accept in api-helpers.hl), invite + member-removal logic out of the faces/routes into invites.hl / tickets.hl; project.hl is the map. /login/callback gets req + the session store by reference. gate 249/0, connect 60/0, real-data reads identicalmre
  • 97e269b5mission 010 (code order) 1/4: .hl files out of the root — lib/ (store, users, connections, invites, migrate, markdown, mdview, import = ticketfile, util = localtime, jsoncheck, api-helpers = api), tools/import.hl, components/styles.hl; import paths only. gate 249/0, connect 60/0, real-data reads identicalmre
  • 38f9d10ftickets: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gate 249/0, connect 60/0mre
  • d3db6139tickets: Hybriel master 190aa11d (fc838894 GC correctness, #126 closure scopes, #127); gate 249/0, connect 60/0mre
  • bce182e3tickets: Hybriel master 7eea0d32 (#126 memory, #48 lambda copies its argument); migrate.hl lambdas take &logmre
  • 4137be0fantcolony#40: mission references point to the moved missionsmre
  • 9bfba36aantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
  • c7bd2645tickets: Hybriel master 73267707 (#122 fixed); compactNow workaround removed (#110 covered)mre
  • 2ab91ee9tickets: gate checks rows appear once (session sync); re-vendor to ff51cf46 stopped on hybriel#122, stays 837fe120mre
  • e01c2b1dtickets#24: installable app (manifest, service worker, offline list), own icon; gate waits for the hello's pongmre
  • 752fbb7fdeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
  • 38bdd5e4deploy.sh: never send .git or .gitignore to Byrodinmre
  • f12fa1bcState of 2026-09-27, before the move to gitoriamre