@things-factory/auth-ui 10.1.198 → 10.1.203

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 (34) hide show
  1. package/dist-client/components/access-decision-popup.js +7 -5
  2. package/dist-client/components/access-decision-popup.js.map +1 -1
  3. package/dist-client/components/access-request-popup.d.ts +9 -1
  4. package/dist-client/components/access-request-popup.js +55 -4
  5. package/dist-client/components/access-request-popup.js.map +1 -1
  6. package/dist-client/components/role-task-edit.d.ts +22 -0
  7. package/dist-client/components/role-task-edit.js +17 -0
  8. package/dist-client/components/role-task-edit.js.map +1 -0
  9. package/dist-client/components/role-task-edit.test.d.ts +1 -0
  10. package/dist-client/components/role-task-edit.test.js +34 -0
  11. package/dist-client/components/role-task-edit.test.js.map +1 -0
  12. package/dist-client/pages/access-request/access-requests.js +4 -2
  13. package/dist-client/pages/access-request/access-requests.js.map +1 -1
  14. package/dist-client/pages/access-request/my-access-requests.js +3 -2
  15. package/dist-client/pages/access-request/my-access-requests.js.map +1 -1
  16. package/dist-client/pages/role/role-privilege-page.d.ts +10 -2
  17. package/dist-client/pages/role/role-privilege-page.js +62 -3
  18. package/dist-client/pages/role/role-privilege-page.js.map +1 -1
  19. package/dist-client/pages/role/role-task-review.test.js +13 -2
  20. package/dist-client/pages/role/role-task-review.test.js.map +1 -1
  21. package/dist-client/tsconfig.tsbuildinfo +1 -1
  22. package/dist-client/utils/access-request.d.ts +15 -1
  23. package/dist-client/utils/access-request.js +19 -2
  24. package/dist-client/utils/access-request.js.map +1 -1
  25. package/dist-client/utils/access-request.test.js +26 -1
  26. package/dist-client/utils/access-request.test.js.map +1 -1
  27. package/dist-server/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +4 -4
  29. package/translations/en.json +11 -1
  30. package/translations/es.json +11 -1
  31. package/translations/ja.json +11 -1
  32. package/translations/ko.json +11 -1
  33. package/translations/ms.json +11 -1
  34. package/translations/zh.json +11 -1
@@ -1 +1 @@
1
- {"version":3,"file":"my-access-requests.js","sourceRoot":"","sources":["../../../client/pages/access-request/my-access-requests.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\n\nimport type { DataGrist } from '@operato/data-grist'\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n\nimport { privilegeWords } from '../../utils/access-request.js'\nimport { arrayFromResponse, listOutcome } from '../../utils/list-result.js'\n\n/**\n * The person's own access requests - what became of each (ADR-0103). A notification may not reach them;\n * this does.\n */\n@customElement('my-access-requests-page')\nexport class MyAccessRequestsPage extends localize(i18next)(PageView) {\n static styles = [\n WorkSurfacePageStyles,\n CommonGristStyles,\n ScrollbarStyles,\n css`\n [failed] {\n padding: var(--spacing-medium);\n color: var(--md-sys-color-error);\n }\n `\n ]\n\n @property({ type: Boolean }) active: boolean = false\n @property({ type: Object }) config: any\n @property({ type: Boolean }) failed: boolean = false\n\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return { title: i18next.t('access-request.title.my access requests') }\n }\n\n render() {\n return html`\n <ox-grist class=\"page-fill\" .mode=${isMobileDevice() ? 'CARD' : 'GRID'} .config=${this.config} .fetchHandler=${this.fetchHandler.bind(this)}></ox-grist>\n ${this.failed ? html`<div failed>${i18next.t('text.the list could not be loaded')}</div>` : ''}\n `\n }\n\n async pageInitialized() {\n const t = i18next.t.bind(i18next)\n this.config = {\n pagination: { infinite: true },\n rows: { appendable: false, selectable: false },\n columns: [\n {\n type: 'gutter',\n gutterName: 'button',\n icon: 'undo',\n title: t('access-request.button.withdraw'),\n handlers: { click: (_c: any, _d: any, _col: any, record: any) => record?.state === 'PENDING' && this.withdraw(record) }\n },\n { type: 'string', name: 'needed', header: t('access-request.field.needed'), record: { editable: false }, width: 260 },\n { type: 'string', name: 'stateWords', header: t('field.state'), record: { align: 'center', editable: false }, width: 100 },\n { type: 'string', name: 'grantedRoleName', header: t('access-request.field.granted role'), record: { editable: false }, width: 140 },\n { type: 'string', name: 'decisionReason', header: t('access-request.field.decision reason'), record: { editable: false }, width: 220 },\n { type: 'datetime', name: 'createdAt', header: t('field.created at'), width: 170 }\n ]\n }\n }\n\n async pageUpdated(_changes: any, _lifecycle: any) {\n if (this.active) {\n await this.updateComplete\n this.grist.fetch()\n }\n }\n\n private async withdraw(record: any) {\n const confirmed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('access-request.button.withdraw'),\n text: record.needed,\n confirmButton: { text: i18next.t('access-request.button.withdraw') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n\n await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n withdrawAccessRequest(id: $id) {\n id\n }\n }\n `,\n variables: { id: record.id },\n context: gqlContext()\n })\n this.grist.fetch()\n }\n\n async fetchHandler() {\n const response = await client.query({\n query: gql`\n query {\n myAccessRequests {\n id\n category\n privilege\n state\n expiresAt\n createdAt\n decisionReason\n grantedRole {\n name\n }\n }\n }\n `,\n context: gqlContext(),\n fetchPolicy: 'network-only'\n })\n const { items, failed } = listOutcome(arrayFromResponse(response, 'myAccessRequests'))\n this.failed = failed\n const t = i18next.t.bind(i18next)\n const records = items.map((item: any) => {\n const state = item.state === 'PENDING' && item.expiresAt && new Date(item.expiresAt).getTime() <= Date.now() ? 'EXPIRED' : item.state\n return {\n ...item,\n state,\n stateWords: t(`access-request.state.${state.toLowerCase()}`),\n needed: privilegeWords(t, item.category, item.privilege),\n grantedRoleName: item.grantedRole?.name || ''\n }\n })\n return { total: records.length, records }\n }\n}\n"]}
1
+ {"version":3,"file":"my-access-requests.js","sourceRoot":"","sources":["../../../client/pages/access-request/my-access-requests.ts"],"names":[],"mappings":";AAAA,OAAO,iCAAiC,CAAA;AAExC,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAGlE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAE3E;;;GAGG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;IAA9D;;QAawB,WAAM,GAAY,KAAK,CAAA;QAEvB,WAAM,GAAY,KAAK,CAAA;IAyGtD,CAAC;aAvHQ,WAAM,GAAG;QACd,qBAAqB;QACrB,iBAAiB;QACjB,eAAe;QACf,GAAG,CAAA;;;;;KAKF;KACF,AAVY,CAUZ;IAQD,IAAI,OAAO;QACT,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,yCAAyC,CAAC,EAAE,CAAA;IACxE,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAA;0CAC2B,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,YAAY,IAAI,CAAC,MAAM,kBAAkB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QACzI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA,eAAe,OAAO,CAAC,CAAC,CAAC,mCAAmC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;KAC/F,CAAA;IACH,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACjC,IAAI,CAAC,MAAM,GAAG;YACZ,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC9B,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;YAC9C,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,QAAQ;oBACpB,IAAI,EAAE,MAAM;oBACZ,KAAK,EAAE,CAAC,CAAC,gCAAgC,CAAC;oBAC1C,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAO,EAAE,EAAO,EAAE,IAAS,EAAE,MAAW,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;iBACxH;gBACD,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,6BAA6B,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBACrH,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC1H,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC,CAAC,mCAAmC,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBACpI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAC,sCAAsC,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBACtI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE;aACnF;SACF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAAa,EAAE,UAAe;QAC9C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,cAAc,CAAA;YACzB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,MAAW;QAChC,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC;YACpC,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,gCAAgC,CAAC;YAClD,IAAI,EAAE,MAAM,CAAC,MAAM;YACnB,aAAa,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,gCAAgC,CAAC,EAAE;YACpE,YAAY,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE;SACnD,CAAC,CAAA;QACF,IAAI,CAAC,SAAS;YAAE,OAAM;QAEtB,MAAM,MAAM,CAAC,MAAM,CAAC;YAClB,QAAQ,EAAE,GAAG,CAAA;;;;;;OAMZ;YACD,SAAS,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE;YAC5B,OAAO,EAAE,UAAU,EAAE;SACtB,CAAC,CAAA;QACF,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;;;;;;;OAgBT;YACD,OAAO,EAAE,UAAU,EAAE;YACrB,WAAW,EAAE,cAAc;SAC5B,CAAC,CAAA;QACF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAA;QACtF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACjC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE;YACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAA;YACrI,OAAO;gBACL,GAAG,IAAI;gBACP,KAAK;gBACL,UAAU,EAAE,CAAC,CAAC,wBAAwB,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC5D,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC;gBAC5B,eAAe,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,EAAE;aAC9C,CAAA;QACH,CAAC,CAAC,CAAA;QACF,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAA;IAC3C,CAAC;;AA1G4B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;oDAAwB;AACxB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDAAY;AACV;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;oDAAwB;AAEzB;IAA1B,KAAK,CAAC,UAAU,CAAC;;mDAA0B;AAjBjC,oBAAoB;IADhC,aAAa,CAAC,yBAAyB,CAAC;GAC5B,oBAAoB,CAwHhC","sourcesContent":["import '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\n\nimport type { DataGrist } from '@operato/data-grist'\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n\nimport { neededWords } from '../../utils/access-request.js'\nimport { arrayFromResponse, listOutcome } from '../../utils/list-result.js'\n\n/**\n * The person's own access requests - what became of each (ADR-0103). A notification may not reach them;\n * this does.\n */\n@customElement('my-access-requests-page')\nexport class MyAccessRequestsPage extends localize(i18next)(PageView) {\n static styles = [\n WorkSurfacePageStyles,\n CommonGristStyles,\n ScrollbarStyles,\n css`\n [failed] {\n padding: var(--spacing-medium);\n color: var(--md-sys-color-error);\n }\n `\n ]\n\n @property({ type: Boolean }) active: boolean = false\n @property({ type: Object }) config: any\n @property({ type: Boolean }) failed: boolean = false\n\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return { title: i18next.t('access-request.title.my access requests') }\n }\n\n render() {\n return html`\n <ox-grist class=\"page-fill\" .mode=${isMobileDevice() ? 'CARD' : 'GRID'} .config=${this.config} .fetchHandler=${this.fetchHandler.bind(this)}></ox-grist>\n ${this.failed ? html`<div failed>${i18next.t('text.the list could not be loaded')}</div>` : ''}\n `\n }\n\n async pageInitialized() {\n const t = i18next.t.bind(i18next)\n this.config = {\n pagination: { infinite: true },\n rows: { appendable: false, selectable: false },\n columns: [\n {\n type: 'gutter',\n gutterName: 'button',\n icon: 'undo',\n title: t('access-request.button.withdraw'),\n handlers: { click: (_c: any, _d: any, _col: any, record: any) => record?.state === 'PENDING' && this.withdraw(record) }\n },\n { type: 'string', name: 'needed', header: t('access-request.field.needed'), record: { editable: false }, width: 260 },\n { type: 'string', name: 'stateWords', header: t('field.state'), record: { align: 'center', editable: false }, width: 100 },\n { type: 'string', name: 'grantedRoleName', header: t('access-request.field.granted role'), record: { editable: false }, width: 140 },\n { type: 'string', name: 'decisionReason', header: t('access-request.field.decision reason'), record: { editable: false }, width: 220 },\n { type: 'datetime', name: 'createdAt', header: t('field.created at'), width: 170 }\n ]\n }\n }\n\n async pageUpdated(_changes: any, _lifecycle: any) {\n if (this.active) {\n await this.updateComplete\n this.grist.fetch()\n }\n }\n\n private async withdraw(record: any) {\n const confirmed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('access-request.button.withdraw'),\n text: record.needed,\n confirmButton: { text: i18next.t('access-request.button.withdraw') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n\n await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n withdrawAccessRequest(id: $id) {\n id\n }\n }\n `,\n variables: { id: record.id },\n context: gqlContext()\n })\n this.grist.fetch()\n }\n\n async fetchHandler() {\n const response = await client.query({\n query: gql`\n query {\n myAccessRequests {\n id\n category\n privilege\n taskId\n state\n expiresAt\n createdAt\n decisionReason\n grantedRole {\n name\n }\n }\n }\n `,\n context: gqlContext(),\n fetchPolicy: 'network-only'\n })\n const { items, failed } = listOutcome(arrayFromResponse(response, 'myAccessRequests'))\n this.failed = failed\n const t = i18next.t.bind(i18next)\n const records = items.map((item: any) => {\n const state = item.state === 'PENDING' && item.expiresAt && new Date(item.expiresAt).getTime() <= Date.now() ? 'EXPIRED' : item.state\n return {\n ...item,\n state,\n stateWords: t(`access-request.state.${state.toLowerCase()}`),\n needed: neededWords(t, item),\n grantedRoleName: item.grantedRole?.name || ''\n }\n })\n return { total: records.length, records }\n }\n}\n"]}
@@ -133,10 +133,18 @@ export declare class RolePrivilegePage extends RolePrivilegePage_base {
133
133
  *
134
134
  * It says what the role holds, nothing more: 「전부」 is not "this person can do the work" (another role, the
135
135
  * data's own scope or an approval line decide that too), and 「일부」 is not a fault — a privilege several tasks
136
- * share makes each of them 「일부」. So nothing here offers to add or take away; the privileges view and the
137
- * template view do that.
136
+ * share makes each of them 「일부」.
137
+ *
138
+ * A task can be added to the role or taken off it (the user, 2026-10-20). The role stores privileges, never tasks,
139
+ * so either is a set of privileges, shown before it is saved. Taking a task off keeps what another task the role
140
+ * holds whole still uses (`role-task-edit.ts`). Both save at once and read the coverage again, so they are not
141
+ * offered over unsaved changes - the coverage is the saved role's.
138
142
  */
139
143
  private renderTasks;
144
+ /** 더하기 for a task the role lacks some or all of, 빼기 for one it holds some or all of. */
145
+ private taskEditButton;
146
+ /** Adds a task's missing privileges to the role, or takes its own off - after showing which - and saves. */
147
+ private editTask;
140
148
  private renderTask;
141
149
  /**
142
150
  * **The role against a template the administrator picks** (ADR-0139 decision 11) — whatever the role is called.
@@ -14,6 +14,7 @@ import { PageView } from '@operato/shell';
14
14
  import { CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
15
15
  import { getDomain } from '@things-factory/auth-base/dist-client';
16
16
  import { filterPrivilegeModules, selectedVisibleModule } from '../../components/role-privilege-picker-state.js';
17
+ import { pairOf, pairsToAdd, pairsToRemove } from '../../components/role-task-edit.js';
17
18
  const GRANT_FIELDS = 'category axes';
18
19
  /**
19
20
  * 한 역할이 무엇을 할 수 있나 — **켜진 것이 문장으로 본문에 선다.**
@@ -795,8 +796,12 @@ let RolePrivilegePage = class RolePrivilegePage extends localize(i18next)(PageVi
795
796
  *
796
797
  * It says what the role holds, nothing more: 「전부」 is not "this person can do the work" (another role, the
797
798
  * data's own scope or an approval line decide that too), and 「일부」 is not a fault — a privilege several tasks
798
- * share makes each of them 「일부」. So nothing here offers to add or take away; the privileges view and the
799
- * template view do that.
799
+ * share makes each of them 「일부」.
800
+ *
801
+ * A task can be added to the role or taken off it (the user, 2026-10-20). The role stores privileges, never tasks,
802
+ * so either is a set of privileges, shown before it is saved. Taking a task off keeps what another task the role
803
+ * holds whole still uses (`role-task-edit.ts`). Both save at once and read the coverage again, so they are not
804
+ * offered over unsaved changes - the coverage is the saved role's.
800
805
  */
801
806
  renderTasks() {
802
807
  const tasks = this.coverage.tasks;
@@ -820,7 +825,7 @@ let RolePrivilegePage = class RolePrivilegePage extends localize(i18next)(PageVi
820
825
  </h4>
821
826
  ${this.showUnheldTasks
822
827
  ? html `<ul tasks quiet>
823
- ${unheld.map(task => html `<li>${taskName(task.taskId)}</li>`)}
828
+ ${unheld.map(task => html `<li>${taskName(task.taskId)} ${this.taskEditButton(task)}</li>`)}
824
829
  </ul>`
825
830
  : nothing}`
826
831
  : nothing}
@@ -834,6 +839,59 @@ let RolePrivilegePage = class RolePrivilegePage extends localize(i18next)(PageVi
834
839
  </div>
835
840
  `;
836
841
  }
842
+ /** 더하기 for a task the role lacks some or all of, 빼기 for one it holds some or all of. */
843
+ taskEditButton(task) {
844
+ if (this.inherited || task.status === 'broken')
845
+ return nothing;
846
+ const disabled = this.dirty || this.saving;
847
+ const why = this.dirty ? i18next.t('text.save before editing by task') : '';
848
+ return html `
849
+ ${task.status !== 'all'
850
+ ? html `<button quiet ?disabled=${disabled} title=${why} @click=${() => this.editTask(task, 'add')}>${i18next.t('button.add task')}</button>`
851
+ : nothing}
852
+ ${task.status !== 'none'
853
+ ? html `<button quiet ?disabled=${disabled} title=${why} @click=${() => this.editTask(task, 'remove')}>${i18next.t('button.remove task')}</button>`
854
+ : nothing}
855
+ `;
856
+ }
857
+ /** Adds a task's missing privileges to the role, or takes its own off - after showing which - and saves. */
858
+ async editTask(task, mode) {
859
+ if (this.dirty || this.saving || this.inherited)
860
+ return;
861
+ const name = taskName(task.taskId);
862
+ const { remove, kept } = mode === 'remove' ? pairsToRemove(task, this.coverage.tasks) : { remove: [], kept: [] };
863
+ const pairs = mode === 'add' ? pairsToAdd(task) : remove;
864
+ const words = (list) => list.map(pair => this.sentenceOf(pair.split(':')[0], pair.split(':')[1])).join(', ');
865
+ const keptLine = kept.length ? ` ${i18next.t('text.kept for other tasks', { list: words(kept) })}` : '';
866
+ if (!pairs.length) {
867
+ await OxPrompt.open({
868
+ type: 'info',
869
+ title: i18next.t('title.remove task'),
870
+ text: i18next.t('text.nothing to remove for task', { task: name }) + keptLine,
871
+ confirmButton: { text: i18next.t('button.confirm') }
872
+ });
873
+ return;
874
+ }
875
+ const confirmed = await OxPrompt.open({
876
+ type: 'info',
877
+ title: i18next.t(mode === 'add' ? 'title.add task' : 'title.remove task'),
878
+ text: i18next.t(mode === 'add' ? 'text.add task privileges' : 'text.remove task privileges', { task: name, count: pairs.length, list: words(pairs) }) + keptLine,
879
+ confirmButton: { text: i18next.t(mode === 'add' ? 'button.add task' : 'button.remove task') },
880
+ cancelButton: { text: i18next.t('button.cancel') }
881
+ });
882
+ if (!confirmed)
883
+ return;
884
+ const idOf = new Map(this.modules.flatMap(module => module.cells.map(cell => [pairOf(module.category, cell.axis), cell.id])));
885
+ const next = new Set(this.assigned);
886
+ for (const pair of pairs) {
887
+ const id = idOf.get(pair);
888
+ if (id)
889
+ mode === 'add' ? next.add(id) : next.delete(id);
890
+ }
891
+ this.assigned = next;
892
+ this.dirty = true;
893
+ await this.onSave();
894
+ }
837
895
  renderTask(task) {
838
896
  const held = countOf(task.held);
839
897
  const total = held + countOf(task.missing);
@@ -847,6 +905,7 @@ let RolePrivilegePage = class RolePrivilegePage extends localize(i18next)(PageVi
847
905
  : task.status === 'all'
848
906
  ? html `<span state all>${i18next.t('text.task held whole')}</span>`
849
907
  : html `<span state>${i18next.t('text.task held in part', { held, total })}</span>`}
908
+ ${this.taskEditButton(task)}
850
909
  </div>
851
910
  ${says ? html `<div task-says>${says}</div>` : nothing}
852
911
  ${task.status === 'some'