@things-factory/worklist 10.1.37 → 10.1.38

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 (113) hide show
  1. package/dist-client/components/activity-instance-preview.js +2 -13
  2. package/dist-client/components/activity-instance-preview.js.map +1 -1
  3. package/dist-client/components/activity-thread-timeline.d.ts +2 -2
  4. package/dist-client/components/can-pick.d.ts +15 -0
  5. package/dist-client/components/can-pick.js +16 -0
  6. package/dist-client/components/can-pick.js.map +1 -0
  7. package/dist-client/pages/activity-catalog/activity-detail-page.d.ts +23 -2
  8. package/dist-client/pages/activity-catalog/activity-detail-page.js +69 -10
  9. package/dist-client/pages/activity-catalog/activity-detail-page.js.map +1 -1
  10. package/dist-client/pages/activity-catalog/activity-panels.d.ts +28 -0
  11. package/dist-client/pages/activity-catalog/activity-panels.js +19 -0
  12. package/dist-client/pages/activity-catalog/activity-panels.js.map +1 -1
  13. package/dist-client/pages/activity-instance/activity-instance-start-page.d.ts +1 -0
  14. package/dist-client/pages/activity-instance/activity-instance-start-page.js +33 -1
  15. package/dist-client/pages/activity-instance/activity-instance-start-page.js.map +1 -1
  16. package/dist-client/pages/activity-instance/discard-offer.d.ts +11 -0
  17. package/dist-client/pages/activity-instance/discard-offer.js +11 -0
  18. package/dist-client/pages/activity-instance/discard-offer.js.map +1 -0
  19. package/dist-client/pages/activity-thread/activity-thread-view.d.ts +1 -1
  20. package/dist-client/pages/todo/approval-done-list-page.d.ts +5 -1
  21. package/dist-client/pages/todo/approval-done-list-page.js +8 -1
  22. package/dist-client/pages/todo/approval-done-list-page.js.map +1 -1
  23. package/dist-client/pages/todo/approval-pending-list-page.d.ts +5 -1
  24. package/dist-client/pages/todo/approval-pending-list-page.js +8 -1
  25. package/dist-client/pages/todo/approval-pending-list-page.js.map +1 -1
  26. package/dist-client/pages/todo/done-list-page.d.ts +5 -1
  27. package/dist-client/pages/todo/done-list-page.js +8 -1
  28. package/dist-client/pages/todo/done-list-page.js.map +1 -1
  29. package/dist-client/pages/todo/draft-list-page.d.ts +5 -1
  30. package/dist-client/pages/todo/draft-list-page.js +18 -12
  31. package/dist-client/pages/todo/draft-list-page.js.map +1 -1
  32. package/dist-client/pages/todo/list-thumbnail.d.ts +25 -0
  33. package/dist-client/pages/todo/list-thumbnail.js +25 -0
  34. package/dist-client/pages/todo/list-thumbnail.js.map +1 -0
  35. package/dist-client/pages/todo/todo-list-page.d.ts +5 -1
  36. package/dist-client/pages/todo/todo-list-page.js +8 -1
  37. package/dist-client/pages/todo/todo-list-page.js.map +1 -1
  38. package/dist-client/pages/work-door/door-links.d.ts +30 -0
  39. package/dist-client/pages/work-door/door-links.js +30 -0
  40. package/dist-client/pages/work-door/door-links.js.map +1 -0
  41. package/dist-client/pages/work-door/work-doors-page.js +15 -5
  42. package/dist-client/pages/work-door/work-doors-page.js.map +1 -1
  43. package/dist-client/route.d.ts +1 -1
  44. package/dist-client/tsconfig.tsbuildinfo +1 -1
  45. package/dist-server/controllers/activity-approval/delegate.js +27 -2
  46. package/dist-server/controllers/activity-approval/delegate.js.map +1 -1
  47. package/dist-server/controllers/activity-approval/delegation.d.ts +31 -0
  48. package/dist-server/controllers/activity-approval/delegation.js +31 -0
  49. package/dist-server/controllers/activity-approval/delegation.js.map +1 -0
  50. package/dist-server/controllers/activity-instance/discard-draft.d.ts +22 -0
  51. package/dist-server/controllers/activity-instance/discard-draft.js +54 -0
  52. package/dist-server/controllers/activity-instance/discard-draft.js.map +1 -0
  53. package/dist-server/controllers/activity-read/assert-may-read.d.ts +15 -0
  54. package/dist-server/controllers/activity-read/assert-may-read.js +99 -0
  55. package/dist-server/controllers/activity-read/assert-may-read.js.map +1 -0
  56. package/dist-server/controllers/activity-read/participates.d.ts +41 -0
  57. package/dist-server/controllers/activity-read/participates.js +42 -0
  58. package/dist-server/controllers/activity-read/participates.js.map +1 -0
  59. package/dist-server/controllers/activity-thread/delegate.js +23 -1
  60. package/dist-server/controllers/activity-thread/delegate.js.map +1 -1
  61. package/dist-server/service/activity/activity-mutation.js +1 -0
  62. package/dist-server/service/activity/activity-mutation.js.map +1 -1
  63. package/dist-server/service/activity-approval/activity-approval-query.js +7 -1
  64. package/dist-server/service/activity-approval/activity-approval-query.js.map +1 -1
  65. package/dist-server/service/activity-approval/activity-approval-subscription.js +2 -11
  66. package/dist-server/service/activity-approval/activity-approval-subscription.js.map +1 -1
  67. package/dist-server/service/activity-instance/activity-instance-mutation.js +12 -20
  68. package/dist-server/service/activity-instance/activity-instance-mutation.js.map +1 -1
  69. package/dist-server/service/activity-instance/activity-instance-query.js +9 -2
  70. package/dist-server/service/activity-instance/activity-instance-query.js.map +1 -1
  71. package/dist-server/service/activity-instance/activity-instance-subscription.js +4 -12
  72. package/dist-server/service/activity-instance/activity-instance-subscription.js.map +1 -1
  73. package/dist-server/service/activity-instance/index.d.ts +1 -2
  74. package/dist-server/service/activity-instance/index.js +1 -2
  75. package/dist-server/service/activity-instance/index.js.map +1 -1
  76. package/dist-server/service/activity-template/activity-template-mutation.js +6 -0
  77. package/dist-server/service/activity-template/activity-template-mutation.js.map +1 -1
  78. package/dist-server/service/activity-thread/activity-thread-mutation.d.ts +0 -3
  79. package/dist-server/service/activity-thread/activity-thread-mutation.js +0 -19
  80. package/dist-server/service/activity-thread/activity-thread-mutation.js.map +1 -1
  81. package/dist-server/service/activity-thread/activity-thread-query.js +7 -1
  82. package/dist-server/service/activity-thread/activity-thread-query.js.map +1 -1
  83. package/dist-server/service/activity-thread/activity-thread-subscription.js +2 -8
  84. package/dist-server/service/activity-thread/activity-thread-subscription.js.map +1 -1
  85. package/dist-server/service/index.d.ts +2 -2
  86. package/dist-server/service/subscription/subscription-filters.d.ts +36 -0
  87. package/dist-server/service/subscription/subscription-filters.js +41 -0
  88. package/dist-server/service/subscription/subscription-filters.js.map +1 -0
  89. package/dist-server/service/work-door/work-door-declaration.d.ts +15 -0
  90. package/dist-server/service/work-door/work-door-declaration.js +18 -0
  91. package/dist-server/service/work-door/work-door-declaration.js.map +1 -1
  92. package/dist-server/service/work-door/work-door-resolver.d.ts +3 -0
  93. package/dist-server/service/work-door/work-door-resolver.js +21 -1
  94. package/dist-server/service/work-door/work-door-resolver.js.map +1 -1
  95. package/dist-server/tsconfig.tsbuildinfo +1 -1
  96. package/package.json +10 -10
  97. package/tests/delegate-approval.test.ts +112 -0
  98. package/tests/discard-draft.test.ts +145 -0
  99. package/tests/may-read-case.test.ts +166 -0
  100. package/tests/open-doors.test.ts +274 -0
  101. package/tests/published-shape.test.ts +85 -0
  102. package/tests/work-door-not-used-refuse.test.ts +110 -0
  103. package/translations/en.json +15 -2
  104. package/translations/ja.json +15 -2
  105. package/translations/ko.json +15 -2
  106. package/translations/ms.json +15 -2
  107. package/translations/zh.json +15 -2
  108. package/dist-server/service/activity-instance/activity-instance-history-query.d.ts +0 -12
  109. package/dist-server/service/activity-instance/activity-instance-history-query.js +0 -79
  110. package/dist-server/service/activity-instance/activity-instance-history-query.js.map +0 -1
  111. package/dist-server/service/activity-instance/activity-instance-history-type.d.ts +0 -5
  112. package/dist-server/service/activity-instance/activity-instance-history-type.js +0 -21
  113. package/dist-server/service/activity-instance/activity-instance-history-type.js.map +0 -1
@@ -6,6 +6,7 @@ import { css, html, LitElement } from 'lit';
6
6
  import { customElement, property, state } from 'lit/decorators.js';
7
7
  import { client } from '@operato/graphql';
8
8
  import { i18next, localize } from '@operato/i18n';
9
+ import { canPick } from './can-pick.js';
9
10
  import { ranMutation } from './say-outcome.js';
10
11
  import { notify } from '@operato/layout';
11
12
  import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles';
@@ -55,7 +56,7 @@ let ActivityInstancePreview = class ActivityInstancePreview extends localize(i18
55
56
  label: item.name
56
57
  };
57
58
  });
58
- const pickable = !this.picked && this.activityThreads?.length < threadsMax;
59
+ const pickable = canPick(this.activityInstance, this.picked);
59
60
  return html `
60
61
  <content>
61
62
  <section>
@@ -130,17 +131,10 @@ let ActivityInstancePreview = class ActivityInstancePreview extends localize(i18
130
131
  }
131
132
  }
132
133
 
133
- activityThreads(filters: [{ name: "activityInstanceId", operator: "eq", value: "${id}"}]) {
134
- items {
135
- id
136
- }
137
- total
138
- }
139
134
  }
140
135
  `
141
136
  });
142
137
  this.activityInstance = response.data.activityInstance;
143
- this.activityThreads = response.data.activityThreads.items;
144
138
  }
145
139
  async pickActivityInstance() {
146
140
  const outcome = await ranMutation(() => client.mutate({
@@ -157,7 +151,6 @@ let ActivityInstancePreview = class ActivityInstancePreview extends localize(i18
157
151
  }));
158
152
  if (outcome.ok) {
159
153
  this.picked = true;
160
- this.activityThreads = [outcome.data.pickActivityInstance];
161
154
  notify({
162
155
  message: i18next.t('text.info_x_successfully', {
163
156
  x: `${i18next.t('label.activity-instance')} ${i18next.t('text.pick')}`
@@ -174,10 +167,6 @@ __decorate([
174
167
  property({ type: Object }),
175
168
  __metadata("design:type", Object)
176
169
  ], ActivityInstancePreview.prototype, "activityInstance", void 0);
177
- __decorate([
178
- property({ type: Array }),
179
- __metadata("design:type", Object)
180
- ], ActivityInstancePreview.prototype, "activityThreads", void 0);
181
170
  __decorate([
182
171
  state(),
183
172
  __metadata("design:type", Boolean)
@@ -1 +1 @@
1
- {"version":3,"file":"activity-instance-preview.js","sourceRoot":"","sources":["../../client/components/activity-instance-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/elevated-button.js'\nimport '@operato/property-editor/ox-properties-dynamic-view.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { ranMutation } from './say-outcome.js'\nimport { notify } from '@operato/layout'\nimport { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'\n\n@customElement('activity-instance-preview')\nclass ActivityInstancePreview extends localize(i18next)(LitElement) {\n static styles = [\n CommonHeaderStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n background-color: var(--md-sys-color-surface);\n }\n\n content {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: var(--spacing-medium);\n overflow: auto;\n }\n\n section {\n display: flex;\n flex-direction: column;\n }\n\n #description {\n height: 60px;\n }\n `\n ]\n\n @property({ type: String }) activityInstanceId?: string\n @property({ type: Object }) activityInstance: any = {}\n @property({ type: Array }) activityThreads: any\n\n @state() picked: boolean = false\n\n render() {\n const {\n id,\n name,\n description,\n input,\n state,\n threadsMin = 0,\n threadsMax = 0,\n activity\n } = this.activityInstance || {}\n const { model, thumbnail } = activity || {}\n\n const inputSpec = (model || [])\n .filter(item => item.inout === 'in' || item.inout === 'inout')\n .map(item => {\n return {\n ...item,\n label: item.name\n }\n })\n\n const pickable = !this.picked && this.activityThreads?.length < threadsMax\n\n return html`\n <content>\n <section>\n <h2>mission statement</h2>\n <div>title: ${name}</div>\n <div>${description}</div>\n <div>min-max: ${threadsMin} - ${threadsMax}</div>\n <img src=${thumbnail} />\n </section>\n\n <label>Input</label>\n ${!model\n ? html``\n : html`\n <ox-properties-dynamic-view\n .props=${inputSpec}\n .value=${input}\n @property-change=${e => {\n this.activityInstance.input = e.currentTarget.value\n console.log('activity-instance', this.activityInstance.input)\n }}\n ></ox-properties-dynamic-view>\n `}\n </content>\n\n <div class=\"footer\">\n <div filler></div>\n <button @click=${e => history.back()}><md-icon>close</md-icon>${i18next.t('button.close')}</button>\n ${pickable\n ? html`<button @click=${this.pickActivityInstance.bind(this)} done>\n <md-icon>done</md-icon>${i18next.t('button.pick')}\n </button>`\n : html``}\n </div>\n `\n }\n\n updated(changes) {\n if (changes.has('activityInstanceId')) {\n this.fetchAll(this.activityInstanceId)\n }\n }\n\n async fetchAll(id) {\n const response = await client.query({\n query: gql`\n {\n activityInstance(id: \"${id}\") {\n id\n name\n description\n input\n state\n threadsMin\n threadsMax\n activity {\n id\n name\n description\n state\n thumbnail\n model {\n name\n description\n active\n tag\n inout\n type\n unit\n options\n quantifier\n spec\n }\n }\n }\n\n activityThreads(filters: [{ name: \"activityInstanceId\", operator: \"eq\", value: \"${id}\"}]) {\n items {\n id\n }\n total\n }\n }\n `\n })\n\n this.activityInstance = response.data.activityInstance\n this.activityThreads = response.data.activityThreads.items\n }\n\n async pickActivityInstance() {\n const outcome = await ranMutation(() => client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n pickActivityInstance(id: $id) {\n id\n }\n }\n `,\n variables: {\n id: this.activityInstanceId\n }\n }))\n\n if (outcome.ok) {\n this.picked = true\n this.activityThreads = [outcome.data.pickActivityInstance]\n notify({\n message: i18next.t('text.info_x_successfully', {\n x: `${i18next.t('label.activity-instance')} ${i18next.t('text.pick')}`\n })\n })\n }\n }\n}\n"]}
1
+ {"version":3,"file":"activity-instance-preview.js","sourceRoot":"","sources":["../../client/components/activity-instance-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/elevated-button.js'\nimport '@operato/property-editor/ox-properties-dynamic-view.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\n\nimport { canPick } from './can-pick.js'\nimport { ranMutation } from './say-outcome.js'\nimport { notify } from '@operato/layout'\nimport { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'\n\n@customElement('activity-instance-preview')\nclass ActivityInstancePreview extends localize(i18next)(LitElement) {\n static styles = [\n CommonHeaderStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n background-color: var(--md-sys-color-surface);\n }\n\n content {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: var(--spacing-medium);\n overflow: auto;\n }\n\n section {\n display: flex;\n flex-direction: column;\n }\n\n #description {\n height: 60px;\n }\n `\n ]\n\n @property({ type: String }) activityInstanceId?: string\n @property({ type: Object }) activityInstance: any = {}\n\n @state() picked: boolean = false\n\n render() {\n const {\n id,\n name,\n description,\n input,\n state,\n threadsMin = 0,\n threadsMax = 0,\n activity\n } = this.activityInstance || {}\n const { model, thumbnail } = activity || {}\n\n const inputSpec = (model || [])\n .filter(item => item.inout === 'in' || item.inout === 'inout')\n .map(item => {\n return {\n ...item,\n label: item.name\n }\n })\n\n const pickable = canPick(this.activityInstance, this.picked)\n\n return html`\n <content>\n <section>\n <h2>mission statement</h2>\n <div>title: ${name}</div>\n <div>${description}</div>\n <div>min-max: ${threadsMin} - ${threadsMax}</div>\n <img src=${thumbnail} />\n </section>\n\n <label>Input</label>\n ${!model\n ? html``\n : html`\n <ox-properties-dynamic-view\n .props=${inputSpec}\n .value=${input}\n @property-change=${e => {\n this.activityInstance.input = e.currentTarget.value\n console.log('activity-instance', this.activityInstance.input)\n }}\n ></ox-properties-dynamic-view>\n `}\n </content>\n\n <div class=\"footer\">\n <div filler></div>\n <button @click=${e => history.back()}><md-icon>close</md-icon>${i18next.t('button.close')}</button>\n ${pickable\n ? html`<button @click=${this.pickActivityInstance.bind(this)} done>\n <md-icon>done</md-icon>${i18next.t('button.pick')}\n </button>`\n : html``}\n </div>\n `\n }\n\n updated(changes) {\n if (changes.has('activityInstanceId')) {\n this.fetchAll(this.activityInstanceId)\n }\n }\n\n async fetchAll(id) {\n const response = await client.query({\n query: gql`\n {\n activityInstance(id: \"${id}\") {\n id\n name\n description\n input\n state\n threadsMin\n threadsMax\n activity {\n id\n name\n description\n state\n thumbnail\n model {\n name\n description\n active\n tag\n inout\n type\n unit\n options\n quantifier\n spec\n }\n }\n }\n\n }\n `\n })\n\n this.activityInstance = response.data.activityInstance\n }\n\n async pickActivityInstance() {\n const outcome = await ranMutation(() => client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n pickActivityInstance(id: $id) {\n id\n }\n }\n `,\n variables: {\n id: this.activityInstanceId\n }\n }))\n\n if (outcome.ok) {\n this.picked = true\n notify({\n message: i18next.t('text.info_x_successfully', {\n x: `${i18next.t('label.activity-instance')} ${i18next.t('text.pick')}`\n })\n })\n }\n }\n}\n"]}
@@ -17,12 +17,12 @@ export declare class ActivityThreadTimeline extends LitElement {
17
17
  * 보일 항목 없음 화면이 「타임라인의 상신 사유를 보고 판단하세요」로 여기를 가리킨다
18
18
  */
19
19
  open?: boolean;
20
- render(): import("lit-html").TemplateResult<1> | typeof nothing;
20
+ render(): typeof nothing | import("lit-html").TemplateResult<1>;
21
21
  /** 접혔을 때 보이는 한 줄 — 마지막 기록의 「무엇을 · 누가 · 언제」. */
22
22
  lastLine(item: any): string;
23
23
  renderActivityThreadHistory(activityThreadHistory: ActivityThreadHistory): import("lit-html").TemplateResult<1>;
24
24
  renderActivityApproval(activityApproval: ActivityApproval): import("lit-html").TemplateResult<1>;
25
25
  /** 적힌 말이 없으면 상자를 그리지 않는다 — 빈 회색 상자는 내용이 있는 줄과 구별되지 않는다. */
26
- renderSaid(said?: string): import("lit-html").TemplateResult<1> | typeof nothing;
26
+ renderSaid(said?: string): typeof nothing | import("lit-html").TemplateResult<1>;
27
27
  renderOutput(output: any): string;
28
28
  }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Whether the preview offers "pick" - the same condition the server's pick applies.
3
+ *
4
+ * The server (controllers/activity-instance/pick.ts) picks when the caller is entitled and the
5
+ * instance is issued or unassigned. The screen used to compare the instance's thread count with
6
+ * threadsMax instead: a limit the server never applies, read from the thread list that asks a
7
+ * management privilege a picker does not hold - and with threadsMax at its default 0, never true.
8
+ *
9
+ * Entitlement is not repeated here: the pickable list already lists only what the caller may pick,
10
+ * and a refusal from the server says why.
11
+ */
12
+ export declare const PICKABLE_STATES: readonly ["issued", "unassigned"];
13
+ export declare function canPick(instance: {
14
+ state?: string;
15
+ } | null | undefined, picked: boolean): boolean;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Whether the preview offers "pick" - the same condition the server's pick applies.
3
+ *
4
+ * The server (controllers/activity-instance/pick.ts) picks when the caller is entitled and the
5
+ * instance is issued or unassigned. The screen used to compare the instance's thread count with
6
+ * threadsMax instead: a limit the server never applies, read from the thread list that asks a
7
+ * management privilege a picker does not hold - and with threadsMax at its default 0, never true.
8
+ *
9
+ * Entitlement is not repeated here: the pickable list already lists only what the caller may pick,
10
+ * and a refusal from the server says why.
11
+ */
12
+ export const PICKABLE_STATES = ['issued', 'unassigned'];
13
+ export function canPick(instance, picked) {
14
+ return !picked && PICKABLE_STATES.includes(String(instance?.state ?? ''));
15
+ }
16
+ //# sourceMappingURL=can-pick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"can-pick.js","sourceRoot":"","sources":["../../client/components/can-pick.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAU,CAAA;AAEhE,MAAM,UAAU,OAAO,CAAC,QAA+C,EAAE,MAAe;IACtF,OAAO,CAAC,MAAM,IAAK,eAAqC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,CAAA;AAClG,CAAC","sourcesContent":["/**\n * Whether the preview offers \"pick\" - the same condition the server's pick applies.\n *\n * The server (controllers/activity-instance/pick.ts) picks when the caller is entitled and the\n * instance is issued or unassigned. The screen used to compare the instance's thread count with\n * threadsMax instead: a limit the server never applies, read from the thread list that asks a\n * management privilege a picker does not hold - and with threadsMax at its default 0, never true.\n *\n * Entitlement is not repeated here: the pickable list already lists only what the caller may pick,\n * and a refusal from the server says why.\n */\nexport const PICKABLE_STATES = ['issued', 'unassigned'] as const\n\nexport function canPick(instance: { state?: string } | null | undefined, picked: boolean): boolean {\n return !picked && (PICKABLE_STATES as readonly string[]).includes(String(instance?.state ?? ''))\n}\n"]}
@@ -9,18 +9,38 @@ export declare class ActivityDetailPage extends ActivityDetailPage_base {
9
9
  private entry;
10
10
  private activity;
11
11
  private loaded;
12
+ /** 구획이 「이 업무에는 보일 것 없음」을 알렸나. 이름으로 센다. */
13
+ private emptyPanels;
12
14
  /** 왜 못 읽었나 — ox-page-state 가 그것을 말하고 다시 시도를 준다. */
13
15
  private loadError;
14
16
  get context(): {
15
17
  frame: string;
16
18
  title: string;
19
+ heading: boolean;
17
20
  help: string;
18
21
  };
19
22
  /**
20
- * 이름이 주소로 온다. `pageUpdated` 는 같은 라우트 안에서 이름만 바뀌어도 다시 오므로, **이름이
21
- * 실제로 바뀌었을 때만** 다시 읽는다.
23
+ * 이름이 주소로 온다. 같은 라우트 안에서 이름만 바뀌어도 이 콜백이 다시 오므로, **이름이 실제로
24
+ * 바뀌었을 때만** 다시 읽는다.
22
25
  */
23
26
  pageUpdated(changes: any, lifecycle: any): Promise<void>;
27
+ /**
28
+ * 처음 그린 뒤에도 한 번 읽는다 — **첫 방문이 통째로 비는 것을 봤다.**
29
+ *
30
+ * [확인함 2026-09-24 :4000] 카드에서 처음 들어가면 주소는 맞고(`?name=…`) `lifecycle.params.name`
31
+ * 도 들어 있는데 화면에 돌아갈 링크만 남았다. 같은 자리로 두 번째 들어가면 멀쩡했고, 그 상태에서
32
+ * 콜백을 손으로 한 번 부르면 바로 채워졌다.
33
+ *
34
+ * 이 화면은 주소로 처음 닿을 때 **모듈이 그때 실려 온다**(route.ts 의 동적 import). 셸은 화면을
35
+ * 처음 세울 때 이 콜백을 다음 animationFrame 으로 미루는데(page-view 가 「엘리먼트가 아직 안
36
+ * 만들어졌을 수 있다」고 적어 둔 그 갈래), 그 사이에 요소가 아직 이 클래스로 올라오지 않았으면
37
+ * **아무 일도 안 하는 기반 클래스의 콜백**이 불린다. 그 뒤로는 생애주기가 더 안 바뀌므로 이름을
38
+ * 받을 기회가 다시 오지 않는다.
39
+ *
40
+ * 그래서 이름을 받는 자리를 둘로 둔다. 둘 다 같은 판단을 지나므로 두 번 읽지 않는다.
41
+ */
42
+ firstUpdated(): void;
43
+ private readName;
24
44
  private shownName;
25
45
  private fetch;
26
46
  render(): import("lit-html").TemplateResult<1>;
@@ -36,6 +56,7 @@ export declare class ActivityDetailPage extends ActivityDetailPage_base {
36
56
  * 「정할 것이 없다」가 같은 화면이 된다.
37
57
  */
38
58
  private renderPanels;
59
+ private panelReported;
39
60
  private setUse;
40
61
  /**
41
62
  * 결재선 편집기는 **organization 것을 그대로 부른다.** 이 화면이 자기 편집기를 따로 지으면 같은
@@ -11,7 +11,7 @@ import { openPopup } from '@operato/layout';
11
11
  import { navigate, PageView } from '@operato/shell';
12
12
  import { DocumentPageStyles, ScrollbarStyles } from '@operato/styles';
13
13
  import { ranMutation } from '../../components/say-outcome.js';
14
- import { activityPanels } from './activity-panels.js';
14
+ import { activityPanels, panelSection } from './activity-panels.js';
15
15
  import { detailActions } from './activity-detail-actions.js';
16
16
  import { canonicalStage } from './activity-stage.js';
17
17
  import { guideLines } from './activity-guide.js';
@@ -85,6 +85,8 @@ let ActivityDetailPage = class ActivityDetailPage extends localize(i18next)(Page
85
85
  this.entry = null;
86
86
  this.activity = null;
87
87
  this.loaded = false;
88
+ /** 구획이 「이 업무에는 보일 것 없음」을 알렸나. 이름으로 센다. */
89
+ this.emptyPanels = {};
88
90
  /** 왜 못 읽었나 — ox-page-state 가 그것을 말하고 다시 시도를 준다. */
89
91
  this.loadError = null;
90
92
  }
@@ -207,19 +209,51 @@ let ActivityDetailPage = class ActivityDetailPage extends localize(i18next)(Page
207
209
  return {
208
210
  frame: 'document',
209
211
  title: this.entry ? this.shownName(this.entry) : i18next.t('title.activity-detail'),
212
+ /*
213
+ * 이름은 **이 화면이 그린다.** 제목 줄은 끈다.
214
+ *
215
+ * 이름 옆에 단계 띠와 「사용 중」이 함께 서야 한다 — 그 셋이 한 줄에 있어야 이 업무를 지금
216
+ * 믿고 쓸 수 있는지가 한눈에 읽힌다. 제목 줄은 그것들을 못 받는다.
217
+ *
218
+ * 끄지 않으면 같은 이름이 화면에 두 번 뜬다(실측 2026-09-24: 제목 줄 「부적합 처분 승인」,
219
+ * 바로 아래 또 「부적합 처분 승인」). `title` 은 그대로 두므로 **탭 이름은 살아 있다** —
220
+ * `heading: false` 가 그리는 자리마다 켜고 끄라고 둔 문이다.
221
+ */
222
+ heading: false,
210
223
  help: 'worklist/activity-catalog-page'
211
224
  };
212
225
  }
213
226
  /**
214
- * 이름이 주소로 온다. `pageUpdated` 는 같은 라우트 안에서 이름만 바뀌어도 다시 오므로, **이름이
215
- * 실제로 바뀌었을 때만** 다시 읽는다.
227
+ * 이름이 주소로 온다. 같은 라우트 안에서 이름만 바뀌어도 이 콜백이 다시 오므로, **이름이 실제로
228
+ * 바뀌었을 때만** 다시 읽는다.
216
229
  */
217
230
  async pageUpdated(changes, lifecycle) {
218
- if (!this.active)
219
- return;
220
- const name = String(lifecycle?.params?.name || '');
231
+ this.readName(lifecycle);
232
+ }
233
+ /**
234
+ * 처음 그린 뒤에도 한 번 읽는다 — **첫 방문이 통째로 비는 것을 봤다.**
235
+ *
236
+ * [확인함 2026-09-24 :4000] 카드에서 처음 들어가면 주소는 맞고(`?name=…`) `lifecycle.params.name`
237
+ * 도 들어 있는데 화면에 돌아갈 링크만 남았다. 같은 자리로 두 번째 들어가면 멀쩡했고, 그 상태에서
238
+ * 콜백을 손으로 한 번 부르면 바로 채워졌다.
239
+ *
240
+ * 이 화면은 주소로 처음 닿을 때 **모듈이 그때 실려 온다**(route.ts 의 동적 import). 셸은 화면을
241
+ * 처음 세울 때 이 콜백을 다음 animationFrame 으로 미루는데(page-view 가 「엘리먼트가 아직 안
242
+ * 만들어졌을 수 있다」고 적어 둔 그 갈래), 그 사이에 요소가 아직 이 클래스로 올라오지 않았으면
243
+ * **아무 일도 안 하는 기반 클래스의 콜백**이 불린다. 그 뒤로는 생애주기가 더 안 바뀌므로 이름을
244
+ * 받을 기회가 다시 오지 않는다.
245
+ *
246
+ * 그래서 이름을 받는 자리를 둘로 둔다. 둘 다 같은 판단을 지나므로 두 번 읽지 않는다.
247
+ */
248
+ firstUpdated() {
249
+ this.readName();
250
+ }
251
+ readName(lifecycle) {
252
+ const name = String((lifecycle ?? this.lifecycle)?.params?.name || '');
221
253
  if (name && name !== this.name) {
222
254
  this.name = name;
255
+ /* 구획이 알린 것은 그 업무의 것이다. 다른 업무로 옮기면 다시 듣는다. */
256
+ this.emptyPanels = {};
223
257
  this.fetch();
224
258
  }
225
259
  }
@@ -344,15 +378,36 @@ let ActivityDetailPage = class ActivityDetailPage extends localize(i18next)(Page
344
378
  renderPanels() {
345
379
  const context = { activityName: this.entry.name, activityId: this.activity?.id };
346
380
  const panels = activityPanels(context);
381
+ const shape = panelSection(panels.map(one => one.name), this.emptyPanels);
382
+ if (shape.kind === 'none-registered') {
383
+ return html `
384
+ <section>
385
+ <h3>${i18next.t('label.activity-panels')}</h3>
386
+ <p class="why">${i18next.t('text.activity-no-panels')}</p>
387
+ </section>
388
+ `;
389
+ }
390
+ /*
391
+ * 비었다고 알린 구획도 **그리기는 계속한다** — 숨길 뿐이다. 지우면 그 구획이 다시 읽고
392
+ * 「있다」고 알릴 자리가 없어진다.
393
+ */
394
+ const hidden = shape.kind === 'all-empty' ? panels.map(one => one.name) : shape.hidden;
347
395
  return html `
348
- <section>
396
+ <section ?hidden=${shape.kind === 'all-empty'}>
349
397
  <h3>${i18next.t('label.activity-panels')}</h3>
350
- ${panels.length
351
- ? panels.map(panel => html `<div>${panel.render(context)}</div>`)
352
- : html `<p class="why">${i18next.t('text.activity-no-panels')}</p>`}
398
+ ${panels.map(panel => html `<div
399
+ ?hidden=${hidden.includes(panel.name)}
400
+ @activity-panel-content=${(event) => this.panelReported(panel.name, event)}
401
+ >
402
+ ${panel.render(context)}
403
+ </div>`)}
353
404
  </section>
354
405
  `;
355
406
  }
407
+ panelReported(name, event) {
408
+ event.stopPropagation();
409
+ this.emptyPanels = { ...this.emptyPanels, [name]: event.detail?.empty === true };
410
+ }
356
411
  async setUse(on) {
357
412
  const outcome = await ranMutation(() => client.mutate({
358
413
  mutation: on
@@ -419,6 +474,10 @@ __decorate([
419
474
  state(),
420
475
  __metadata("design:type", Object)
421
476
  ], ActivityDetailPage.prototype, "loaded", void 0);
477
+ __decorate([
478
+ state(),
479
+ __metadata("design:type", Object)
480
+ ], ActivityDetailPage.prototype, "emptyPanels", void 0);
422
481
  __decorate([
423
482
  state(),
424
483
  __metadata("design:type", Object)
@@ -1 +1 @@
1
- {"version":3,"file":"activity-detail-page.js","sourceRoot":"","sources":["../../../client/pages/activity-catalog/activity-detail-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/list-state/ox-page-state.js'\nimport '@things-factory/organization'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { navigate, PageView } from '@operato/shell'\nimport { DocumentPageStyles, ScrollbarStyles } from '@operato/styles'\n\nimport { ranMutation } from '../../components/say-outcome.js'\nimport { activityPanels } from './activity-panels.js'\nimport { detailActions } from './activity-detail-actions.js'\nimport { canonicalStage } from './activity-stage.js'\nimport { guideLines } from './activity-guide.js'\n\n/**\n * 업무 하나 — **그 업무에 관한 일을 여기서 끝낸다.**\n *\n * ── 왜 이 화면이 생겼나 (2026-09-23) ───────────────────────────────────────\n * 결재선 하나를 세워서 문이 서는지 보려면 화면을 셋 열어야 했고, 역할에 사람이 없으면 넷에서\n * 다섯이었다 [확인함]. 카탈로그는 카드에 「결재선 없음」이라고 적어 놓고 **어디로 가야 하는지는\n * 말하지 않았다** — 그 파일 전체에 `navigate()` 도 `<a href>` 도 없었다. 사람은 메뉴로 돌아가\n * 이름으로 찾아야 했다.\n *\n * 그래서 카탈로그는 고르는 자리로 두고, 고른 뒤의 일은 이 화면 안에서 끝낸다.\n *\n * ── 돌아갈 길을 화면이 갖는다 ─────────────────────────────────────────────\n * 탭 줄에 카탈로그가 남아 있으니 됐다고 볼 수 없다. 탭 줄은 **없을 수 있다** — 페이지가\n * `mdibar: false` 를 적으면 셸이 걷고(`apptool-ui/client/layout/mdibar-visibility.ts`), 무엇보다\n * **주소를 받아서 바로 여는 사람에게는 이전 화면이 아예 없다.** 「이 업무 결재선 좀 봐 주세요」로\n * 링크를 받은 사람이 그 경우다. 그래서 머리에 돌아갈 자리를 직접 둔다.\n *\n * ── 못 하는 일은 까닭을 말한다 ────────────────────────────────────────────\n * 눌리지 않는 단추만 남기지 않는다. 무엇을 할 수 있는지와 못 하면 왜인지는\n * `activity-detail-actions.ts` 가 값으로 답하고, 이 화면은 그 값을 문장으로 바꾼다. 원인을 여기서\n * 지어내지 않는다.\n */\nconst DETAIL = gql`\n query ($name: String!) {\n responses: installableActivities {\n items {\n name\n label\n labelTKey\n description\n guide\n guideTKeys\n approvalSteps\n activity {\n id\n }\n readiness {\n stage\n }\n }\n }\n activityByName(name: $name) {\n id\n name\n description\n approvalLine {\n type\n value\n participation\n approver {\n id\n name\n description\n controlNo\n }\n }\n }\n }\n`\n\n/** 결재선에서 **서명하는 단계만** 센다. 참조는 알기만 하고 막지 않는다. */\nfunction approvalSteps(line?: any[]): number {\n return (line || []).filter(one => String(one?.participation || '') !== 'reference').length\n}\n\n@customElement('activity-detail-page')\nexport class ActivityDetailPage extends localize(i18next)(PageView) {\n static styles = [\n ScrollbarStyles,\n DocumentPageStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow: auto;\n }\n\n /* 돌아갈 자리. 제목 위에 둔다 — 읽기 전에 보인다. */\n a[back] {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n align-self: flex-start;\n margin-bottom: var(--spacing-small);\n color: var(--md-sys-color-on-surface-variant);\n font-size: var(--ui-t-body, 13px);\n text-decoration: none;\n cursor: pointer;\n }\n\n a[back]:hover {\n color: var(--md-sys-color-on-surface);\n text-decoration: underline;\n }\n\n a[back] md-icon {\n --md-icon-size: 16px;\n }\n\n .head {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--spacing-small);\n }\n\n .head .name {\n font: var(--ui-t-title-font, bold 18px var(--theme-font));\n color: var(--md-sys-color-on-surface);\n }\n\n .stage,\n .use {\n padding: 1px 8px;\n border-radius: var(--md-sys-shape-corner-full);\n font-size: var(--ui-t-small, 12px);\n background-color: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .use:not([idle]) {\n background-color: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n\n section {\n margin-top: var(--spacing-large, 16px);\n }\n\n section > h3 {\n margin: 0 0 var(--spacing-small) 0;\n font-size: var(--ui-t-small, 12px);\n color: var(--md-sys-color-on-surface-variant);\n text-transform: uppercase;\n }\n\n .guide p {\n margin: 0 0 4px 0;\n line-height: 1.5;\n }\n\n /* 못 하는 까닭 · 비어 있다는 사실. 둘 다 **문장으로** 남는다. */\n .why {\n margin: 4px 0 0 0;\n color: var(--md-sys-color-on-surface-variant);\n font-size: var(--ui-t-body, 13px);\n }\n\n .steps {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: var(--spacing-small);\n }\n\n .steps span {\n padding: 2px 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-full);\n font-size: var(--ui-t-small, 12px);\n }\n\n button {\n font: inherit;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-full);\n padding: 6px 14px;\n cursor: pointer;\n }\n\n button:hover:not(:disabled) {\n background-color: var(--md-sys-color-surface-container-high);\n }\n\n button:disabled {\n opacity: 0.4;\n cursor: default;\n }\n `\n ]\n\n @state() private name = ''\n @state() private entry: any = null\n @state() private activity: any = null\n @state() private loaded = false\n /** 왜 못 읽었나 — ox-page-state 가 그것을 말하고 다시 시도를 준다. */\n @state() private loadError: unknown = null\n\n get context() {\n return {\n frame: 'document',\n title: this.entry ? this.shownName(this.entry) : i18next.t('title.activity-detail'),\n help: 'worklist/activity-catalog-page'\n }\n }\n\n /**\n * 이름이 주소로 온다. `pageUpdated` 는 같은 라우트 안에서 이름만 바뀌어도 다시 오므로, **이름이\n * 실제로 바뀌었을 때만** 다시 읽는다.\n */\n async pageUpdated(changes: any, lifecycle: any) {\n if (!this.active) return\n\n const name = String(lifecycle?.params?.name || '')\n\n if (name && name !== this.name) {\n this.name = name\n this.fetch()\n }\n }\n\n private shownName(one: any): string {\n if (!one?.label) return String(one?.name ?? '')\n return one.labelTKey ? String(i18next.t(one.labelTKey, { defaultValue: one.label })) : String(one.label)\n }\n\n private async fetch() {\n this.loaded = false\n this.loadError = null\n\n try {\n const response = await client.query({ query: DETAIL, variables: { name: this.name }, fetchPolicy: 'no-cache' })\n\n /*\n * 목록에 필터가 없어서 전부 받아 하나를 고른다. 업무는 설치된 모듈이 선언하는 것이라 수가\n * 닫혀 있다 [확인함 2026-09-23: 마흔 안쪽]. 그 전제가 깨지면 서버에 이름으로 찾는 갈래를\n * 두는 것이 맞다 — 여기서 자르지 않는다.\n */\n const items = response?.data?.responses?.items || []\n\n this.entry = items.find((one: any) => one.name === this.name) || null\n this.activity = response?.data?.activityByName || null\n this.loaded = true\n } catch (thrown) {\n this.loadError = thrown\n this.loaded = true\n }\n }\n\n render() {\n return html`\n <a back @click=${() => navigate('activity-catalog')}>\n <md-icon>chevron_left</md-icon>${i18next.t('button.back-to-activity-catalog')}\n </a>\n\n <ox-page-state .loading=${!this.loaded} .error=${this.loadError} @retry=${() => this.fetch()}\n >${this.loaded && !this.loadError ? this.renderDetail() : ''}</ox-page-state\n >\n `\n }\n\n private renderDetail() {\n if (!this.entry) {\n /* 없는 이름으로 들어온 주소. 빈 화면으로 두지 않는다 — 무엇을 못 찾았는지 이름을 댄다. */\n return html`<div class=\"why\">${i18next.t('text.activity-not-found', { name: this.name })}</div>`\n }\n\n const actions = detailActions(this.entry)\n const stage = canonicalStage(this.entry.readiness?.stage)\n\n return html`\n <div class=\"head\">\n <span class=\"name\">${this.shownName(this.entry)}</span>\n ${stage ? html`<span class=\"stage\">${i18next.t('label.readiness-' + stage)}</span>` : ''}\n ${actions.registered\n ? html`<span class=\"use\" ?idle=${!actions.inUse}\n >${actions.inUse\n ? i18next.t('text.activity-in-use-here')\n : i18next.t('text.activity-not-in-use-here')}</span\n >`\n : ''}\n </div>\n\n ${this.renderGuide()} ${this.renderUse(actions)} ${this.renderApprovalLine(actions)} ${this.renderPanels()}\n `\n }\n\n /** 이 업무가 무엇인가. `guide` 가 그 자리이고, 없으면 설명이다. */\n private renderGuide() {\n const shown = guideLines(this.entry, (key, options) => i18next.t(key, options)).filter(Boolean)\n\n if (!shown.length && !this.entry.description) return html``\n\n return html`\n <section class=\"guide\">\n <h3>${i18next.t('label.activity-what-is-it')}</h3>\n ${shown.length\n ? shown.map(line => html`<p>${line}</p>`)\n : html`<p>${this.entry.description}</p>`}\n </section>\n `\n }\n\n private renderUse(actions: ReturnType<typeof detailActions>) {\n if (!actions.registered) return html``\n\n return html`\n <section>\n <h3>${i18next.t('label.activity-use-here')}</h3>\n ${actions.canInstall\n ? html`<button @click=${() => this.setUse(true)}>${i18next.t('button.activity-install')}</button>`\n : html`<button @click=${() => this.setUse(false)}>${i18next.t('button.activity-uninstall')}</button>`}\n </section>\n `\n }\n\n private renderApprovalLine(actions: ReturnType<typeof detailActions>) {\n const line = this.activity?.approvalLine || []\n const steps = approvalSteps(line)\n\n return html`\n <section>\n <h3>${i18next.t('label.activity-approval-line')}</h3>\n\n ${steps\n ? html`<div class=\"steps\">\n ${line.map((one: any) => html`<span>${one.approver?.name || one.value || ''}</span>`)}\n </div>`\n : ''}\n\n <button ?disabled=${!actions.canSetApprovalLine} @click=${() => this.openApprovalLine(line)}>\n ${i18next.t('button.activity-set-approval-line')}\n </button>\n\n <p class=\"why\">\n ${actions.approvalLineBlock\n ? i18next.t('text.activity-line-' + actions.approvalLineBlock)\n : steps\n ? i18next.t('text.activity-line-steps', { steps })\n : i18next.t('text.activity-line-none')}\n </p>\n </section>\n `\n }\n\n /**\n * 다른 꾸러미가 건 구획.\n *\n * **하나도 안 걸렸으면 그 사실을 말한다.** 빈자리로 두면 「이 앱에는 그 구획이 없다」와\n * 「정할 것이 없다」가 같은 화면이 된다.\n */\n private renderPanels() {\n const context = { activityName: this.entry.name, activityId: this.activity?.id }\n const panels = activityPanels(context)\n\n return html`\n <section>\n <h3>${i18next.t('label.activity-panels')}</h3>\n ${panels.length\n ? panels.map(panel => html`<div>${panel.render(context)}</div>`)\n : html`<p class=\"why\">${i18next.t('text.activity-no-panels')}</p>`}\n </section>\n `\n }\n\n private async setUse(on: boolean) {\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: on\n ? gql`\n mutation ($name: String!) {\n activateInstallableActivity(name: $name) {\n name\n }\n }\n `\n : gql`\n mutation ($name: String!) {\n deactivateInstallableActivity(name: $name) {\n name\n }\n }\n `,\n variables: { name: this.entry.name }\n })\n )\n\n if (!outcome.ok) return\n\n this.fetch()\n }\n\n /**\n * 결재선 편집기는 **organization 것을 그대로 부른다.** 이 화면이 자기 편집기를 따로 지으면 같은\n * 결재선이 두 화면에서 다르게 편집된다.\n */\n private openApprovalLine(line: any[]) {\n const popup = openPopup(\n html`<approval-line-items-editor-popup\n .value=${line}\n .confirmCallback=${(after?: any[] | null) => this.saveApprovalLine(after || [])}\n ></approval-line-items-editor-popup>`,\n { backdrop: true, size: 'large', title: i18next.t('title.approval-line editor') }\n )\n\n return popup\n }\n\n private async saveApprovalLine(after: any[]) {\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: gql`\n mutation ($patches: [ActivityPatch!]!) {\n updateMultipleActivity(patches: $patches) {\n name\n }\n }\n `,\n variables: { patches: [{ id: this.activity.id, approvalLine: after, cuFlag: 'M' }] }\n })\n )\n\n if (!outcome.ok) return\n\n this.fetch()\n }\n}\n"]}
1
+ {"version":3,"file":"activity-detail-page.js","sourceRoot":"","sources":["../../../client/pages/activity-catalog/activity-detail-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/list-state/ox-page-state.js'\nimport '@things-factory/organization'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { navigate, PageView } from '@operato/shell'\nimport { DocumentPageStyles, ScrollbarStyles } from '@operato/styles'\n\nimport { ranMutation } from '../../components/say-outcome.js'\nimport { activityPanels, panelSection } from './activity-panels.js'\nimport { detailActions } from './activity-detail-actions.js'\nimport { canonicalStage } from './activity-stage.js'\nimport { guideLines } from './activity-guide.js'\n\n/**\n * 업무 하나 — **그 업무에 관한 일을 여기서 끝낸다.**\n *\n * ── 왜 이 화면이 생겼나 (2026-09-23) ───────────────────────────────────────\n * 결재선 하나를 세워서 문이 서는지 보려면 화면을 셋 열어야 했고, 역할에 사람이 없으면 넷에서\n * 다섯이었다 [확인함]. 카탈로그는 카드에 「결재선 없음」이라고 적어 놓고 **어디로 가야 하는지는\n * 말하지 않았다** — 그 파일 전체에 `navigate()` 도 `<a href>` 도 없었다. 사람은 메뉴로 돌아가\n * 이름으로 찾아야 했다.\n *\n * 그래서 카탈로그는 고르는 자리로 두고, 고른 뒤의 일은 이 화면 안에서 끝낸다.\n *\n * ── 돌아갈 길을 화면이 갖는다 ─────────────────────────────────────────────\n * 탭 줄에 카탈로그가 남아 있으니 됐다고 볼 수 없다. 탭 줄은 **없을 수 있다** — 페이지가\n * `mdibar: false` 를 적으면 셸이 걷고(`apptool-ui/client/layout/mdibar-visibility.ts`), 무엇보다\n * **주소를 받아서 바로 여는 사람에게는 이전 화면이 아예 없다.** 「이 업무 결재선 좀 봐 주세요」로\n * 링크를 받은 사람이 그 경우다. 그래서 머리에 돌아갈 자리를 직접 둔다.\n *\n * ── 못 하는 일은 까닭을 말한다 ────────────────────────────────────────────\n * 눌리지 않는 단추만 남기지 않는다. 무엇을 할 수 있는지와 못 하면 왜인지는\n * `activity-detail-actions.ts` 가 값으로 답하고, 이 화면은 그 값을 문장으로 바꾼다. 원인을 여기서\n * 지어내지 않는다.\n */\nconst DETAIL = gql`\n query ($name: String!) {\n responses: installableActivities {\n items {\n name\n label\n labelTKey\n description\n guide\n guideTKeys\n approvalSteps\n activity {\n id\n }\n readiness {\n stage\n }\n }\n }\n activityByName(name: $name) {\n id\n name\n description\n approvalLine {\n type\n value\n participation\n approver {\n id\n name\n description\n controlNo\n }\n }\n }\n }\n`\n\n/** 결재선에서 **서명하는 단계만** 센다. 참조는 알기만 하고 막지 않는다. */\nfunction approvalSteps(line?: any[]): number {\n return (line || []).filter(one => String(one?.participation || '') !== 'reference').length\n}\n\n@customElement('activity-detail-page')\nexport class ActivityDetailPage extends localize(i18next)(PageView) {\n static styles = [\n ScrollbarStyles,\n DocumentPageStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow: auto;\n }\n\n /* 돌아갈 자리. 제목 위에 둔다 — 읽기 전에 보인다. */\n a[back] {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n align-self: flex-start;\n margin-bottom: var(--spacing-small);\n color: var(--md-sys-color-on-surface-variant);\n font-size: var(--ui-t-body, 13px);\n text-decoration: none;\n cursor: pointer;\n }\n\n a[back]:hover {\n color: var(--md-sys-color-on-surface);\n text-decoration: underline;\n }\n\n a[back] md-icon {\n --md-icon-size: 16px;\n }\n\n .head {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--spacing-small);\n }\n\n .head .name {\n font: var(--ui-t-title-font, bold 18px var(--theme-font));\n color: var(--md-sys-color-on-surface);\n }\n\n .stage,\n .use {\n padding: 1px 8px;\n border-radius: var(--md-sys-shape-corner-full);\n font-size: var(--ui-t-small, 12px);\n background-color: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .use:not([idle]) {\n background-color: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n\n section {\n margin-top: var(--spacing-large, 16px);\n }\n\n section > h3 {\n margin: 0 0 var(--spacing-small) 0;\n font-size: var(--ui-t-small, 12px);\n color: var(--md-sys-color-on-surface-variant);\n text-transform: uppercase;\n }\n\n .guide p {\n margin: 0 0 4px 0;\n line-height: 1.5;\n }\n\n /* 못 하는 까닭 · 비어 있다는 사실. 둘 다 **문장으로** 남는다. */\n .why {\n margin: 4px 0 0 0;\n color: var(--md-sys-color-on-surface-variant);\n font-size: var(--ui-t-body, 13px);\n }\n\n .steps {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: var(--spacing-small);\n }\n\n .steps span {\n padding: 2px 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-full);\n font-size: var(--ui-t-small, 12px);\n }\n\n button {\n font: inherit;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-full);\n padding: 6px 14px;\n cursor: pointer;\n }\n\n button:hover:not(:disabled) {\n background-color: var(--md-sys-color-surface-container-high);\n }\n\n button:disabled {\n opacity: 0.4;\n cursor: default;\n }\n `\n ]\n\n @state() private name = ''\n @state() private entry: any = null\n @state() private activity: any = null\n @state() private loaded = false\n /** 구획이 「이 업무에는 보일 것 없음」을 알렸나. 이름으로 센다. */\n @state() private emptyPanels: Record<string, boolean> = {}\n /** 왜 못 읽었나 — ox-page-state 가 그것을 말하고 다시 시도를 준다. */\n @state() private loadError: unknown = null\n\n get context() {\n return {\n frame: 'document',\n title: this.entry ? this.shownName(this.entry) : i18next.t('title.activity-detail'),\n /*\n * 이름은 **이 화면이 그린다.** 제목 줄은 끈다.\n *\n * 이름 옆에 단계 띠와 「사용 중」이 함께 서야 한다 — 그 셋이 한 줄에 있어야 이 업무를 지금\n * 믿고 쓸 수 있는지가 한눈에 읽힌다. 제목 줄은 그것들을 못 받는다.\n *\n * 끄지 않으면 같은 이름이 화면에 두 번 뜬다(실측 2026-09-24: 제목 줄 「부적합 처분 승인」,\n * 바로 아래 또 「부적합 처분 승인」). `title` 은 그대로 두므로 **탭 이름은 살아 있다** —\n * `heading: false` 가 그리는 자리마다 켜고 끄라고 둔 문이다.\n */\n heading: false,\n help: 'worklist/activity-catalog-page'\n }\n }\n\n /**\n * 이름이 주소로 온다. 같은 라우트 안에서 이름만 바뀌어도 이 콜백이 다시 오므로, **이름이 실제로\n * 바뀌었을 때만** 다시 읽는다.\n */\n async pageUpdated(changes: any, lifecycle: any) {\n this.readName(lifecycle)\n }\n\n /**\n * 처음 그린 뒤에도 한 번 읽는다 — **첫 방문이 통째로 비는 것을 봤다.**\n *\n * [확인함 2026-09-24 :4000] 카드에서 처음 들어가면 주소는 맞고(`?name=…`) `lifecycle.params.name`\n * 도 들어 있는데 화면에 돌아갈 링크만 남았다. 같은 자리로 두 번째 들어가면 멀쩡했고, 그 상태에서\n * 콜백을 손으로 한 번 부르면 바로 채워졌다.\n *\n * 이 화면은 주소로 처음 닿을 때 **모듈이 그때 실려 온다**(route.ts 의 동적 import). 셸은 화면을\n * 처음 세울 때 이 콜백을 다음 animationFrame 으로 미루는데(page-view 가 「엘리먼트가 아직 안\n * 만들어졌을 수 있다」고 적어 둔 그 갈래), 그 사이에 요소가 아직 이 클래스로 올라오지 않았으면\n * **아무 일도 안 하는 기반 클래스의 콜백**이 불린다. 그 뒤로는 생애주기가 더 안 바뀌므로 이름을\n * 받을 기회가 다시 오지 않는다.\n *\n * 그래서 이름을 받는 자리를 둘로 둔다. 둘 다 같은 판단을 지나므로 두 번 읽지 않는다.\n */\n firstUpdated() {\n this.readName()\n }\n\n private readName(lifecycle?: any) {\n const name = String((lifecycle ?? this.lifecycle)?.params?.name || '')\n\n if (name && name !== this.name) {\n this.name = name\n /* 구획이 알린 것은 그 업무의 것이다. 다른 업무로 옮기면 다시 듣는다. */\n this.emptyPanels = {}\n this.fetch()\n }\n }\n\n private shownName(one: any): string {\n if (!one?.label) return String(one?.name ?? '')\n return one.labelTKey ? String(i18next.t(one.labelTKey, { defaultValue: one.label })) : String(one.label)\n }\n\n private async fetch() {\n this.loaded = false\n this.loadError = null\n\n try {\n const response = await client.query({ query: DETAIL, variables: { name: this.name }, fetchPolicy: 'no-cache' })\n\n /*\n * 목록에 필터가 없어서 전부 받아 하나를 고른다. 업무는 설치된 모듈이 선언하는 것이라 수가\n * 닫혀 있다 [확인함 2026-09-23: 마흔 안쪽]. 그 전제가 깨지면 서버에 이름으로 찾는 갈래를\n * 두는 것이 맞다 — 여기서 자르지 않는다.\n */\n const items = response?.data?.responses?.items || []\n\n this.entry = items.find((one: any) => one.name === this.name) || null\n this.activity = response?.data?.activityByName || null\n this.loaded = true\n } catch (thrown) {\n this.loadError = thrown\n this.loaded = true\n }\n }\n\n render() {\n return html`\n <a back @click=${() => navigate('activity-catalog')}>\n <md-icon>chevron_left</md-icon>${i18next.t('button.back-to-activity-catalog')}\n </a>\n\n <ox-page-state .loading=${!this.loaded} .error=${this.loadError} @retry=${() => this.fetch()}\n >${this.loaded && !this.loadError ? this.renderDetail() : ''}</ox-page-state\n >\n `\n }\n\n private renderDetail() {\n if (!this.entry) {\n /* 없는 이름으로 들어온 주소. 빈 화면으로 두지 않는다 — 무엇을 못 찾았는지 이름을 댄다. */\n return html`<div class=\"why\">${i18next.t('text.activity-not-found', { name: this.name })}</div>`\n }\n\n const actions = detailActions(this.entry)\n const stage = canonicalStage(this.entry.readiness?.stage)\n\n return html`\n <div class=\"head\">\n <span class=\"name\">${this.shownName(this.entry)}</span>\n ${stage ? html`<span class=\"stage\">${i18next.t('label.readiness-' + stage)}</span>` : ''}\n ${actions.registered\n ? html`<span class=\"use\" ?idle=${!actions.inUse}\n >${actions.inUse\n ? i18next.t('text.activity-in-use-here')\n : i18next.t('text.activity-not-in-use-here')}</span\n >`\n : ''}\n </div>\n\n ${this.renderGuide()} ${this.renderUse(actions)} ${this.renderApprovalLine(actions)} ${this.renderPanels()}\n `\n }\n\n /** 이 업무가 무엇인가. `guide` 가 그 자리이고, 없으면 설명이다. */\n private renderGuide() {\n const shown = guideLines(this.entry, (key, options) => i18next.t(key, options)).filter(Boolean)\n\n if (!shown.length && !this.entry.description) return html``\n\n return html`\n <section class=\"guide\">\n <h3>${i18next.t('label.activity-what-is-it')}</h3>\n ${shown.length\n ? shown.map(line => html`<p>${line}</p>`)\n : html`<p>${this.entry.description}</p>`}\n </section>\n `\n }\n\n private renderUse(actions: ReturnType<typeof detailActions>) {\n if (!actions.registered) return html``\n\n return html`\n <section>\n <h3>${i18next.t('label.activity-use-here')}</h3>\n ${actions.canInstall\n ? html`<button @click=${() => this.setUse(true)}>${i18next.t('button.activity-install')}</button>`\n : html`<button @click=${() => this.setUse(false)}>${i18next.t('button.activity-uninstall')}</button>`}\n </section>\n `\n }\n\n private renderApprovalLine(actions: ReturnType<typeof detailActions>) {\n const line = this.activity?.approvalLine || []\n const steps = approvalSteps(line)\n\n return html`\n <section>\n <h3>${i18next.t('label.activity-approval-line')}</h3>\n\n ${steps\n ? html`<div class=\"steps\">\n ${line.map((one: any) => html`<span>${one.approver?.name || one.value || ''}</span>`)}\n </div>`\n : ''}\n\n <button ?disabled=${!actions.canSetApprovalLine} @click=${() => this.openApprovalLine(line)}>\n ${i18next.t('button.activity-set-approval-line')}\n </button>\n\n <p class=\"why\">\n ${actions.approvalLineBlock\n ? i18next.t('text.activity-line-' + actions.approvalLineBlock)\n : steps\n ? i18next.t('text.activity-line-steps', { steps })\n : i18next.t('text.activity-line-none')}\n </p>\n </section>\n `\n }\n\n /**\n * 다른 꾸러미가 건 구획.\n *\n * **하나도 안 걸렸으면 그 사실을 말한다.** 빈자리로 두면 「이 앱에는 그 구획이 없다」와\n * 「정할 것이 없다」가 같은 화면이 된다.\n */\n private renderPanels() {\n const context = { activityName: this.entry.name, activityId: this.activity?.id }\n const panels = activityPanels(context)\n const shape = panelSection(\n panels.map(one => one.name),\n this.emptyPanels\n )\n\n if (shape.kind === 'none-registered') {\n return html`\n <section>\n <h3>${i18next.t('label.activity-panels')}</h3>\n <p class=\"why\">${i18next.t('text.activity-no-panels')}</p>\n </section>\n `\n }\n\n /*\n * 비었다고 알린 구획도 **그리기는 계속한다** — 숨길 뿐이다. 지우면 그 구획이 다시 읽고\n * 「있다」고 알릴 자리가 없어진다.\n */\n const hidden = shape.kind === 'all-empty' ? panels.map(one => one.name) : shape.hidden\n\n return html`\n <section ?hidden=${shape.kind === 'all-empty'}>\n <h3>${i18next.t('label.activity-panels')}</h3>\n ${panels.map(\n panel =>\n html`<div\n ?hidden=${hidden.includes(panel.name)}\n @activity-panel-content=${(event: CustomEvent) => this.panelReported(panel.name, event)}\n >\n ${panel.render(context)}\n </div>`\n )}\n </section>\n `\n }\n\n private panelReported(name: string, event: CustomEvent) {\n event.stopPropagation()\n this.emptyPanels = { ...this.emptyPanels, [name]: event.detail?.empty === true }\n }\n\n private async setUse(on: boolean) {\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: on\n ? gql`\n mutation ($name: String!) {\n activateInstallableActivity(name: $name) {\n name\n }\n }\n `\n : gql`\n mutation ($name: String!) {\n deactivateInstallableActivity(name: $name) {\n name\n }\n }\n `,\n variables: { name: this.entry.name }\n })\n )\n\n if (!outcome.ok) return\n\n this.fetch()\n }\n\n /**\n * 결재선 편집기는 **organization 것을 그대로 부른다.** 이 화면이 자기 편집기를 따로 지으면 같은\n * 결재선이 두 화면에서 다르게 편집된다.\n */\n private openApprovalLine(line: any[]) {\n const popup = openPopup(\n html`<approval-line-items-editor-popup\n .value=${line}\n .confirmCallback=${(after?: any[] | null) => this.saveApprovalLine(after || [])}\n ></approval-line-items-editor-popup>`,\n { backdrop: true, size: 'large', title: i18next.t('title.approval-line editor') }\n )\n\n return popup\n }\n\n private async saveApprovalLine(after: any[]) {\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: gql`\n mutation ($patches: [ActivityPatch!]!) {\n updateMultipleActivity(patches: $patches) {\n name\n }\n }\n `,\n variables: { patches: [{ id: this.activity.id, approvalLine: after, cuFlag: 'M' }] }\n })\n )\n\n if (!outcome.ok) return\n\n this.fetch()\n }\n}\n"]}
@@ -43,6 +43,34 @@ export type PanelRejection = 'duplicate-name' | 'no-name' | 'no-label-key' | 'no
43
43
  export declare function registerActivityPanel(panel: ActivityPanel): void;
44
44
  /** 걸린 구획. 건 차례 그대로다 — 앱이 적은 순서가 화면의 순서다. */
45
45
  export declare function activityPanels(context?: ActivityPanelContext): ActivityPanel[];
46
+ /**
47
+ * 구획이 읽은 뒤 「이 업무에는 보일 것이 없다」를 알리는 사건의 이름.
48
+ *
49
+ * `appliesTo` 는 그리기 전에 답해야 해서, **자기 자료를 읽어 봐야 아는** 구획은 거기서 답하지 못한다.
50
+ * 기준정보 변경 정책이 그렇다 — 모든 업무에 걸리고, 읽고 나서야 이 업무를 부르는 축이 0 인 줄 안다.
51
+ * 그 칸이 휴무 승인 같은 업무에 서서 「없다」고 말하면 아무도 기대하지 않은 자리의 잡음이다.
52
+ *
53
+ * 구획은 `new CustomEvent(ACTIVITY_PANEL_CONTENT, { detail: { empty }, bubbles: true, composed: true })`
54
+ * 를 보낸다. `empty: false` 를 다시 보내면 칸이 돌아온다. **안 보낸 구획은 보인다** — 아직 못 읽은
55
+ * 것과 없는 것은 다르고, 못 읽었으면 구획이 스스로 그렇다고 말한다.
56
+ */
57
+ export declare const ACTIVITY_PANEL_CONTENT = "activity-panel-content";
58
+ export type PanelSection =
59
+ /** 걸린 구획이 없다. 화면이 그 사실을 말한다. */
60
+ {
61
+ kind: 'none-registered';
62
+ }
63
+ /** 걸린 구획이 모두 비었다고 알렸다. 칸째로 접는다. */
64
+ | {
65
+ kind: 'all-empty';
66
+ }
67
+ /** 보일 것이 있다. `hidden` 에 든 구획만 접는다. */
68
+ | {
69
+ kind: 'shown';
70
+ hidden: string[];
71
+ };
72
+ /** 걸린 구획 이름과 각 구획이 알린 것으로 칸의 모양을 정한다. */
73
+ export declare function panelSection(names: string[], emptyByName: Record<string, boolean>): PanelSection;
46
74
  /** 거절된 등록. 화면이 아니라 개발 중에 읽는 값이고, 빈 배열이 정상이다. */
47
75
  export declare function rejectedActivityPanels(): {
48
76
  name: string;
@@ -22,6 +22,25 @@ export function activityPanels(context) {
22
22
  return [...panels];
23
23
  return panels.filter(one => (one.appliesTo ? one.appliesTo(context) : true));
24
24
  }
25
+ /**
26
+ * 구획이 읽은 뒤 「이 업무에는 보일 것이 없다」를 알리는 사건의 이름.
27
+ *
28
+ * `appliesTo` 는 그리기 전에 답해야 해서, **자기 자료를 읽어 봐야 아는** 구획은 거기서 답하지 못한다.
29
+ * 기준정보 변경 정책이 그렇다 — 모든 업무에 걸리고, 읽고 나서야 이 업무를 부르는 축이 0 인 줄 안다.
30
+ * 그 칸이 휴무 승인 같은 업무에 서서 「없다」고 말하면 아무도 기대하지 않은 자리의 잡음이다.
31
+ *
32
+ * 구획은 `new CustomEvent(ACTIVITY_PANEL_CONTENT, { detail: { empty }, bubbles: true, composed: true })`
33
+ * 를 보낸다. `empty: false` 를 다시 보내면 칸이 돌아온다. **안 보낸 구획은 보인다** — 아직 못 읽은
34
+ * 것과 없는 것은 다르고, 못 읽었으면 구획이 스스로 그렇다고 말한다.
35
+ */
36
+ export const ACTIVITY_PANEL_CONTENT = 'activity-panel-content';
37
+ /** 걸린 구획 이름과 각 구획이 알린 것으로 칸의 모양을 정한다. */
38
+ export function panelSection(names, emptyByName) {
39
+ if (!names.length)
40
+ return { kind: 'none-registered' };
41
+ const hidden = names.filter(name => emptyByName[name] === true);
42
+ return hidden.length === names.length ? { kind: 'all-empty' } : { kind: 'shown', hidden };
43
+ }
25
44
  /** 거절된 등록. 화면이 아니라 개발 중에 읽는 값이고, 빈 배열이 정상이다. */
26
45
  export function rejectedActivityPanels() {
27
46
  return [...rejected];
@@ -1 +1 @@
1
- {"version":3,"file":"activity-panels.js","sourceRoot":"","sources":["../../../client/pages/activity-catalog/activity-panels.ts"],"names":[],"mappings":"AA0CA,MAAM,MAAM,GAAoB,EAAE,CAAA;AAClC,MAAM,QAAQ,GAA+C,EAAE,CAAA;AAE/D;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAoB;IACxD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAE7C,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;IAC9E,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAA;IACxG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;IACrG,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;IAEhG,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AACjC,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,MAAM,CAAC,CAAA;IAChC,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;AAC9E,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,sBAAsB;IACpC,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAA;AACtB,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,oBAAoB;IAClC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;IACjB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;AACrB,CAAC","sourcesContent":["import type { TemplateResult } from 'lit'\n\n/**\n * 업무 상세에 다른 꾸러미가 구획을 거는 문.\n *\n * ── 왜 문이 필요한가 ───────────────────────────────────────────────────────\n * 업무 상세는 「이 업무가 어떻게 처리되나」를 한 자리에서 끝내는 화면이다. 그런데 그 답의 일부를\n * worklist 가 모른다 — 기준정보 변경 정책은 `@things-factory/ops-master` 가 알고, 그 둘은 서로를\n * 의존하지 않는다 [확인함 2026-09-23: worklist 의존에 ops-master 0건, 그 반대도 0건].\n *\n * 그래서 **worklist 가 문을 열고, 앱이 건다.** ops-master 는 구획 컴포넌트만 내놓고 worklist 를\n * 모른다. 둘을 잇는 값을 아는 것은 둘 다 물고 있는 앱뿐이다(아키텍트 판정 2026-09-23). 서버 쪽\n * 같은 이음새가 먼저 이 모양으로 섰다 — `registerApprovalRouteInspector` 는 ops-master 가 열고\n * plant 가 자기 짝을 건다.\n *\n * ── 안 걸린 것이 오류보고없이 지나가면 안 된다 ────────────────────────────\n * 구획이 하나도 안 걸린 화면과 「정할 것이 없는」 화면은 **같은 빈자리로 보인다.** 그래서 이 문은\n * 걸린 것의 이름을 남기고, 화면은 그 목록이 비었을 때 비었다고 말한다. 판정의 조건 1 이다.\n *\n * ── 구획은 자기 자료를 자기가 읽는다 ──────────────────────────────────────\n * 이 문은 **자리와 이름만** 넘긴다. 구획이 그릴 값을 worklist 가 받아다 넘기지 않는다 — 그러면 두\n * 꾸러미가 같은 사실을 두 벌로 세고, 한쪽이 낡아도 알 수 없다. 판정의 조건 2 다.\n */\nexport type ActivityPanelContext = {\n /** 업무의 이름. 콜백을 찾는 키이므로 번역하지 않는다. */\n activityName: string\n /** 설치된 업무만 갖는다. 없으면 이 업무는 이 도메인에 아직 설치되지 않았다. */\n activityId?: string\n}\n\nexport type ActivityPanel = {\n /** 이 구획의 이름. 목록에 남고 중복을 가린다. 사람에게 보이는 글자가 아니다. */\n name: string\n /** 사람에게 보이는 이름은 **열쇠로 받는다.** 값으로 받으면 언어를 바꿔도 안 바뀐다. */\n labelKey: string\n /** 이 업무에 이 구획이 해당하나. 없으면 늘 해당한다. */\n appliesTo?: (context: ActivityPanelContext) => boolean\n render: (context: ActivityPanelContext) => TemplateResult\n}\n\nexport type PanelRejection = 'duplicate-name' | 'no-name' | 'no-label-key' | 'no-render'\n\nconst panels: ActivityPanel[] = []\nconst rejected: { name: string; reason: PanelRejection }[] = []\n\n/**\n * 구획을 건다. 같은 이름이 두 번 오면 **먼저 온 것이 선다** — 뒤에 온 것이 앞의 것을 조용히 덮으면\n * 어느 꾸러미가 그리고 있는지 알 수 없다.\n */\nexport function registerActivityPanel(panel: ActivityPanel): void {\n const name = String(panel?.name ?? '').trim()\n\n if (!name) return void rejected.push({ name: '(no name)', reason: 'no-name' })\n if (panels.some(one => one.name === name)) return void rejected.push({ name, reason: 'duplicate-name' })\n if (!String(panel.labelKey ?? '').trim()) return void rejected.push({ name, reason: 'no-label-key' })\n if (typeof panel.render !== 'function') return void rejected.push({ name, reason: 'no-render' })\n\n panels.push({ ...panel, name })\n}\n\n/** 걸린 구획. 건 차례 그대로다 — 앱이 적은 순서가 화면의 순서다. */\nexport function activityPanels(context?: ActivityPanelContext): ActivityPanel[] {\n if (!context) return [...panels]\n return panels.filter(one => (one.appliesTo ? one.appliesTo(context) : true))\n}\n\n/** 거절된 등록. 화면이 아니라 개발 중에 읽는 값이고, 빈 배열이 정상이다. */\nexport function rejectedActivityPanels(): { name: string; reason: PanelRejection }[] {\n return [...rejected]\n}\n\n/** 시험을 위한 자리. 제품 코드는 부르지 않는다. */\nexport function forgetActivityPanels(): void {\n panels.length = 0\n rejected.length = 0\n}\n"]}
1
+ {"version":3,"file":"activity-panels.js","sourceRoot":"","sources":["../../../client/pages/activity-catalog/activity-panels.ts"],"names":[],"mappings":"AA0CA,MAAM,MAAM,GAAoB,EAAE,CAAA;AAClC,MAAM,QAAQ,GAA+C,EAAE,CAAA;AAE/D;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAoB;IACxD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAE7C,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;IAC9E,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAA;IACxG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;IACrG,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;IAEhG,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AACjC,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,GAAG,MAAM,CAAC,CAAA;IAChC,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;AAC9E,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,wBAAwB,CAAA;AAU9D,yCAAyC;AACzC,MAAM,UAAU,YAAY,CAAC,KAAe,EAAE,WAAoC;IAChF,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAA;IAErD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAA;IAE/D,OAAO,MAAM,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAA;AAC3F,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,sBAAsB;IACpC,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAA;AACtB,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,oBAAoB;IAClC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;IACjB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;AACrB,CAAC","sourcesContent":["import type { TemplateResult } from 'lit'\n\n/**\n * 업무 상세에 다른 꾸러미가 구획을 거는 문.\n *\n * ── 왜 문이 필요한가 ───────────────────────────────────────────────────────\n * 업무 상세는 「이 업무가 어떻게 처리되나」를 한 자리에서 끝내는 화면이다. 그런데 그 답의 일부를\n * worklist 가 모른다 — 기준정보 변경 정책은 `@things-factory/ops-master` 가 알고, 그 둘은 서로를\n * 의존하지 않는다 [확인함 2026-09-23: worklist 의존에 ops-master 0건, 그 반대도 0건].\n *\n * 그래서 **worklist 가 문을 열고, 앱이 건다.** ops-master 는 구획 컴포넌트만 내놓고 worklist 를\n * 모른다. 둘을 잇는 값을 아는 것은 둘 다 물고 있는 앱뿐이다(아키텍트 판정 2026-09-23). 서버 쪽\n * 같은 이음새가 먼저 이 모양으로 섰다 — `registerApprovalRouteInspector` 는 ops-master 가 열고\n * plant 가 자기 짝을 건다.\n *\n * ── 안 걸린 것이 오류보고없이 지나가면 안 된다 ────────────────────────────\n * 구획이 하나도 안 걸린 화면과 「정할 것이 없는」 화면은 **같은 빈자리로 보인다.** 그래서 이 문은\n * 걸린 것의 이름을 남기고, 화면은 그 목록이 비었을 때 비었다고 말한다. 판정의 조건 1 이다.\n *\n * ── 구획은 자기 자료를 자기가 읽는다 ──────────────────────────────────────\n * 이 문은 **자리와 이름만** 넘긴다. 구획이 그릴 값을 worklist 가 받아다 넘기지 않는다 — 그러면 두\n * 꾸러미가 같은 사실을 두 벌로 세고, 한쪽이 낡아도 알 수 없다. 판정의 조건 2 다.\n */\nexport type ActivityPanelContext = {\n /** 업무의 이름. 콜백을 찾는 키이므로 번역하지 않는다. */\n activityName: string\n /** 설치된 업무만 갖는다. 없으면 이 업무는 이 도메인에 아직 설치되지 않았다. */\n activityId?: string\n}\n\nexport type ActivityPanel = {\n /** 이 구획의 이름. 목록에 남고 중복을 가린다. 사람에게 보이는 글자가 아니다. */\n name: string\n /** 사람에게 보이는 이름은 **열쇠로 받는다.** 값으로 받으면 언어를 바꿔도 안 바뀐다. */\n labelKey: string\n /** 이 업무에 이 구획이 해당하나. 없으면 늘 해당한다. */\n appliesTo?: (context: ActivityPanelContext) => boolean\n render: (context: ActivityPanelContext) => TemplateResult\n}\n\nexport type PanelRejection = 'duplicate-name' | 'no-name' | 'no-label-key' | 'no-render'\n\nconst panels: ActivityPanel[] = []\nconst rejected: { name: string; reason: PanelRejection }[] = []\n\n/**\n * 구획을 건다. 같은 이름이 두 번 오면 **먼저 온 것이 선다** — 뒤에 온 것이 앞의 것을 조용히 덮으면\n * 어느 꾸러미가 그리고 있는지 알 수 없다.\n */\nexport function registerActivityPanel(panel: ActivityPanel): void {\n const name = String(panel?.name ?? '').trim()\n\n if (!name) return void rejected.push({ name: '(no name)', reason: 'no-name' })\n if (panels.some(one => one.name === name)) return void rejected.push({ name, reason: 'duplicate-name' })\n if (!String(panel.labelKey ?? '').trim()) return void rejected.push({ name, reason: 'no-label-key' })\n if (typeof panel.render !== 'function') return void rejected.push({ name, reason: 'no-render' })\n\n panels.push({ ...panel, name })\n}\n\n/** 걸린 구획. 건 차례 그대로다 — 앱이 적은 순서가 화면의 순서다. */\nexport function activityPanels(context?: ActivityPanelContext): ActivityPanel[] {\n if (!context) return [...panels]\n return panels.filter(one => (one.appliesTo ? one.appliesTo(context) : true))\n}\n\n/**\n * 구획이 읽은 뒤 「이 업무에는 보일 것이 없다」를 알리는 사건의 이름.\n *\n * `appliesTo` 는 그리기 전에 답해야 해서, **자기 자료를 읽어 봐야 아는** 구획은 거기서 답하지 못한다.\n * 기준정보 변경 정책이 그렇다 — 모든 업무에 걸리고, 읽고 나서야 이 업무를 부르는 축이 0 인 줄 안다.\n * 그 칸이 휴무 승인 같은 업무에 서서 「없다」고 말하면 아무도 기대하지 않은 자리의 잡음이다.\n *\n * 구획은 `new CustomEvent(ACTIVITY_PANEL_CONTENT, { detail: { empty }, bubbles: true, composed: true })`\n * 를 보낸다. `empty: false` 를 다시 보내면 칸이 돌아온다. **안 보낸 구획은 보인다** — 아직 못 읽은\n * 것과 없는 것은 다르고, 못 읽었으면 구획이 스스로 그렇다고 말한다.\n */\nexport const ACTIVITY_PANEL_CONTENT = 'activity-panel-content'\n\nexport type PanelSection =\n /** 걸린 구획이 없다. 화면이 그 사실을 말한다. */\n | { kind: 'none-registered' }\n /** 걸린 구획이 모두 비었다고 알렸다. 칸째로 접는다. */\n | { kind: 'all-empty' }\n /** 보일 것이 있다. `hidden` 에 든 구획만 접는다. */\n | { kind: 'shown'; hidden: string[] }\n\n/** 걸린 구획 이름과 각 구획이 알린 것으로 칸의 모양을 정한다. */\nexport function panelSection(names: string[], emptyByName: Record<string, boolean>): PanelSection {\n if (!names.length) return { kind: 'none-registered' }\n\n const hidden = names.filter(name => emptyByName[name] === true)\n\n return hidden.length === names.length ? { kind: 'all-empty' } : { kind: 'shown', hidden }\n}\n\n/** 거절된 등록. 화면이 아니라 개발 중에 읽는 값이고, 빈 배열이 정상이다. */\nexport function rejectedActivityPanels(): { name: string; reason: PanelRejection }[] {\n return [...rejected]\n}\n\n/** 시험을 위한 자리. 제품 코드는 부르지 않는다. */\nexport function forgetActivityPanels(): void {\n panels.length = 0\n rejected.length = 0\n}\n"]}
@@ -47,6 +47,7 @@ export declare class ActivityInstanceStartPage extends ActivityInstanceStartPage
47
47
  updated(changes: any): void;
48
48
  pageUpdated(changes: any, lifecycle: any, changedBefore: any): Promise<void>;
49
49
  fetchActivityInstance(id: any): Promise<void>;
50
+ discardDraft(): Promise<void>;
50
51
  draftActivityInstance(): Promise<void>;
51
52
  issueActivityInstance(): Promise<void>;
52
53
  }
@@ -12,7 +12,9 @@ import { keyed } from 'lit/directives/keyed.js';
12
12
  import { client } from '@operato/graphql';
13
13
  import { i18next, localize } from '@operato/i18n';
14
14
  import { ranMutation } from '../../components/say-outcome.js';
15
- import { PageView } from '@operato/shell';
15
+ import { offersDiscard } from './discard-offer.js';
16
+ import { navigate, PageView } from '@operato/shell';
17
+ import { OxPrompt } from '@operato/popup';
16
18
  import { CommonButtonStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
17
19
  import { provider } from '@operato/board/board-provider.js';
18
20
  import { notify } from '@operato/layout';
@@ -58,6 +60,11 @@ let ActivityInstanceStartPage = class ActivityInstanceStartPage extends localize
58
60
  */
59
61
  menu: 'draft-list',
60
62
  actions: [
63
+ offersDiscard(this.activityInstance) && {
64
+ title: i18next.t('button.discard-draft'),
65
+ action: this.discardDraft.bind(this),
66
+ ...CommonButtonStyles.delete
67
+ },
61
68
  issuable && {
62
69
  title: i18next.t('button.draft'),
63
70
  action: this.draftActivityInstance.bind(this),
@@ -306,6 +313,31 @@ let ActivityInstanceStartPage = class ActivityInstanceStartPage extends localize
306
313
  this.loadError = error;
307
314
  }
308
315
  }
316
+ /* Asked once, since a discarded draft cannot come back; then back to the list it was opened from. */
317
+ async discardDraft() {
318
+ const { id } = this.activityInstance || {};
319
+ const sure = await OxPrompt.open({
320
+ type: 'warning',
321
+ title: i18next.t('text.discard-draft-confirm'),
322
+ text: i18next.t('text.discard-draft-for-good'),
323
+ confirmButton: { text: i18next.t('button.discard-draft'), destructive: true },
324
+ cancelButton: { text: i18next.t('button.cancel') }
325
+ });
326
+ if (!sure)
327
+ return;
328
+ const outcome = await ranMutation(() => client.mutate({
329
+ mutation: gql `
330
+ mutation ($id: String!) {
331
+ deleteDraftActivityInstance(id: $id)
332
+ }
333
+ `,
334
+ variables: { id }
335
+ }));
336
+ if (outcome.ok) {
337
+ notify({ message: i18next.t('text.draft-discarded') });
338
+ navigate('draft-list');
339
+ }
340
+ }
309
341
  async draftActivityInstance() {
310
342
  var { id: activityId } = this.activity;
311
343
  var { id, name, description, priority, threadsMin, threadsMax, dueAt, input, output, reason, uiType, assignees, approvalLine } = this.activityInstance;