@maudecode/kanvil 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/apps/server/dist/{auth-CFjetlB4.mjs → auth-CVnX2XIg.mjs} +3828 -3463
  2. package/apps/server/dist/client.mjs +2 -2
  3. package/apps/server/dist/{config-BQw6mPzE.mjs → config-DUIdB3c2.mjs} +1 -1
  4. package/apps/server/dist/index.mjs +165 -43
  5. package/apps/server/dist/migrate.mjs +1 -1
  6. package/apps/server/dist/recover.mjs +3 -3
  7. package/apps/server/dist/{src-BaTNDJB7.mjs → src-CC6lPIU_.mjs} +190 -35
  8. package/apps/server/drizzle/0040_upload_urls.sql +20 -0
  9. package/apps/server/drizzle/0041_project_repositories.sql +2 -0
  10. package/apps/server/drizzle/0042_public_boards.sql +37 -0
  11. package/apps/server/drizzle/0043_epics.sql +18 -0
  12. package/apps/server/drizzle/meta/0040_snapshot.json +7170 -0
  13. package/apps/server/drizzle/meta/0041_snapshot.json +7183 -0
  14. package/apps/server/drizzle/meta/0042_snapshot.json +7315 -0
  15. package/apps/server/drizzle/meta/0043_snapshot.json +7466 -0
  16. package/apps/server/drizzle/meta/_journal.json +28 -0
  17. package/apps/web/dist/assets/{CompositeItem-Dotj8JZL.js → CompositeItem-GgAhkI4m.js} +1 -1
  18. package/apps/web/dist/assets/{Trans-CE4EAAY1.js → Trans-CT9xiBED.js} +1 -1
  19. package/apps/web/dist/assets/admin-settings-C7yxWm-w.js +1 -0
  20. package/apps/web/dist/assets/{archive-restore-BMtgjFUu.js → archive-restore-UgUTJyLs.js} +1 -1
  21. package/apps/web/dist/assets/az-AZ-IkRP1MKo.js +1 -0
  22. package/apps/web/dist/assets/{az-B-DxMc__.js → az-CZ46yOUl.js} +1 -1
  23. package/apps/web/dist/assets/configuration-CU0QOaRw.js +1 -0
  24. package/apps/web/dist/assets/{corner-left-up-O4OsTW-L.js → corner-left-up-BFY7AqdS.js} +1 -1
  25. package/apps/web/dist/assets/{de-Btbkq-aX.js → de-CseN-3gF.js} +1 -1
  26. package/apps/web/dist/assets/de-DE-DCGKtpLI.js +1 -0
  27. package/apps/web/dist/assets/default-CN9zcRIy.js +56 -0
  28. package/apps/web/dist/assets/delivery-settings-xx9Bb6Ux.js +1 -0
  29. package/apps/web/dist/assets/el-GR-C5w6p2-z.js +1 -0
  30. package/apps/web/dist/assets/{el-DBVEX16k.js → el-psH0HOR3.js} +1 -1
  31. package/apps/web/dist/assets/{ellipsis-CvEPDi9V.js → ellipsis-CcIJddV1.js} +1 -1
  32. package/apps/web/dist/assets/{es-CMJoxZBk.js → es-BVvGrD23.js} +1 -1
  33. package/apps/web/dist/assets/es-ES-PjJlJCgT.js +1 -0
  34. package/apps/web/dist/assets/{forms-DbfnnPg_.js → forms-BUNl5uNV.js} +1 -1
  35. package/apps/web/dist/assets/{fr-OyEmYoRn.js → fr-C43nghuI.js} +1 -1
  36. package/apps/web/dist/assets/fr-FR-CGWH-Juq.js +1 -0
  37. package/apps/web/dist/assets/{hi-CIgXSseB.js → hi-Ds3DGtT-.js} +1 -1
  38. package/apps/web/dist/assets/hi-IN-B3bq8hDJ.js +1 -0
  39. package/apps/web/dist/assets/{highlight-B4K3yEEh.js → highlight-BPZqoBaO.js} +1 -1
  40. package/apps/web/dist/assets/id-ID-98H240ET.js +1 -0
  41. package/apps/web/dist/assets/{id-WV2oSAPB.js → id-tg9awgd8.js} +1 -1
  42. package/apps/web/dist/assets/index-CvFGdLre.js +17 -0
  43. package/apps/web/dist/assets/index-DPT5LAs1.css +2 -0
  44. package/apps/web/dist/assets/{invitations-BTB8f2Xl.js → invitations-fH1M9hYm.js} +1 -1
  45. package/apps/web/dist/assets/{it-BWSrRqus.js → it-DRwaWQyE.js} +1 -1
  46. package/apps/web/dist/assets/it-IT-CNS9cL7I.js +1 -0
  47. package/apps/web/dist/assets/{item-HmFuRZet.js → item-BbaYy50i.js} +1 -1
  48. package/apps/web/dist/assets/ja-JP-BFTqtCdV.js +1 -0
  49. package/apps/web/dist/assets/{ja-BtOHWvUJ.js → ja-jj4Ubayf.js} +1 -1
  50. package/apps/web/dist/assets/{ko-DAryK0HN.js → ko-Dn2pRrar.js} +1 -1
  51. package/apps/web/dist/assets/ko-KR-C58VNccr.js +1 -0
  52. package/apps/web/dist/assets/{label-settings-ssfEX2uR.js → label-settings-CbBHSL0c.js} +1 -1
  53. package/apps/web/dist/assets/{markdown-blocks-B1kO6iku.js → markdown-blocks-B6mE_S6n.js} +1 -1
  54. package/apps/web/dist/assets/{member-directory-BA4ZT1qU.js → member-directory-CoGg7WkW.js} +1 -1
  55. package/apps/web/dist/assets/{mk-DBbiGUk5.js → mk-DQUnXaPE.js} +1 -1
  56. package/apps/web/dist/assets/mk-MK-Bf5RDo6j.js +1 -0
  57. package/apps/web/dist/assets/{nl-D4V-8WdF.js → nl-C_Ijir3O.js} +1 -1
  58. package/apps/web/dist/assets/nl-NL-D1WFJyOY.js +1 -0
  59. package/apps/web/dist/assets/{pl-C--QrxO0.js → pl-DgWXgYL-.js} +1 -1
  60. package/apps/web/dist/assets/pl-PL--jAcwyAK.js +1 -0
  61. package/apps/web/dist/assets/project-lifecycle-BHWCMq7o.js +1 -0
  62. package/apps/web/dist/assets/project-settings-CBG5CZX1.js +1 -0
  63. package/apps/web/dist/assets/pt-BR-CJQfuq9b.js +1 -0
  64. package/apps/web/dist/assets/{pt-BR-DvFVKpmO.js → pt-BR-N-Xvp9fC.js} +1 -1
  65. package/apps/web/dist/assets/public-project-DmTr9q4c.js +1 -0
  66. package/apps/web/dist/assets/{rotate-ccw-CB36Zu3L.js → rotate-ccw-BFYpBtuj.js} +1 -1
  67. package/apps/web/dist/assets/{ru-UmbPUOka.js → ru-Ctq-5FYl.js} +1 -1
  68. package/apps/web/dist/assets/ru-RU-BtvJ8Glt.js +1 -0
  69. package/apps/web/dist/assets/{search-Cvetr1OK.js → search-C2QaoLwI.js} +6 -6
  70. package/apps/web/dist/assets/{send-CJDct4Oj.js → send-qRPqbT2m.js} +1 -1
  71. package/apps/web/dist/assets/settings-BNPajW83.js +1 -0
  72. package/apps/web/dist/assets/{sliders-horizontal-dmqWngxy.js → sliders-horizontal-S16oyCiS.js} +1 -1
  73. package/apps/web/dist/assets/{switch-BtScTpP9.js → switch-C44Ouoqh.js} +1 -1
  74. package/apps/web/dist/assets/{table-byFokzo-.js → table-esnQpDtb.js} +1 -1
  75. package/apps/web/dist/assets/ticket-detail-BVyScTlg.js +2 -0
  76. package/apps/web/dist/assets/ticket-editor-BjuSeeiA.js +2 -0
  77. package/apps/web/dist/assets/ticket-list-BCvUvYju.js +6 -0
  78. package/apps/web/dist/assets/ticket-list-controls-BiC5CIwM.js +1 -0
  79. package/apps/web/dist/assets/{tr-CNpgQ4ka.js → tr-CYT8BT69.js} +1 -1
  80. package/apps/web/dist/assets/tr-TR-Bahy-EKO.js +1 -0
  81. package/apps/web/dist/assets/uk-UA-DDVgxWqX.js +1 -0
  82. package/apps/web/dist/assets/{uk-MQoTXdpk.js → uk-y7QuyfrN.js} +1 -1
  83. package/apps/web/dist/assets/{upload-D1x_ib3B.js → upload-e7CmLHhp.js} +1 -1
  84. package/apps/web/dist/assets/{useQueries-BnlNCoy4.js → useQueries-U3MOyy3t.js} +1 -1
  85. package/apps/web/dist/assets/{vi-CEw4gTWc.js → vi-Bz2g5X1k.js} +1 -1
  86. package/apps/web/dist/assets/vi-VN-CuHCYxNQ.js +1 -0
  87. package/apps/web/dist/assets/{visual-editor-CpdMRn3e.js → visual-editor-UmVaDXWu.js} +1 -1
  88. package/apps/web/dist/assets/workspace-settings-BiWJqW0m.js +1 -0
  89. package/apps/web/dist/assets/zh-CN-C94sYOI_.js +1 -0
  90. package/apps/web/dist/assets/{zh-CN-ChzoXkfr.js → zh-CN-y_sluDUm.js} +1 -1
  91. package/apps/web/dist/assets/zh-TW-BoRQfGQL.js +1 -0
  92. package/apps/web/dist/assets/{zh-TW-bWBKARrK.js → zh-TW-DIEfy239.js} +1 -1
  93. package/apps/web/dist/index.html +8 -8
  94. package/docs/api.md +63 -5
  95. package/docs/cli.md +1 -1
  96. package/docs/mobile.md +1 -1
  97. package/docs/plan.md +1 -0
  98. package/docs/releasing.md +1 -1
  99. package/package.json +1 -1
  100. package/apps/web/dist/assets/admin-settings-CO7nnD9T.js +0 -1
  101. package/apps/web/dist/assets/az-AZ-R8_HFWJr.js +0 -1
  102. package/apps/web/dist/assets/configuration-Bnt72HRu.js +0 -1
  103. package/apps/web/dist/assets/de-DE-BHgMj19Y.js +0 -1
  104. package/apps/web/dist/assets/default-DqsN66dq.js +0 -56
  105. package/apps/web/dist/assets/delivery-settings-Cji0N0m0.js +0 -1
  106. package/apps/web/dist/assets/el-GR-CQFGM5Sr.js +0 -1
  107. package/apps/web/dist/assets/es-ES-DZkcRVA4.js +0 -1
  108. package/apps/web/dist/assets/fr-FR-CRPAezXU.js +0 -1
  109. package/apps/web/dist/assets/hi-IN-2YFE4UQk.js +0 -1
  110. package/apps/web/dist/assets/id-ID-B-J0_QIW.js +0 -1
  111. package/apps/web/dist/assets/index-BAnwOrh1.css +0 -2
  112. package/apps/web/dist/assets/index-nltuyQtq.js +0 -17
  113. package/apps/web/dist/assets/it-IT-CJ0HYggO.js +0 -1
  114. package/apps/web/dist/assets/ja-JP-DDQS7D7b.js +0 -1
  115. package/apps/web/dist/assets/ko-KR-CkpoewEa.js +0 -1
  116. package/apps/web/dist/assets/mk-MK-C0RhS6XR.js +0 -1
  117. package/apps/web/dist/assets/nl-NL-BSKqlzcL.js +0 -1
  118. package/apps/web/dist/assets/pl-PL-CHz38SiU.js +0 -1
  119. package/apps/web/dist/assets/project-lifecycle-COZb9FqM.js +0 -1
  120. package/apps/web/dist/assets/project-settings-DwmTm9KA.js +0 -1
  121. package/apps/web/dist/assets/pt-BR-BotWHRiO.js +0 -1
  122. package/apps/web/dist/assets/public-project-B3hEkeCF.js +0 -1
  123. package/apps/web/dist/assets/ru-RU-DtOwNna6.js +0 -1
  124. package/apps/web/dist/assets/settings-3IXm9lJ-.js +0 -1
  125. package/apps/web/dist/assets/ticket-detail-C3YA3Hzc.js +0 -2
  126. package/apps/web/dist/assets/ticket-editor-CZnZQMG1.js +0 -2
  127. package/apps/web/dist/assets/ticket-list-BXUorfmj.js +0 -6
  128. package/apps/web/dist/assets/ticket-list-controls-BxGKCZMo.js +0 -1
  129. package/apps/web/dist/assets/tr-TR-CdG2alU2.js +0 -1
  130. package/apps/web/dist/assets/uk-UA-Ceu5gorw.js +0 -1
  131. package/apps/web/dist/assets/vi-VN-CxshgV4W.js +0 -1
  132. package/apps/web/dist/assets/workspace-settings-BickBtZx.js +0 -1
  133. package/apps/web/dist/assets/zh-CN-D7Yrjhd3.js +0 -1
  134. package/apps/web/dist/assets/zh-TW-DyF38kqx.js +0 -1
package/docs/api.md CHANGED
@@ -26,7 +26,9 @@ Reuse the same UUID `requestId` when retrying a write. Reusing it with a differe
26
26
 
27
27
  Signed-in sessions may make `KANVIL_SESSION_WRITE_LIMIT` write requests per minute (120 by default) to `/api/v1`, and MCP OAuth clients get the same budget for write tools; exceeding it returns `429 rate_limited` with a `Retry-After` header. This counter is per application process. API keys have a separate limit of 600 requests per minute per key across HTTP and MCP. An instance operator can set `KANVIL_API_KEY_RATE_LIMIT_ENABLED=false` and restart the server to disable that limit for existing and new keys. The default is `true`; restoring it enforces the limit again. Session, MCP OAuth, and sign-in limits are unchanged, as are key expiration, revocation, scopes, and account permissions. Authentication rereads each key and skips timestamp-only bookkeeping writes. When a key is unlimited, authentication leaves its stored `last_request` and `updated_at` unchanged; enabled rate limits and usage quotas still consume their allowance synchronously and atomically. A request that consumed a key's last use is admitted even when a concurrent request finds the key exhausted and deletes it first.
28
28
 
29
- Claims require an eligible ticket and current version. Successful claiming moves it to In progress. Renew an unexpired claim using its ID and the same credential. Once a ticket has been claimed, machine ticket updates and evidence submissions require a current matching claim ID, including after release. Recovery without `reschedule` lifts that requirement and increments the ticket version, publishing `ticket.updated` with `changedFields: ["claimRequired"]`, so a ticket left outside To do (for example, In review after its work merged) accepts ordinary worker writes again; a write that still names the recovered claim ID returns `409 claim_conflict`. Submit completion and evidence before releasing ownership. Human sessions can edit and reschedule work without a machine claim. Release unexpired ownership after the worker stops or completes. The ticket response exposes `canReleaseClaim` for the exact credential or client identity; sharing an account does not confer ownership. An expired claim remains blocking until a caller with `claim` scope and `ticket.write` permission confirms the old worker stopped and explicitly calls `recover_claim` with its ID, a reason, and `confirmedStopped: true`. Administrator access is not required for expired claims. If a worker has stopped while its claim is still live, for example after revoking its credential, a caller with `project.manage` and `admin` scope can explicitly pass `cancelActive: true` to cancel it before expiry. This still requires `confirmedStopped: true` and a reason, and the recovery event records the cancellation. Without that flag, live claims remain protected. Recovery does not automatically reschedule work or grant a replacement claim. To resume without a human session, explicitly pass `reschedule: true` and the ticket's current `expectedVersion` to `recover_claim`; this returns it to To do and increments its version atomically with recovery. Then call `get_ticket` and `claim_ticket` with the new version for a fresh claim ID. A missing version returns `400 validation_error`, and a stale one returns `409 version_conflict`, leaving the claim and status unchanged. A ticket that still requires ownership but has no claim left, for example one released outside To do or recovered by an older server that kept the requirement, is recovered the same way without a `claimId`: pass a reason, `confirmedStopped: true`, and the ticket's current `expectedVersion`, which this form requires. Without `reschedule` it lifts the requirement so ordinary worker writes pass again; with `reschedule: true` it returns the ticket to To do for a fresh claim. Both increment the version, and the `claim.recovered` event records `claimId: null`. If the ticket has any claim, live or expired, this form returns `409 claim_conflict` with the claim in `details.claimId`; if it does not require ownership, it returns `409 claim_not_required`. Neither changes the ticket. Initial claims and renewals accept `leaseSeconds` of at least 30, bounded by `KANVIL_CLAIM_MAX_LEASE_SECONDS`, which defaults to 86400 (24 hours). The default lease remains 300 seconds, or the configured maximum if smaller. A request above the configured maximum returns `400 validation_error`; renew before expiry to keep ownership.
29
+ Claims require an eligible ticket. `expectedVersion` is optional on `claim_ticket`: when sent, a stale version returns `409 version_conflict`; when omitted, readiness alone guards the claim, so a ticket that is held, blocked, already claimed, or outside To do still returns `409 not_ready` with its readiness reasons. Successful claiming moves it to In progress and returns the claim's fields at the top level plus `ticket`, the claimed ticket as `get_ticket` reads it next, including its body and new `version`. Renew an unexpired claim using its ID and the same credential. Once a ticket has been claimed, machine ticket updates and evidence submissions require a current matching claim ID, including after release. Recovery without `reschedule` lifts that requirement and increments the ticket version, publishing `ticket.updated` with `changedFields: ["claimRequired"]`, so a ticket left outside To do (for example, In review after its work merged) accepts ordinary worker writes again; a write that still names the recovered claim ID returns `409 claim_conflict`. Submit completion and evidence before releasing ownership. Human sessions can edit and reschedule work without a machine claim. Release unexpired ownership after the worker stops or completes. The ticket response exposes `canReleaseClaim` for the exact credential or client identity; sharing an account does not confer ownership. An expired claim remains blocking until a caller with `claim` scope and `ticket.write` permission confirms the old worker stopped and explicitly calls `recover_claim` with its ID, a reason, and `confirmedStopped: true`. Administrator access is not required for expired claims. If a worker has stopped while its claim is still live, for example after revoking its credential, a caller with `project.manage` and `admin` scope can explicitly pass `cancelActive: true` to cancel it before expiry. This still requires `confirmedStopped: true` and a reason, and the recovery event records the cancellation. Without that flag, live claims remain protected. Recovery does not automatically reschedule work or grant a replacement claim. To resume without a human session, explicitly pass `reschedule: true` and the ticket's current `expectedVersion` to `recover_claim`; this returns it to To do and increments its version atomically with recovery. Then call `get_ticket` and `claim_ticket` with the new version for a fresh claim ID. A missing version returns `400 validation_error`, and a stale one returns `409 version_conflict`, leaving the claim and status unchanged. A ticket that still requires ownership but has no claim left, for example one released outside To do or recovered by an older server that kept the requirement, is recovered the same way without a `claimId`: pass a reason, `confirmedStopped: true`, and the ticket's current `expectedVersion`, which this form requires. Without `reschedule` it lifts the requirement so ordinary worker writes pass again; with `reschedule: true` it returns the ticket to To do for a fresh claim. Both increment the version, and the `claim.recovered` event records `claimId: null`. If the ticket has any claim, live or expired, this form returns `409 claim_conflict` with the claim in `details.claimId`; if it does not require ownership, it returns `409 claim_not_required`. Neither changes the ticket. Initial claims and renewals accept `leaseSeconds` of at least 30, bounded by `KANVIL_CLAIM_MAX_LEASE_SECONDS`, which defaults to 86400 (24 hours). The default lease remains 300 seconds, or the configured maximum if smaller. A request above the configured maximum returns `400 validation_error`; renew before expiry to keep ownership.
30
+
31
+ A worker handed a ticket key claims it in one call, with no read first: `claim_ticket({ projectId: "<key prefix>", ticketId: "<key>", workerId, leaseSeconds })`. Call `resolve_ticket` only after a `404 not_found`, which means the ticket moved since the key was issued.
30
32
 
31
33
  Evidence records identify the submitting account and supplied commit. A reported passing outcome is an assertion by that identity, not independent verification by Kanvil.
32
34
 
@@ -36,19 +38,23 @@ Attachments use `upload_attachment` or the corresponding HTTP POST with `name`,
36
38
 
37
39
  Files embedded in a ticket that does not exist yet are staged first. `stage_upload` (`POST /api/v1/projects/{projectId}/uploads`) takes the same `name`, `contentType`, and base64 `data` and returns `{id, projectId, actorId, name, contentType, size, createdAt, expiresAt}` without publishing an event. Until it is claimed, only its uploader can read it, at the same `/attachments/{id}/content` address it keeps afterwards, so Markdown that embeds it never needs rewriting. `create_ticket` accepts `uploads`, a list of up to 50 staged IDs; in the same transaction they become the ticket's attachments with unchanged IDs and one `attachment.created` event each after `ticket.created`. If any ID is missing, expired, staged by someone else, or staged in another project, nothing is created and the request fails with `not_found`, whose `details.uploads` lists the unusable IDs. Retrying with the same `requestId` returns the created ticket without claiming twice. `discard_upload` (`DELETE /api/v1/projects/{projectId}/uploads/{uploadId}`) deletes one of your unclaimed uploads with its bytes. Unclaimed uploads expire after 24 hours, and the storage cleanup job removes them and their bytes. Both operations require `write`.
38
40
 
41
+ A file on disk does not need to pass through a tool call as base64. `create_upload_url` (`POST /api/v1/projects/{projectId}/upload-urls`, `write`) takes `name`, `contentType`, and the file's exact `size` in bytes (at most 10 MB, with the same name and content-type rules), plus an optional `ticketId`, and returns `{uploadId, url, expiresAt}`. Send the bytes with one `PUT` to `url` within 15 minutes, for example `curl --upload-file recording.mp4 "$url"`; the request needs no other credential, must carry a `Content-Length` equal to `size` (curl sends it), and its `Content-Type` is ignored in favor of the declared one. Without `ticketId` the `PUT` stages the file as `stage_upload` would, and `create_ticket` claims it by listing `uploadId` in `uploads`; with `ticketId` it becomes that ticket's attachment with ID `uploadId` and an `attachment.created` event. Either answers `200` with the staged upload or the attachment. The write runs as the account and credential that requested the URL and needs their write access at the time of the `PUT`; once that API credential is revoked or expires, the URL answers `401 unauthorized`. A missing or different `Content-Length`, or a body of another length, fails with `400 invalid_attachment` and leaves the URL usable. While one `PUT` is sending its body, once a `PUT` stores the file, or after 15 minutes, the URL answers `410 upload_url_unavailable`; a `PUT` that fails releases it. An unknown URL answers `404 not_found`. The URL accepts only that one upload: it serves no reads and reaches no other file. Creating one publishes no event and takes no `requestId`, so no receipt keeps the URL; request a new one instead of retrying. Intake credentials cannot create upload URLs.
42
+
39
43
  Tickets carry optional `startAt` and `dueAt` ISO timestamps. Both name whole days: clients store a start as the first instant of the chosen day and a due date as its last instant, each in the author's time zone, so a ticket that starts and is due on the same day is valid. A start after the due date fails with `400 validation_error` (path `startAt`) on create, update, bulk update, and duplication, checked against the dates the write leaves on the ticket. Clients reading a server that predates start dates get `startAt: null`. `list_tickets` takes `scheduledFrom` and `scheduledTo` (ISO timestamps) to return tickets whose span, from the start (or due) date to the due (or start) date, overlaps `[scheduledFrom, scheduledTo)`; either bound alone also leaves out undated tickets. `sort: "start"` orders by that span's first day. `list_tickets` accepts `due` as `overdue` (past due), `today` (due within the next 24 hours), `week` (due within the next 7 days), `scheduled`, or `none`; the first three apply to open tickets only and use the database clock and `sort: "due"` for soonest first with undated tickets last. Other sorts: `priority` (default), `updated`, `created`, `title`, `labels`, `number`, `status`, `difficulty`, `kind`, and `assignee`. Each has a natural direction (newest first for `updated` and `created`, ascending otherwise); pass `order: "asc"` or `"desc"` to override it. Ties break by ticket number, and null values sort last in both directions. `manual` follows the project's shared manual order (see below). The `assigneeId` filter takes a user id or `none` for unassigned tickets.
40
44
 
41
45
  The manual order is one server-kept order per project, shared by every list, board, and planning view; a status section shows it filtered. `move_ticket` (`POST /api/v1/projects/{projectId}/tickets/{ticketId}/move`) takes the ticket's `expectedVersion` and places it directly `before` or `after` one other ticket of the project (an ID or key; name one, not both), optionally setting `status`, `stateId`, or both in the same write, resolved like a ticket update's (see Workflow states); a status or state alone changes it and keeps the place. Clients never send positions: only the moved ticket changes place, so a move made in a filtered view leaves hidden tickets in their relative order, and an anchor that has moved since the caller read it still places the ticket beside it. A deleted or foreign anchor is a `404 not_found` and nothing moves. The write needs `write`, follows the machine claim rules of `update_ticket` (pass `claimId`), accepts `requestId`, bumps the ticket's version, and publishes `ticket.updated` with `changedFields` naming `position` and, when the state changed, `status` and `stateId`. A reorder alone notifies no follower. New tickets join the end of the order; existing tickets started in creation order. `move_project` (`POST /api/v1/projects/{projectId}/move`) places a project `before` or `after` another project of the same workspace that the caller can read, with the project's `expectedVersion`; it needs project administration and publishes `project.updated`. `list_projects` returns projects in that order, which started alphabetical.
42
46
 
43
- The property filters `status`, `priority`, `kind`, `difficulty`, `label`, `assigneeId`, and `customField` (see [Custom fields](#custom-fields)) each take one value or a list and match tickets with any of them. Each has a `Not` twin (`statusNot`, `labelNot`, `assigneeIdNot`, and so on) that excludes the listed values; excluding a value keeps tickets where the field is unset. Over HTTP a list is a repeated query parameter, such as `?status=todo&status=in_progress&labelNot=blocked`; JSON and MCP callers pass an array. Filters combine with AND. A list holds at most 25 values, and a value in both a filter and its `Not` twin is a `400 validation_error`. Saved views use the same schema, so views saved with single values keep their meaning. New filterable fields join this catalog in the contracts package and get both operators. `list_projects` returns `openTickets` and `overdueTickets` per project (unarchived tickets not done or cancelled; overdue means a due date in the past), `closedTickets` (unarchived tickets done or cancelled), `nextDueAt` (the soonest upcoming due date of an open ticket, or `null`), and the caller's effective `role` there (`reader`, `writer`, or `admin`; always `admin` for instance administrators), with `roleId` and `capabilities` as described in [Roles and permissions](#roles-and-permissions).
47
+ The property filters `status`, `priority`, `kind`, `difficulty`, `automation`, `label`, `assigneeId`, and `customField` (see [Custom fields](#custom-fields)) each take one value or a list and match tickets with any of them. `automation` is `ready` (`automationReady` is true and `holdReason` is null) or `manual` (everything else, the tickets agents won't take). Each has a `Not` twin (`statusNot`, `labelNot`, `assigneeIdNot`, and so on) that excludes the listed values; excluding a value keeps tickets where the field is unset. Over HTTP a list is a repeated query parameter, such as `?status=todo&status=in_progress&labelNot=blocked`; JSON and MCP callers pass an array. Filters combine with AND. A list holds at most 25 values, and a value in both a filter and its `Not` twin is a `400 validation_error`. Saved views use the same schema, so views saved with single values keep their meaning. New filterable fields join this catalog in the contracts package and get both operators. `list_projects` returns `openTickets` and `overdueTickets` per project (unarchived tickets not done or cancelled; overdue means a due date in the past), `closedTickets` (unarchived tickets done or cancelled), `nextDueAt` (the soonest upcoming due date of an open ticket, or `null`), and the caller's effective `role` there (`reader`, `writer`, or `admin`; always `admin` for instance administrators), with `roleId` and `capabilities` as described in [Roles and permissions](#roles-and-permissions).
44
48
 
45
49
  `list_my_work` (`GET /api/v1/my-work`) pages through the open, unarchived tickets assigned to the caller across every project they can read, optionally narrowed to one `workspaceId`. It accepts `q`, `sort`, and `order` like `list_tickets`, plus `limit` and `offset`; `total` counts every match so clients can fetch the remaining pages. Set `activity` (up to 50) to also receive that many of the most recent events from the same accessible projects, newest first. Revoked memberships and project-bound credentials narrow the tickets, totals, and activity alike.
46
50
 
47
51
  `search_tickets` (`GET /api/v1/tickets/search?q=...`) finds unarchived tickets by exact key or text across every project the caller can read, optionally limited to one `workspaceId`, returning each ticket with its project `id`, `key`, and `name`. Exact key matches sort first, then the most relevant matches (the external index's score, or on PostgreSQL title matches before matches only in other text), then the most recently updated.
48
52
 
49
- Ticket text queries in `list_tickets`, `list_my_work`, `search_tickets`, and `search_workspace` treat each double-quoted segment of `q` as an exact phrase: `q="live activity" acknowledge` needs the phrase `live activity` and the term `acknowledge`. Terms outside quotes keep fuzzy matching on the external index, and on PostgreSQL they must appear together as written. An unpaired quote is an ordinary character. Repeated phrases count once, and only the first 8 distinct phrases count.
53
+ Ticket text queries in `list_tickets`, `list_my_work`, `search_tickets`, and `search_workspace` treat each double-quoted segment of `q` as an exact phrase: `q="live activity" acknowledge` needs the phrase `live activity` and the term `acknowledge`. A phrase matches case-insensitively with its punctuation as written, its words separated only by whitespace, and no letter, digit, or underscore against either outer edge, so `q="IME"` does not match `time` and `q="exp-v0.52.6"` does not match `exp-v0.52.61`. Outside quotes, `search_tickets` and `search_workspace` match terms fuzzily on the external index, and on PostgreSQL the terms must appear together as written. An unpaired quote is an ordinary character. Repeated phrases count once, and only the first 8 distinct phrases count.
54
+
55
+ `list_tickets` and `list_my_work` filter rather than search, so their `q` keeps only tickets that contain its text on every engine: each phrase on its word boundaries and the free text as written in the key, title, description, acceptance criteria, labels, or scopes, as PostgreSQL matches for `search_tickets`, or the exact key or ID. `q="oxlint"` never returns a ticket that only says `lint`, and `total` counts only those tickets. With `q` and no `sort`, they order like `search_tickets`: exact key match, then relevance (the external index ranks when it is current), then most recently updated. An explicit `sort` applies as usual; without `q`, the default `sort` is `priority`.
50
56
 
51
- `list_tickets`, `search_tickets`, and `search_workspace` take `view`: `full` (the default) or `summary`. A summary row has only `id`, `key`, `number`, `projectId`, `title`, `status`, `stateId`, `kind`, `difficulty`, `priority`, `labels`, `scopes`, `archived`, `automationReady`, `updatedAt`, and `version`, so agents can scan many results without every description and acceptance criteria. In summary view each `search_tickets` result and `search_workspace` result also carries `snippet`: up to 240 characters of its body text (a ticket's description and acceptance criteria) around the first literal match of a phrase or term, or empty when only the title, key, or a fuzzy term matched; a `search_workspace` result's `ticket` is its summary row.
57
+ `list_tickets`, `list_epic_tickets`, `search_tickets`, and `search_workspace` take `view`: `full` (the default) or `summary`. A summary row has only `id`, `key`, `number`, `projectId`, `title`, `status`, `stateId`, `kind`, `difficulty`, `priority`, `labels`, `scopes`, `archived`, `automationReady`, `epicId`, `updatedAt`, and `version`, so agents can scan many results without every description and acceptance criteria. In summary view each `search_tickets` result and `search_workspace` result also carries `snippet`: up to 240 characters of its body text (a ticket's description and acceptance criteria) around the first literal match of a phrase or term, or empty when only the title, key, or a fuzzy term matched; a `search_workspace` result's `ticket` is its summary row.
52
58
 
53
59
  `search_workspace` (`GET /api/v1/search?q=...`) searches projects, unarchived tickets, comments on unarchived tickets, and activity across every project the caller can read, optionally within one `workspaceId`. Each result has a `type` (`project`, `ticket`, `comment`, or `activity`), a stable `id` (the project, ticket, or comment ID, or the event ID), its `project`, its `ticket` (`id`, `key`, `title`, `status`, or `null`), a display `title`, an `excerpt` around the first literal match, `actorId`, and `at`. Activity matches its event label, its ticket key, or the name of an active (not disabled) actor. Results order by exact key match, then type in the order above, then ticket relevance as in `search_tickets`, then most recent first. `limit` (1–50, default 20) and `offset` (up to 1000) page through them; `type` narrows the page to one kind while `counts` still reports every kind and `total` counts the page's kind. The property filters above restrict results to matching tickets and their comments and activity, and leave out projects. Ticket text uses the external index when one is configured and current (`engine: "index"`); otherwise PostgreSQL answers (`engine: "postgres"`), and `indexing: true` says an index is configured but behind or unavailable. Projects, comments, and activity always come from PostgreSQL, and every result is filtered by current project access before counts and excerpts are computed.
54
60
 
@@ -72,12 +78,34 @@ Within a workspace a name belongs to the workspace or to projects, never both, s
72
78
 
73
79
  Label colors are a preset name (`default`, `red`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`, `gray`) or a custom `#rrggbb` hex value. Saving a label with a hex value adds it to the site-wide custom palette; `list_label_colors` (`GET /api/v1/label-colors`) returns that palette newest first and `remove_label_color` (`POST /api/v1/label-colors/remove`) drops an entry without recoloring the labels that use it; only an instance administrator or the person who added the color may remove it, and the removal publishes `label.color_removed` to every project the caller can write. Saving a label with a color it already had does not re-add a removed color.
74
80
 
81
+ ### Epics
82
+
83
+ An Epic groups tickets from any project of one workspace. A ticket belongs to at most one, named by its nullable `epicId`, which full and summary ticket rows carry. `create_ticket`, `update_ticket`, and `bulk_update_tickets` set it, and `list_tickets` filters by it. A change is an ordinary `ticket.updated` in the ticket's project feed, with `epicId` in `changedFields` and the new value as `data.epicId`. Membership has no effect on readiness, eligibility, claims, or relations.
84
+
85
+ | Operation | Route | Scope | Notes |
86
+ | ------------------- | ------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
87
+ | `list_epics` | `GET /api/v1/workspaces/{workspaceId}/epics` | `read` | Pages Epics by title with `openTickets` and `closedTickets`, counted over unarchived tickets of projects the caller can read. `archived` takes `include` or `only`. |
88
+ | `get_epic` | `GET /api/v1/workspaces/{workspaceId}/epics/{epicId}` | `read` | One Epic with the same counts. |
89
+ | `create_epic` | `POST /api/v1/workspaces/{workspaceId}/epics` | `write` | `title` (1 to 200 characters), optional `description`, optional `requestId`. |
90
+ | `update_epic` | `PATCH /api/v1/workspaces/{workspaceId}/epics/{epicId}` | `write` | `expectedVersion` with at least one of `title`, `description`, `archived`. There is no deletion; archive instead. |
91
+ | `list_epic_tickets` | `GET .../epics/{epicId}/tickets` | `read` | Pages members across projects, oldest first, with `limit`, `offset`, `view`, `archived`, `total`, and `restrictedCount`. |
92
+
93
+ Any workspace member can read Epics; a project-bound credential or a direct project member outside the workspace cannot. Creating or updating one needs `write` scope and `ticket.write` in the workspace. Setting `epicId` needs write on the ticket's project: an Epic the caller cannot read, or one in another workspace, returns `404 not_found`, and an archived Epic refuses new members with `400 validation_error`. Clearing `epicId` or saving a ticket with the value it already holds needs neither, and archiving keeps existing members. `list_epic_tickets` returns only tickets of projects the caller can read and counts the rest in `restrictedCount`, never revealing their keys or titles.
94
+
95
+ `create_epic` and `update_epic` publish `epic.created` and `epic.updated` to each project of the workspace, in the same transaction, with `workspaceId`, `epicId`, and `version` (and `changedFields` on an update). The title stays out of the event, because a project's feed reaches people who cannot read Epics; read it with `get_epic`.
96
+
97
+ `relocate_ticket` keeps `epicId`. `duplicate_ticket` copies it while the caller can still add to that Epic, and otherwise starts the copy outside any Epic. `transfer_project` clears it on the project's tickets in its transaction, publishing a `ticket.updated` for each without notifying followers, and `get_project_impact` reports how many in `transfer.epicTickets`. `export_project` leaves Epics out and counts them in `completeness.omitted`. Public project and board responses omit `epicId`. `delete_workspace` removes the workspace's Epics, and `get_workspace_impact` counts them in `epics`.
98
+
75
99
  ### Ticket keys, prefix changes, and moves
76
100
 
77
101
  A key such as `ABC-12` is the project prefix and the ticket's number in that project. Numbers only grow, and every prefix a project has had stays reserved for it, so no key is ever issued twice. Every ID-or-key parameter (`projectId`, `ticketId`, neighbors, the event feed's `projectId`) resolves through one server-side resolver that accepts an ID, the current key, or any earlier key. The resolver only names a record: access, project-bound credentials, versions, and claims are checked exactly as for the ID, and an unknown or unreachable record reads as `404 not_found`.
78
102
 
79
103
  Prefixes were permanent before this release; they are now editable and permanently reserved instead. A project administrator changes one with `update_project` and a new `key` (same format as at creation) at the project's `expectedVersion`. Every ticket of the project takes the new prefix with its number unchanged, new tickets keep counting, and the old prefix and every key issued under it keep resolving, so existing links and stored keys keep working. Ticket versions do not change. A prefix that any other project has or had fails with `409 duplicate_prefix`; a project can return to one of its own. The write publishes `project.updated` with `key` and `previousKey`, on which a configured external search index reindexes every ticket of the project. Clients that cache keys should refresh project tickets on that event.
80
104
 
105
+ A project can link the git remotes of its repositories, so an agent can name the project with the output of `git remote get-url origin` instead of looking it up. `update_project` takes optional `repositories`, at most 20 remotes, and replaces the stored list; omitting it leaves the list unchanged. Each entry may be `https://[user[:password]@]host[:port]/path[.git]`, `ssh://[user@]host[:port]/path[.git]`, scp-style `user@host:path[.git]`, or the stored form, and is stored as `host[:port]/path`: lowercased, with credentials, the scheme's default port, a trailing `.git`, and a trailing slash removed, and repeats dropped. Anything else fails with `400 validation_error`. `repositories` on a project returns the stored form. The write needs project administration and publishes `project.updated`. Within a workspace a remote belongs to at most one unarchived project: linking one another unarchived project holds, restoring an archived project, or moving a project into a workspace where one of its remotes is taken fails with `409 duplicate_repository`, and `get_project_impact` for that move fails the same way. An archived project keeps its remotes, and another project can link them meanwhile.
106
+
107
+ Any `projectId` of an authenticated request, including the snapshot's and the event feed's, also accepts a linked remote in any of those spellings, up to 300 characters; percent-encode it in a URL path or query. Access, project-bound credentials, and permissions are checked as for the ID. A remote that links nothing the caller can reach reads as `404 not_found`, like an unknown one. When it links several projects the caller can reach, unarchived ones win over archived ones, and if more than one remains, as when two workspaces each link it, the request fails with `409 ambiguous_project`, whose `details.projects` lists their keys. Use the key or ID in that case. A write retried with the same `requestId` replays against the project it wrote to, even if the remote no longer names that project.
108
+
81
109
  `check_ticket_relocation` (`GET /api/v1/projects/{projectId}/tickets/{ticketId}/relocation?destinationProjectId=...`) previews a move to another project of the same workspace. It returns the `destination` (`id`, `key`, `name`), its workflow `states`, the default `stateId` (the destination's first state of the ticket's current status), and `blockers`. Each blocker has a `code` (`claimed`, `claim_conflict`, `parent`, `children`, `relations`, `assignee`, `custom_fields`, or `required_fields`), a `message`, and for hierarchy and relationship blockers the related `ticketId`. `claim_conflict` reaches only machine callers: a ticket that has been claimed keeps requiring ownership for worker writes after its claim is released or recovered with `reschedule`, so a worker cannot move it without a claim. A person can still move it, or return it to To do so a worker can claim it again. Kanvil never severs a claim, a parent link, children, or relationships to make a move succeed, never drops an assignee who cannot access the destination, and never discards custom field values, which belong to the source project's fields; resolve them first. A destination field that is required and has no default also blocks the move, since the moved ticket takes the destination's custom field defaults.
82
110
 
83
111
  `relocate_ticket` (`POST /api/v1/projects/{projectId}/tickets/{ticketId}/relocate`) moves the ticket with its `expectedVersion`, `destinationProjectId` (ID or key), and optionally `status`, `stateId`, or both, resolved in the destination like a ticket update's. Both projects require `write`, so a project-bound credential can move tickets only between projects it is bound to; a destination the caller cannot reach reads as `404 not_found`, an archived destination is a `409 project_archived`, and a different workspace or the same project is a `400 validation_error`. An archived source refuses the move like any other write. Any blocker fails the move with `409 relocation_blocked` and `details.blockers`. Machine callers follow the claim rules of `update_ticket`, reported as the `claimed` or `claim_conflict` blocker so the preview predicts the result. The ticket keeps its ID, content, comments, attachments, evidence, links, and followers, takes the destination's next number as its new key, bumps its version, and its labels register in the destination. Every key it had in the source, under each prefix the source has used, keeps resolving to it in the destination and through `resolve_ticket`, never in the source. Retrying with the same `requestId` returns the moved ticket only while the caller can still write to both projects. Files embedded before the move keep loading from their original `/attachments/{id}/content` address, authorized in the ticket's current project.
@@ -150,6 +178,16 @@ Three operations answer under `/api/public` without a session or credential, and
150
178
 
151
179
  **Anyone with the link can read every unarchived ticket's key, title, description, acceptance criteria, status, priority, kind, difficulty, labels, scopes, start and due dates, and relationships within the project.** Each response is the public projection of the member read it reuses: people (assignees, authors, members, emails), comments, activity, attachments, evidence, claims, readiness, hold reasons, and versions are never included, and archived tickets do not exist publicly: a `parentId` naming one reads as `null`. The project ID must be its UUID. An unknown, unshared, or revoked project and an unknown or archived ticket all return `404 not_found`. Each request reads the sharing flag and the rows in one snapshot, and every public response, errors included, carries `Cache-Control: no-store`, so turning sharing off takes effect on the next request. Sharing grants nothing elsewhere: the `/api/v1` operations, search, realtime, attachment and image routes, and MCP still require their own access, and the public routes accept only `GET`.
152
180
 
181
+ ### Public boards
182
+
183
+ A public board publishes a chosen subset of one project's tickets, with far fewer fields than a [public project link](#public-project-links). Which labels a board includes or excludes is entirely the administrator's configuration; no label name has special meaning. `get_project_board` (`GET /api/v1/projects/{projectId}/public-board`) returns `board`, the configuration or null, and `tickets`, the rows the board selects exactly as they would publish, even while it is disabled. `update_project_board` (`PATCH /api/v1/projects/{projectId}/public-board`) creates or replaces it with `enabled`, `title`, `labels`, `excludedLabels`, and `expectedVersion`: the version it read, or null when the project has no board yet. A stale version returns `409 version_conflict`, and enabling a board with no `labels` returns `400 validation_error`. Both operations need the `sharing.manage` permission and `admin` scope; an update publishes `public_board.updated` in the same transaction. Neither adds an anonymous route: this API serves no board without a session or credential, and a board does not open the project's `/api/public` routes.
184
+
185
+ A board publishes **only `project_id`, `key`, `title`, `kind`, `status`, `created_at`, and `updated_at`** of each selected ticket. Never its description, acceptance criteria, labels, scopes, priority, difficulty, assignee, ID, version, or workflow state name. A ticket is selected when it carries any of `labels` and none of `excludedLabels`, is not archived, has no hold reason, and its project is neither archived nor deleted. Holding a ticket also withdraws it, whether a person or an automation held it. Empty `labels` select nothing, the opposite of a calendar feed, and exclusion beats inclusion.
186
+
187
+ The database enforces this through three security-barrier views. `public_board_candidates` holds the selection, `public_board_tickets` narrows it to enabled boards, and `public_board_summaries` lists the `project_id` and `title` of enabled boards in live projects. A database role granted `SELECT` on only `public_board_tickets` and `public_board_summaries` reads the published rows and is refused every table beneath them.
188
+
189
+ Renaming or merging a label rewrites both `labels` and `excludedLabels` and increments the board's version. Deleting a label named in an enabled board's `excludedLabels` returns `409 label_in_use`, with the project's key in `details.projects`, and changes nothing, because stripping the label from its tickets would publish them; a workspace label deletion rolls back across every project. Remove the label from the board first. Deleting a label named in `labels` is allowed: the name stays and matches nothing. An archived project accepts board edits and publishes nothing until it is restored, and `delete_project` removes the board.
190
+
153
191
  ## Realtime
154
192
 
155
193
  `GET /api/v1/snapshot?projectId=<id>` returns a consistent project snapshot and event cursor. Connect to `/api/v1/events?projectId=<id>&cursor=<cursor>` to replay committed changes and continue receiving new ones.
@@ -287,6 +325,26 @@ While the connection is paused nothing syncs; issue changes reported meanwhile a
287
325
 
288
326
  The authenticated Streamable HTTP endpoint is `/mcp`. Tool inputs use the shared HTTP contracts, including expected versions, request IDs, and claim IDs. Successful structured results have a `result` property. A rejected call returns an error result (`isError: true`) whose text content is the same JSON envelope an HTTP response carries, `{"error": {"status", "code", "message", "details"}}`, so claim readiness reasons, lease maximums, version conflicts, and validation paths read the same on both transports. Unexpected failures return only `internal_error`.
289
327
 
328
+ `tools/list` returns one list for every client. The agent core marked `mcp: "core"` in the operation registry serves as its own tools: `list_my_work`, `list_eligible_tickets`, `search_tickets`, `get_ticket`, `create_ticket`, `update_ticket`, `claim_ticket`, `renew_claim`, `release_claim`, `add_comment`, `add_evidence`, and `resolve_ticket`. Tools advertise inputs only, with no `outputSchema`; results keep the `result` property. Three discovery tools reach every operation in the registry:
329
+
330
+ | Tool | Input | Behavior |
331
+ | ---------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
332
+ | `find_operation` | `query` or `name`, both optional | `query` returns `{operations: [{name, description, access}]}`, with `access` either `read` or `write`, for operations whose name and description contain every word, ignoring case. No `query` returns all. `name` returns that one operation with `method`, `scope`, and `inputSchema` (JSON Schema). |
333
+ | `read_operation` | `name`, `input` (defaults to `{}`) | Runs a `GET` operation. Hinted `readOnlyHint` and `idempotentHint`, so a client can approve reads automatically. A write operation returns `400 invalid_request` without running. |
334
+ | `run_operation` | `name`, `input` (defaults to `{}`) | Runs any other operation and counts against the OAuth write budget. Hinted `destructiveHint`. A read operation returns `400 invalid_request`. |
335
+
336
+ The runners call the same implementation as a direct tool, so results, error envelopes, scopes, and permissions match. An unknown name returns `404 not_found` from all three tools. A key the operation's schema does not name, in a core tool's arguments or a runner's `input`, returns `400 invalid_request`, naming the key and listing the valid ones, without running the operation or counting against the OAuth write budget.
337
+
338
+ Results from operations, through their own tools and the runners, are smaller than the same HTTP responses:
339
+
340
+ - **Summary rows by default.** An operation with a `view` input (`list_tickets`, `search_tickets`, `search_workspace`) defaults to `view: "summary"`. Pass `view: "full"` for whole tickets. HTTP keeps its default of `full`.
341
+ - **No echoed bodies.** When an operation takes `description` or `acceptanceCriteria` as input, at the top level or inside `changes` (`create_ticket`, `update_ticket`, `bulk_update_tickets`), tickets in its result leave that field out, even when this call did not change it. Read it with `get_ticket`. Other operations return tickets with their bodies, and a project keeps its `description`.
342
+ - **No empty keys.** Object keys whose value is `null`, `[]`, or `{}` are removed at any depth, so a missing key means null or empty. `false`, `0`, and `""` stay, and the top-level `result` property is always present. Results therefore do not validate against the HTTP output schemas. An `export_project` bundle read this way does not import; export over HTTP to get a file for `preview_import`.
343
+
344
+ `find_operation` is not an operation result: its input schemas keep their empty values, such as `default: null`.
345
+
346
+ The `initialize` response carries `instructions`, at most 600 characters of English for agents: what ticket keys look like, how to work a ticket through claim, status, comment, evidence, `done`, and release, that tickets with `automationReady: false` stay with people, how to use the discovery tools, and how replies are shortened.
347
+
290
348
  Clients supporting custom headers can connect using a scoped API credential. Interactive clients can discover OAuth authorization through the endpoint's protected-resource metadata, then sign in and consent through Kanvil. Resource scopes are `kanvil:read`, `kanvil:write`, `kanvil:claim`, and `kanvil:admin`. The web app supplies the login and consent pages. Configure OIDC callback URLs as `/api/auth/callback/oidc` and explicitly link an existing local account from Settings before using OIDC sign-in. Protocol compatibility is tested using the official SDK, including discovery, tool listing, and writes.
291
349
 
292
350
  ## Instance administration and profile photos
@@ -341,7 +399,7 @@ Each workspace's `ownerId` names its owner: an active administrator member who a
341
399
 
342
400
  ### Export and import
343
401
 
344
- `export_project` (`GET /api/v1/projects/{projectId}/export`, `read`) returns the project as a `kanvil.tickets` bundle, version 1, read in one snapshot. It holds the project's name, key, and description; its workflow states, custom fields (option IDs included), and labels with colors (workspace labels only when tickets use them); every ticket, archived ones included, with its content, properties, dates, labels, scopes, readiness, hold, state, assignee, parent, field values, creator, and creation time; relationships; comments; evidence; links; and the name and email of each person a record names. Records reference each other by `ref`, the source ID, never by key. `completeness.omitted` lists what the bundle leaves out with counts: attachments, activity history, claims, follows, and the project's settings (access, saved views, calendar feeds, destinations, public link, icon, and background). The schema is `ticketExportSchema` in `@kanvil/contracts`; import sources from other trackers translate into it.
402
+ `export_project` (`GET /api/v1/projects/{projectId}/export`, `read`) returns the project as a `kanvil.tickets` bundle, version 1, read in one snapshot. It holds the project's name, key, and description; its workflow states, custom fields (option IDs included), and labels with colors (workspace labels only when tickets use them); every ticket, archived ones included, with its content, properties, dates, labels, scopes, readiness, hold, state, assignee, parent, field values, creator, and creation time; relationships; comments; evidence; links; and the name and email of each person a record names. Records reference each other by `ref`, the source ID, never by key. `completeness.omitted` lists what the bundle leaves out with counts: attachments, activity history, claims, follows, and the project's settings (access, saved views, calendar feeds, destinations, public link, public board, icon, and background). The schema is `ticketExportSchema` in `@kanvil/contracts`; import sources from other trackers translate into it.
345
403
 
346
404
  An import creates a new project in a workspace, as the signed-in person; credentials get `403 session_required`. It runs in three steps:
347
405
 
package/docs/cli.md CHANGED
@@ -63,7 +63,7 @@ JSON
63
63
  kanvil task delete APP-12 --profile work --expected-version 7 --yes
64
64
  ```
65
65
 
66
- Use the version returned by a read or write. The CLI never fetches a newer version to bypass a conflict. Claims retain the server's readiness, ownership and lease rules; include `claimId` in an update when the operation requires it. Once a lease expires, renew and release refuse it; after confirming the old worker stopped, any writer with `claim` scope can run `claim recover` with the claim ID, a reason and `confirmedStopped: true`. Recovery never reschedules or reclaims on its own: pass `reschedule: true` with the ticket's current version to return it to To do, then read the ticket and `claim acquire` at the new version. When a ticket still requires ownership but no claim is left, for example after a release outside To do, omit the claim ID and pass `--expected-version` with the ticket's current version to lift the requirement, or to reschedule it with `reschedule: true`. `cancelActive: true` cancels a live claim only for project managers whose credential has `admin` scope. See [writes and ownership](api.md#writes-and-ownership) for the full policy. Deletion requires an explicit version and `--yes`; noninteractive invocations receive no confirmation prompt.
66
+ Use the version returned by a read or write. `claim acquire` may omit the version, since readiness still guards the claim, and prints the claim with the claimed `ticket`, whose `version` the next write needs. The CLI never fetches a newer version to bypass a conflict. Claims retain the server's readiness, ownership and lease rules; include `claimId` in an update when the operation requires it. Once a lease expires, renew and release refuse it; after confirming the old worker stopped, any writer with `claim` scope can run `claim recover` with the claim ID, a reason and `confirmedStopped: true`. Recovery never reschedules or reclaims on its own: pass `reschedule: true` with the ticket's current version to return it to To do, then read the ticket and `claim acquire` at the new version. When a ticket still requires ownership but no claim is left, for example after a release outside To do, omit the claim ID and pass `--expected-version` with the ticket's current version to lift the requirement, or to reschedule it with `reschedule: true`. `cancelActive: true` cancels a live claim only for project managers whose credential has `admin` scope. See [writes and ownership](api.md#writes-and-ownership) for the full policy. Deletion requires an explicit version and `--yes`; noninteractive invocations receive no confirmation prompt.
67
67
 
68
68
  The typed client retries a transport failure once for reads and receipted writes, using the identical request body, request ID and expected version. HTTP errors, including conflicts, are never retried. For a retry across CLI invocations, supply the original `--request-id UUID` (or `requestId` in JSON) and unchanged input. If both transport attempts fail, the outcome may be unknown: reconcile by reading or replaying that same request ID; do not submit a new write identity blindly. Requests have a finite transport timeout.
69
69
 
package/docs/mobile.md CHANGED
@@ -102,7 +102,7 @@ pnpm --filter @kanvil/mobile exec expo run:ios --configuration Release
102
102
 
103
103
  A Release app requires HTTPS and includes its JavaScript bundle, so Metro is unnecessary. Keep simulator ad-hoc signing enabled; removing signing also removes the entitlements SecureStore needs. For an isolated simulator backend using HTTPS, run `pnpm native:auth-fixture --https --simulator <udid>` and connect to `https://localhost:5188`. Only this test process and selected simulator trust the temporary certificate.
104
104
 
105
- Tagged releases publish iOS to TestFlight after CI passes, using the tag's version and a build number above every uploaded build for that version. See [releases](releasing.md). Local and EAS builds retain the versions in `app.config.ts`; increment `ios.buildNumber` and `android.versionCode` for subsequent builds distributed through those paths. Keep signing material outside the repository. Building a simulator app or preview APK does not publish to an app store or install on a physical device.
105
+ Tagged releases publish iOS to TestFlight after CI passes, using the tag's version and a build number above every uploaded iOS build of the app, whatever its version. See [releases](releasing.md). Local and EAS builds retain the versions in `app.config.ts`; increment `ios.buildNumber` and `android.versionCode` for subsequent builds distributed through those paths. Keep signing material outside the repository. Building a simulator app or preview APK does not publish to an app store or install on a physical device.
106
106
 
107
107
  After changing a native dependency patch, regenerate the native project and reinstall Pods. CocoaPods can retain a path to an older pnpm patch directory even when the dependency version is unchanged. A JavaScript export or incremental archive does not establish which native implementation was linked. Verify the archived executable before exporting, and repeat against the executable extracted from the final IPA:
108
108
 
package/docs/plan.md CHANGED
@@ -98,6 +98,7 @@ The installed `kanvil` executable provides namespaced task, project, workspace,
98
98
  - Start and due dates on tickets, with read-only Calendar (month) and Timeline (six-week) layouts of a project's filtered tickets, period navigation and Today, and project calendar feeds that administrators share and revoke as iCalendar links. Drag-to-reschedule and dependency editing on the timeline stay out of scope.
99
99
  - Ticket detail view containing requirements, relationships, editable links, discussion, attachments, activity, and supplied evidence.
100
100
  - Read-only public links that project administrators turn on and off under Project settings, showing anyone with the link the project's unarchived tickets and their requirements, properties, labels, dates, and relationships, without people, discussion, attachments, or coordination details.
101
+ - Public boards: a project administrator selects tickets by label and previews them, and security-barrier database views expose only each selected ticket's key, title, kind, status, and dates to a role that can read nothing else.
101
102
  - Ticket composer that can target any writable project and stay open to create several tickets in a row.
102
103
  - Custom fields edit through one property component in the composer, detail sidebar, list cells (shown from the Display menu), board cards, and selection toolbar, filter through the shared filter picker, and read on native ticket screens.
103
104
  - Comment mentions: typing `@` in a comment offers the project's members and inserts a `mention:` link that renders as "@Name"; mentioned members are notified even when they do not follow the ticket, and the Inbox has a Mentions view.
package/docs/releasing.md CHANGED
@@ -13,7 +13,7 @@ The public npm application package is `@maudecode/kanvil`; the workspace root an
13
13
 
14
14
  The workflow publishes the exact npm tarball installed by CI to the `latest` dist-tag, while separate amd64/arm64 publishers push the exact image archives tested by CI. Docker builds and restoration tests run on matching native runners; release publishing does not rebuild or emulate either image. A final Docker manifest job joins those immutable digests under `ghcr.io/maudecode/kanvil:X.Y.Z` and `:latest`. Stable and experimental builds share this publisher, `.github/workflows/publish-images.yml`. Stable releases only; prerelease versions are rejected. Do not tag an older version to move `latest` backward.
15
15
 
16
- If a publisher fails, rerun the failed release jobs. npm, Docker, and iOS publication are independent after validation. The npm step accepts an existing version only when its SHA-512 integrity matches the tested tarball; different bytes fail and require a new version. Publication is not atomic across npm, GHCR, and Apple. An iOS rerun selects a fresh build number for the same version. Never move a published tag or reuse a version for changed bytes. Avoid rerunning an old release after a newer release, since Docker's `latest` tag would move backward.
16
+ If a publisher fails, rerun the failed release jobs. npm, Docker, and iOS publication are independent after validation. The npm step accepts an existing version only when its SHA-512 integrity matches the tested tarball; different bytes fail and require a new version. Publication is not atomic across npm, GHCR, and Apple. iOS build numbers increase across every version of the app and never restart, so an upload can't match an earlier version's build; an iOS rerun selects the next number for the same version. Never move a published tag or reuse a version for changed bytes. Avoid rerunning an old release after a newer release, since Docker's `latest` tag would move backward.
17
17
 
18
18
  After npm publication, the Docker manifest job, and Apple processing all succeed, the workflow creates a GitHub Release for the existing tag with an automatically generated title and release notes. Only this final job has repository contents write permission. Reruns preserve an existing release and its notes.
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maudecode/kanvil",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "Open-source, self-hosted ticketing for agents and people.",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -1 +0,0 @@
1
- import{i as e}from"./rolldown-runtime-COiEfhsA.js";import{G as t,On as n,Rt as r,Vn as i,W as a,_n as o,an as s,bn as c,bt as l,gn as u,kn as d,mn as f,mt as p,r as m,vn as h}from"./default-DqsN66dq.js";import{$a as g,G as _,Gi as v,H as y,J as b,Ki as x,Lt as S,Pa as C,Q as w,Qi as T,U as E,X as D,Y as O,Z as k,Za as ee,Zi as A,at as j,ca as M,ct as te,da as N,dr as P,ea as F,et as I,it as L,ko as R,la as z,mi as B,oa as V,ot as H,q as U,rt as ne,sa as re,st as W,ua as ie,xt as G,yo as ae}from"./search-Cvetr1OK.js";import{g as oe}from"./item-HmFuRZet.js";import{l as se,r as ce}from"./project-lifecycle-COZb9FqM.js";import{r as le}from"./CompositeItem-Dotj8JZL.js";import{t as ue}from"./ellipsis-CvEPDi9V.js";import{t as de}from"./invitations-BTB8f2Xl.js";import{t as K}from"./forms-DbfnnPg_.js";import{A as fe,D as pe,E as me,M as he,O as ge,T as _e,fn as ve,i as q,k as ye,ln as be,n as J,nn as xe,r as Se,zt as Ce}from"./index-nltuyQtq.js";import{t as we}from"./switch-BtScTpP9.js";import{a as Te,i as Y,n as Ee,o as X,r as Z,t as De}from"./table-byFokzo-.js";import{t as Oe}from"./member-directory-BA4ZT1qU.js";var ke={name:`user-plus`,size:24,node:[[`path`,{d:`M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2`,key:`1yyitq`}],[`circle`,{cx:`9`,cy:`7`,r:`4`,key:`nufk8`}],[`line`,{x1:`19`,x2:`19`,y1:`8`,y2:`14`,key:`1bvyxn`}],[`line`,{x1:`22`,x2:`16`,y1:`11`,y2:`11`,key:`1shjgl`}]]};ke.node;var Ae=ee(ke),Q=e(i(),1),$=m(),je={get local(){return f(`admin.instance.provider.local`)},get s3(){return f(`admin.instance.provider.s3`)},get postgres(){return f(`admin.instance.provider.postgres`)},get elasticsearch(){return f(`admin.instance.provider.elasticsearch`)},rabbitmq:`RabbitMQ`};function Me({data:e,section:t,onConfigure:n}){if(t===`general`)return(0,$.jsxs)(`section`,{children:[(0,$.jsx)(H,{title:f(`admin.instance.heading`),action:(0,$.jsx)(D,{label:f(`admin.instance.edit`),onClick:n,children:(0,$.jsx)(le,{})})}),(0,$.jsx)(J,{title:f(`admin.instance.name`),description:e.name}),(0,$.jsx)(J,{title:f(`admin.instance.description`),description:e.description||f(`admin.instance.noDescription`)}),(0,$.jsx)(J,{title:f(`admin.instance.version`),children:(0,$.jsx)(`code`,{children:e.deployment.version})}),(0,$.jsx)(J,{title:f(`admin.instance.publicUrl`),children:(0,$.jsx)(_,{label:f(`admin.instance.publicUrl`),value:e.deployment.url,inline:!0})})]});let r=t===`storage`?e.storage:t===`search`?e.search:e.broker,i=f(t===`storage`?`admin.instance.section.storage`:t===`search`?`admin.instance.section.search`:`admin.instance.section.events`),a=r.source===`deployment`;return(0,$.jsxs)(`section`,{children:[(0,$.jsx)(H,{title:i,action:(0,$.jsx)(D,{label:f(a?`admin.instance.viewConfiguration`:`admin.instance.configure.${t}`),onClick:n,children:(0,$.jsx)(xe,{})})}),(0,$.jsx)(J,{title:f(`admin.instance.providerTitle`),description:je[r.provider],children:(0,$.jsx)(G,{variant:`secondary`,children:f(a?`admin.instance.deploymentManaged`:`admin.instance.instanceManaged`)})}),(0,$.jsx)(J,{title:f(t===`storage`?`admin.instance.transfers`:t===`search`?`admin.instance.indexStatus`:`admin.instance.connection`),description:t===`storage`?e.storage.provider===`s3`?f(`admin.instance.transfersS3`):f(`admin.instance.transfersLocal`):t===`search`?r.status===`builtin`?f(`admin.instance.searchBuiltIn`):f(`admin.instance.searchIndexed`):f(`admin.instance.eventsConnection`),children:(0,$.jsx)(G,{variant:r.status===`unavailable`?`destructive`:`outline`,children:r.status===`builtin`?f(`admin.instance.builtIn`):r.status===`ready`?f(t===`events`?`admin.instance.connected`:`admin.instance.upToDate`):f(`admin.instance.status.${r.status}`)})}),t===`storage`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(J,{title:f(`admin.instance.pendingTransfers`),description:e.storage.pending?f(`admin.instance.pendingRemain`):f(`admin.instance.noPending`),children:e.storage.pending}),e.storage.settings&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(J,{title:f(`admin.instance.bucket`),description:e.storage.settings.bucket}),(0,$.jsx)(_,{label:f(`admin.instance.storageEndpoint`),value:e.storage.settings.endpoint||`Amazon S3`})]}),(0,$.jsx)(J,{title:f(`admin.instance.backup`),description:e.locations.length?f(`admin.instance.backupBuckets`):f(`admin.instance.backupDatabase`)})]}),t===`search`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(J,{title:f(`admin.instance.fallback`),description:f(`admin.instance.fallbackDescription`)}),e.search.settings&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(_,{label:f(`admin.instance.searchEndpoint`),value:e.search.settings.url}),(0,$.jsx)(J,{title:f(`admin.instance.index`),description:e.search.settings.index})]})]}),t===`events`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(J,{title:f(`admin.instance.delivery`),description:f(`admin.instance.deliveryDescription`)}),e.broker.settings&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(_,{label:f(`admin.instance.brokerEndpoint`),value:e.broker.settings.endpoint}),(0,$.jsx)(J,{title:f(`admin.instance.exchange`),description:e.broker.settings.exchange})]})]}),(0,$.jsx)(b,{error:r.error?Error(r.error):void 0})]})}function Ne({children:e,badge:t}){return(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-4 border-b py-3`,children:[e,(0,$.jsx)(G,{variant:`outline`,children:t})]})}function Pe({userId:e,onClose:t,onChanged:n}){let{shown:r,settled:i}=_e(e,e=>e),[a,o]=(0,Q.useState)(!1);return(0,$.jsx)(me,{open:!!e,onOpenChange:e=>{!e&&!a&&t()},onOpenChangeComplete:i,children:(0,$.jsx)(pe,{className:`w-full! sm:max-w-lg!`,children:r&&(0,$.jsx)(Fe,{userId:r,pending:a,setPending:o,onClose:t,onChanged:n},r)})})}function Fe({userId:e,pending:n,setPending:i,onClose:o,onChanged:s}){let c=A(),[l,u]=(0,Q.useState)(null),[d,m]=(0,Q.useState)(),h=V(),_=F(`set_workspace_member`),y=g({queryKey:[`kanvil`,`user`,e],queryFn:()=>v.request(`/api/v1/admin/users/${encodeURIComponent(e)}`,z)}),T=y.data;async function D(e,t){i(!0),m(void 0);try{let n=await e();if(n&&typeof n==`object`&&`error`in n&&n.error){let e=n.error;throw Error(typeof e==`object`&&`message`in e?String(e.message):f(`admin.account.updateFailed`))}await s(),u(null),C.success(t)}catch(e){throw m(e),e}finally{i(!1)}}return(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(ye,{className:`shrink-0 border-b pr-14`,children:[(0,$.jsx)(fe,{className:`mb-0`,children:T?.name??f(`admin.account.details`)}),(0,$.jsx)(ge,{className:`break-all`,children:T?.email})]}),(0,$.jsxs)(`div`,{className:`@container min-h-0 overflow-y-auto px-6 pb-8`,children:[(0,$.jsx)(b,{error:y.error??(l?void 0:d)}),y.isPending?(0,$.jsx)(w,{}):T&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`div`,{className:`mb-7 flex flex-wrap items-center gap-4`,children:[(0,$.jsx)(re,{name:T.name,image:T.image,className:`size-16 text-xl`}),(0,$.jsxs)(`div`,{className:`min-w-40 flex-1`,children:[(0,$.jsx)(G,{variant:T.banned?`destructive`:`secondary`,children:T.banned?f(`admin.users.disabled`):f(`admin.users.active`)}),(0,$.jsx)(`p`,{className:`mt-2 mb-0 text-xs text-muted-foreground`,children:f(`admin.account.joined`,{date:a(T.createdAt)})})]}),(0,$.jsxs)(B,{className:`ml-auto`,onClick:()=>{m(void 0),u(`profile`)},children:[(0,$.jsx)(le,{}),f(`admin.account.edit`)]})]}),(0,$.jsx)(J,{title:f(`admin.account.instanceRole`),description:t(T.role)?f(`admin.account.adminDescription`):f(`admin.account.userDescription`),children:(0,$.jsx)(B,{onClick:()=>{m(void 0),u(`role`)},children:f(`admin.account.changeRole`)})}),(0,$.jsx)(J,{title:f(`admin.account.signInMethods`),description:se(T.providers),children:(0,$.jsxs)(B,{onClick:()=>{m(void 0),u(`password`)},children:[(0,$.jsx)(be,{}),f(`admin.account.setPassword`)]})}),(0,$.jsx)(J,{title:f(`admin.account.activeSessions`),description:f(`admin.account.sessions`,{count:T.sessionCount}),children:(0,$.jsx)(E,{label:f(`admin.account.revokeSessions`),description:f(`admin.account.revokeConfirm`,{name:T.name}),onConfirm:()=>D(()=>x.admin.revokeUserSessions({userId:T.id}),f(`admin.account.sessionsRevoked`))})}),T.id!==c.id&&(0,$.jsx)(J,{title:f(`admin.account.access`),description:T.banned?f(`admin.account.accessDisabled`):f(`admin.account.accessPreserved`),children:(0,$.jsx)(E,{label:T.banned?f(`admin.account.enable`):f(`admin.account.disable`),danger:!T.banned,description:T.banned?f(`admin.account.enableConfirm`,{name:T.name}):f(`admin.account.disableConfirm`,{name:T.name}),onConfirm:()=>D(()=>T.banned?x.admin.unbanUser({userId:T.id}):x.admin.banUser({userId:T.id,banReason:`Disabled by an instance administrator`}),T.banned?f(`admin.account.enabled`):f(`admin.account.disabledToast`))})}),T.id!==c.id&&(0,$.jsx)(ce,{account:T,onDeleted:async()=>{o(),await s()}}),(0,$.jsxs)(`section`,{className:`pt-7`,children:[(0,$.jsxs)(`div`,{className:`mb-3 flex items-center justify-between gap-3`,children:[(0,$.jsx)(`h3`,{className:`mb-0`,children:f(`admin.account.workspaces`)}),(0,$.jsxs)(B,{size:`sm`,onClick:()=>{m(void 0),u(`workspace`)},children:[(0,$.jsx)(oe,{}),f(`admin.account.addToWorkspace`)]})]}),T.workspaces.length?T.workspaces.map(e=>(0,$.jsx)(Ne,{badge:r[e.role],children:(0,$.jsx)(Ce,{to:`/workspaces/$workspaceId/settings`,params:{workspaceId:e.id},search:{section:`members`},onClick:o,className:`text-primary`,dir:`auto`,children:e.name})},e.id)):(0,$.jsx)(U,{title:f(`admin.account.noWorkspaces`),className:`py-5`})]}),!!T.projects.length&&(0,$.jsxs)(`section`,{className:`pt-7`,children:[(0,$.jsx)(`h3`,{children:f(`admin.account.projectAccess`)}),T.projects.map(e=>(0,$.jsx)(Ne,{badge:r[e.role],children:(0,$.jsx)(Ce,{to:`/projects/$projectId/settings`,params:{projectId:e.id},search:{section:`members`},onClick:o,className:`text-primary`,dir:`auto`,children:e.name})},e.id))]})]})]}),T&&(0,$.jsx)(I,{title:f(l===`profile`?`admin.account.edit`:l===`role`?`admin.account.changeRoleTitle`:l===`workspace`?`admin.account.addToWorkspace`:`admin.account.setPassword`),open:!!l,onOpenChange:e=>{e||u(null)},busy:n,children:(0,$.jsxs)(`form`,{onSubmit:e=>{e.preventDefault();let t=new FormData(e.currentTarget),n,r;if(l===`profile`)n=()=>x.admin.updateUser({userId:T.id,data:{name:K(t,`name`),email:K(t,`email`)}}),r=f(`admin.account.updated`);else if(l===`role`)n=()=>x.admin.setRole({userId:T.id,role:t.get(`role`)===`admin`?`admin`:`user`}),r=f(`admin.account.roleUpdated`);else if(l===`workspace`)n=()=>_.mutateAsync({workspaceId:K(t,`workspace`),email:T.email,role:K(t,`role`)===`reader`?`reader`:K(t,`role`)===`admin`?`admin`:`writer`}),r=f(`admin.account.workspaceAdded`);else{let e=K(t,`password`);if(e!==K(t,`confirm`)){m(Error(f(`admin.account.passwordMismatch`)));return}n=()=>v.request(`/api/v1/admin/users/${encodeURIComponent(T.id)}/password`,p,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({password:e})}),r=f(`admin.account.passwordSet`)}D(n,r).catch(()=>{})},children:[l===`profile`?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(k,{label:f(`admin.account.name`),name:`name`,defaultValue:T.name,required:!0,maxLength:120,autoFocus:!0}),(0,$.jsx)(k,{label:f(`admin.account.email`),name:`email`,type:`email`,defaultValue:T.email,required:!0})]}):l===`role`?(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(W,{label:f(`admin.account.instanceRole`),name:`role`,defaultValue:t(T.role)?`admin`:`user`,children:[(0,$.jsx)(S,{value:`user`,children:f(`admin.account.user`)}),(0,$.jsx)(S,{value:`admin`,children:f(`admin.account.administrator`)})]}),(0,$.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:f(`admin.account.adminsNote`)})]}):l===`workspace`?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(W,{label:f(`admin.account.workspace`),name:`workspace`,required:!0,children:h.data?.filter(e=>!T.workspaces.some(t=>t.id===e.id)).map(e=>(0,$.jsx)(S,{value:e.id,children:e.name},e.id))}),(0,$.jsxs)(W,{label:f(`admin.account.workspaceRole`),name:`role`,defaultValue:`writer`,children:[(0,$.jsx)(S,{value:`reader`,children:r.reader}),(0,$.jsx)(S,{value:`writer`,children:r.writer}),(0,$.jsx)(S,{value:`admin`,children:r.admin})]})]}):(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(k,{label:f(`admin.account.newPassword`),name:`password`,type:`password`,autoComplete:`new-password`,required:!0,minLength:12,maxLength:128}),(0,$.jsx)(k,{label:f(`admin.account.confirmPassword`),name:`confirm`,type:`password`,autoComplete:`new-password`,required:!0,minLength:12,maxLength:128}),(0,$.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:f(`admin.account.passwordNote`)})]}),(0,$.jsx)(O,{error:d,pending:n,submitLabel:f(`admin.account.save`),pendingLabel:f(`common.state.saving`),onCancel:()=>u(null)})]},l)})]})}function Ie({onCreated:e,onCancel:t,pending:n,setPending:r}){let i=R(),[a,o]=(0,Q.useState)();async function s(t){t.preventDefault();let n=t.currentTarget,a=new FormData(n);r(!0),o(void 0);try{let t=await x.admin.createUser({name:K(a,`name`),email:K(a,`email`),password:K(a,`password`),role:a.get(`role`)===`admin`?`admin`:`user`});if(t.error)throw Error(t.error.message);await i.invalidateQueries({queryKey:[`kanvil`,`users`]}),e(t.data.user.id,t.data.user.email)}catch(e){o(e)}finally{r(!1)}}return(0,$.jsx)(`section`,{children:(0,$.jsxs)(`form`,{onSubmit:e=>{s(e)},children:[(0,$.jsx)(k,{label:f(`admin.users.name`),name:`name`,required:!0,maxLength:120,autoFocus:!0}),(0,$.jsx)(k,{label:f(`admin.users.email`),name:`email`,type:`email`,required:!0,autoComplete:`off`}),(0,$.jsx)(k,{label:f(`admin.users.initialPassword`),name:`password`,type:`password`,autoComplete:`new-password`,minLength:12,maxLength:128,required:!0}),(0,$.jsxs)(W,{label:f(`admin.users.instanceRole`),name:`role`,children:[(0,$.jsx)(S,{value:`user`,children:f(`admin.users.user`)}),(0,$.jsx)(S,{value:`admin`,children:f(`admin.users.administrator`)})]}),(0,$.jsx)(O,{error:a,pending:n,submitLabel:f(`admin.users.create`),pendingLabel:f(`admin.users.creating`),onCancel:t})]})})}function Le(){let e=R(),n=x.useSession(),[r,i]=(0,Q.useState)(!1),[a,o]=(0,Q.useState)(!1),[s,c]=(0,Q.useState)(``),[l,u]=(0,Q.useState)(`all`),[d,p]=(0,Q.useState)(`all`),[m,h]=(0,Q.useState)(0),[_,y]=(0,Q.useState)(null),T=g({queryKey:[`kanvil`,`users`,s,l,d,m],queryFn:()=>v.request(`/api/v1/admin/users?${new URLSearchParams({q:s,role:l,status:d,offset:String(m),limit:`25`})}`,ie),placeholderData:ae});async function E(){await Promise.all([e.invalidateQueries({queryKey:[`kanvil`]}),n.refetch()])}return(0,$.jsxs)(`section`,{children:[(0,$.jsx)(H,{title:f(`admin.users.title`),action:(0,$.jsxs)(`div`,{className:`flex min-w-0 flex-1 items-center justify-end gap-2`,children:[(0,$.jsx)(j,{label:f(`admin.users.search`),placeholder:f(`admin.users.searchPlaceholder`),value:s,onChange:e=>{c(e),h(0)}}),(0,$.jsx)(D,{label:f(`admin.users.new`),variant:`default`,onClick:()=>i(!0),children:(0,$.jsx)(Ae,{})})]})}),(0,$.jsxs)(`div`,{className:`mb-5 flex flex-wrap items-center gap-2`,children:[(0,$.jsxs)(W,{label:f(`admin.users.roleFilter`),labelHidden:!0,className:`mb-0 w-40`,value:l,onValueChange:e=>{e&&u(e),h(0)},children:[(0,$.jsx)(S,{value:`all`,children:f(`admin.users.allRoles`)}),(0,$.jsx)(S,{value:`admin`,children:f(`admin.users.administrators`)}),(0,$.jsx)(S,{value:`user`,children:f(`admin.users.users`)})]}),(0,$.jsxs)(W,{label:f(`admin.users.statusFilter`),labelHidden:!0,className:`mb-0 w-40`,value:d,onValueChange:e=>{e&&p(e),h(0)},children:[(0,$.jsx)(S,{value:`all`,children:f(`admin.users.allAccounts`)}),(0,$.jsx)(S,{value:`active`,children:f(`admin.users.active`)}),(0,$.jsx)(S,{value:`disabled`,children:f(`admin.users.disabled`)})]})]}),(0,$.jsx)(b,{error:T.error}),T.isPending?(0,$.jsx)(w,{}):T.data?.users.length?(0,$.jsx)(`div`,{"aria-busy":T.isFetching,children:(0,$.jsxs)(De,{mobileLayout:`rows`,"aria-label":f(`admin.users.title`),children:[(0,$.jsx)(Te,{children:(0,$.jsxs)(X,{children:[(0,$.jsx)(Y,{children:f(`admin.users.user`)}),(0,$.jsx)(Y,{children:f(`admin.users.role`)}),(0,$.jsx)(Y,{children:f(`admin.users.status`)}),(0,$.jsx)(Y,{children:f(`admin.users.joined`)}),(0,$.jsx)(Y,{children:(0,$.jsx)(`span`,{className:`sr-only`,children:f(`admin.users.actions`)})})]})}),(0,$.jsx)(Ee,{children:T.data.users.map(e=>(0,$.jsx)(he,{name:e.name,email:e.email,onOpen:()=>y(e.id),children:(0,$.jsxs)(X,{children:[(0,$.jsx)(Z,{className:`whitespace-normal`,children:(0,$.jsx)(B,{variant:`ghost`,className:`h-auto max-w-full justify-start p-0 text-left hover:bg-transparent`,onClick:()=>y(e.id),children:(0,$.jsx)(M,{name:e.name,email:e.email,image:e.image})})}),(0,$.jsx)(Z,{"data-label":f(`admin.users.role`),children:(0,$.jsx)(G,{variant:`secondary`,children:t(e.role)?f(`admin.users.administrator`):f(`admin.users.user`)})}),(0,$.jsx)(Z,{"data-label":f(`admin.users.status`),children:(0,$.jsx)(G,{variant:e.banned?`destructive`:`outline`,children:e.banned?f(`admin.users.disabled`):f(`admin.users.active`)})}),(0,$.jsx)(Z,{"data-label":f(`admin.users.joined`),className:`text-xs text-muted-foreground`,children:(0,$.jsx)(L,{value:e.createdAt})}),(0,$.jsx)(Z,{children:(0,$.jsx)(B,{variant:`ghost`,size:`icon`,"aria-label":f(`admin.users.manage`,{name:e.name}),onClick:()=>y(e.id),children:(0,$.jsx)(ue,{})})})]})},e.id))})]})}):(0,$.jsx)(U,{title:f(s||l!==`all`||d!==`all`?`admin.users.noMatches`:`admin.users.empty`),className:`py-12`}),(0,$.jsx)(ne,{noun:f(`admin.users.pagerNoun`),offset:m,total:T.data?.total??0,size:25,onChange:h}),(0,$.jsx)(I,{title:f(`admin.users.create`),open:r,onOpenChange:i,busy:a,children:(0,$.jsx)(Ie,{pending:a,setPending:o,onCancel:()=>i(!1),onCreated:(e,t)=>{i(!1),c(``),u(`all`),p(`all`),h(0),y(e),C.success(f(`admin.users.created`,{email:t}))}})}),(0,$.jsx)(Pe,{userId:_,onClose:()=>y(null),onChanged:E})]})}var Re=25;function ze(){let[e,t]=(0,Q.useState)(``),[n,r]=(0,Q.useState)(0),[i,a]=(0,Q.useState)(null),o=g({queryKey:[`kanvil`,`admin-workspaces`,e,n],queryFn:()=>v.request(`/api/v1/admin/workspaces?${new URLSearchParams({q:e,offset:String(n),limit:String(Re)})}`,N),placeholderData:ae});return(0,$.jsxs)(`section`,{children:[(0,$.jsx)(H,{title:f(`settings.workspaces.title`),action:(0,$.jsx)(`div`,{className:`flex min-w-0 flex-1 items-center justify-end gap-2`,children:(0,$.jsx)(j,{label:f(`settings.workspaces.search`),placeholder:f(`settings.workspaces.searchPlaceholder`),value:e,onChange:e=>{t(e),r(0)}})})}),(0,$.jsx)(b,{error:o.error}),o.isPending?(0,$.jsx)(w,{}):o.data?.workspaces.length?(0,$.jsx)(`div`,{"aria-busy":o.isFetching,children:(0,$.jsxs)(De,{mobileLayout:`rows`,"aria-label":f(`settings.workspaces.title`),children:[(0,$.jsx)(Te,{children:(0,$.jsxs)(X,{children:[(0,$.jsx)(Y,{children:f(`settings.workspaces.workspace`)}),(0,$.jsx)(Y,{children:f(`settings.workspaces.administrators`)}),(0,$.jsx)(Y,{children:f(`settings.workspaces.members`)}),(0,$.jsx)(Y,{children:f(`settings.workspaces.projects`)}),(0,$.jsx)(Y,{children:f(`settings.workspaces.created`)}),(0,$.jsx)(Y,{children:(0,$.jsx)(`span`,{className:`sr-only`,children:f(`settings.workspaces.actions`)})})]})}),(0,$.jsx)(Ee,{children:o.data.workspaces.map(e=>(0,$.jsxs)(X,{children:[(0,$.jsx)(Z,{className:`whitespace-normal`,children:(0,$.jsxs)(B,{variant:`ghost`,className:`h-auto max-w-full flex-col items-start gap-0.5 p-0 text-left hover:bg-transparent`,onClick:()=>a(e),children:[(0,$.jsx)(`span`,{dir:`auto`,className:`font-medium wrap-anywhere whitespace-normal`,children:e.name}),(0,$.jsx)(`span`,{className:`font-mono text-xs font-normal text-muted-foreground`,children:e.slug})]})}),(0,$.jsx)(Z,{"data-label":f(`settings.workspaces.administrators`),className:`text-xs wrap-anywhere whitespace-normal`,children:e.administrators.length?s(e.administrators.map(e=>e.name)):(0,$.jsx)(G,{variant:`destructive`,children:f(`settings.workspaces.noAdministrator`)})}),(0,$.jsx)(Z,{"data-label":f(`settings.workspaces.members`),className:`tabular-nums`,children:e.memberCount}),(0,$.jsx)(Z,{"data-label":f(`settings.workspaces.projects`),className:`tabular-nums`,children:e.projectCount}),(0,$.jsx)(Z,{"data-label":f(`settings.workspaces.created`),className:`text-xs text-muted-foreground`,children:(0,$.jsx)(L,{value:e.createdAt})}),(0,$.jsx)(Z,{children:(0,$.jsx)(B,{variant:`ghost`,size:`icon`,"aria-label":f(`settings.workspaces.manage`,{name:e.name}),onClick:()=>a(e),children:(0,$.jsx)(ue,{})})})]},e.id))})]})}):(0,$.jsx)(U,{title:f(e?`settings.workspaces.noMatches`:`settings.workspaces.empty`),className:`py-12`}),(0,$.jsx)(ne,{noun:f(`settings.workspaces.pagerNoun`),offset:n,total:o.data?.total??0,size:Re,onChange:r}),(0,$.jsx)(Be,{workspace:i,onClose:()=>a(null)})]})}function Be({workspace:e,onClose:t}){let{shown:n,settled:r}=_e(e,e=>e.id),i=n?.id??``,a=g({...v.query(`list_workspace_members`,{workspaceId:i}),enabled:!!n}),o=g({...v.query(`list_projects`,{workspaceId:i,archived:`include`}),enabled:!!n}),s=g({...v.query(`list_workspace_roles`,{workspaceId:i}),enabled:!!n}),c=F(`set_workspace_member`);return(0,$.jsx)(me,{open:!!e,onOpenChange:e=>{!e&&!c.isPending&&t()},onOpenChangeComplete:r,children:(0,$.jsxs)(pe,{className:`w-full! sm:max-w-lg!`,children:[(0,$.jsxs)(ye,{className:`shrink-0 border-b pr-14`,children:[(0,$.jsx)(fe,{className:`mb-0`,children:n?.name??f(`settings.workspaces.workspace`)}),(0,$.jsx)(ge,{className:`font-mono`,children:n?.slug})]}),(0,$.jsxs)(`div`,{className:`@container min-h-0 overflow-y-auto px-6 pb-8`,children:[(0,$.jsx)(Oe,{people:a.data??[],pending:c.isPending,loading:a.isPending,error:a.error,canAdmin:!0,ownerId:n?.ownerId,scope:`workspace`,roles:s.data,authority:{admin:!0,capabilities:l},onChange:(e,t)=>c.mutateAsync({workspaceId:i,email:e,role:t})}),n&&(0,$.jsx)(de,{workspaceId:n.id}),(0,$.jsxs)(`section`,{className:`pt-7`,children:[(0,$.jsx)(`h3`,{children:f(`settings.workspaces.projects`)}),(0,$.jsx)(b,{error:o.error}),o.isPending?(0,$.jsx)(w,{}):o.data?.length?o.data.map(e=>(0,$.jsx)(Ne,{badge:e.visibility===`private`?f(`settings.workspaces.private`):f(`settings.members.workspaceAccess`),children:(0,$.jsx)(Ce,{to:`/projects/$projectId/settings`,params:{projectId:e.id},search:{section:`members`},onClick:t,className:`text-primary`,children:e.name})},e.id)):(0,$.jsx)(U,{title:f(`settings.workspaces.noProjects`),className:`py-5`})]})]},n?.id)]})})}var Ve={queryKey:[`kanvil`,`instance`],queryFn:()=>v.request(`/api/v1/admin/instance`,u)};function He({section:e}){let t=g({...Ve,refetchOnMount:`always`,refetchInterval:[`search`,`storage`,`events`].includes(e)?5e3:!1});return t.data&&t.isFetchedAfterMount?(0,$.jsx)(Ue,{data:t.data,section:e},e):t.isPending||t.isFetching?(0,$.jsx)(w,{}):(0,$.jsx)(b,{error:t.error})}function Ue({data:e,section:t}){let r=R(),i=T(),[a,l]=(0,Q.useState)(()=>o(e)),[u,m]=(0,Q.useState)(!1),[g,x]=(0,Q.useState)(),[w,D]=(0,Q.useState)(``),[ee,A]=(0,Q.useState)(!1),j=e.storage.source===`deployment`,M=e.search.source===`deployment`,N=e.broker.source===`deployment`,F=t===`storage`?j:t===`search`?M:t===`events`&&N,L=e=>l(t=>({...t,s3:{...t.s3,...e}})),z=e=>l(t=>({...t,elastic:{...t.elastic,...e}}));async function V(e,t,n=!1){x(void 0),D(``),m(!0);try{await e(),n?C.success(t,{id:`instance-settings`}):D(t)}catch(e){x(e)}finally{m(!1)}}async function U(){let e=h(a,t===`authentication`);await v.request(`/api/v1/admin/instance`,p,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)}),l(e=>({...e,expectedVersion:e.expectedVersion+1,s3:{...e.s3,secretAccessKey:``},elastic:{...e.elastic,apiKey:``,password:``},rabbit:{...e.rabbit,url:``}})),await r.invalidateQueries({queryKey:[`kanvil`,`instance`]}),await r.invalidateQueries({queryKey:[`info`]}),A(!1)}return(0,$.jsxs)(Se,{title:f(`admin.instance.title`),section:t,children:[[`general`,`storage`,`search`,`events`].map(t=>(0,$.jsx)(q,{value:t,children:(0,$.jsx)(Me,{data:e,section:t,onConfigure:()=>{l(o(e)),x(void 0),D(``),A(!0)}})},t)),!ee&&t!==`authentication`&&(0,$.jsx)(b,{error:g}),(0,$.jsx)(I,{title:f(t===`general`?`admin.instance.edit`:t===`storage`?`admin.configure.storageTitle`:t===`search`?`admin.configure.searchTitle`:`admin.configure.eventsTitle`),open:ee,onOpenChange:A,busy:u,children:(0,$.jsxs)(`form`,{className:`[&_section]:py-0 [&>[data-slot=field-set]>section:first-child]:pt-0`,onSubmit:e=>{e.preventDefault(),F||V(U,f(`admin.configure.saved`),!0)},children:[(0,$.jsxs)(P,{disabled:u,className:`gap-0`,children:[t===`general`&&(0,$.jsx)($.Fragment,{children:(0,$.jsxs)(`section`,{children:[(0,$.jsx)(`h2`,{children:f(`admin.instance.heading`)}),(0,$.jsx)(k,{label:f(`admin.configure.instanceName`),required:!0,maxLength:100,value:a.name,onChange:e=>l({...a,name:e.target.value})}),(0,$.jsx)(te,{label:f(`admin.configure.instanceDescription`),maxLength:500,value:a.description,onChange:e=>l({...a,description:e.target.value})}),(0,$.jsx)(_,{label:f(`admin.instance.publicUrl`),value:e.deployment.url}),(0,$.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:f(`admin.configure.publicUrlSource`)})]})}),t===`storage`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`section`,{children:[(0,$.jsxs)(`div`,{className:`mb-5 flex items-center gap-3`,children:[(0,$.jsx)(`h2`,{className:`mb-0`,children:f(`admin.instance.section.storage`)}),(0,$.jsx)(G,{variant:`secondary`,children:f(j?`admin.instance.deploymentManaged`:`admin.instance.instanceManaged`)})]}),(0,$.jsx)(`p`,{children:f(`admin.configure.storageIntro`)}),(0,$.jsxs)(W,{label:f(`admin.configure.storageProvider`),disabled:j,value:a.storageProvider,onValueChange:e=>l({...a,storageProvider:e===`s3`?`s3`:`local`}),children:[(0,$.jsx)(S,{value:`local`,children:f(`admin.instance.provider.local`)}),(0,$.jsx)(S,{value:`s3`,children:f(`admin.instance.provider.s3`)})]}),a.storageProvider===`local`?(0,$.jsx)(`p`,{children:f(`admin.configure.localDescription`)}):(0,$.jsxs)(P,{disabled:j,className:`gap-0`,children:[(0,$.jsx)(k,{label:f(`admin.configure.s3Endpoint`),type:`url`,placeholder:`https://s3.example.com`,value:a.s3.endpoint,onChange:e=>L({endpoint:e.target.value})}),(0,$.jsx)(`p`,{className:`-mt-2 text-xs`,children:f(`admin.configure.s3EndpointHint`)}),(0,$.jsxs)(`div`,{className:`grid grid-cols-1 gap-x-5 sm:grid-cols-2`,children:[(0,$.jsx)(k,{label:f(`admin.instance.bucket`),value:a.s3.bucket,required:!0,onChange:e=>L({bucket:e.target.value})}),(0,$.jsx)(k,{label:f(`admin.configure.region`),value:a.s3.region,required:!0,onChange:e=>L({region:e.target.value})})]}),(0,$.jsx)(k,{label:f(`admin.configure.prefix`),value:a.s3.prefix,onChange:e=>L({prefix:e.target.value})}),(0,$.jsx)(k,{label:f(`admin.configure.accessKeyId`),autoComplete:`off`,value:a.s3.accessKeyId,onChange:e=>L({accessKeyId:e.target.value})}),(0,$.jsx)(k,{label:f(`admin.configure.secretAccessKey`),type:`password`,autoComplete:`new-password`,value:a.s3.secretAccessKey,placeholder:e.storage.secretConfigured?f(`admin.configure.savedSecret`):f(`admin.configure.deploymentCredentials`),onChange:e=>L({secretAccessKey:e.target.value})}),(0,$.jsx)(y,{label:f(`admin.configure.pathStyle`),checked:a.s3.forcePathStyle,onCheckedChange:e=>L({forcePathStyle:e})})]}),a.storageProvider===`s3`&&(0,$.jsx)(B,{disabled:u,onClick:()=>{V(()=>v.request(`/api/v1/admin/storage/test`,p,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(a.s3)}),f(`admin.configure.storageVerified`))},children:f(`admin.configure.testStorage`)}),(0,$.jsx)(`p`,{className:`mt-4 text-xs`,role:`status`,children:e.storage.pending?f(`admin.configure.filesWaiting`,{count:e.storage.pending}):f(`admin.configure.transfersComplete`)}),e.storage.error&&(0,$.jsx)(b,{error:Error(e.storage.error)}),j&&(0,$.jsx)(`p`,{className:`mt-4 text-xs`,children:f(`admin.configure.storageDeployment`)})]}),e.locations.length>0&&(0,$.jsxs)(`section`,{children:[(0,$.jsx)(`h2`,{children:f(`admin.configure.locations`)}),e.locations.map(e=>(0,$.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3 border-b py-2.5 text-xs wrap-anywhere [&>:first-child]:min-w-24 [&>:first-child]:flex-1`,children:[(0,$.jsxs)(`span`,{className:`grid gap-1`,children:[(0,$.jsx)(`strong`,{children:e.bucket}),(0,$.jsxs)(`span`,{className:`text-muted-foreground`,children:[e.endpoint||`Amazon S3`,` / `,e.prefix]})]}),(0,$.jsx)(`span`,{children:f(`admin.configure.locationFiles`,{count:e.objects})})]},e.id)),(0,$.jsx)(`div`,{className:`mt-4`,children:(0,$.jsx)(E,{label:f(`admin.configure.cleanUp`),description:f(`admin.configure.cleanUpConfirm`),onConfirm:async()=>{await v.request(`/api/v1/admin/storage/cleanup`,d({removed:n()}),{method:`POST`}),await r.invalidateQueries({queryKey:[`kanvil`,`instance`]})}})}),(0,$.jsx)(`p`,{className:`mt-3 text-xs`,children:f(`admin.configure.cleanUpNote`)})]})]}),t===`search`&&(0,$.jsx)($.Fragment,{children:(0,$.jsxs)(`section`,{children:[(0,$.jsxs)(`div`,{className:`mb-5 flex items-center gap-3`,children:[(0,$.jsx)(`h2`,{className:`mb-0`,children:f(`admin.instance.section.search`)}),(0,$.jsx)(G,{variant:`secondary`,children:e.search.status===`builtin`?`PostgreSQL`:f(`admin.instance.status.${e.search.status}`)})]}),(0,$.jsxs)(W,{label:f(`admin.configure.searchProvider`),disabled:M,value:a.searchProvider,onValueChange:e=>l({...a,searchProvider:e===`elasticsearch`?`elasticsearch`:`postgres`}),children:[(0,$.jsx)(S,{value:`postgres`,children:f(`admin.configure.searchBuiltIn`)}),(0,$.jsx)(S,{value:`elasticsearch`,children:f(`admin.instance.provider.elasticsearch`)})]}),a.searchProvider===`elasticsearch`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(P,{disabled:M,className:`gap-0`,children:[(0,$.jsx)(k,{label:f(`admin.instance.searchEndpoint`),type:`url`,required:!0,value:a.elastic.url,onChange:e=>z({url:e.target.value})}),(0,$.jsx)(k,{label:f(`admin.configure.indexName`),required:!0,value:a.elastic.index,onChange:e=>z({index:e.target.value})}),(0,$.jsxs)(W,{label:f(`admin.configure.searchAuthentication`),value:a.elastic.auth,onValueChange:e=>z({auth:c.parse(e)}),children:[(0,$.jsx)(S,{value:`none`,children:f(`admin.configure.noAuthentication`)}),(0,$.jsx)(S,{value:`apiKey`,children:f(`admin.configure.apiKey`)}),(0,$.jsx)(S,{value:`basic`,children:f(`admin.configure.basic`)})]}),a.elastic.auth===`apiKey`&&(0,$.jsx)(k,{label:f(`admin.configure.searchApiKey`),type:`password`,autoComplete:`new-password`,placeholder:e.search.secretConfigured?f(`admin.configure.savedSecret`):f(`admin.configure.optional`),value:a.elastic.apiKey,onChange:e=>z({apiKey:e.target.value})}),a.elastic.auth===`basic`&&(0,$.jsxs)(`div`,{className:`grid grid-cols-1 gap-x-5 sm:grid-cols-2`,children:[(0,$.jsx)(k,{label:f(`admin.configure.searchUsername`),autoComplete:`off`,value:a.elastic.username,onChange:e=>z({username:e.target.value})}),(0,$.jsx)(k,{label:f(`admin.configure.searchPassword`),type:`password`,autoComplete:`new-password`,value:a.elastic.password,onChange:e=>z({password:e.target.value})})]})]}),(0,$.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,$.jsx)(B,{onClick:()=>{V(()=>v.request(`/api/v1/admin/search/test`,p,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(a.elastic)}),f(`admin.configure.searchVerified`))},children:f(`admin.configure.testSearch`)}),e.search.provider===`elasticsearch`&&(0,$.jsx)(B,{onClick:()=>{V(()=>v.request(`/api/v1/admin/search/reindex`,p,{method:`POST`}),f(`admin.configure.rebuildQueued`))},children:f(`admin.configure.rebuild`)})]})]}),(0,$.jsx)(`p`,{className:`mt-5`,children:f(`admin.configure.searchNote`)}),e.search.error&&(0,$.jsx)(b,{error:Error(e.search.error)})]})}),t===`events`&&(0,$.jsx)($.Fragment,{children:(0,$.jsxs)(`section`,{children:[(0,$.jsxs)(`div`,{className:`mb-5 flex items-center gap-3`,children:[(0,$.jsx)(`h2`,{className:`mb-0`,children:f(`admin.instance.section.events`)}),(0,$.jsx)(G,{variant:`secondary`,children:e.broker.status===`builtin`?`PostgreSQL`:f(`admin.instance.status.${e.broker.status}`)})]}),(0,$.jsxs)(W,{label:f(`admin.configure.brokerProvider`),disabled:N,value:a.brokerProvider,onValueChange:e=>l({...a,brokerProvider:e===`rabbitmq`?`rabbitmq`:`postgres`}),children:[(0,$.jsx)(S,{value:`postgres`,children:f(`admin.configure.brokerBuiltIn`)}),(0,$.jsx)(S,{value:`rabbitmq`,children:`RabbitMQ`})]}),a.brokerProvider===`rabbitmq`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(P,{disabled:N,className:`gap-0`,children:[(0,$.jsx)(k,{label:f(`admin.configure.rabbitUrl`),type:`password`,autoComplete:`new-password`,value:a.rabbit.url,placeholder:e.broker.secretConfigured?f(`admin.configure.savedSecret`):`amqps://user:password@host/vhost`,onChange:e=>l({...a,rabbit:{...a.rabbit,url:e.target.value}})}),e.broker.settings&&(0,$.jsx)(_,{label:f(`admin.configure.savedBrokerEndpoint`),value:e.broker.settings.endpoint}),(0,$.jsx)(k,{label:f(`admin.configure.rabbitExchange`),required:!0,value:a.rabbit.exchange,onChange:e=>l({...a,rabbit:{...a.rabbit,exchange:e.target.value}})})]}),(0,$.jsx)(B,{onClick:()=>{V(()=>v.request(`/api/v1/admin/broker/test`,p,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({...a.rabbit,url:a.rabbit.url||void 0})}),f(`admin.configure.brokerVerified`))},children:f(`admin.configure.testBroker`)})]}),(0,$.jsx)(`p`,{className:`mt-5`,children:f(`admin.configure.brokerNote`)}),N&&(0,$.jsx)(`p`,{className:`mt-4 text-xs`,children:f(`admin.configure.brokerDeployment`)}),e.broker.error&&(0,$.jsx)(b,{error:Error(e.broker.error)})]})})]}),[`general`,`storage`,`search`,`events`].includes(t)&&(0,$.jsxs)(`div`,{className:`sticky bottom-0 mt-6 flex flex-wrap items-center gap-3 border-t bg-background/95 py-4 backdrop-blur`,children:[(0,$.jsx)(B,{disabled:u,onClick:()=>A(!1),children:f(F?`common.action.done`:`common.action.cancel`)}),!F&&(0,$.jsx)(B,{variant:`default`,type:`submit`,disabled:u,children:f(u?`common.state.saving`:`admin.configure.save`)}),w&&(0,$.jsx)(`span`,{role:`status`,className:`text-sm text-muted-foreground`,children:w}),(0,$.jsx)(b,{error:g})]})]})}),(0,$.jsx)(q,{value:`users`,children:(0,$.jsx)(Le,{})}),(0,$.jsx)(q,{value:`workspaces`,children:(0,$.jsx)(ze,{})}),(0,$.jsx)(q,{value:`authentication`,children:(0,$.jsxs)(`section`,{children:[(0,$.jsx)(H,{title:f(`admin.authentication.title`)}),(0,$.jsxs)(`form`,{className:`mb-6`,onSubmit:e=>{e.preventDefault(),V(U,f(`admin.authentication.saved`),!0)},children:[(0,$.jsx)(J,{title:f(`admin.authentication.emailPassword`),description:f(`admin.authentication.emailPasswordDescription`),children:(0,$.jsx)(we,{"aria-label":f(`admin.authentication.emailPassword`),checked:a.authentication.emailPasswordEnabled,disabled:u,onCheckedChange:e=>l(t=>({...t,authentication:{...t.authentication,emailPasswordEnabled:e}}))})}),(0,$.jsx)(J,{title:f(`admin.authentication.ssoProvision`),description:f(`admin.authentication.ssoProvisionDescription`),children:(0,$.jsx)(we,{"aria-label":f(`admin.authentication.ssoProvision`),checked:a.authentication.ssoAutoProvision,disabled:u,onCheckedChange:e=>l(t=>({...t,authentication:{...t.authentication,ssoAutoProvision:e}}))})}),(0,$.jsx)(J,{title:f(`admin.authentication.registration`),children:f(`admin.authentication.registrationClosed`)}),(0,$.jsx)(J,{title:f(`admin.authentication.identityProvider`),children:e.deployment.oidc?.name??f(`admin.authentication.notConfigured`)}),(0,$.jsx)(J,{title:f(`admin.authentication.linkedProviders`),description:f(`admin.authentication.linkedProvidersDescription`),children:s(i.data?.providers.filter(e=>e.id!==`oidc`).map(e=>e.name)??[])||f(`admin.authentication.notConfigured`)}),(0,$.jsx)(O,{layout:`inline`,error:g,pending:u,submitLabel:f(`admin.authentication.save`),pendingLabel:f(`common.state.saving`),submitDisabled:a.authentication.emailPasswordEnabled===e.authentication.emailPasswordEnabled&&a.authentication.ssoAutoProvision===e.authentication.ssoAutoProvision})]}),e.deployment.oidc&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(_,{label:f(`admin.authentication.discoveryUrl`),value:e.deployment.oidc.discoveryUrl}),(0,$.jsx)(_,{label:f(`admin.authentication.clientId`),value:e.deployment.oidc.clientId})]}),(0,$.jsx)(`p`,{children:f(`admin.authentication.oidcSetup`)}),(0,$.jsx)(`h3`,{children:f(`admin.authentication.consumers`)}),(0,$.jsx)(_,{label:`HTTP API`,value:`${e.deployment.url}/api/v1`}),(0,$.jsx)(_,{label:f(`admin.authentication.mcpEndpoint`),value:`${e.deployment.url}/mcp`}),(0,$.jsx)(_,{label:f(`admin.authentication.eventFeed`),value:`${e.deployment.url}/api/v1/events`}),(0,$.jsxs)(`a`,{className:`inline-flex items-center gap-2 text-primary`,href:`/api/openapi.json`,target:`_blank`,rel:`noreferrer`,children:[f(`admin.authentication.openApi`),(0,$.jsx)(ve,{className:`size-4`})]})]})})]})}export{He as AdminSettings};
@@ -1 +0,0 @@
1
- var e={common:{status:{backlog:`Gözləmə siyahısı`,todo:`Ediləcək`,in_progress:`İcrada`,in_review:`Yoxlamada`,done:`Hazırdır`,cancelled:`Ləğv edilib`},priority:{urgent:`Təcili`,high:`Yüksək`,medium:`Orta`,low:`Aşağı`,none:`Prioritet yoxdur`},kind:{feature:`Funksiya`,bug:`Xəta`,chore:`Rutin iş`,research:`Araşdırma`,documentation:`Sənədləşmə`},difficulty:{low:`Aşağı`,med:`Orta`,high:`Yüksək`,none:`Çətinlik yoxdur`},role:{reader:`Yalnız oxuma`,writer:`Redaktə edə bilər`,admin:`Administrator`},permission:{ticket:{write:{label:`Tiketləri redaktə etmək`,description:`Tiketləri yaratmaq, redaktə etmək, köçürmək, götürmək və şərh yazmaq; layihə yaratmaq`},delete:{label:`Tiketləri silmək`,description:`Tiketləri həmişəlik silmək`}},project:{manage:{label:`Layihələri idarə etmək`,description:`Layihə məlumatları, iş axını, etiketlər, sahələr, inteqrasiyalar və həyat dövrü`}},member:{manage:{label:`Üzvləri idarə etmək`,description:`Üzvləri əlavə etmək, dəyişmək və çıxarmaq`}},invitation:{manage:{label:`Dəvətləri idarə etmək`,description:`İnsanları e-poçtla dəvət etmək`}},sharing:{manage:{label:`Paylaşımı idarə etmək`,description:`İctimai yalnız oxuma keçidləri və təqvim lentləri`}},workspace:{manage:{label:`İş sahəsini idarə etmək`,description:`İş sahəsi məlumatları, ümumi etiketlər və rollar`}}},customFieldType:{text:`Mətn`,number:`Rəqəm`,date:`Tarix`,checkbox:`Qeyd qutusu`,select:`Seçim`,multiselect:`Çoxlu seçim`},customField:{removedOption:`Silinmiş seçim`,checked:`Bəli`},filterField:{status:`Status`,priority:`Prioritet`,kind:`Növ`,difficulty:`Çətinlik`,label:`Etiket`,assigneeId:`İcraçı`,customField:`Sahə`},sort:{priority:`Prioritet`,updated:`Son yenilənənlər`,created:`Son yaradılanlar`,title:`Başlıq`,labels:`Etiketlər`,due:`Son tarix`,start:`Başlama tarixi`,number:`Tiket nömrəsi`,status:`Status`,difficulty:`Çətinlik`,kind:`Növ`,assignee:`İcraçı`,manual:`Əl ilə`},due:{overdue:`Gecikmiş`,today:`Bu gün bitir`,week:`Bu həftə bitir`,scheduled:`Son tarixi var`,none:`Son tarix yoxdur`},relation:{blocks:{outgoing:`Bloklayır`,incoming:`Tərəfindən bloklanır`},related:{outgoing:`Əlaqəlidir`,incoming:`Əlaqəlidir`},duplicate:{outgoing:`Təkrarlayır`,incoming:`Tərəfindən təkrarlanır`},causes:{outgoing:`Səbəb olur`,incoming:`Səbəbi`},clones:{outgoing:`Klonlayır`,incoming:`Tərəfindən klonlanır`},implements:{outgoing:`Həyata keçirir`,incoming:`Tərəfindən həyata keçirilir`},reviews:{outgoing:`Yoxlayır`,incoming:`Tərəfindən yoxlanılır`}},remoteKind:{branch:`Budaq`,pull_request:`Pull request`,issue:`Məsələ`},notificationCategory:{assignment:`Təyinatlar`,status:`Status dəyişiklikləri`,comment:`Şərhlər`,mention:`İstinadlar`,update:`Digər yeniliklər`,due_soon:`Müddəti yaxınlaşır`,overdue:`Gecikmiş`},destinationEvent:{created:`Yeni tiketlər`},deliveryKind:{email:`E-poçt`,webhook:`Webhook`},change:{pull_request:{name:`Pull request`,opened:`Pull request açıldı`,draft:`Qaralama pull request`,merged:`Pull request birləşdirildi`,closed:`Pull request birləşdirilmədən bağlandı`},merge_request:{name:`Merge request`,opened:`Merge request açıldı`,draft:`Qaralama merge request`,merged:`Merge request birləşdirildi`,closed:`Merge request birləşdirilmədən bağlandı`},branchCreated:`Budaq yaradıldı`},issueLinkState:{active:`Sinxronlaşdırılır`,paused:`Dayandırılıb`,review:`Yoxlama tələb olunur`,creating:`Məsələ yaradılır`,unknown:`Yaradılma təsdiqlənməyib`},importStatus:{previewed:`İdxala hazırdır`,running:`İdxal edilir`,completed:`İdxal edildi`,failed:`Uğursuz oldu`,cancelled:`Ləğv edildi`},importProvider:{linear:{project:`Komanda`,setup:`Linear-da Settings › Security & access bölməsində şəxsi API açarı yaradın; oxuma icazəsi kifayətdir.`},jira:{project:`Layihə`,setup:`Jira Cloud hesabınızın e-poçtu və id.atlassian.com-dan alınan API tokeni ilə daxil olur; Data Center və Server şəxsi giriş tokenindən istifadə edir. Layihəyə baxış icazəsi kifayətdir.`},trello:{project:`Lövhə`,setup:`trello.com/power-ups/admin ünvanında idarə etdiyiniz Power-Up-ın API açarından və onun üçün yaradılmış tokendən istifadə edin; oxuma icazəsi kifayətdir.`},plane:{project:`Layihə`,setup:`Plane-də Profile settings › Personal access tokens bölməsində şəxsi giriş tokeni yaradın. Plane Cloud API ünvanı: https://api.plane.so; iş sahəsinin URL açarı onun veb ünvanının ilk hissəsidir.`},planka:{project:`Lövhə`,setup:`Lövhəni aça bilən PLANKA hesabı ilə daxil olun; Kanvil-in açdığı sessiya oxuma bitəndə başa çatır. PLANKA 2 administratorları bunun əvəzinə API açarları verə bilər.`},kaneo:{project:`Layihə`,setup:`Kaneo-da Settings › Account › Developer bölməsində API açarı yaradın.`}},plankaSignIn:{password:`İstifadəçi adı və şifrə`,api_key:`API açarı`},jiraEdition:{cloud:`Cloud`,data_center:`Data Center və ya Server`},event:{time_entry:{created:`Vaxt qeydi yaradıldı`,updated:`Vaxt qeydi yeniləndi`,deleted:`Vaxt qeydi silindi`},label:{created:`Etiket yaradıldı`,updated:`Etiket yeniləndi`,deleted:`Etiket silindi`},workflow:{created:`İş axını vəziyyəti əlavə edildi`,updated:`İş axını vəziyyəti dəyişdirildi`,deleted:`İş axını vəziyyəti silindi`},custom_field:{created:`Sahə yaradıldı`,updated:`Sahə yeniləndi`,deleted:`Sahə silindi`},ticket:{created:`Tiket yaradıldı`,updated:`Tiket yeniləndi`,deleted:`Tiket silindi`,moved:`Başqa layihədən köçürüldü`,moved_away:`Başqa layihəyə köçürüldü`,due_soon:`Müddəti yaxınlaşır`,overdue:`Gecikmiş`,claimed:`İş götürüldü`},claim:{renewed:`Götürmə yeniləndi`,released:`Götürmə buraxıldı`,recovered:`Götürmə bərpa edildi`},comment:{created:`Şərh əlavə edildi`,updated:`Şərh redaktə edildi`,deleted:`Şərh silindi`},relation:{added:`Əlaqə əlavə edildi`,removed:`Əlaqə silindi`},attachment:{created:`Fayl qoşuldu`,deleted:`Fayl silindi`},evidence:{added:`Sübut qoşuldu`},resource:{added:`Keçid əlavə edildi`,updated:`Keçid redaktə edildi`,removed:`Keçid silindi`},calendar_feed:{created:`Təqvim lenti yaradıldı`,revoked:`Təqvim lenti ləğv edildi`},project:{created:`Layihə yaradıldı`,updated:`Layihə yeniləndi`,archived:`Layihə arxivləşdirildi`,restored:`Layihə bərpa edildi`,transferred:`Layihə başqa iş sahəsinə köçürüldü`,deleted:`Layihə silindi`},membership:{updated:`Layihəyə giriş dəyişdirildi`},workspace:{owner_changed:`İş sahəsinin sahibi dəyişdirildi`},role:{created:`Rol yaradıldı`,updated:`Rol dəyişdirildi`,deleted:`Rol silindi`},invitation:{updated:`Dəvət dəyişdirildi`},destination:{created:`Təyinat əlavə edildi`,updated:`Təyinat dəyişdirildi`,deleted:`Təyinat silindi`},repository:{connected:`Repozitori qoşuldu`,updated:`Repozitori bağlantısı dəyişdirildi`,disconnected:`Repozitori ayrıldı`,received:`Repozitori webhooku alındı`},issue_link:{updated:`Məsələ sinxronizasiyası dəyişdirildi`}},error:{network:`Kanvil ilə əlaqə qurulmadı. Bağlantınızı yoxlayıb yenidən cəhd edin.`,fallback:`Sorğu tamamlana bilmədi`,input:`Daxil edilən məlumat`,code:{unauthorized:`Davam etmək üçün daxil olun.`,session_required:`Bu əməliyyat üçün daxil olmuş hesabdan istifadə edin.`,account_disabled:`Bu hesab deaktiv edilib.`,forbidden:`Bunu etməyə icazəniz yoxdur.`,insufficient_scope:`Bu giriş məlumatı bu əməliyyata icazə vermir.`,workspace_scope_required:`Bu əməliyyat üçün iş sahəsinə girişi olan hesabdan istifadə edin.`,invalid_origin:`Bu mənbəyə icazə verilmir.`,not_found:`Bu element tapılmadı və ya ona baxmaq icazəniz yoxdur.`,version_conflict:`Yükləndikdən sonra dəyişdirilib. Yenidən yükləyib təkrar cəhd edin.`,time_entry_conflict:`Bu istifadəçinin tapşırıqda artıq davam edən vaxt qeydi var.`,claim_conflict:`Bu tiket başqa işçidədir.`,idempotency_conflict:`Bu sorğu artıq başqa dəyişiklik üçün istifadə edilib.`,project_archived:`Dəyişməzdən əvvəl arxivləşdirilmiş layihəni bərpa edin.`,rate_limited:`Qısa müddətdə həddən artıq dəyişiklik. Gözləyib yenidən cəhd edin.`,snapshot_required:`Canlı lent geri qaldı. Davam etmək üçün yenidən yükləyin.`,request_failed:`Sorğu tamamlana bilmədi`}},action:{add:`Əlavə et`,apply:`Tətbiq et`,back:`Geri`,cancel:`Ləğv et`,clear:`Təmizlə`,close:`Bağla`,confirm:`Təsdiqlə`,continue:`Davam et`,copy:`Kopyala`,create:`Yarat`,delete:`Sil`,done:`Hazır`,edit:`Redaktə et`,open:`Aç`,remove:`Çıxar`,rename:`Adını dəyiş`,reset:`Sıfırla`,retry:`Yenidən cəhd et`,save:`Yadda saxla`,search:`Axtar`},state:{copied:`Kopyalandı`,loading:`Yüklənir…`,saved:`Yadda saxlanıldı`,saving:`Yadda saxlanılır…`,none:`Yoxdur`,unassigned:`Təyin edilməyib`},evidenceKind:{pull_request:`Pull request`,test:`Test`,review:`Yoxlama`,deployment:`Yerləşdirmə`,device:`Cihaz`,other:`Digər`},evidenceOutcome:{reported:`Bildirilib`,pending:`Gözləyir`,passed:`Uğurlu`,failed:`Uğursuz`},readinessReason:{project_archived:`Layihə arxivləşdirilib`,not_open:`Tiket “Ediləcək” vəziyyətində olmalı və arxivdə olmamalıdır`,not_enabled:`Agent işi aktivləşdirilməyib`,missing_description:`Təsvir əlavə edin`,missing_acceptance_criteria:`Qəbul meyarları əlavə edin`,missing_difficulty:`Çətinlik seçin`,duplicate:`Bu tiket təkrar kimi qeyd edilib`,claim_recovery_required:`Sahibliyi bərpa etməzdən əvvəl əvvəlki işçinin dayandığını təsdiqləyin`,claimed:`{{worker}} tərəfindən götürülüb`,dependency:`{{key}} tamamlanmayıb`},person:{deleted:`Silinmiş hesab`,former:`Keçmiş üzv`},signIn:{codeTitle:`Kodla daxil ol`,resetTitle:`Şifrəni sıfırla`,codeSent:`{{email}} ünvanının hesabı varsa, kod göndərilir. Kodun müddəti 10 dəqiqəyə bitir.`,code:`Kod`,newPassword:`Yeni şifrə`,sendCode:`Kodu e-poçtla göndər`,back:`Daxil olmaya qayıt`,resend:`Kodu yenidən göndər`,resent:`Kod yenidən göndərildi`,emailCode:`Daxil olma kodunu e-poçtla göndər`,forgot:`Şifrəni unutmusunuz?`,resetDone:`Şifrə sıfırlandı. Yeni şifrə ilə daxil olun.`,resetDoneTitle:`Şifrə sıfırlandı`,resetDoneBody:`Yeni şifrə ilə daxil olun.`,invalidCode:`Bu kod yanlışdır, artıq istifadə olunub və ya müddəti bitib. Etibarlı kod almaq üçün yenidən göndərin.`,localPassword:`Yerli şifrə`},language:{label:`Dil`,system:`Sistem standartı`}},shell:{ui:{showValue:`{{label, lowercase}} göstər`,hideValue:`{{label, lowercase}} gizlət`,previousPage:`Əvvəlki {{noun}}`,nextPage:`Növbəti {{noun}}`,pageRange:`{{from}}–{{to}} / {{total}}`,priority:`Prioritet: {{label}}`,working:`İcra olunur…`,searchPlaceholder:`Axtar…`,clearValue:`{{label, lowercase}} təmizlə`,copyValue:`{{label, lowercase}} kopyala`,copied:`{{value}} kopyalandı`},documentTitle:`{{name}} · Kanvil`,openKanvil:`Kanvil-i aç`,page:{home:`Əsas səhifə`,myWork:`Mənim işlərim`,inbox:`Gələnlər`,search:`Axtarış`,settings:`Parametrlər`,planning:`Planlaşdırma`,ticket:`Tiket`,tickets:`Tiketlər`,projects:`Layihələr`},connection:{restored:`Bağlantı bərpa edildi`,lost:`Bağlantı kəsildi`,reconnecting:`Yenidən qoşulur…`,reload:`Yenidən yüklə`,signIn:`Yenidən qoşulmaq üçün daxil olun`},app:{skipToContent:`Məzmuna keç`,assigned:`Sizə təyin edilənlər`,openCount_one:`{{count, number}} açıq`,openCount_other:`{{count, number}} açıq`,noAssigned:`Sizə təyin edilmiş açıq tiket yoxdur`,allInMyWork_one:`“Mənim işlərim”də hamısı ({{count, number}})`,allInMyWork_other:`“Mənim işlərim”də hamısı ({{count, number}})`,openMyWork:`“Mənim işlərim”i aç`,recentActivity:`Son fəaliyyət`,noActivity:`Hələ fəaliyyət yoxdur`,searchProjects:`Layihələrdə axtar`,projectCount_one:`{{count, number}} layihə`,projectCount_other:`{{count, number}} layihə`,noMatchingProjects:`Uyğun layihə yoxdur`,clearSearch:`Axtarışı təmizlə`,noWorkspaces:`Hələ iş sahəsi yoxdur`,noActiveProjects:`Aktiv layihə yoxdur`,noProjects:`Hələ layihə yoxdur`,archivedProjects:`Arxivləşdirilmiş layihələr`,archived:`Arxivləşdirilib`,noOpenTickets:`Açıq tiket yoxdur`,overdueCount_one:`{{count, number}} gecikmiş`,overdueCount_other:`{{count, number}} gecikmiş`,percentDone:`{{percent}} hazırdır`,nextDue:`növbəti son tarix <time/>`,projectSettingsFor:`{{name}} layihəsinin parametrləri`,pageNotFound:`Səhifə tapılmadı`,openHome:`Əsas səhifəni aç`,tryAgain:`Yenidən cəhd edin`,adminRequired:`Nüsxə administratoru girişi tələb olunur`,projectNotFound:`Layihə tapılmadı`,noWorkspaceSelected:`İş sahəsi seçilməyib`},auth:{signIn:`Daxil ol`,createOwner:`Sahib hesabınızı yaradın`,ownerHint:`İlk hesab bu nüsxəni idarə edir.`,invitedHint:`Dəvəti qəbul etmək üçün dəvət edilmiş e-poçt ünvanı ilə daxil olun.`,signInWith:`{{provider}} ilə daxil ol`,useEmail:`E-poçt və şifrədən istifadə et`,or:`və ya`,name:`Ad`,setupToken:`Quraşdırma tokeni`,setupTokenHint:`Nüsxə konfiqurasiyanızdakı KANVIL_SETUP_TOKEN dəyərindən istifadə edin.`,email:`E-poçt`,password:`Şifrə`,signingIn:`Daxil olunur…`,createAccount:`Hesab yarat`,consent:{expired:`Avtorizasiya sorğusunun müddəti bitib. Klientinizi yenidən qoşun.`,title:`{{client}} üçün icazə ver`,titleUnnamed:`Klientə icazə ver`,subtitle:`Bu klient hesabınız adından giriş istəyir. Layihə icazələri yenə də tətbiq olunur.`,deny:`Rədd et`,allow:`Girişə icazə ver`},invitation:{unavailable:`Dəvət əlçatan deyil`,join:`{{workspace}} iş sahəsinə qoşul`,invited:`Dəvət edilən`,from:`Göndərən`,access:`Giriş`,removedProjects:`Silinmiş layihələr`,wholeWorkspace:`Bütün iş sahəsi`,expires:`Bitmə tarixi`,expired:`Bu dəvətin müddəti bitib. İş sahəsi administratorundan yeni dəvət istəyin.`,revoked:`Bu dəvət ləğv edilib. İş sahəsi administratorundan yeni dəvət istəyin.`,accepted:`Bu dəvət artıq qəbul edilib. İş sahəsi administratorundan yeni dəvət istəyin.`,wrongAccount:`<account>{{current}}</account> kimi daxil olmusunuz. Qəbul etmək üçün {{invited}} kimi daxil olun.`,signOut:`Çıxış et`,joined:`{{workspace}} iş sahəsinə qoşuldunuz`,accept:`Dəvəti qəbul et`},callback:{cancelled:`Daxil olma ləğv edildi. Hazır olduqda yenidən cəhd edin.`,linkedElsewhere:`Bu identiklik artıq başqa Kanvil hesabına bağlanıb. Başqa identiklikdən istifadə edin və ya administratorunuzla əlaqə saxlayın.`,notLinked:`Bu identiklik heç bir Kanvil hesabına bağlanmayıb. Yerli hesabınızla daxil olub onu Parametrlərdə bağlayın.`,expired:`Daxil olma sorğusunun müddəti bitib və ya təsdiqlənə bilmədi. Bu ekrandan yenidən cəhd edin.`,emailMismatch:`İdentiklik provayderinin e-poçtu bağlana bilmədi. Administratorunuzla əlaqə saxlayın.`,failed:`Daxil olmaq mümkün olmadı. Yenidən cəhd edin və ya administratorunuzla əlaqə saxlayın.`}},you:`Siz`,inbox:{markAllRead:`Hamısını oxunmuş kimi qeyd et`,clearRead:`Oxunmuşları təmizlə`,cleared_one:`{{count, number}} oxunmuş bildiriş təmizləndi`,cleared_other:`{{count, number}} oxunmuş bildiriş təmizləndi`,unread:`Oxunmamış`,mentions:`İstinadlar`,all:`Bütün bildirişlər`,noUnread:`Oxunmamış bildiriş yoxdur`,noMentions:`İstinad yoxdur`,noNotifications:`Bildiriş yoxdur`,markUnread:`Oxunmamış kimi qeyd et`,markRead:`Oxunmuş kimi qeyd et`,dismiss:`Rədd et`},search:{field:`İş sahəsində axtar`,placeholder:`Tiketlər, layihələr, şərhlər, fəaliyyət üzrə axtar…`,tab:{all:`Hamısı`,project:`Layihələr`,ticket:`Tiketlər`,comment:`Şərhlər`,activity:`Fəaliyyət`},indexing:`Xarici axtarış yenilənir, ona görə tiket nəticələri PostgreSQL-dən gəlir.`,empty:`Tiketlər, layihələr, şərhlər və fəaliyyət üzrə axtarın`,noResults:`“{{query}}” üçün nəticə yoxdur`,results:`Axtarış nəticələri`,pages:`Axtarış səhifələri`,previous:`Əvvəlki`,next:`Növbəti`,type:{project:`Layihə`,ticket:`Tiket`,comment:`Şərh`,activity:`Fəaliyyət`}},column:{key:`Tiket`,labels:`Etiketlər`,scopes:`Əhatələr`,status:`Status`,priority:`Prioritet`,difficulty:`Çətinlik`,kind:`Növ`,assigneeId:`İcraçı`,startAt:`Başlama`,dueAt:`Son tarix`,createdAt:`Yaradılıb`,updatedAt:`Yenilənib`},public:{notShared:`Bu layihə paylaşılmayıb`,notSharedHint:`Keçid söndürülüb və ya heç vaxt mövcud olmayıb. Üzvlər oxumaq üçün daxil ola bilər.`,noTickets:`Tiket yoxdur`,ticketUnavailable:`Tiket əlçatan deyil`,backToTickets:`Tiketlərə qayıt`,description:`Təsvir`,noDescription:`_Təsvir yoxdur_`,acceptanceCriteria:`Qəbul meyarları`,noAcceptanceCriteria:`_Qəbul meyarları yoxdur_`,ticketProperties:`Tiket xüsusiyyətləri`,properties:`Xüsusiyyətlər`,status:`Status`,priority:`Prioritet`,kind:`Növ`,difficulty:`Çətinlik`,startDate:`Başlama tarixi`,dueDate:`Son tarix`,labels:`Etiketlər`,scopes:`Əhatələr`,created:`Yaradılıb`,updated:`Yenilənib`,relationships:`Əlaqələr`,parent:`Valideyn`,child:`Alt tiket`},sidebar:{workspace:`İş sahəsi`,unread_one:`{{count, number}} oxunmamış`,unread_other:`{{count, number}} oxunmamış`,userSettings:`İstifadəçi parametrləri`,administration:`İdarəetmə`,signOut:`Çıxış et`,projectSettings:`Layihə parametrləri`},workspace:{new:`Yeni iş sahəsi`,name:`İş sahəsinin adı`,key:`İş sahəsinin açarı`,create:`İş sahəsi yarat`,workspaces:`İş sahələri`,switch:`İş sahəsini dəyiş`,settings:`İş sahəsi parametrləri`},palette:{open:`Keç…`,title:`Keç`,description:`Layihə, tiket, səhifə və ya əməliyyat tapın`,placeholder:`Tiketlər, layihələr, səhifələr, əməliyyatlar üzrə axtar…`,searching:`Axtarılır…`,noResults:`Nəticə yoxdur`,jumpTo:`Keç`,openKey:`{{project}} layihəsində {{key}} aç`,resultsIn:`{{workspace}} üzrə nəticələr`,results:`Nəticələr`,seeAll_one:`Bütün {{count, number}} nəticəyə bax`,seeAll_other:`Bütün {{count, number}} nəticəyə bax`,actions:`Əməliyyatlar`,pages:`Səhifələr`,newTicketIn:`{{project}} layihəsində yeni tiket`,myTicketsIn:`{{project}} layihəsində mənim tiketlərim`,lightMode:`İşıqlı rejimə keç`,darkMode:`Qaranlıq rejimə keç`,keywords:{jump:`{{query}} keç aç jump open`,allResults:`bütün nəticələr {{query}}`,newTicket:`yeni tiket yarat`,myTickets:`mənim tiketlərim mənə təyin edilən`,lightMode:`işıqlı rejimə keç tema görünüş`,darkMode:`qaranlıq rejimə keç tema görünüş`,help:`klaviatura qısayolları kömək`,home:`əsas səhifə icmal bütün layihələr`,myWork:`mənim işlərim təyin edilmiş tiketlər`,search:`axtarış şərhlər fəaliyyət layihələr tiketlər`,inbox:`gələnlər bildirişlər`,settings:`parametrlər {{scope}} {{label}}`}},actionCategory:{general:`Ümumi`,ticketList:`Tiket siyahısı`,inbox:`Gələnlər`,ticket:`Tiket`,search:`Axtarış`,markdown:`Markdown`},action:{palette:`İstənilən yerə keç`,help:`Klaviatura qısayolları`,sidebar:`Yan paneli aç/bağla`,newTicket:`Yeni tiket`,search:`Tiketlərdə axtar`,highlight:`Növbəti və ya əvvəlki tiketi vurğula`,open:`Vurğulanmış tiketi aç`,preview:`Vurğulanmış tiketə siyahının yanında bax`,select:`Vurğulanmış tiketi seç`,properties:`Vurğulanmış tiketin statusunu, prioritetini və ya icraçısını təyin et`,inboxHighlight:`Növbəti və ya əvvəlki bildirişi vurğula`,inboxOpen:`Vurğulanmış bildirişin tiketini Gələnlərin yanında oxu`,inboxRead:`Vurğulanmış bildirişi oxunmuş və ya oxunmamış kimi qeyd et`,inboxDismiss:`Vurğulanmış bildirişi rədd et`,step:`Növbəti və ya əvvəlki tiketi aç`,edit:`Tiketi redaktə et`,save:`Tiketi və ya şərhi yadda saxla`,results:`Axtarış nəticələri arasında hərəkət et`,insertBlock:`Vizual redaktorda cədvəl, diaqram, fayl və ya başqa blok daxil et`},move:{top:{label:`Vurğulanmış tiketi ən yuxarı köçür`,menu:`Ən yuxarı köçür`},up:{label:`Vurğulanmış tiketi yuxarı köçür`,menu:`Yuxarı köçür`},down:{label:`Vurğulanmış tiketi aşağı köçür`,menu:`Aşağı köçür`},bottom:{label:`Vurğulanmış tiketi ən aşağı köçür`,menu:`Ən aşağı köçür`},previous:{label:`Vurğulanmış tiketi əvvəlki statusa köçür`,menu:`Əvvəlki statusa köçür`},next:{label:`Vurğulanmış tiketi növbəti statusa köçür`,menu:`Növbəti statusa köçür`}},copy:{link:{label:`Keçidi kopyala`,copied:`Tiket keçidi`},keyAndTitle:{label:`Açarı və başlığı kopyala`,copied:`Tiket`},title:{label:`Başlığı kopyala`,copied:`Başlıq`},markdown:{label:`Markdown kimi kopyala`,copied:`Markdown`},branch:{label:`Budaq adını kopyala`,copied:`Budaq adı`}},settingsNav:{account:`Hesab`,settings:`Parametrlər`,workspace:`İş sahəsi`,workspaceSettings:`İş sahəsi parametrləri`,projects:`Layihələr`,projectSettings:`Layihə parametrləri`,administration:`İdarəetmə`,section:{profile:`Profil və təhlükəsizlik`,appearance:`Görünüş`,preferences:`Seçimlər`,credentials:`API açarları`,notifications:`Bildirişlər`,general:`Ümumi`,members:`Üzvlər`,roles:`Rollar`,labels:`Etiketlər`,workflow:`İş axını`,fields:`Sahələr`,integrations:`İnteqrasiyalar`,calendar:`Təqvim lentləri`,storage:`Fayl yaddaşı`,search:`Axtarış`,events:`Hadisə çatdırılması`,users:`İstifadəçilər`,workspaces:`İş sahələri`,authentication:`Autentifikasiya`}},browser:{copyFailed:`Kopyalamaq alınmadı. Mətni seçib əl ilə kopyalayın.`,readFailed:`Fayl oxuna bilmədi`,encodeFailed:`Fayl kodlaşdırıla bilmədi`,imageTooBig:`10 MB-dan kiçik şəkil seçin.`,cannotResize:`Bu brauzer şəklin ölçüsünü dəyişə bilmir.`,prepareFailed:`Şəkil hazırlana bilmədi.`,stillTooLarge:`Ölçü dəyişdikdən sonra da bu şəkil çox böyükdür. Daha sadə şəkil seçin.`},primitive:{close:`Bağla`,more:`Daha çox`,breadcrumb:`naviqasiya zənciri`,sidebar:`Yan panel`,sidebarDescription:`Mobil yan paneli göstərir.`,toggleSidebar:`Yan paneli aç/bağla`,resizeSidebar:`Yan panelin ölçüsünü dəyiş`,expandSidebar:`Yan paneli genişləndir`,commandTitle:`Əmr paneli`,commandDescription:`İcra etmək üçün əmr axtarın…`}},ticket:{list:{manageViews:`Görünüşləri idarə et`,selectSection:`{{section}} tiketlərini seç`,expandSection:`{{section}} tiketlərini genişləndir`,collapseSection:`{{section}} tiketlərini yığ`,sectionTickets:`{{section}} tiketləri`,later:`Sonra`,selectAll:`Göstərilən bütün tiketləri seç`,selectTicket:`{{key}} seç`,title:`Başlıq`,all:`Hamısı`,planned:`Planlaşdırılmış`,archived:`Arxivləşdirilmiş`,allTickets:`Bütün tiketlər`,customView:`Fərdi görünüş`,sectionCount:`{{n, number}} {{section}}`,planning:`{{project}} planlaşdırması`,collection:`{{source}} ({{sections}})`,moved:`{{key}} köçürüldü`,movedTo:`{{key}} {{status}} statusuna köçürüldü`,movedAbove:`{{key}} {{neighbor}} üzərinə köçürüldü`,movedBelow:`{{key}} {{neighbor}} altına köçürüldü`,movedToAbove:`{{key}} {{status}} statusuna, {{neighbor}} üzərinə köçürüldü`,movedToBelow:`{{key}} {{status}} statusuna, {{neighbor}} altına köçürüldü`,emptyFiltered:`Uyğun tiket yoxdur`,emptyPlanning:`Planlaşdırılmış və ya arxivləşdirilmiş heç nə yoxdur`,emptyProject:`Hələ tiket yoxdur`,emptyWork:`Sizə təyin edilmiş açıq tiket yoxdur`,createFirst:`İlk tiketi yarat`,activeWork:`Aktiv iş`,views:`Tiket görünüşləri`,saveCurrentView:`Cari görünüşü yadda saxla`,deleteView:`{{name}} görünüşünü sil`,deleteViewConfirm:`{{name}} yadda saxlanılmış görünüşlərdən silinsin? Tiketlər dəyişməz qalır.`,planningLink:`Planlaşdırma`,newTicketShortcut:`Yeni tiket ({{shortcut}})`,saveView:`Görünüşü yadda saxla`,viewSaved:`Görünüş yadda saxlanıldı`,viewName:`Görünüşün adı`,retry:`Tiketləri yenidən yüklə`,board:`Tiket lövhəsi`,tickets:`Tiketlər`,resizeColumn:`{{column}} sütununun ölçüsünü dəyiş`,count_one:`{{count, number}} tiket`,count_other:`{{count, number}} tiket`,scheduledCount_one:`{{count, number}} planlaşdırılmış tiket`,scheduledCount_other:`{{count, number}} planlaşdırılmış tiket`},move:{destination:`Təyinat`,projectPlaceholder:`Layihəyə köçür…`,blockers:`Köçürməzdən əvvəl həll edin`,submit:`Tiketi köçür`,pending:`Köçürülür…`},picker:{placeholder:`Tiket seçin`,loading:`Tiket yüklənir…`,searchLabel:`{{label, lowercase}} axtar`,searchPlaceholder:`Başlıq və ya açar üzrə axtar…`,noParent:`Valideyn yoxdur`,searching:`Axtarılır…`,empty:`Tiket tapılmadı`},property:{status:`Status`,priority:`Prioritet`,kind:`Növ`,difficulty:`Çətinlik`,assignee:`İcraçı`,you:`{{name}} (siz)`,assignTo:`Təyin et…`,set:`{{label, lowercase}} təyin et…`,search:`{{label, lowercase}} axtar`,noMatches:`Uyğunluq yoxdur`},field:{mixed:`Qarışıq`,unchecked:`Xeyr`,noValue:`Dəyər yoxdur`},project:{label:`Layihə`,placeholder:`Layihədə yarat…`,search:`Layihələrdə axtar`},tags:{labels:`Etiketlər`,scopes:`Əhatələr`,summary:`{{label}}: {{tags}}`,edit:`{{label, lowercase}} redaktə et`,editValues:`{{label, lowercase}} redaktə et: {{tags}}`,editMixed:`{{label, lowercase}} redaktə et: qarışıq`,mixed:`Qarışıq {{label, lowercase}}`,none:`{{label}} yoxdur`,mixedNote:`Seçilmiş tiketlərin {{label, lowercase}} fərqlidir.`,clear:`{{label, lowercase}} təmizlə`,replace:`{{label, lowercase}} əvəz et`},tagInput:{remove:`{{label, lowercase}} {{tag}} çıxar`,addAnother:`Daha birini əlavə et…`,add:`{{label, lowercase}} əlavə et…`,choose:`{{label, lowercase}} seç`,chooseLabels:`Etiketləri seç`,searchPlaceholder:`Etiketlərdə axtar…`,searchLabel:`Layihə etiketlərində axtar`,empty:`Uyğun etiket yoxdur`},link:{previewUnavailable:`Önizləmə əlçatan deyil`},userPicker:{label:`Hesab`,placeholder:`Hesab seçin`,searchPlaceholder:`Ad və ya e-poçt üzrə axtar…`,searchLabel:`Hesablarda axtar`,useEmail:`{{email}} istifadə et`,emptyAnyAccount:`Üzv axtarın və ya mövcud hesabın e-poçtunu daxil edin.`,empty:`Uyğun üzv yoxdur`},date:{placeholder:`Tarix seçin`,clear:`{{label, lowercase}} təmizlə`,start:`Başlama tarixi`,startShort:`Başlama`,noStart:`Başlama tarixi yoxdur`,due:`Son tarix`,dueShort:`Son tarix`,noDue:`Son tarix yoxdur`},filter:{add:`Filtr əlavə et`,me:`Mən`,searchTickets:`Tiketlərdə axtar`,searchTicketsPlaceholder:`Tiketlərdə axtar…`,filters:`Filtrlər`,filtersActive:`Filtrlər ({{active, number}})`,due:`Son tarix`,anyDue:`İstənilən son tarix`,archived:`Arxivləşdirilmiş`,hideArchived:`Arxivləşdirilmişləri gizlət`,includeArchived:`Arxivləşdirilmişləri daxil et`,archivedOnly:`Yalnız arxivləşdirilmiş`,includingArchived:`Arxivləşdirilmişlər daxil`,reset:`Filtrləri sıfırla`,clear:`Filtrləri təmizlə`,active:`Aktiv tiket filtrləri`,searchChip:`Axtarış: {{query}}`,chipAny:`{{field}}:`,chipNone:`{{field}} deyil:`,notValues:`{{values}} deyil`,match:`{{label}} uyğunluğu`,is:`Bərabərdir`,isNot:`Bərabər deyil`,searchPlaceholder:`{{search}}…`,anyValue:{status:`Bütün statuslar`,priority:`Bütün prioritetlər`,kind:`Bütün növlər`,difficulty:`Bütün çətinliklər`,label:`Bütün etiketlər`,assigneeId:`İstənilən şəxs`,customField:`İstənilən sahə dəyəri`},searchValues:{status:`Statuslarda axtar`,priority:`Prioritetlərdə axtar`,kind:`Növlərdə axtar`,difficulty:`Çətinliklərdə axtar`,label:`Etiketlərdə axtar`,assigneeId:`Şəxslərdə axtar`,customField:`Sahələrdə axtar`}},display:{title:`Görünüş`,orderBy:`Sıralama`,direction:`İstiqamət`,ascending:`Artan`,descending:`Azalan`,reset:`Görünüşü sıfırla`,groupBy:`Qruplaşdırma`,dueDate:`Son tarix`,layout:`Tərtibat`,list:`Siyahı`,board:`Lövhə`,backgrounds:`Layihə fonları`,show:`Göstər`,hide:`Gizlət`,rowSpacing:`Sətir aralığı`,compact:`Sıx`,comfortable:`Rahat`,labelDisplay:`Etiket görünüşü`,compactPreview:`Yığcam önizləmə`,allLabels:`Bütün etiketlər`,properties:`Xüsusiyyətlər`},selection:{region:`Seçilmiş tiketlər`,clear:`Seçimi təmizlə`,selected_one:`{{count, number}} seçilib`,selected_other:`{{count, number}} seçilib`,selectedOf_one:`{{count, number}} / {{total}}<wide> seçilib</wide>`,selectedOf_other:`{{count, number}} / {{total}}<wide> seçilib</wide>`,applied_one:`{{label}} {{count, number}} tiketə tətbiq edildi`,applied_other:`{{label}} {{count, number}} tiketə tətbiq edildi`,partlyApplied_one:`{{count, number}} tiketdən {{done, number}} tiketə tətbiq edildi`,partlyApplied_other:`{{count, number}} tiketdən {{done, number}} tiketə tətbiq edildi`,mixedDue:`Qarışıq son tarixlər`,fields:`Sahələr`,more:`Digər əməliyyatlar`,moveTo:`{{status}} statusuna köçür`,restoreChange:`Bərpa`,restore:`Seçilənləri bərpa et`,archive:`Seçilənləri arxivləşdir`,delete:`Seçilənləri sil`,deleteLimit:`Bir dəfəyə ən çox {{limit, number}} sil`,archiveConfirm_one:`{{collection}} mənbəyindən {{count, number}} tiket arxivləşdirilsin?`,archiveConfirm_other:`{{collection}} mənbəyindən {{count, number}} tiket arxivləşdirilsin?`,deleteConfirm_one:`{{collection}} mənbəyindən {{count, number}} tiket şərhləri, əlaqələri və qoşmaları ilə birlikdə həmişəlik silinsin? Seçimdən kənar alt tiketlər valideynini itirəcək.`,deleteConfirm_other:`{{collection}} mənbəyindən {{count, number}} tiket şərhləri, əlaqələri və qoşmaları ilə birlikdə həmişəlik silinsin? Seçimdən kənar alt tiketlər valideynini itirəcək.`,deleted_one:`{{count, number}} tiket silindi`,deleted_other:`{{count, number}} tiket silindi`},menu:{copied:`{{label}} kopyalandı`,open:`Tiketi aç`,preview:`Önizləmə`,newTab:`Yeni tabda aç`,edit:`Tiketi redaktə et`,editTitle:`{{key}} redaktə et`,duplicate:`Tiketin surətini çıxar`,created:`{{key}} yaradıldı`,move:`Köçür`,moveToProject:`Layihəyə köçür…`,moveTitle:`{{key}} köçür`,moved:`{{key}} olaraq köçürüldü`,follow:`Tiketi izlə`,unfollow:`Tiketi izləməyi dayandır`,archive:`Tiketi arxivləşdir`,unarchive:`Tiketi arxivdən çıxar`,delete:`Tiketi sil`,deleteConfirm:`{{key}}, onun şərhləri, əlaqələri və qoşmaları həmişəlik silinsin?`,openProject:`Layihəni aç`,projectLink:`Layihə keçidi`,copyLink:`Keçidi kopyala`,newTicket:`Yeni tiket`,projectSettings:`Layihə parametrləri`,manageAccount:`Hesabı idarə et`,email:`E-poçt`,copyEmail:`E-poçtu kopyala`},schedule:{range:`{{from}} – {{to}}`,calendar:`Təqvim`,timeline:`Zaman xətti`,today:`Bu gün`,previousMonth:`Əvvəlki ay`,nextMonth:`Növbəti ay`,previousWeeks:`Əvvəlki iki həftə`,nextWeeks:`Növbəti iki həftə`,emptyWeeks:`Bu həftələrdə planlaşdırılmış heç nə yoxdur`,monthTickets:`{{title}} tiketləri`,showDay_one:`{{day}} tarixindəki bütün {{count, number}} tiketi göstər`,showDay_other:`{{day}} tarixindəki bütün {{count, number}} tiketi göstər`,more_one:`daha {{count, number}}`,more_other:`daha {{count, number}}`,ticketColumn:`Tiket`,scheduledTickets:`Planlaşdırılmış tiketlər`},copy:{description:`Təsvir`,acceptanceCriteria:`Qəbul meyarları`}},detail:{field:{title:`Başlıq`,description:`Təsvir`,acceptanceCriteria:`Qəbul meyarları`,parent:`Valideyn`,parentTicket:`Valideyn tiket`,labels:`Etiketlər`,scopes:`Əhatələr`,holdReason:`Gözləmə səbəbi`,url:`URL`},page:{preview:`Tiket önizləməsi`,unavailable:`Tiket əlçatan deyil`,noProject:`Keçiddə tiketin layihəsi göstərilməyib.`,gone:`Bu tiket silinib və ya artıq ona girişiniz yoxdur.`,closePreview:`Önizləməni bağla`,openFullPage:`Tam səhifədə aç`,backToTickets:`Tiketlərə qayıt`,tickets:`Tiketlər`,previousTicket:`Əvvəlki tiket`,previousTicketShortcut:`Əvvəlki tiket (K)`,nextTicket:`Növbəti tiket`,nextTicketShortcut:`Növbəti tiket (J)`,position:`{{position, number}} / {{total, number}}`,subscribe:`Abunə ol`,unsubscribe:`Abunəlikdən çıx`,subscribed:`Yeniliklərə abunə oldunuz`,unsubscribed:`Abunəlik ləğv edildi`,archive:`Tiketi arxivləşdir`,unarchive:`Tiketi arxivdən çıxar`,archivedToast:`Tiket arxivləşdirildi`,unarchivedToast:`Tiket arxivdən çıxarıldı`,archived:`Arxivləşdirilib`,edit:`Tiketi redaktə et`,editShortcut:`Tiketi redaktə et (E)`,editTitle:`{{key}} redaktə et`,actions:`Tiket əməliyyatları`,copyKey:`Tiket açarını kopyala`,keyCopied:`{{key}} kopyalandı`,noDescription:`_Təsvir yoxdur_`,noAcceptanceCriteria:`_Qəbul meyarları yoxdur_`,coordination:`Tiket koordinasiyası`,properties:`Xüsusiyyətlər`,created:`Yaradılıb`,updated:`Yenilənib`},section:{discussion:`Müzakirə`,evidence:`Sübutlar`,activity:`Fəaliyyət`},readiness:{heading:`Agent hazırlığı`,ready:`Götürməyə hazırdır`,notReady:`Götürməyə hazır deyil`,hold:`Gözləmə: {{reason}}`,claimedUntil:`{{date}} tarixinədək <strong>{{worker}}</strong> tərəfindən götürülüb.`},claim:{release:`Götürməmi burax`,releaseDescription:`İşçiniz yazmağı dayandırdıqdan sonra sahiblik buraxılsın?`,recover:`Müddəti bitmiş götürməni bərpa et`,recoverHelp:`Əvvəlcə işçinin dayandığını təsdiqləyin.`,reason:`Bərpa səbəbi`,confirmed:`İşçinin dayandığını təsdiqləmişəm`,submit:`Sahibliyi bərpa et`},editor:{storageUnavailable:`Bu brauzer qaralamanızı yadda saxlaya bilmir. Yadda saxlayana qədər redaktoru açıq saxlayın.`,saved:`{{key}} yadda saxlanıldı`,created:`{{key}} yaradıldı`,properties:`Tiket xüsusiyyətləri`,readyForAgents:`Agentlər üçün hazırdır`,readyForAgentsHelp:`Tələblər, bloklayıcılar və sahiblik imkan verdikdə agentlər bu tiketi götürə bilər`,agents:`Agentlər`,notReady:`Hazır deyil`,moreProperties:`Daha çox xüsusiyyət`,holdPlaceholder:`İş niyə gözləməlidir?`,claimsNote:`Götürmələr yenə də tələblərdən, bloklayıcılardan və cari sahiblikdən asılıdır.`,restoreSize:`Redaktorun ölçüsünü bərpa et`,expand:`Redaktoru genişləndir`,staleTitle:`Bu tiket dəyişib.`,staleDescription:`Qaralamanız hələ {{version}} versiyasından istifadə edir; yadda saxlamaq üçün cari versiya tələb olunacaq.`,discardAndReload:`Qaralamanı at və yenidən yüklə`,titlePlaceholder:`Tiket başlığı`,descriptionPlaceholder:`Kontekst, tələblər və ya təkrarlama addımları əlavə edin…`,criteriaPlaceholder:`- [ ] Bunun tamamlandığını nə göstərəcək?`,removeCriteria:`Qəbul meyarlarını sil`,addCriteria:`Qəbul meyarları əlavə et`,savedLocally:`Qaralama bu cihazda yadda saxlanıldı`,notSavedLocally:`Qaralama yadda saxlanıla bilmədi`,discardDraft:`Qaralamanı at`,createMore:`Daha çox yarat`,save:`Tiketi yadda saxla`,create:`Tiket yarat`},resource:{removeConfirm:`{{name}} silinsin?`,uploading:`Yüklənir…`},attachment:{heading:`Qoşmalar`,tooLarge:`Hər biri 10 MB-dan böyük olmayan fayllar seçin.`,attached_one:`Fayl qoşuldu`,attached_other:`{{count, number}} fayl qoşuldu`,attach:`Fayl qoş`,emptyWritable:`Qoşma yoxdur. Faylları bura atın.`,empty:`Qoşma yoxdur`,remove:`Qoşmanı sil`,fileInput:`Fayl (10 MB-a qədər)`},evidence:{description:`Bu tiket üzərində işləyən insanların və inteqrasiyaların bildirdiyi keçidlər və nəticələr.`,add:`Sübut əlavə et`,empty:`Qoşulmuş sübut yoxdur`,added:`Sübut əlavə edildi`,kindLabel:`Növ`,outcomeLabel:`Bildirilən nəticə`,commit:`Commit (tam hash)`,note:`Qeyd`},link:{heading:`Keçidlər`,add:`Keçid əlavə et`,edit:`Keçidi redaktə et`,save:`Keçidi yadda saxla`,remove:`Keçidi sil`,empty:`Keçid yoxdur`,stateUnknown:`Vəziyyət naməlumdur`,remote:`{{kind}} · {{state}}`,remoteState:{open:`Açıq`,closed:`Bağlı`,merged:`Birləşdirilib`,draft:`Qaralama`,active:`Aktiv`,deleted:`Silinib`}},activity:{previous:`Əvvəlki fəaliyyət`,next:`Növbəti fəaliyyət`},relations:{heading:`Əlaqələr`,add:`Əlaqə əlavə et`,linkExisting:`Mövcud tiketi bağla`,newChild:`Yeni alt tiket`,newChildTitle:`{{key}} üçün yeni alt tiket`,children:`Alt tiketlər <count>{{done, number}}/{{total, number}}</count>`,previousChildren:`Əvvəlki alt tiketlər`,nextChildren:`Növbəti alt tiketlər`,empty:`Əlaqə yoxdur`,type:`Əlaqə`,ticket:`Tiket`,statusOf:`{{key}} statusu`,assigneeOf:`{{key}} icraçısı`,remove:{blocks:`“Bloklayır” əlaqəsini sil`,related:`“Əlaqəlidir” əlaqəsini sil`,duplicate:`“Təkrarlayır” əlaqəsini sil`,causes:`“Səbəb olur” əlaqəsini sil`,clones:`“Klonlayır” əlaqəsini sil`,implements:`“Həyata keçirir” əlaqəsini sil`,reviews:`“Yoxlayır” əlaqəsini sil`}},discussion:{empty:`Hələ şərh yoxdur`,previous:`Əvvəlki şərhlər`,next:`Növbəti şərhlər`,comment:`Şərh yaz`,placeholder:`Şərh yazın…`,posting:`Göndərilir…`,edited:`Redaktə edilib`,edit:`Şərhi redaktə et`,delete:`Şərhi sil`,deleteConfirm:`Bu şərh həmişəlik silinsin?`},markdown:{format:{heading:`Başlıq`,bold:`Qalın`,italic:`Kursiv`,link:`Keçid`,linkHelp:`Seçilmiş mətnə keçid əlavə et`,bullets:`Markerli siyahı`,numbers:`Nömrəli siyahı`,tasks:`Yoxlama siyahısı`,code:`Kod bloku`},formatting:`{{label}} formatlaşdırması`,editor:`{{label}} redaktoru`,insertFile:`Fayl daxil et`,insertFileInto:`{{label}} sahəsinə fayl daxil et`,write:`Yaz`,visual:`Vizual`,preview:`Önizləmə`,unsupported:`Bu mətndə vizual redaktorun saxlaya bilmədiyi Markdown var, məsələn, HTML və ya haşiyələr.`,nothingToPreview:`Önizləmək üçün heç nə yoxdur`,mention:`Üzvü qeyd et`,insertBlock:`Blok daxil et`,block:{quote:`Sitat`,table:`Cədvəl`,diagram:`Diaqram`,divider:`Ayırıcı`,file:`Fayl`},table:{addRow:`Sətir əlavə et`,addColumn:`Sütun əlavə et`,deleteRow:`Sətri sil`,deleteColumn:`Sütunu sil`,deleteTable:`Cədvəli sil`},linkAddress:`Keçid ünvanı`,diagram:`Diaqram`,diagramFailed:`Bu diaqram çəkilə bilmədi: {{error}}`,diagramSource:`Diaqram mənbəyi`}},settings:{account:{title:`Parametrlər`,security:`Daxil olma və təhlükəsizlik`,localPassword:`Yerli şifrə`,localPasswordDescription:`Şifrəni dəyişmək digər seanslarınızdan çıxış edir.`,changePassword:`Şifrəni dəyiş`,oidcLinked:`Eyni hesaba daxil olmaq üçün bu identiklik provayderindən istifadə edin.`,oidcUnlinked:`Bu provayder vasitəsilə daxil olmazdan əvvəl hesabınızı bağlayın.`,linked:`Bağlanıb`,link:`{{name}} bağla`,currentPassword:`Cari şifrə`,newPassword:`Yeni şifrə`,confirmPassword:`Yeni şifrəni təsdiqləyin`,passwordMismatch:`Yeni şifrələr uyğun gəlmir.`,passwordUpdated:`Şifrə yeniləndi. Digər seanslardan çıxış edildi.`},preferences:{behavior:`Davranış`,languageDescription:`Bu brauzerdə, o cümlədən daxil olma səhifəsində tətbiq olunur.`,weekStart:`Həftənin başlanğıcı`,weekStartDescription:`Bütün tarix seçicilərinə tətbiq olunur.`,weekStartLanguage:`Dil standartı`,sidebar:`Açılışda yan panel`,sidebarDescription:`Kanvil növbəti dəfə açılanda qüvvəyə minir.`,sidebarLast:`Son vəziyyətdə`,sidebarExpanded:`Genişləndirilmiş`,sidebarCollapsed:`Yığılmış`,ticketLists:`Tiket siyahıları`,reset:`Seçimləri sıfırla`,resetDescription:`Bu brauzerdə bu seçimləri standart dəyərlərə qaytarır.`,resetDone:`Seçimlər sıfırlandı`},notifications:{title:`Bildirişlər`,inbox:`Gələnlər`,inboxDescription:`İzlədiyiniz tiketlərin yenilikləri Kanvil-də görünür.`,inboxSwitch:`Abunə olunmuş tiket yenilikləri Gələnlərdə`,push:`Push bildirişləri`,pushDescription:`Qeydiyyatdan keçirdiyiniz cihazlara yenilikləri göndərin.`,pushUnavailable:`Bu nüsxədə push çatdırılması aktiv deyil.`,pushSwitch:`Qeydiyyatlı cihazlarda push bildirişləri`,categories:`Kateqoriyalar`,categorySwitch:`{{label}} bildirişləri`,reminder:`Müddət yaxınlaşma xatırlatması`,reminderDescription:`İzlənilən tiketin son tarixindən nə qədər əvvəl.`,reminderUnit:`Xatırlatma vahidi`,reminderAmount:`Xatırlatma miqdarı`,unit:{minutes:`Dəqiqə`,hours:`Saat`,days:`Gün`,weeks:`Həftə`}},layout:{nav:`Parametrlər`,section:`Parametrlər bölməsi`,nested:`{{parent}} · {{child}}`,name:`Ad`,description:`Təsvir`,descriptionPlaceholder:`Təsvir əlavə edin`,noDescription:`Təsvir yoxdur`,stale:`Bu başqa yerdə dəyişdirilib. Ləğv edib yenidən başlayın.`},profile:{title:`Profil`,displayName:`Görünən ad`,edit:`Profili redaktə et`,changePhoto:`Şəkli dəyiş`,removePhoto:`Şəkli sil`,photoFile:`Profil şəkli faylı`,photoUpdated:`Şəkil yeniləndi`,photoRemoved:`Şəkil silindi`,saved:`Profil yadda saxlanıldı`},credentials:{global:`Qlobal`,globalDescription:`Hesabınızın indi və gələcəkdə giriş əldə etdiyi bütün layihələrə və iş məkanlarına giriş.`,title:`API açarları`,description:`Skriptləri, inteqrasiyaları və agentləri ləğv edə biləcəyiniz girişlə qoşun.`,create:`API açarı yarat`,creating:`Yaradılır…`,created:`API açarı yaradıldı`,name:`Ad`,access:`Giriş`,projects:`Layihələr`,expires:`Bitmə tarixi`,actions:`Əməliyyatlar`,unavailableProject:`Əlçatan olmayan layihə`,untitled:`Adsız açar`,expired:`Müddəti bitib`,disabled:`Deaktiv`,custom:`Fərdi`,noProjects:`Layihəyə giriş yoxdur`,never:`Heç vaxt`,revoke:`API açarını ləğv et`,revokeNamed:`{{name}} ləğv edilsin? Ondan istifadə edən bağlantılar dərhal girişini itirəcək.`,revokeUnnamed:`Bu API açarı ləğv edilsin? Ondan istifadə edən bağlantılar dərhal girişini itirəcək.`,empty:`API açarı yoxdur`,emptyDescription:`Skript, inteqrasiya və ya agentin layihələrinizə girişi lazım olduqda açar yaradın.`,copyNow:`Bu açarı indi kopyalayın. Onu bir daha görə bilməyəcəksiniz.`,newKey:`Yeni API açarı`,copy:`API açarını kopyala`,noProjectTitle:`Əvvəlcə layihə seçin`,noProjectDescription:`API açarlarının ən azı bir layihəyə girişi olmalıdır.`,goToProjects:`Layihələrə keç`,keyName:`Açarın adı`,keyNamePlaceholder:`məs. Buraxılış avtomatlaşdırması`,projectAccess:`Layihəyə giriş`,findProject:`Layihə tap`,projectDescription:`{{key}} · {{workspace}}`,workspace:`İş sahəsi`,noMatches:`Uyğun layihə yoxdur`,roleLimit:`Mövcud layihə rolunuz bu açarın imkanlarını yenə də məhdudlaşdırır.`,accessLevel:`Giriş səviyyəsi`,preset:{read:`Yalnız oxuma`,write:`Oxuma və yazma`,automation:`Oxuma, yazma və iş götürmə`,custom:`Fərdi icazələr`},permission:{intake:{name:`Hesabat göndər`,description:`Yalnız karantində biletlər yaradır və onlara fayl əlavə edir. Biletləri oxuya və ya redaktə edə bilməz. Tək istifadə edin.`},read:{name:`Tiketləri oxumaq`,description:`Layihələrə, tiketlərə, şərhlərə və qoşmalara baxmaq.`},write:{name:`Tiketləri redaktə etmək`,description:`Tiketləri, şərhləri və qoşmaları yaratmaq və yeniləmək.`},claim:{name:`İş götürmək`,description:`Tiket sahibliyini götürmək, yeniləmək və buraxmaq.`},admin:{name:`Layihələri və iş məkanlarını idarə et`,description:`Rolunuzun icazə verdiyi layihə və iş məkanı parametrlərini və üzvlükləri dəyişin.`}},expiry:{7:`7 gün`,30:`30 gün`,90:`90 gün`,365:`1 il`,custom:`Fərdi tarix`,never:`Müddətsiz`},expiresOn:`Bitmə tarixi`,neverDescription:`Açar siz onu silənə qədər işləyir.`},calendarFeeds:{title:`Təqvim lentləri`,new:`Yeni təqvim lenti`,created:`Təqvim lenti yaradıldı`,create:`Lent yarat`,adminOnly:`Təqvim lentlərini layihə administratorları paylaşır və ləğv edir.`,labels:`Etiketlər`,everyLabel:`Bütün etiketlər`,stopped:`· Dayandırılıb: yaradanın artıq girişi yoxdur`,revoke:`{{name}} ləğv et`,revokeDescription:`{{name}} lentinə abunə olan təqvimlər dərhal yenilənməyi dayandırır. Tiketlər dəyişməz qalır.`,revoked:`Təqvim lenti ləğv edildi`,empty:`Təqvim lenti yoxdur`,copyNow:`Bu keçidi indi təqvim tətbiqinə kopyalayın. O, yalnız bir dəfə göstərilir və ona sahib olan hər kəs paylaşılan tiketləri oxuya bilər.`,link:`Lent keçidi`,name:`Lentin adı`,onlyLabels:`Yalnız bu etiketləri olan tiketlər`,explanation:`Lent başlama və ya son tarixi olan açıq, arxivləşdirilməmiş tiketləri {{zone}} saat qurşağında bütün günlük hadisələr kimi göstərir. Keçidə sahib olan hər kəs onların açarlarını, başlıqlarını, statuslarını, prioritetlərini və tarixlərini görür.`},project:{title:`Layihə parametrləri`,edit:`Layihəni redaktə et`,copyPrefix:`Tiket prefiksini kopyala`,saved:`Layihə yadda saxlanıldı`,noOpenTickets:`Açıq tiket yoxdur`,openTickets_one:`{{count, number}} açıq tiket`,openTickets_other:`{{count, number}} açıq tiket`,overdueTickets_one:`· {{count, number}} gecikmiş`,overdueTickets_other:`· {{count, number}} gecikmiş`,created:`· Yaradılıb: <time/>`,access:`Layihəyə giriş`,accessWorkspace:`İş sahəsi üzvləri girişi iş sahəsindəki rollarından alır.`,accessPrivate:`Bu layihəni yalnız əlavə etdiyiniz üzvlər və iş sahəsi administratorları görə bilər.`,accessUpdated:`Layihəyə giriş yeniləndi`,visibility:{workspace:`İş sahəsindəki hər kəs`,private:`Yalnız layihə üzvləri`},workspace:`İş sahəsi`,workspaceSettings:`İş sahəsi parametrləri`,prefix:`Tiket prefiksi`,prefixDescription:`Dəyişiklikdən sonra əvvəlki prefikslər və tiket açarları işləməyə davam edir.`,changePrefix:`Prefiksi dəyiş`,changePrefixTitle:`Tiket prefiksini dəyiş`,changePrefixDescription:`{{name}} layihəsindəki hər tiket yeni prefiksi alır və nömrəsini saxlayır.`,changing:`Dəyişdirilir…`,prefixChanged:`Tiketlər indi {{key}} istifadə edir`,publicLink:`İctimai keçid`,publicDisclosure:`Keçidə sahib olan hər kəs bu layihədəki hər arxivləşdirilməmiş tiketin başlığını, təsvirini, qəbul meyarlarını, statusunu, prioritetini, növünü, çətinliyini, etiketlərini, əhatələrini, tarixlərini və digər tiketlərə keçidlərini oxuya bilər. İnsanlar, şərhlər, fəaliyyət, qoşmalar, sübutlar və götürmələr gizli qalır.`,publicOn:`İctimai keçid aktivdir`,publicOff:`İctimai keçid deaktivdir`,off:`Deaktiv`,copyPublicLink:`İctimai keçidi kopyala`,openPublic:`İctimai görünüşü aç`,stopSharing:`Paylaşımı dayandır`,share:`İctimai paylaş`,background:`Fon`,backgroundDescription:`Bu layihənin tiket siyahısı və lövhəsinin arxasında göstərilir.`,backgroundUpdated:`Fon yeniləndi`,backgroundRemoved:`Fon silindi`,currentBackground:`Cari fon`,uploading:`Yüklənir…`,replace:`Əvəz et`,upload:`Şəkil yüklə`,backgroundFile:`Layihə fonu faylı`},tickets_one:`{{count, number}} tiket`,tickets_other:`{{count, number}} tiket`,moveUp:`{{name}} yuxarı köçür`,moveDown:`{{name}} aşağı köçür`,workflow:{title:`İş axını`,new:`Yeni vəziyyət`,edit:`Vəziyyəti redaktə et`,editNamed:`{{name}} vəziyyətini redaktə et`,deleteNamed:`{{name}} vəziyyətini sil`,onlyState:`{{name}} yeganə “{{category}}” vəziyyətidir`,saved:`Vəziyyət yadda saxlanıldı`,name:`Vəziyyətin adı`,category:`Kateqoriya`,color:`Vəziyyətin rəngi`,categoryChange:`Onun tiketlərinin statusu {{status}} olaraq dəyişir.`,save:`Vəziyyəti yadda saxla`,delete:`Vəziyyəti sil`,deleteTitle:`{{name}} sil`,deleted:`Vəziyyət silindi`,reassign_one:`{{count, number}} tiketi buraya köçür:`,reassign_other:`{{count, number}} tiketi buraya köçür:`},labels:{title:`Etiketlər`,search:`Etiketlərdə axtar`,new:`Yeni etiket`,edit:`Etiketi redaktə et`,workspace:`İş sahəsi`,editNamed:`{{name}} etiketini redaktə et`,deleteNamed:`{{name}} etiketini sil`,deleteUsed_one:`{{name}} arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Bu etiketdən istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleteUsed_other:`{{name}} arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Bu etiketdən istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleteUsedShared_one:`{{name}} bütün layihələrdə arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Bu etiketdən istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleteUsedShared_other:`{{name}} bütün layihələrdə arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Bu etiketdən istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleteUnused:`İstifadə olunmayan {{name}} etiketi silinsin? Ondan istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleted:`Etiket silindi`,noMatches:`Uyğun etiket yoxdur`,empty:`Hələ etiket yoxdur`,saved:`Etiket yadda saxlanıldı`,name:`Etiketin adı`,color:`Etiketin rəngi`,rename:`Adı dəyişdirmək bu etiketi tiketlərdə və yadda saxlanılmış görünüşlərdə yeniləyir.`,renameShared:`Adı dəyişdirmək bu etiketi bütün layihələrdə tiketlərdə və yadda saxlanılmış görünüşlərdə yeniləyir.`,adopt:`Layihə etiketlərini mənimsə`,adoptDescription:`{{projects}} layihələrində müəyyən edilib. Onların tiketləri etiketi saxlayır və bu rəngi alır.`,save:`Etiketi yadda saxla`},fields:{title:`Sahələr`,new:`Yeni sahə`,edit:`Sahəni redaktə et`,editNamed:`{{name}} sahəsini redaktə et`,deleteNamed:`{{name}} sahəsini sil`,deleteUsed_one:`{{name}} silinsin və onun dəyərləri arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Yadda saxlanılmış görünüşlər bu sahə üzrə filtrlərini itirəcək.`,deleteUsed_other:`{{name}} silinsin və onun dəyərləri arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Yadda saxlanılmış görünüşlər bu sahə üzrə filtrlərini itirəcək.`,deleteUnused:`İstifadə olunmayan {{name}} sahəsi silinsin? Yadda saxlanılmış görünüşlər bu sahə üzrə filtrlərini itirəcək.`,deleted:`Sahə silindi`,empty:`Hələ sahə yoxdur`,saved:`Sahə yadda saxlanıldı`,required:`Məcburi`,requiredDescription:`Yeni tiketlər üçün dəyər tələb olunur.`,defaultValue:`Standart: {{value}}`,default:`Standart`,name:`Sahənin adı`,type:`Tip`,options:`Seçimlər`,option:`Seçim {{number}}`,optionInline:`seçim {{number}}`,optionColor:`{{name}} rəngi`,removeOption:`{{name}} sil`,addOption:`Seçim əlavə et`,reassign:`{{name}} olan tiketlər`,clearValue:`Dəyəri təmizlə`,moveTo:`{{name}} seçiminə köçür`,save:`Sahəni yadda saxla`},lifecycle:{archivedToast:`Layihə arxivləşdirildi`,restoredToast:`Layihə bərpa edildi`,archivedNotice:`Arxivləşdirilib: <time/> · yalnız oxuma`,restore:`Bərpa et`,archive:`Arxivləşdir`,archivedDescription:`Arxivləşdirilib: <time/>. Onun tiketləri yalnız oxunur və siyahılarda, axtarışda, “Mənim işlərim”də, təqvim lentlərində və agent növbələrində görünmür.`,archiveDescription:`Layihəni siyahılardan, axtarışdan, “Mənim işlərim”dən, təqvim lentlərindən və agent növbələrindən gizlədir və tiketlərini yalnız oxunan edir. Tarixçə saxlanılır.`,archiveProject:`Layihəni arxivləşdir`,archiveConfirm:`{{name}} bərpa edilənə qədər layihə siyahılarından və agent növbələrindən çıxarılır. Götürülmüş tiketlər əvvəlcə buraxılmalıdır.`,blocker:`{{message}}: {{tickets}}`,row:{tickets:`Tiketlər`,losesAccess:`Girişini itirir`,gainsAccess:`Giriş əldə edir`,labelsCopied:`Kopyalanan etiketlər`,labelsMerged:`Birləşdirilən etiketlər`,followsRemoved:`Silinən izləmələr`,invitations:`Dəvətlər`,comments:`Şərhlər`,files:`Fayllar`,kept:`Saxlanılır`,projects:`Layihələr`,members:`Üzvlər`,sharedLabels:`Ümumi etiketlər`,signIn:`Daxil olma`,sessions:`Seanslar`,credentials:`Giriş məlumatları`,devices:`Cihazlar`,access:`Giriş`,claims:`Götürmələr`,imports:`İdxallar`,profile:`Profil`},transferTickets_one:`{{count, number}}, açarları və keçidləri saxlanılmaqla`,transferTickets_other:`{{count, number}}, açarları və keçidləri saxlanılmaqla`,noOne:`Heç kim`,transferInvitations_one:`{{count, number}} gözləyən dəvətdən layihə çıxarılır`,transferInvitations_other:`{{count, number}} gözləyən dəvətdən layihə çıxarılır`,files_one:`{{count, number}} · {{size}}`,files_other:`{{count, number}} · {{size}}`,keptProject_one:`{{count, number}} fəaliyyət qeydi və {{key}} prefiksi`,keptProject_other:`{{count, number}} fəaliyyət qeydi və {{key}} prefiksi`,keptWorkspace_one:`{{count, number}} fəaliyyət qeydi və {{slug}} URL açarı`,keptWorkspace_other:`{{count, number}} fəaliyyət qeydi və {{slug}} URL açarı`,archivedProject:`arxivləşdirilib`,membersLoseAccess_one:`{{count, number}} nəfər girişini itirir; hesabı qalır`,membersLoseAccess_other:`{{count, number}} nəfər girişini itirir; hesabları qalır`,pendingInvitations_one:`{{count, number}} gözləyir`,pendingInvitations_other:`{{count, number}} gözləyir`,move:`Köçür`,moveTitle:`{{name}} köçür`,moved:`{{name}} iş sahəsinə köçürüldü`,movedUnnamed:`İş sahəsinə köçürüldü`,destination:`Təyinat iş sahəsi`,moveProject:`Layihəni köçür`,moving:`Köçürülür…`,export:`İxrac`,exportDescription:`Tiketlər iş axını, sahələr, etiketlər, əlaqələr, şərhlər, sübutlar və keçidlərlə birlikdə versiyalı JSON kimi. Qoşmalar və fəaliyyət tarixçəsi burada qalır.`,exporting:`İxrac edilir…`,exportJson:`JSON ixrac et`,delete:`Sil`,deleteTitle:`{{name}} sil`,deleted:`{{name}} silindi`,typeToConfirm:`Təsdiqləmək üçün {{value}} yazın`,deleting:`Silinir…`,deleteProject:`Layihəni sil`,deleteProjectDescription:`Tiketləri, şərhləri və faylları həmişəlik silir. Fəaliyyət tarixçəsi qalır və {{key}} bir daha istifadə edilmir.`,signInAgain:`Yenidən daxil ol`,deleteWorkspace:`İş sahəsini sil`,deleteWorkspaceDescription:`İş sahəsindəki bütün layihələri tiketləri, şərhləri və faylları ilə birlikdə həmişəlik silir və üzvlərini çıxarır. Hesablar və fəaliyyət tarixçəsi qalır.`,noSignInMethods:`Bağlanmış daxil olma üsulu yoxdur`,sessions_one:`{{count, number}} seansdan çıxış edildi`,sessions_other:`{{count, number}} seansdan çıxış edildi`,credentials_one:`{{count, number}} API açarı və ya tətbiq bağlantısı ləğv edildi`,credentials_other:`{{count, number}} API açarı və ya tətbiq bağlantısı ləğv edildi`,devices_one:`{{count, number}} cihaz və ya bildiriş kanalı silindi`,devices_other:`{{count, number}} cihaz və ya bildiriş kanalı silindi`,workspaceCount_one:`{{count, number}} iş sahəsi`,workspaceCount_other:`{{count, number}} iş sahəsi`,directProjectCount_one:`{{count, number}} birbaşa layihə`,directProjectCount_other:`{{count, number}} birbaşa layihə`,accessWorkspaces:`{{workspaces}} silindi`,accessBoth:`{{workspaces}} və {{projects}} silindi`,claims_one:`{{count, number}} götürmə buraxıldı`,claims_other:`{{count, number}} götürmə buraxıldı`,imports_one:`{{count, number}} idxal ləğv edildi`,imports_other:`{{count, number}} idxal ləğv edildi`,profileErased:`Ad, e-poçt və şəkil silindi`,commentCount_one:`{{count, number}} şərh`,commentCount_other:`{{count, number}} şərh`,activityCount_one:`{{count, number}} fəaliyyət qeydi`,activityCount_other:`{{count, number}} fəaliyyət qeydi`,keptAccount:`{{tickets}}, {{comments}} və {{events}} “{{name}}” kimi göstərilir`,deleteAccount:`Hesabı sil`,deleteOwnAccount:`Daxil olma məlumatlarınızı, seanslarınızı, açarlarınızı, cihazlarınızı, üzvlüklərinizi və şəxsi məlumatlarınızı həmişəlik silir. Tiketlər, şərhlər və fəaliyyət qalır və “{{name}}” kimi göstərilir.`,deleteOtherAccount:`Bu hesabın daxil olma məlumatlarını, seanslarını, açarlarını, cihazlarını, üzvlüklərini və şəxsi məlumatlarını həmişəlik silir. Tiketlər, şərhlər və fəaliyyət qalır və “{{name}}” kimi göstərilir.`},icon:{change:`İkonu dəyiş`,title:`Layihə ikonu`,library:`İkon kitabxanası`,upload:`Şəkil yüklə`,search:`İkonlarda axtar`,searchPlaceholder:`İkonlarda axtar…`,color:`İkonun rəngi`,choose:`{{name}} seç`,noMatches:`Uyğun ikon yoxdur`,showMore:`Daha çox ikon göstər`,uploading:`Yüklənir…`,chooseImage:`Şəkil seç`,formats:`PNG, JPEG və ya WebP · 10 MB-a qədər`,file:`Layihə ikonu faylı`,reset:`İkonu sıfırla`},createProject:{title:`Yeni layihə`,inWorkspace:`{{name}} iş sahəsində`,everyoneLabel:`Bu iş sahəsindəki hər kəs`,name:`Layihənin adı`,namePlaceholder:`Mobil tətbiq`,create:`Layihə yarat`},workspace:{title:`İş sahəsi parametrləri`,owner:`Sahib`,noOwner:`Sahib yoxdur. Nüsxə administratoru təyin edə bilər.`,transfer:`Sahibliyi ötür`,transferring:`Ötürülür…`,transferred:`{{workspace}} artıq {{name}} istifadəçisinə məxsusdur`,newOwner:`Yeni sahib`,noEligible:`Sahiblik aktiv iş sahəsi administratoruna keçir. Əvvəlcə başqa bir üzvü administrator edin.`,chosenCan:`Bundan sonra {{name}} iş sahəsini ötürə və ya silə bilər.`,newOwnerCan:`Yeni sahib iş sahəsini ötürə və ya silə bilər.`,ownerStays:`{{name}} administrator olaraq qalır.`,unavailable:`İş sahəsi əlçatan deyil`,backToProjects:`Layihələrə qayıt`,name:`İş sahəsinin adı`,edit:`İş sahəsini redaktə et`,copyKey:`İş sahəsinin açarını kopyala`,saved:`İş sahəsi yadda saxlanıldı`,projects:`Layihələr`,projectCount_one:`Bu iş sahəsində girişiniz olan {{count, number}} layihə.`,projectCount_other:`Bu iş sahəsində girişiniz olan {{count, number}} layihə.`,viewProjects:`Layihələrə bax`,members:`Üzvlər`,membersDescription:`İş sahəsi rolları iş sahəsi girişindən istifadə edən layihələrə tətbiq olunur.`,manageMembers:`Üzvləri idarə et`},members:{title:`Üzvlər`,customRole:`Fərdi rol`,search:`Üzvlərdə axtar`,searchPlaceholder:`Ad və ya e-poçt…`,add:`Üzv əlavə et`,added:`Üzv əlavə edildi`,workspaceAccess:`İş sahəsi girişi`,owner:`Sahib`,roleFor:`{{name}} üçün rol`,useWorkspaceAccess:`İş sahəsi girişindən istifadə et`,remove:`Üzvü çıxar`,removeFromWorkspace:`{{name}} bu iş sahəsindən və onun bütün layihələrindən çıxarılsın?`,restoreAccess:`{{name}} üçün {{role}} iş sahəsi girişi bərpa edilsin?`,removeFromProject:`{{name}} üçün layihə girişi silinsin?`,noMatches:`Uyğun üzv yoxdur`,empty:`Üzv yoxdur`,projectRole:`Layihə rolu`,workspaceRole:`İş sahəsi rolu`},invitations:{title:`Dəvətlər`,invite:`E-poçtla dəvət et`,removedProjects:`Silinmiş layihələr`,wholeWorkspace:`Bütün iş sahəsi`,selectedProjects:`Seçilmiş layihələr`,expired:`Müddəti bitib`,expires:`Bitir: <time/>`,resend:`Dəvəti {{email}} ünvanına yenidən göndər`,revoke:`Dəvəti ləğv et`,revokeDescription:`{{email}} ünvanına göndərilən keçid işləməyi dayandıracaq.`,revoked:`Dəvət ləğv edildi`,empty:`Gözləyən dəvət yoxdur`,email:`E-poçt`,role:`Rol`,access:`Giriş`,projects:`Layihələr`,noProjects:`Bu iş sahəsində layihə yoxdur.`,linkExpires:`Keçidin bitmə müddəti`,inDays_one:`{{count, number}} gün sonra`,inDays_other:`{{count, number}} gün sonra`,submit:`Dəvət et`,link:`Dəvət keçidi`,copyLink:`Dəvət keçidini kopyala`,emailed:`{{email}} ünvanına göndərildi. Keçid həmin ünvan üçün bir dəfə işləyir.`,notEmailed:`Bu nüsxədə e-poçt konfiqurasiya edilməyib. Bu keçidi {{email}} ünvanına göndərin; o, həmin ünvan üçün bir dəfə işləyir.`,notEmailedUnknown:`Bu nüsxədə e-poçt konfiqurasiya edilməyib. Bu keçidi alıcıya göndərin; o, həmin ünvan üçün bir dəfə işləyir.`},workspaces:{title:`İş sahələri`,search:`İş sahələrində axtar`,searchPlaceholder:`Ad və ya URL açarı…`,noMatches:`Uyğun iş sahəsi yoxdur`,empty:`İş sahəsi yoxdur`,workspace:`İş sahəsi`,administrators:`Administratorlar`,members:`Üzvlər`,projects:`Layihələr`,created:`Yaradılıb`,actions:`Əməliyyatlar`,noAdministrator:`Administrator yoxdur`,manage:`{{name}} idarə et`,pagerNoun:`iş sahələri`,private:`Gizli`,noProjects:`Layihə yoxdur`},roles:{title:`Rollar`,new:`Yeni rol`,edit:`Rolu redaktə et`,builtIn:`Daxili`,readOnly:`Oxumadan başqa əməliyyat yoxdur`,memberships_one:`{{count, number}} üzvlük`,memberships_other:`{{count, number}} üzvlük`,editNamed:`{{name}} rolunu redaktə et`,deleteNamed:`{{name}} rolunu sil`,deleteUnused:`İstifadə olunmayan {{name}} rolu silinsin?`,deleted:`Rol silindi`,empty:`Rol yoxdur`,saved:`Rol yadda saxlanıldı`,name:`Rolun adı`,actions:`Əməliyyatlar`,everyoneReads:`Hər üzv çatdığı layihələri oxuya bilər.`,changesApply_one:`Dəyişikliklər bu rola malik {{count, number}} üzvlüyə dərhal tətbiq olunur.`,changesApply_other:`Dəyişikliklər bu rola malik {{count, number}} üzvlüyə dərhal tətbiq olunur.`,save:`Rolu yadda saxla`,delete:`Rolu sil`,reassignDescription_one:`{{count, number}} üzvlük {{name}} roluna malikdir. Keçəcəkləri rolu seçin.`,reassignDescription_other:`{{count, number}} üzvlük {{name}} roluna malikdir. Keçəcəkləri rolu seçin.`,moveTo:`Üzvləri bu rola köçür`},theme:{mode:{light:`İşıqlı`,dark:`Qaranlıq`,system:`Sistem`},aligned:`Uyğunlaşdırılıb`,hexColor:`Altırəqəmli hex rəngindən istifadə edin`,contrast:{light:`İşıqlı temanın mətn kontrastını ən azı 4.5:1-ə qədər artırın.`,dark:`Qaranlıq temanın mətn kontrastını ən azı 4.5:1-ə qədər artırın.`},fileTooLarge:`Tema faylları 100 KB-dan kiçik olmalıdır.`,tooMany:`Ən çox 50 fərdi tema saxlayın.`},appearance:{colorScheme:`Rəng sxemi`,theme:`Tema`,themes:`Temalar`,colorTheme:`Rəng teması`,search:`Temalarda axtar`,searchPlaceholder:`Temalarda axtar…`,create:`Tema yarat`,add:`Tema əlavə et`,importFile:`Tema faylını idxal et`,useMode:{light:`{{name}} işıqlı rejimindən istifadə et`,dark:`{{name}} qaranlıq rejimindən istifadə et`},modeOnly:{light:`Yalnız işıqlı rejim üçün istifadə et`,dark:`Yalnız qaranlıq rejim üçün istifadə et`},copyName:`{{name}} surəti`,duplicate:`{{name}} surətini çıxar`,edit:`{{name}} redaktə et`,export:`{{name}} ixrac et`,delete:`{{name}} sil`,deleteDescription:`{{name}} bu brauzerdən silinsin? Ondan istifadə edən işıqlı və ya qaranlıq görünüşlər əsas temanıza qayıdacaq.`,noMatches:`Uyğun tema yoxdur`,customTheme:`Fərdi tema`,name:`Temanın adı`,paletteColor:`{{mode}}: {{color, lowercase}}`,color:{background:`Fon`,foreground:`Mətn`,sidebar:`Yan panel`,primary:`Vurğu`,primaryForeground:`Vurğu mətni`},save:`Temanı yadda saxla`},color:{swatch:`{{color}} {{label, lowercase}}`,removeCustom:`{{hex}} fərdi rənglərdən sil`,custom:`Fərdi rəng`,default:`standart`,red:`qırmızı`,orange:`narıncı`,amber:`kəhrəba`,yellow:`sarı`,lime:`laym`,green:`yaşıl`,emerald:`zümrüd`,teal:`firuzəyi-yaşıl`,cyan:`mavi-yaşıl`,sky:`səma mavisi`,blue:`göy`,indigo:`indiqo`,violet:`bənövşəyi`,purple:`tünd bənövşəyi`,fuchsia:`fuksiya`,pink:`çəhrayı`,rose:`qızılgül`,gray:`boz`}},admin:{instance:{title:`İdarəetmə`,heading:`Nüsxə`,edit:`Nüsxəni redaktə et`,name:`Ad`,description:`Təsvir`,noDescription:`Təsvir yoxdur`,version:`Tətbiq versiyası`,publicUrl:`İctimai URL`,provider:{local:`Yerli PostgreSQL yaddaşı`,s3:`S3-uyğun obyekt yaddaşı`,postgres:`Daxili PostgreSQL`,elasticsearch:`Elasticsearch-uyğun axtarış`},section:{storage:`Fayl yaddaşı`,search:`Tiket axtarışı`,events:`Hadisə çatdırılması`},viewConfiguration:`Konfiqurasiyaya bax`,configure:{storage:`Yaddaşı konfiqurasiya et`,search:`Axtarışı konfiqurasiya et`,events:`Hadisə çatdırılmasını konfiqurasiya et`},providerTitle:`Provayder`,deploymentManaged:`Yerləşdirmə tərəfindən idarə olunur`,instanceManaged:`Nüsxə tərəfindən idarə olunur`,transfers:`Ötürmələr`,indexStatus:`İndeks statusu`,connection:`Bağlantı`,transfersS3:`Fayllar yerli olaraq yadda saxlanıldıqdan sonra obyekt yaddaşına köçürülür.`,transfersLocal:`Yeni fayllar PostgreSQL-də saxlanılır.`,searchBuiltIn:`Axtarış birbaşa PostgreSQL-də icra olunur.`,searchIndexed:`Tiket dəyişiklikləri arxa planda indeksləşdirilir.`,eventsConnection:`Bildirişlər istehlakçıları oyadır; PostgreSQL təkrar oxutma tarixçəsini saxlayır.`,builtIn:`Daxili`,connected:`Qoşulub`,upToDate:`Aktualdır`,status:{ready:`hazırdır`,transferring:`ötürülür`,indexing:`indeksləşdirilir`,connecting:`qoşulur`,unavailable:`əlçatan deyil`},pendingTransfers:`Gözləyən ötürmələr`,pendingRemain:`Ötürmə tamamlanana qədər bu fayllar PostgreSQL-dən əlçatan qalır.`,noPending:`Ötürülməyi gözləyən fayl yoxdur.`,bucket:`Bucket`,storageEndpoint:`Yaddaş ünvanı`,backup:`Ehtiyat nüsxə`,backupBuckets:`PostgreSQL-in və istinad edilən hər obyekt yaddaşı bucket-inin ehtiyat nüsxəsini çıxarın.`,backupDatabase:`Fayl baytları PostgreSQL ehtiyat nüsxənizə daxildir.`,fallback:`Ehtiyat variant`,fallbackDescription:`Xarici indeks geri qaldıqda və ya oflayn olduqda PostgreSQL axtarışı əlçatandır.`,searchEndpoint:`Axtarış ünvanı`,index:`İndeks`,delivery:`Çatdırılma`,deliveryDescription:`Davamlı hadisələr və təkrar oxutma PostgreSQL-də qalır. İstəyə bağlı broker oyanma siqnallarını paylayır.`,brokerEndpoint:`Broker ünvanı`,exchange:`Exchange`},configure:{storageTitle:`Fayl yaddaşını konfiqurasiya et`,searchTitle:`Tiket axtarışını konfiqurasiya et`,eventsTitle:`Hadisə çatdırılmasını konfiqurasiya et`,saved:`Nüsxə parametrləri yadda saxlanıldı`,instanceName:`Nüsxənin adı`,instanceDescription:`Nüsxənin təsviri`,publicUrlSource:`İctimai URL yerləşdirmənizdəki KANVIL_URL ilə təyin edilir.`,storageIntro:`Qoşmalar və profil şəkilləri bu provayderdən istifadə edir. Mövcud fayllar ilkin saxlanma yerində qalır.`,storageProvider:`Yaddaş provayderi`,localDescription:`Fayl baytları yerli verilənlər bazasında saxlanılır və PostgreSQL ehtiyat nüsxələrinə daxil edilir.`,s3Endpoint:`S3 ünvanı`,s3EndpointHint:`Amazon S3 üçün ünvanı boş saxlayın.`,region:`Region`,prefix:`Obyekt açarı prefiksi`,accessKeyId:`Giriş açarı ID-si`,secretAccessKey:`Gizli giriş açarı`,savedSecret:`Yadda saxlanılıb. Saxlamaq üçün boş buraxın.`,deploymentCredentials:`Göstərilmədikdə yerləşdirmənin giriş məlumatları`,pathStyle:`Yol üslublu bucket URL-lərindən istifadə et`,storageVerified:`Yaddaş bağlantısı təsdiqləndi`,testStorage:`Yaddaş bağlantısını yoxla`,filesWaiting_one:`{{count, number}} fayl ötürülməyi gözləyir. Gözləyən fayllar PostgreSQL-dən oxuna bilir.`,filesWaiting_other:`{{count, number}} fayl ötürülməyi gözləyir. Gözləyən fayllar PostgreSQL-dən oxuna bilir.`,transfersComplete:`Bütün fayl ötürmələri tamamlandı.`,storageDeployment:`Yaddaş KANVIL_STORAGE_PROVIDER və S3_* yerləşdirmə dəyişənləri ilə konfiqurasiya edilir.`,locations:`Mövcud saxlanma yerləri`,locationFiles_one:`{{count, number}} fayl`,locationFiles_other:`{{count, number}} fayl`,cleanUp:`İstifadə olunmayan faylları təmizlə`,cleanUpConfirm:`İstinad edilməyən fayllar və bir gündən köhnə uğursuz yükləmələr silinsin? Tiketlərə və ya profillərə qoşulmuş fayllar saxlanılır.`,cleanUpNote:`Təmizləmə həm də avtomatik işləyir. S3 obyektləri üçün verilənlər bazasının ehtiyat nüsxəsi ilə yanaşı bucket-in ehtiyat nüsxəsi lazımdır.`,searchProvider:`Axtarış provayderi`,searchBuiltIn:`Daxili PostgreSQL axtarışı`,indexName:`Kanvil indeksinin adı`,searchAuthentication:`Axtarış autentifikasiyası`,noAuthentication:`Autentifikasiya yoxdur`,apiKey:`API açarı`,basic:`İstifadəçi adı və şifrə`,searchApiKey:`Axtarış API açarı`,optional:`İstəyə bağlı`,searchUsername:`Axtarış istifadəçi adı`,searchPassword:`Axtarış şifrəsi`,searchVerified:`Axtarış bağlantısı təsdiqləndi`,testSearch:`Axtarış bağlantısını yoxla`,rebuildQueued:`Axtarışın yenidən qurulması növbəyə qoyuldu`,rebuild:`İndeksi yenidən qur`,searchNote:`İcazələr və filtrlər üçün PostgreSQL əsas mənbə olaraq qalır. Xarici indeks yenilənərkən və ya əlçatan olmadıqda axtarış PostgreSQL-ə keçir.`,brokerProvider:`Hadisə çatdırılması provayderi`,brokerBuiltIn:`Daxili PostgreSQL bildirişləri`,rabbitUrl:`RabbitMQ bağlantı URL-i`,savedBrokerEndpoint:`Yadda saxlanılmış broker ünvanı`,rabbitExchange:`RabbitMQ exchange`,brokerVerified:`RabbitMQ çatdırılması təsdiqləndi`,testBroker:`Broker bağlantısını yoxla`,brokerNote:`RabbitMQ təsdiqlənmiş hadisə oyanma siqnallarını Kanvil nüsxələrinə paylayır və arxa plan işçilərini oyadır. PostgreSQL davamlı hadisələri və gözləyən işləri saxlayır, broker əlçatan olmadıqda oyanma siqnallarını özü çatdırır.`,brokerDeployment:`Hadisə çatdırılması KANVIL_BROKER_PROVIDER və RABBITMQ_* yerləşdirmə dəyişənləri ilə konfiqurasiya edilir.`,save:`Nüsxə parametrlərini yadda saxla`},authentication:{title:`Autentifikasiya`,saved:`Autentifikasiya yadda saxlanıldı`,emailPassword:`E-poçt və şifrə ilə daxil olma`,emailPasswordDescription:`E-poçtla göndərilən daxil olma kodlarını və şifrə sıfırlamanı əhatə edir.`,ssoProvision:`SSO-dan istifadəçiləri avtomatik yarat`,ssoProvisionDescription:`Yeni SSO istifadəçilərinin dəvət edilənə qədər iş sahəsinə və ya layihəyə girişi olmur.`,registration:`İctimai e-poçt qeydiyyatı`,registrationClosed:`Bağlı`,identityProvider:`İdentiklik provayderi`,notConfigured:`Konfiqurasiya edilməyib`,linkedProviders:`Bağlı hesab provayderləri`,linkedProvidersDescription:`Yalnız onları bağlamış hesablarla daxil olmağa imkan verir; heç vaxt hesab yaratmır.`,save:`Autentifikasiyanı yadda saxla`,discoveryUrl:`OIDC kəşf URL-i`,clientId:`OIDC klient ID-si`,oidcSetup:`Yerləşdirmənizdə OIDC_DISCOVERY_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET və OIDC_NAME dəyərlərini və ya GITHUB_, GOOGLE_ ya da DISCORD_ üçün CLIENT_ID və CLIENT_SECRET cütünü konfiqurasiya edin, sonra Kanvil-i yenidən başladın. Provayder sirləri heç vaxt brauzerə qaytarılmır.`,consumers:`İstehlakçı interfeysləri`,mcpEndpoint:`MCP ünvanı`,eventFeed:`Real vaxt hadisə lenti`,openApi:`Open API spesifikasiyası`},users:{title:`İstifadəçilər`,search:`İstifadəçilərdə axtar`,searchPlaceholder:`Ad və ya e-poçt…`,new:`Yeni istifadəçi`,roleFilter:`Nüsxə rolu filtri`,allRoles:`Bütün rollar`,administrators:`Administratorlar`,users:`İstifadəçilər`,statusFilter:`Hesab statusu filtri`,allAccounts:`Bütün hesablar`,active:`Aktiv`,disabled:`Deaktiv`,noMatches:`Uyğun istifadəçi yoxdur`,empty:`İstifadəçi yoxdur`,user:`İstifadəçi`,role:`Rol`,status:`Status`,joined:`Qoşulub`,actions:`Əməliyyatlar`,administrator:`Administrator`,manage:`{{name}} idarə et`,pagerNoun:`istifadəçilər`,create:`Hesab yarat`,creating:`Yaradılır…`,created:`{{email}} yaradıldı`,name:`Ad`,email:`E-poçt`,initialPassword:`İlkin şifrə`,instanceRole:`Nüsxə rolu`},account:{details:`Hesab məlumatları`,joined:`Qoşulub: {{date}}`,edit:`Hesabı redaktə et`,instanceRole:`Nüsxə rolu`,adminDescription:`Bütün iş sahələrini, hesabları və yerləşdirmə parametrlərini idarə edə bilər.`,userDescription:`Girişi iş sahəsi və layihə rolları müəyyən edir.`,changeRole:`Rolu dəyiş`,changeRoleTitle:`Nüsxə rolunu dəyiş`,signInMethods:`Daxil olma üsulları`,setPassword:`Yerli şifrə təyin et`,activeSessions:`Aktiv seanslar`,sessions_one:`{{count, number}} seans`,sessions_other:`{{count, number}} seans`,revokeSessions:`Seansları ləğv et`,revokeConfirm:`{{name}} bütün cihazlardan çıxarılsın? API açarları aktiv qalır.`,sessionsRevoked:`Seanslar ləğv edildi`,access:`Hesaba giriş`,accessDisabled:`Daxil olma və API girişi deaktiv edilib.`,accessPreserved:`Hesabın deaktiv edilməsi tiketləri və fəaliyyət tarixçəsini saxlayır.`,enable:`Hesabı aktivləşdir`,disable:`Hesabı deaktiv et`,enableConfirm:`{{name}} üçün daxil olma bərpa edilsin?`,disableConfirm:`{{name}} üçün daxil olma və API girişi deaktiv edilsin və seansları ləğv edilsin?`,enabled:`Hesab aktivləşdirildi`,disabledToast:`Hesab deaktiv edildi`,updateFailed:`Hesab yenilənə bilmədi`,workspaces:`İş sahələri`,addToWorkspace:`İş sahəsinə əlavə et`,noWorkspaces:`İş sahəsi üzvlüyü yoxdur`,projectAccess:`Birbaşa layihə girişi`,updated:`Hesab yeniləndi`,roleUpdated:`Nüsxə rolu yeniləndi`,workspaceAdded:`İş sahəsi girişi əlavə edildi`,passwordMismatch:`Yeni şifrələr uyğun gəlmir.`,passwordSet:`Yerli şifrə təyin edildi və seanslar ləğv edildi`,name:`Ad`,email:`E-poçt`,user:`İstifadəçi`,administrator:`Administrator`,adminsNote:`Administratorlar bütün iş sahələrini, istifadəçiləri və nüsxə parametrlərini idarə edə bilər.`,workspace:`İş sahəsi`,workspaceRole:`İş sahəsi rolu`,newPassword:`Yeni şifrə`,confirmPassword:`Yeni şifrəni təsdiqləyin`,passwordNote:`Bu, yerli şifrə təyin edir və istifadəçini bütün cihazlardan çıxarır.`,save:`Dəyişiklikləri yadda saxla`},delivery:{field:{address:`E-poçt ünvanı`,server:`Server`,topic:`Mövzu`,accessToken:`Giriş tokeni`,applicationToken:`Tətbiq tokeni`,endpointUrl:`Ünvan URL-i`,signingSecret:`İmzalama sirri`,incomingWebhookUrl:`Daxil olan webhook URL-i`,webhookUrl:`Webhook URL-i`,channel:`Kanal`,botToken:`Bot tokeni`,chatId:`Çat ID-si`,chatIdPlaceholder:`-1001234567890 və ya @channel`,topicId:`Mövzu ID-si`,optional:`{{label}} (istəyə bağlı)`,removeSaved:`Yadda saxlanılmış {{label, lowercase}} sil`,savedSecret:`Yadda saxlanılıb · saxlamaq üçün boş buraxın`},failure:{blockedAddress:`Bu ünvana icazə verilmir`,redirectBlocked:`Ünvan yönləndirmə etdi`,dnsError:`Host tapılmadı`,networkError:`Qoşulmaq mümkün olmadı`,timeout:`Vaxt bitdi`,invalidEndpoint:`Yanlış ünvan`,emailUnavailable:`Bu serverdə e-poçt konfiqurasiya edilməyib`,smtpError:`Poçt serveri əlçatan deyil`,accessRevoked:`Onu qoşan administrator girişini itirib; yenidən qoşun`,repositoryMismatch:`Çatdırılma başqa repozitoridən gəldi`,http:`HTTP {{status}} ilə rədd edildi`,smtp:`Poçt serveri {{code}} cavabını verdi`},health:{delivery:{done:`Çatdırıldı: <time/>`,pending:`Göndərilir…`,none:`Hələ heç nə göndərilməyib`,failed:`{{reason}}`},access:{done:`Giriş yoxlanıldı: <time/>`,pending:`Giriş yoxlanılır…`,none:`Giriş yoxlanılmayıb`,failed:`Giriş yoxlaması: {{reason}}`},retrying:`yenidən cəhd edilir`},paused:`Dayandırılıb`,failing:`Uğursuz olur`,sendTest:`{{name}} üçün test göndər`,testQueued:`Test növbəyə qoyuldu`,resume:`{{name}} davam etdir`,pause:`{{name}} dayandır`,edit:`{{name}} redaktə et`,disconnect:`{{name}} ayır`,disconnected:`{{name}} ayrıldı`,name:`Ad`,type:`Tip`,channels:`Kanallar`,addChannel:`Kanal əlavə et`,noChannels:`Kanal yoxdur`,editChannel:`Kanalı redaktə et`,channelSaved:`Kanal yadda saxlanıldı`,channelRemoval:`{{name}} ayrılsın? Onun parametrləri və növbədəki bildirişləri silinəcək.`,sendFrom:`Bildirişləri buradan göndər`,routeOff:`Heç nə`,routeAll:`Bütün layihələr`,routeSelected:`Seçilmiş layihələr`,saveChannel:`Kanalı yadda saxla`,adminsOnly:`İnteqrasiyaları layihə administratorları idarə edir`,destinations:`Təyinatlar`,addDestination:`Təyinat əlavə et`,noDestinations:`Təyinat yoxdur`,editDestination:`Təyinatı redaktə et`,destinationSaved:`Təyinat yadda saxlanıldı`,destinationRemoval:`{{name}} ayrılsın? Onun parametrləri və növbədəki mesajları silinəcək.`,send:`Göndər`,saveDestination:`Təyinatı yadda saxla`},repository:{setup:{github:`Tokenin repozitoriyə oxuma girişi, şərh yazmaq və ya məsələləri sinxronlaşdırmaq üçün isə issue və pull request yazma girişi olmalıdır. Bu repozitorinin sətrindən kopyalanan URL, application/json məzmun tipi, bu sirr və Branch or tag creation, Branch or tag deletion, Issues, Issue comments və Pull requests hadisələri ilə GitHub webhooku əlavə edin.`,gitea:`Tokenə read:repository əhatəsi, şərh yazmaq və ya məsələləri sinxronlaşdırmaq üçün isə write:issue lazımdır. Bu repozitorinin sətrindən kopyalanan URL, application/json məzmun tipi, bu sirr və Create, Delete, Issues, Issue Label, Issue Comment və Pull Request hadisələri ilə Gitea webhooku əlavə edin.`,gitlab:`Tokenə read_api əhatəsi, şərh yazmaq və ya məsələləri sinxronlaşdırmaq üçün isə api lazımdır. Bu repozitorinin sətrindən kopyalanan URL, bu gizli token və Push, Issues, Comments və Merge request hadisələri ilə GitLab webhooku əlavə edin.`},noWebhook:`Webhook alınmayıb`,webhookReceived:`Webhook alındı: <time/>`,saved:`Repozitori yadda saxlanıldı`,title:`Repozitorilər`,connect:`Repozitori qoş`,empty:`Repozitori yoxdur`,checkAccess:`{{repository}} girişini yoxla`,accessQueued:`Giriş yoxlaması növbəyə qoyuldu`,copyWebhook:`{{repository}} üçün webhook URL-ini kopyala`,issueSync:`{{repository}} üçün məsələ sinxronizasiyası`,removal:`{{repository}} ayrılsın? Onun webhooku dərhal işləməyi dayandırır. Onun əlavə etdiyi keçidlər tiketlərdə qalır.`,edit:`Repozitorini redaktə et`,host:`Host`,serverUrl:`Server URL-i`,serverUrlOptional:`Server URL-i (istəyə bağlı)`,repository:`{{host}} repozitorisi`,nestedPlaceholder:`group/project`,placeholder:`owner/repository`,accessToken:`Giriş tokeni`,webhookSecret:`Webhook sirri`,savedSecret:`Yadda saxlanılıb · saxlamaq üçün boş buraxın`,commentOnLink:{pull_request:`Yeni bağlanmış məsələlərə və pull request-lərə şərh yaz`,merge_request:`Yeni bağlanmış məsələlərə və merge request-lərə şərh yaz`},moveTickets:`Bağlı tiketləri köçür`,noMove:`Köçürmə yoxdur`,save:`Repozitorini yadda saxla`},issueSync:{reason:{labels:`Onun sinxronizasiya etiketləri dəyişdi`,conflict:`Hər iki tərəfdə dəyişdirilib`,changedWhilePaused:`Repozitori dayandırılmış ikən məsələ dəyişdi`,unverified:`Hostun cavabı dəyişikliyi təsdiqləmədi`,claimConflict:`Tiket götürülüb`,invalidContent:`Məsələ tiketin icazə verdiyindən uzundur`},issuesSyncing_one:`{{count, number}} məsələ sinxronlaşdırılır`,issuesSyncing_other:`{{count, number}} məsələ sinxronlaşdırılır`,linksStopped_one:`{{count, number}} bağlantı dayandı`,linksStopped_other:`{{count, number}} bağlantı dayandı`,match:`Uyğunluq`,matchAny:`Bu etiketlərdən hər hansı biri`,matchAll:`Bu etiketlərin hamısı`,preview:{matching:`Sinxronizasiyanı davam etdir`,export:`Yaradılacaq yeni məsələlər`,pause:`Dayandırılacaq bağlantılar`,review:`Yoxlanılacaq bağlantılar`},applied:`Məsələ sinxronizasiyası tətbiq edildi`,title:`Məsələ sinxronizasiyası · {{repository}}`,outbound:`Kanvil → {{host}}`,exportTickets:`Tiketləri məsələ kimi ixrac et`,exportDescription:`Açıq tiketlər üçün yeni məsələ yaradılır; başlıq, təsvir, vəziyyət və yeni şərhlər uyğun saxlanılır.`,ticketLabels:`Yalnız bu etiketləri olan tiketlər`,everyTicket:`Bütün açıq tiketlər`,inbound:`{{host}} → Kanvil`,importIssues:`Məsələləri tiket kimi idxal et`,importDescription:`Açıq məsələlər {{host}} onları növbəti dəfə bildirəndə idxal edilir.`,issueLabels:`Yalnız bu etiketləri olan məsələlər`,everyIssue:`Bütün açıq məsələlər`,previewLabel:`Önizləmə`,previewAction:`Önizlə`,stopped:`Dayanmış bağlantılar · {{repository}}`,review:`Yoxla`,linkRemoved:`Bağlantı silindi`,resumed:`Sinxronizasiya bərpa edildi`,open:`Açıq`,closed:`Bağlı`,reviewTitle:`{{key}} yoxla`,mayExist:`{{host}}-da bu məsələ artıq ola bilər; yenidən yaratmazdan əvvəl yoxlayın.`,comparison:`Müqayisə`,field:{title:`başlıq`,description:`təsvir`,state:`vəziyyət`},notReported:`Bildirilməyib`,unlink:`Bağlantını kəs`,keepHost:`{{host}} versiyasını saxla`,keepKanvil:`Kanvil versiyasını saxla`,createAgain:`Məsələni yenidən yarat`,unlinkTicket:`{{key}} bağlantısını kəs`,unlinkConfirm:`{{key}} sinxronizasiyası dayandırılsın? Tiket də, məsələ də qalır.`},import:{count:{tickets:`Tiketlər`,workflow:`İş axını vəziyyətləri`,fields:`Fərdi sahələr`,labels:`Etiketlər`,relations:`Əlaqələr`,comments:`Şərhlər`,evidence:`Sübutlar`,links:`Keçidlər`},notExport:`Bu fayl Kanvil tiket ixracı deyil.`,progress_one:`{{status}} · {{count, number}} qeyddən {{done, number}}`,progress_other:`{{status}} · {{count, number}} qeyddən {{done, number}}`,progressFailed_one:`{{status}} · {{count, number}} qeyddən {{done, number}} · {{failed, number}} uğursuz`,progressFailed_other:`{{status}} · {{count, number}} qeyddən {{done, number}} · {{failed, number}} uğursuz`,title:`İdxal`,description:`Kanvil tiket ixracından və ya başqa izləmə sistemindən layihə yaradır: Linear, Jira, Trello, Plane, PLANKA və ya Kaneo.`,action:`Layihəni idxal et`,detailsOf:`{{name}} məlumatları`,details:`Məlumatlar`,exportFile:`Kanvil ixrac faylı`,source:`Mənbə`,file:`İxrac faylı`,jiraEdition:`Jira versiyası`,siteUrl:`Sayt URL-i`,accountEmail:`Hesab e-poçtu`,apiKey:`API açarı`,apiToken:`API tokeni`,personalToken:`Şəxsi giriş tokeni`,token:`Token`,apiUrl:`API URL-i`,workspaceSlug:`İş sahəsinin URL açarı`,signInWith:`Giriş üsulu`,username:`İstifadəçi adı və ya e-poçt`,password:`Şifrə`,checkConnection:`Bağlantı məlumatlarını yoxlayın`,noProjects:`Bu giriş məlumatları heç bir {{label, lowercase}} oxuya bilmir.`,includeComments:`Şərhləri daxil et`,projectName:`Layihənin adı`,start:`İdxal et`,connect:`Qoş`,preview:`Önizlə`,starting:`Başladılır…`,connecting:`Qoşulur…`,checking:`Yoxlanılır…`,matched:`Uyğunlaşdırılmış şəxslər`,unmatched:`Uyğunlaşdırılmamış şəxslər`,previewLabel:`İdxal önizləməsi`,errorPrefix:`Xəta: `,notePrefix:`Qeyd: `,progressLabel:`İdxal gedişatı`,notImported:`İdxal edilməyən qeydlər`,stop:`İdxalı dayandır`,openProject:`Layihəni aç`}},native:{"navigation.projectButton":`{{project}} ⌄`,"navigation.recentProjects":`Son layihələr`,"navigation.switchProject":`İş sahəsini və ya layihəni dəyiş`,labelValue:`{{label}}: {{value}}`,ui:{dismiss:`Bağla`,loading:`Yüklənir`},server:{url:`Server URL-i`,connect:`Qoş`,change:`Serveri dəyiş`,invalidOrigin:`Kanvil serverinizin yol və ya giriş məlumatları olmadan HTTP və ya HTTPS mənbəyindən istifadə edin.`,httpsRequired:`Kanvil serverinizə qoşulmaq üçün HTTPS-dən istifadə edin.`},login:{setupTitle:`Sahib hesabınızı yaradın`,signIn:`Daxil ol`,signingIn:`Daxil olunur…`,createAccount:`Hesab yarat`,signInWith:`{{provider}} ilə daxil ol`,name:`Ad`,setupToken:`Quraşdırma tokeni`,email:`E-poçt`,password:`Şifrə`},connection:{connecting:`qoşulur`,connected:`● Canlı`,offline:`Oflayn · keşlənmiş məlumat`,closed:`bağlıdır`,signedOut:`Yenidən qoşulmaq üçün daxil olun`},screen:{projects:`Layihələr`,inbox:`Gələnlər`,settings:`Parametrlər`,tickets:`Tiketlər`,ticket:`Tiket`,newTicket:`Yeni tiket`,editTicket:`Tiketi redaktə et`},projects:{empty:`Hələ layihə yoxdur`,new:`Yeni layihə`,name:`Layihənin adı`,prefix:`Tiket prefiksi`,description:`Təsvir`,create:`Layihə yarat`},tickets:{options:`Siyahı seçimləri`,expandAll:`Bütün bölmələri genişləndir`,collapseAll:`Bütün bölmələri yığ`,select:`Biletləri seç`,search:`Tiketlərdə axtar`,searchPlaceholder:`Başlıq, təsvir və ya açar`,allStatuses:`Bütün statuslar`,empty:`Uyğun tiket yoxdur`,count:`{{shown}} / {{total}}`},ticket:{priority:`Prioritet: {{priority}}`,noDifficulty:`Çətinlik göstərilməyib`,version:`v{{version}}`,starts:`Başlayır: {{date}}`,due:`Son tarix: {{date}}`},editor:{stale:`Bu tiket dəyişib. Yadda saxlanılmış qaralamanız hələ {{version}} versiyasından istifadə edir.`,discardDraft:`Qaralamanı at və yenidən yüklə`,title:`Başlıq`,noDifficulty:`Göstərilməyib`,labels:`Etiketlər (vergüllə ayrılmış)`,scopes:`Əhatələr (vergüllə ayrılmış)`,editMarkdown:`Markdown-u redaktə et`,previewMarkdown:`Markdown önizləməsi`,description:`Təsvir (Markdown)`,acceptanceCriteria:`Qəbul meyarları (Markdown)`,parent:`Valideyn tiketin ID-si`,automationReady:`Tələblər və asılılıqlar imkan verdikdə agentlər üçün hazırdır`,holdReason:`Gözləmə səbəbi`,draftSaved:`Qaralama bu cihazda yadda saxlanıldı`,save:`Tiketi yadda saxla`},detail:{archived:`Arxivləşdirilib`,subscribe:`Abunə ol`,unsubscribe:`Abunəlikdən çıx`,share:`Paylaş`,description:`Təsvir`,noDescription:`_Təsvir yoxdur_`,acceptanceCriteria:`Qəbul meyarları`,noAcceptanceCriteria:`_Qəbul meyarları yoxdur_`,fields:`Sahələr`,archive:{action:`Tiketi arxivləşdir`,title:`Tiket arxivləşdirilsin?`,message:`Bu tiket aktiv siyahılardan və agentlər üçün uyğunluqdan çıxarılsın?`},unarchive:{action:`Tiketi arxivdən çıxar`,title:`Tiket arxivdən çıxarılsın?`,message:`Bu tiket aktiv siyahılara qaytarılsın?`}},readiness:{title:`Agent hazırlığı`,ready:`Götürməyə hazırdır`,notReady:`Götürməyə hazır deyil`,hold:`Gözləmə: {{reason}}`,claimed:`{{time}} vaxtınadək {{worker}} tərəfindən götürülüb.`,release:{action:`Götürməmi burax`,title:`Götürmə buraxılsın?`,message:`Sahibliyi buraxmazdan əvvəl işçinizi dayandırın.`,confirm:`Burax`},recover:{action:`Müddəti bitmiş götürməni bərpa et`,explanation:`Əvvəlcə işçinin dayandığını təsdiqləyin.`,reason:`Bərpa səbəbi`,confirm:`İşçi dayandı · bərpa et`}},relations:{title:`Əlaqələr`,parent:`Valideyn · {{key}} · {{title}}`,openParent:`Valideyn tiketi aç`,children:`Alt tiketlər`,previousChildren:`Əvvəlki alt tiketlər`,nextChildren:`Növbəti alt tiketlər`,remove:`Əlaqəni sil`,removeTitle:`Əlaqə silinsin?`,add:`Əlaqə əlavə et`,find:`Tiket tap`,type:`Əlaqə`,ticket:`Tiket`,choose:`Tiket seçin`},discussion:{title:`Müzakirə`,previous:`Əvvəlki şərhlər`,next:`Növbəti şərhlər`,field:`Şərh (Markdown)`,submit:`Şərh yaz`,you:`Siz`,edit:`Şərhi redaktə et`,editField:`Şərh`,delete:`Şərhi sil`,deleteTitle:`Şərh silinsin?`,deleteMessage:`Bu şərh həmişəlik silinsin?`,save:`Şərhi yadda saxla`},attachments:{title:`Qoşmalar`,item:`{{name}} · {{size}} kB`,remove:`{{name}} sil`,removeTitle:`Qoşma silinsin?`,working:`İcra olunur…`,add:`Qoşma əlavə et`,tooLarge:`10 MB-dan böyük olmayan fayl seçin.`,sharingUnavailable:`Bu cihazda fayl paylaşımı əlçatan deyil.`},evidence:{title:`Sübutlar`,explanation:`Nəticələri onları təqdim edən hesablar bildirir.`,add:`Sübut əlavə et`,name:`Başlıq`,url:`URL`,outcome:`Bildirilən nəticə`,commit:`Commit (tam hash)`,note:`Qeyd`,save:`Sübutu yadda saxla`},activity:{title:`Fəaliyyət`,previous:`Əvvəlki fəaliyyət`,next:`Növbəti fəaliyyət`},inbox:{read:`Oxunub`,markAllRead:`Hamısını oxunmuş kimi qeyd et`,empty:`Bildiriş yoxdur`},notifications:{title:`Bildirişlər`,inApp:`Abunə olunmuş tiket yenilikləri Gələnlərdə`,push:`Push bildirişləri`,enableDevice:`Bu cihazda push-u aktivləşdir`,disableDevice:`Bu cihazda push-u deaktiv et`,unconfigured:`Bu quraşdırma push üçün Expo layihə ID-si və imzalama konfiqurasiyası tələb edir.`,noProjectId:`Bu quraşdırma push bildirişləri üçün Expo layihə ID-si tələb edir.`,permissionDenied:`Push-u aktivləşdirmək üçün cihaz parametrlərində bildirişlərə icazə verin.`,channel:`Tiket yenilikləri`},settings:{password:{title:`Yerli şifrə`,current:`Cari şifrə`,new:`Yeni şifrə`,change:`Şifrəni dəyiş`,updated:`Şifrə yeniləndi; digər seanslar ləğv edildi`},oidc:{linked:`Hesab bağlandı`,explanation:`Bu provayderlə daxil olmazdan əvvəl bu hesabı bağlayın.`,link:`{{provider}} bağla`},signOut:{action:`Çıxış et`,title:`Çıxış edilsin?`,message:`Bu hesabın keşlənmiş tiketləri və qaralamaları bu cihazdan silinəcək.`},changeServer:{title:`Server dəyişdirilsin?`,message:`Çıxış edilsin və bu hesabın yerli keşi və qaralamaları təmizlənsin?`}}}};export{e as default};
@@ -1 +0,0 @@
1
- import{et as e,mn as t,w as n,xt as r}from"./default-DqsN66dq.js";function i(e,t,r,i=[]){let o={kind:e};for(let s of a()[e]){let a=t[s.name]?.trim()??``;i.includes(s.name)?o[s.name]=``:a?o[s.name]=s.type===`number`?Number(a):a:r&&!n[e].includes(s.name)&&(o[s.name]=``)}return o}var a=()=>({email:[{name:`address`,label:t(`admin.delivery.field.address`),type:`email`}],ntfy:[{name:`server`,label:t(`admin.delivery.field.server`),type:`url`,placeholder:`https://ntfy.sh`,optional:!0},{name:`topic`,label:t(`admin.delivery.field.topic`)},{name:`token`,label:t(`admin.delivery.field.accessToken`),optional:!0}],gotify:[{name:`server`,label:t(`admin.delivery.field.server`),type:`url`,placeholder:`https://gotify.example.com`},{name:`token`,label:t(`admin.delivery.field.applicationToken`)}],webhook:[{name:`url`,label:t(`admin.delivery.field.endpointUrl`),type:`url`,placeholder:`https://`},{name:`secret`,label:t(`admin.delivery.field.signingSecret`),optional:!0}],slack:[{name:`url`,label:t(`admin.delivery.field.incomingWebhookUrl`),type:`url`}],discord:[{name:`url`,label:t(`admin.delivery.field.webhookUrl`),type:`url`}],mattermost:[{name:`url`,label:t(`admin.delivery.field.incomingWebhookUrl`),type:`url`},{name:`channel`,label:t(`admin.delivery.field.channel`),placeholder:`town-square`,optional:!0}],telegram:[{name:`token`,label:t(`admin.delivery.field.botToken`)},{name:`chatId`,label:t(`admin.delivery.field.chatId`),placeholder:t(`admin.delivery.field.chatIdPlaceholder`)},{name:`topicId`,label:t(`admin.delivery.field.topicId`),type:`number`,optional:!0}]}),o={edition:`cloud`,signIn:`password`,baseUrl:``,workspace:``,email:``,username:``,password:``,apiKey:``,token:``},s=e=>({name:`baseUrl`,label:t(`admin.import.siteUrl`),type:`url`,placeholder:e}),c={linear:()=>[{name:`token`,label:t(`admin.import.apiKey`),type:`password`}],jira:({edition:n})=>[{name:`edition`,label:t(`admin.import.jiraEdition`),choices:e},s(n===`cloud`?`https://example.atlassian.net`:`https://jira.example.com`),...n===`cloud`?[{name:`email`,label:t(`admin.import.accountEmail`),type:`email`},{name:`token`,label:t(`admin.import.apiToken`),type:`password`}]:[{name:`token`,label:t(`admin.import.personalToken`),type:`password`}]],trello:()=>[{name:`apiKey`,label:t(`admin.import.apiKey`),type:`password`},{name:`token`,label:t(`admin.import.token`),type:`password`}],plane:()=>[{...s(`https://api.plane.so`),label:t(`admin.import.apiUrl`)},{name:`workspace`,label:t(`admin.import.workspaceSlug`),placeholder:`my-team`},{name:`token`,label:t(`admin.import.apiToken`),type:`password`}],planka:({signIn:e})=>[s(`https://planka.example.com`),{name:`signIn`,label:t(`admin.import.signInWith`),choices:r},...e===`password`?[{name:`username`,label:t(`admin.import.username`)},{name:`password`,label:t(`admin.import.password`),type:`password`}]:[{name:`token`,label:t(`admin.import.apiKey`),type:`password`}]],kaneo:()=>[s(`https://kaneo.example.com`),{name:`token`,label:t(`admin.import.apiKey`),type:`password`}]},l={get blocked_address(){return t(`admin.delivery.failure.blockedAddress`)},get redirect_blocked(){return t(`admin.delivery.failure.redirectBlocked`)},get dns_error(){return t(`admin.delivery.failure.dnsError`)},get network_error(){return t(`admin.delivery.failure.networkError`)},get timeout(){return t(`admin.delivery.failure.timeout`)},get invalid_endpoint(){return t(`admin.delivery.failure.invalidEndpoint`)},get email_unavailable(){return t(`admin.delivery.failure.emailUnavailable`)},get smtp_error(){return t(`admin.delivery.failure.smtpError`)},get access_revoked(){return t(`admin.delivery.failure.accessRevoked`)},get repository_mismatch(){return t(`admin.delivery.failure.repositoryMismatch`)}},u=e=>l[e]??(e.startsWith(`http_`)?t(`admin.delivery.failure.http`,{status:e.slice(5)}):e.startsWith(`smtp_`)?t(`admin.delivery.failure.smtp`,{code:e.slice(5)}):e.replaceAll(`_`,` `));export{c as a,o as i,i as n,u as r,a as t};