@things-factory/worklist 10.1.44 → 10.1.47
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.
- package/dist-client/bootstrap.js +2 -0
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/components/activity-approval-ribon.d.ts +1 -1
- package/dist-client/components/activity-approval-ribon.js +2 -2
- package/dist-client/components/activity-approval-ribon.js.map +1 -1
- package/dist-client/components/activity-fields.d.ts +7 -0
- package/dist-client/components/activity-fields.js +8 -0
- package/dist-client/components/activity-fields.js.map +1 -1
- package/dist-client/components/activity-instance-ribon.d.ts +1 -1
- package/dist-client/components/activity-instance-ribon.js +2 -2
- package/dist-client/components/activity-instance-ribon.js.map +1 -1
- package/dist-client/components/activity-thread-ribon.d.ts +1 -1
- package/dist-client/components/activity-thread-ribon.js +2 -2
- package/dist-client/components/activity-thread-ribon.js.map +1 -1
- package/dist-client/components/activity-thread-timeline.js +3 -4
- package/dist-client/components/activity-thread-timeline.js.map +1 -1
- package/dist-client/components/state-ribon.d.ts +9 -1
- package/dist-client/components/state-ribon.js +43 -34
- package/dist-client/components/state-ribon.js.map +1 -1
- package/dist-client/entity360/person-open-work-section.d.ts +23 -0
- package/dist-client/entity360/person-open-work-section.js +79 -0
- package/dist-client/entity360/person-open-work-section.js.map +1 -0
- package/dist-client/pages/activity-approval/activity-approval-page.js +2 -2
- package/dist-client/pages/activity-approval/activity-approval-page.js.map +1 -1
- package/dist-client/pages/activity-catalog/activity-detail-page.js +2 -2
- package/dist-client/pages/activity-catalog/activity-detail-page.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-view.js +19 -40
- package/dist-client/pages/activity-instance/activity-instance-view.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-page.js +2 -2
- package/dist-client/pages/activity-thread/activity-thread-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-view-page.js +2 -2
- package/dist-client/pages/activity-thread/activity-thread-view-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-view.js +2 -2
- package/dist-client/pages/activity-thread/activity-thread-view.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/templates/activity-approval-context-template.js +6 -5
- package/dist-client/templates/activity-approval-context-template.js.map +1 -1
- package/dist-client/templates/activity-thread-context-template.js +3 -2
- package/dist-client/templates/activity-thread-context-template.js.map +1 -1
- package/dist-client/tsconfig.chk.tsbuildinfo +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/activity-summary/activity-summary-query.js +6 -53
- package/dist-server/service/activity-summary/activity-summary-query.js.map +1 -1
- package/dist-server/service/activity-summary/index.d.ts +2 -1
- package/dist-server/service/activity-summary/index.js +2 -1
- package/dist-server/service/activity-summary/index.js.map +1 -1
- package/dist-server/service/activity-summary/open-work.d.ts +5 -0
- package/dist-server/service/activity-summary/open-work.js +63 -0
- package/dist-server/service/activity-summary/open-work.js.map +1 -0
- package/dist-server/service/activity-summary/person-open-work.d.ts +17 -0
- package/dist-server/service/activity-summary/person-open-work.js +68 -0
- package/dist-server/service/activity-summary/person-open-work.js.map +1 -0
- package/dist-server/service/index.d.ts +2 -2
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -10
- package/tests/person-open-work.test.ts +91 -0
- package/translations/en.json +3 -11
- package/translations/ja.json +3 -11
- package/translations/ko.json +3 -11
- package/translations/ms.json +3 -11
- package/translations/zh.json +3 -11
package/dist-client/bootstrap.js
CHANGED
|
@@ -13,7 +13,9 @@ import { OxGristRendererDuration } from '@operato/grist-editor/ox-grist-renderer
|
|
|
13
13
|
import { OxGristRendererQuantifier } from '@operato/grist-editor/ox-grist-renderer-quantifier.js';
|
|
14
14
|
import { GristEditorActivitySearchKeys } from './grist-editor/grist-editor-activity-search-key.js';
|
|
15
15
|
import { GristRendererActivitySearchKeys } from './grist-editor/grist-renderer-activity-search-key.js';
|
|
16
|
+
import { registerPersonOpenWorkSection } from './entity360/person-open-work-section.js';
|
|
16
17
|
export default function bootstrap() {
|
|
18
|
+
registerPersonOpenWorkSection();
|
|
17
19
|
/*
|
|
18
20
|
* 여기에 'activity-info-overlay' aside viewpart 를 등록해 두었었다. 걷었다 —
|
|
19
21
|
* 기본 정보는 이제 팝업으로 연다(activity-thread-ribon 의 openInfo 머리말 참조).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../client/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,4BAA4B,CAAA;AACnC,OAAO,mDAAmD,CAAA;AAC1D,OAAO,iDAAiD,CAAA;AACxD,OAAO,mDAAmD,CAAA;AAE1D,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAE5E,OAAO,EAAE,cAAc,IAAI,mBAAmB,EAAE,gBAAgB,IAAI,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AACtH,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAA;AACzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAA;AAC7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAA;AAC7F,OAAO,EAAE,yBAAyB,EAAE,MAAM,uDAAuD,CAAA;AACjG,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAA;AAClG,OAAO,EAAE,+BAA+B,EAAE,MAAM,sDAAsD,CAAA;
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../client/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,4BAA4B,CAAA;AACnC,OAAO,mDAAmD,CAAA;AAC1D,OAAO,iDAAiD,CAAA;AACxD,OAAO,mDAAmD,CAAA;AAE1D,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAE5E,OAAO,EAAE,cAAc,IAAI,mBAAmB,EAAE,gBAAgB,IAAI,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AACtH,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAA;AACzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAA;AAC7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAA;AAC7F,OAAO,EAAE,yBAAyB,EAAE,MAAM,uDAAuD,CAAA;AACjG,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAA;AAClG,OAAO,EAAE,+BAA+B,EAAE,MAAM,sDAAsD,CAAA;AACtG,OAAO,EAAE,6BAA6B,EAAE,MAAM,yCAAyC,CAAA;AAEvF,MAAM,CAAC,OAAO,UAAU,SAAS;IAC/B,6BAA6B,EAAE,CAAC;IAEhC;;;;OAIG;IACH;QACE;YACE,QAAQ,EAAE,IAAI,CAAA,6EAA6E;YAC3F,OAAO,EAAE,kBAAkB;SAC5B;QACD;YACE,QAAQ,EAAE,IAAI,CAAA,yEAAyE;YACvF,OAAO,EAAE,gBAAgB;SAC1B;QACD;YACE,QAAQ,EAAE,IAAI,CAAA,6EAA6E;YAC3F,OAAO,EAAE,kBAAkB;SAC5B;KACF,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;QAClC,KAAK,CAAC,QAAQ,CAAC;YACb,IAAI,EAAE,mBAAmB;YACzB,IAAI,EAAE;gBACJ,QAAQ,EAAE,aAAa,CAAC,KAAK;gBAC7B,QAAQ,EAAE,IAAI,CAAA;;qBAED,KAAK,EAAC,CAAC,EAAC,EAAE;oBACjB,WAAW,CAAC,yBAAyB,EAAE;wBACrC,QAAQ;qBACT,CAAC,CAAA;gBACJ,CAAC;;;SAGJ;gBACD,OAAO;aACR;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,mBAAmB,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAA;IACtD,qBAAqB,CAAC,UAAU,EAAE,uBAAuB,CAAC,CAAA;IAC1D,mBAAmB,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAA;IAC1D,qBAAqB,CAAC,YAAY,EAAE,yBAAyB,CAAC,CAAA;IAE9D,mBAAmB,CAAC,sBAAsB,EAAE,6BAA6B,CAAC,CAAA;IAC1E,qBAAqB,CAAC,sBAAsB,EAAE,+BAA+B,CAAC,CAAA;AAChF,CAAC","sourcesContent":["import '@material/web/icon/icon.js'\nimport './templates/activity-approval-context-template.js'\nimport './templates/activity-thread-context-template.js'\nimport './templates/activity-instance-context-template.js'\n\nimport { html } from 'lit-html'\nimport { store } from '@operato/shell'\nimport { openOverlay, TOOL_POSITION } from '@operato/layout'\nimport { APPEND_CONTEXT_TOOL } from '@things-factory/context-ui/dist-client'\n\nimport { registerEditor as registerGristEditor, registerRenderer as registerGristRenderer } from '@operato/data-grist'\nimport { OxGristEditorDuration } from '@operato/grist-editor/ox-grist-editor-duration.js'\nimport { OxGristEditorQuantifier } from '@operato/grist-editor/ox-grist-editor-quantifier.js'\nimport { OxGristRendererDuration } from '@operato/grist-editor/ox-grist-renderer-duration.js'\nimport { OxGristRendererQuantifier } from '@operato/grist-editor/ox-grist-renderer-quantifier.js'\nimport { GristEditorActivitySearchKeys } from './grist-editor/grist-editor-activity-search-key.js'\nimport { GristRendererActivitySearchKeys } from './grist-editor/grist-renderer-activity-search-key.js'\nimport { registerPersonOpenWorkSection } from './entity360/person-open-work-section.js'\n\nexport default function bootstrap() {\n registerPersonOpenWorkSection();\n\n /*\n * 여기에 'activity-info-overlay' aside viewpart 를 등록해 두었었다. 걷었다 —\n * 기본 정보는 이제 팝업으로 연다(activity-thread-ribon 의 openInfo 머리말 참조).\n * ASIDEBAR 은 앱이 하나만 갖는 자리이고, 페이지에 매인 판이 그 자리를 차지할 이유가 없다.\n */\n [\n {\n template: html` <activity-approval-context-template></activity-approval-context-template> `,\n context: 'activityApproval'\n },\n {\n template: html` <activity-thread-context-template></activity-thread-context-template> `,\n context: 'activityThread'\n },\n {\n template: html` <activity-instance-context-template></activity-instance-context-template> `,\n context: 'activityInstance'\n }\n ].forEach(({ template, context }) => {\n store.dispatch({\n type: APPEND_CONTEXT_TOOL,\n tool: {\n position: TOOL_POSITION.FRONT,\n template: html`\n <md-icon\n @click=${async e => {\n openOverlay('context-toolbar-overlay', {\n template\n })\n }}\n >info</md-icon\n >\n `,\n context\n }\n })\n })\n\n registerGristEditor('duration', OxGristEditorDuration)\n registerGristRenderer('duration', OxGristRendererDuration)\n registerGristEditor('quantifier', OxGristEditorQuantifier)\n registerGristRenderer('quantifier', OxGristRendererQuantifier)\n\n registerGristEditor('activity-search-keys', GristEditorActivitySearchKeys)\n registerGristRenderer('activity-search-keys', GristRendererActivitySearchKeys)\n}\n"]}
|
|
@@ -4,7 +4,7 @@ import '../templates/activity-approval-context-template.js';
|
|
|
4
4
|
import { LitElement } from 'lit';
|
|
5
5
|
declare const ActivityApprovalRibon_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
6
6
|
export declare class ActivityApprovalRibon extends ActivityApprovalRibon_base {
|
|
7
|
-
static styles: import("lit").CSSResult[];
|
|
7
|
+
static styles: import("lit").CSSResult[][];
|
|
8
8
|
activityApproval: any;
|
|
9
9
|
render(): import("lit-html").TemplateResult<1>;
|
|
10
10
|
/**
|
|
@@ -5,7 +5,7 @@ import '../templates/activity-approval-context-template.js';
|
|
|
5
5
|
import { html, LitElement } from 'lit';
|
|
6
6
|
import { customElement, property } from 'lit/decorators.js';
|
|
7
7
|
import { i18next, localize } from '@operato/i18n';
|
|
8
|
-
import { openPopup } from '@operato/layout';
|
|
8
|
+
import { entity360Name, openPopup } from '@operato/layout';
|
|
9
9
|
import { StateRibonStyles } from './state-ribon.js';
|
|
10
10
|
let ActivityApprovalRibon = class ActivityApprovalRibon extends localize(i18next)(LitElement) {
|
|
11
11
|
static { this.styles = [StateRibonStyles]; }
|
|
@@ -54,7 +54,7 @@ let ActivityApprovalRibon = class ActivityApprovalRibon extends localize(i18next
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
${startingType == 'issue' && assignee
|
|
57
|
-
? html `<span class="who"><span class="who-label">${i18next.t('field.assignee')}</span>${assignee
|
|
57
|
+
? html `<span class="who"><span class="who-label">${i18next.t('field.assignee')}</span>${entity360Name(assignee)}</span>`
|
|
58
58
|
: ''}
|
|
59
59
|
${approvalLine
|
|
60
60
|
? html `<approval-line-brief
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-approval-ribon.js","sourceRoot":"","sources":["../../client/components/activity-approval-ribon.ts"],"names":[],"mappings":";AAAA,OAAO,wDAAwD,CAAA;AAC/D,OAAO,2EAA2E,CAAA;AAClF,OAAO,oDAAoD,CAAA;AAE3D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"activity-approval-ribon.js","sourceRoot":"","sources":["../../client/components/activity-approval-ribon.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/property-editor/ox-properties-dynamic-view.js'\nimport '@things-factory/organization/dist-client/component/approval-line-brief.js'\nimport '../templates/activity-approval-context-template.js'\n\nimport { html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { entity360Name, openPopup } from '@operato/layout'\n\nimport { StateRibonStyles } from './state-ribon.js'\n\n@customElement('activity-approval-ribon')\nexport class ActivityApprovalRibon extends localize(i18next)(LitElement) {\n static styles = [StateRibonStyles]\n\n @property({ type: Object }) activityApproval: any\n\n render() {\n const { activityInstance, state } = this.activityApproval?.activityThread || {}\n const { description, assignee, approvalLine, activity } = activityInstance || {}\n const { startingType, label, labelTKey } = activity || {}\n\n /*\n * What kind of decision this is — the activity's own name, not this instance's.\n *\n * Those are different facts and both belong here: 「부적합 처분 승인」 says what kind, and the\n * headline the activity declares says what this one asks for. The activity's name reached only\n * the catalog until it became a column on the row, so this screen showed the instance\n * description alone.\n *\n * ── The fallback puts English on a Korean screen ──────────────────────\n * Activity.description is what a developer wrote for other developers (\"The disposition waiting\n * for approval\"), so an activity with no label shows that to the person approving. That is worse\n * than the instance description and it is the reason to fill label in — it is not a fallback\n * anybody should be content with.\n */\n const named = label && labelTKey ? String(i18next.t(labelTKey, { defaultValue: label })) : label\n const kind = named || description || ''\n\n return html`\n <span class=\"state\">\n <i class=\"dot\"></i>\n ${i18next.t('label.activity-state-' + state)}\n </span>\n\n <md-icon\n class=\"info\"\n role=\"button\"\n tabindex=\"0\"\n title=${String(i18next.t('label.approval-info'))}\n @click=${() => this.openInfo(state)}\n @keydown=${(e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.openInfo(state)\n }\n }}\n >info</md-icon\n >\n\n <span class=\"desc\" title=${kind}>${kind}</span>\n\n\n ${startingType == 'issue' && assignee\n ? html`<span class=\"who\"><span class=\"who-label\">${i18next.t('field.assignee')}</span>${entity360Name(assignee)}</span>`\n : ''}\n ${approvalLine\n ? html`<approval-line-brief\n .model=${approvalLine}\n .current=${this.activityApproval.order}\n ></approval-line-brief>`\n : ''}\n `\n }\n\n /**\n * 이 건의 기본 정보 — **팝업으로 연다.**\n *\n * ── 왜 aside 가 아닌가 (2026-09-06) ────────────────────────────────────\n * 전에는 화면 오른쪽에서 나오는 aside 판이었다. 두 가지가 걸린다.\n *\n * 자리를 다툰다 화면 오른쪽 세로 판은 operato-twin 의 twin-entity360 이 쓰는 자리다\n * (position: fixed · right: 0 · height: 100vh). 같은 자리에 둘이 선다.\n * 전역 슬롯이다 VIEWPART_POSITION.ASIDEBAR 은 앱이 하나만 갖는다. 열 개가 넘는 패키지가\n * 그 자리를 쓰고 있어(twin 의 AI 협의 도크 · board-ui · help · more-ui …),\n * 이 판을 열면 그중 열려 있던 것이 닫힌다.\n *\n * 그리고 자리의 뜻이 다르다. 오른쪽 드로어는 **보고 있는 대상을 따라다니는 상세**이고(360 이\n * 그것이다 — 포커스가 바뀌면 내용이 바뀌고 화면을 옮겨도 남는다), 이 판은 **이 화면의 이 건**에\n * 대한 것이라 화면을 벗어나면 뜻이 없다. 페이지에 매인 것을 앱 전역 자리에 두지 않는다.\n *\n * 내용도 팝업 크기다 — 값 여덟 줄과 접힌 타임라인이라 세로 100vh 를 쓸 일이 없었다.\n */\n private openInfo(state: string) {\n openPopup(\n html`<activity-approval-context-template .activityApproval=${this.activityApproval} state=${state}></activity-approval-context-template>`,\n { title: i18next.t('label.approval-info') }\n )\n }\n\n updated(changes) {\n if (changes.has('activityApproval') && this.activityApproval) {\n const { state } = this.activityApproval.activityThread\n\n this.setAttribute('state', state)\n }\n }\n}\n"]}
|
|
@@ -68,3 +68,10 @@ export declare function declaredLabel(item: any): string;
|
|
|
68
68
|
* 값이 없으면 빈 칸으로 두지 않는다 — 빈 칸은 값이 없는 것인지 화면이 못 낸 것인지 모른다.
|
|
69
69
|
*/
|
|
70
70
|
export declare function fieldValue(value: any, depth?: number): unknown;
|
|
71
|
+
/**
|
|
72
|
+
* A person as a value - their name opens their 360 (ADR-0067 decision 1), and no one says so as
|
|
73
|
+
* `fieldValue` would. `entity360Name` draws plain text until the person kind is registered.
|
|
74
|
+
*/
|
|
75
|
+
export declare function personValue(person?: {
|
|
76
|
+
name?: string;
|
|
77
|
+
} | null): unknown;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import '@material/web/icon/icon.js';
|
|
2
2
|
import { css, html } from 'lit';
|
|
3
3
|
import { i18next } from '@operato/i18n';
|
|
4
|
+
import { entity360Name } from '@operato/layout';
|
|
4
5
|
/**
|
|
5
6
|
* 업무 항목을 화면에 내는 방식 — **두 화면이 한 벌을 쓴다.**
|
|
6
7
|
*
|
|
@@ -413,4 +414,11 @@ function layOut(value, depth) {
|
|
|
413
414
|
</dl>
|
|
414
415
|
`;
|
|
415
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* A person as a value - their name opens their 360 (ADR-0067 decision 1), and no one says so as
|
|
419
|
+
* `fieldValue` would. `entity360Name` draws plain text until the person kind is registered.
|
|
420
|
+
*/
|
|
421
|
+
export function personValue(person) {
|
|
422
|
+
return person?.name ? entity360Name(person) : fieldValue(undefined);
|
|
423
|
+
}
|
|
416
424
|
//# sourceMappingURL=activity-fields.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-fields.js","sourceRoot":"","sources":["../../client/components/activity-fields.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html } from 'lit'\n\nimport { i18next } from '@operato/i18n'\n\n/**\n * 업무 항목을 화면에 내는 방식 — **두 화면이 한 벌을 쓴다.**\n *\n * 업무 상세(activity-thread-page)와 결재 상세(activity-approval-page)는 거의 복사본이다. 항목을\n * 고르는 함수, 화면 종류에 따라 그리는 분기, 절을 그리는 부분이 두 파일에 같은 코드로 있었다.\n * 그래서 한쪽만 고치면 나머지가 그대로 남는다 — 실제로 그랬다(2026-09-06, 상세 화면의\n * Input · Output 영문 legend 를 고쳤는데 결재 화면에는 그대로 있었다).\n *\n * 화면마다 다른 것은 남긴다 — 상세는 결과를 적고, 결재는 읽기만 한다.\n */\n\n/** 절과 값 표의 모양. 카드 값은 SurfaceStyles 의 .ui-card 와 같다. */\nexport const ActivityFieldsStyles = css`\n section {\n margin: var(--spacing-large, 16px) var(--spacing-large, 16px) 0;\n padding: 14px;\n background: var(--ui-panel, #ffffff);\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r, 10px);\n }\n\n section > h2 {\n margin: 0 0 10px;\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--ui-ink, #17242c);\n }\n\n /* 읽기만 하는 절 — 값을 바꿀 수 없다는 것이 보여야 한다. */\n section[readonly] > h2 {\n color: var(--ui-muted, #57676f);\n }\n\n /* 받은 값 — 이름과 값을 두 칸으로. 이름은 오른쪽 정렬이라 값의 왼쪽 끝이 한 줄로 선다. */\n dl.given {\n display: grid;\n grid-template-columns: minmax(80px, max-content) 1fr;\n gap: 7px var(--spacing-large, 16px);\n margin: 0;\n }\n\n dl.given dt {\n text-align: right;\n color: var(--ui-muted, #57676f);\n word-break: keep-all;\n }\n\n dl.given dd {\n margin: 0;\n color: var(--ui-ink, #17242c);\n word-break: break-word;\n }\n\n dl.given .none {\n color: var(--ui-faint, #8a959c);\n }\n\n dl.given code {\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-small, 11.5px);\n }\n\n /*\n * 안쪽으로 펼친 오브젝트 · 배열.\n *\n * 바깥과 같은 두 칸 구조를 쓰되 **한 단 들여쓰고 왼쪽에 선을 긋는다.** 선이 없으면 세 단이 겹칠 때\n * 어느 이름이 어느 값에 딸린 것인지 눈으로 못 가른다.\n *\n * 안쪽 이름은 왼쪽 정렬이다. 바깥은 오른쪽 정렬이라 값의 왼쪽 끝이 한 줄로 서는데(dl.given dt),\n * 안쪽까지 그러면 단이 깊어질수록 이름이 값 쪽으로 밀려 붙는다.\n */\n dl.given .nested {\n margin: 2px 0 0;\n padding-left: 10px;\n border-left: 1px solid var(--ui-line, #d5dce0);\n }\n\n dl.given dl.nested {\n display: grid;\n grid-template-columns: minmax(60px, max-content) 1fr;\n gap: 4px var(--spacing-medium, 8px);\n }\n\n dl.given dl.nested dt {\n text-align: left;\n color: var(--ui-faint, #8a959c);\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-small, 11.5px);\n }\n\n /*\n * 페이로드를 그대로 가져가는 자리.\n *\n * 화면은 값을 읽기 좋게 펴 놓지만, 이 값을 **다른 도구에 넣어 볼 일**이 있다 — 트윈 명령 하나가\n * 네 단짜리 오브젝트이고, 그것을 손으로 옮겨 적을 수는 없다. 화면이 편 글자가 아니라 원문 JSON 을\n * 준다. 붙여 넣는 쪽이 기대하는 것은 원문이다.\n *\n * 값 위에 얹지 않고 오른쪽 위 구석에 둔다 — 값이 한 줄일 때도 두 단 깊을 때도 자리가 같다.\n */\n dl.given dd:has(> .payload) {\n position: relative;\n }\n\n .copy {\n position: absolute;\n top: 0;\n right: 0;\n display: inline-flex;\n align-items: center;\n gap: 3px;\n padding: 1px 5px;\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: 999px;\n background: var(--ui-panel, #ffffff);\n color: var(--ui-muted, #57676f);\n font-size: var(--ui-t-micro, 11px);\n line-height: 16px;\n cursor: pointer;\n }\n\n .copy md-icon {\n font-size: 13px;\n width: 13px;\n height: 13px;\n }\n\n .copy:hover {\n color: var(--ui-ink, #17242c);\n border-color: var(--ui-muted, #57676f);\n }\n\n .copy:focus-visible {\n outline: 2px solid var(--ui-accent, #00727e);\n outline-offset: 1px;\n }\n\n /* 복사한 직후 — 눌린 것이 보여야 한다. 소리도 움직임도 없는 조작이라 이 표시가 유일한 대답이다. */\n .copy[done] {\n border-color: var(--ui-good, #297d39);\n color: var(--ui-good, #297d39);\n }\n\n /*\n * 못 했을 때도 **같은 크기로** 말한다.\n *\n * 처음에는 실패를 title 로만 적었다. 그러면 누른 사람 눈에는 아무 일도 안 일어난 것과 같고,\n * 이유를 알려면 그 자리에 마우스를 올려 두어야 한다 — 손가락으로 쓰는 화면에는 그 길이 없다.\n * 클립보드는 브라우저가 막을 수 있는 자리라(보안 컨텍스트가 아니거나 권한이 없으면) 실제로 나는 일이다.\n */\n .copy[failed] {\n border-color: var(--ui-crit, #d14946);\n color: var(--ui-crit, #d14946);\n }\n\n /* 배열 — 번호로 몇 번째인지 말한다. 순서가 뜻을 갖는 값이 온다(주의 목록 · 권장 조치). */\n dl.given ol.nested {\n list-style: decimal;\n padding-left: 24px;\n }\n\n dl.given ol.nested li {\n margin: 2px 0;\n }\n\n dl.given ol.nested li::marker {\n color: var(--ui-faint, #8a959c);\n font-size: var(--ui-t-small, 11.5px);\n }\n\n /*\n * 빈 화면은 가장 중요한 자리다. 없다고만 하지 않고 왜 비어 있는지 적는다.\n *\n * 두 가지가 있다. 화면 전체가 비는 것(건을 못 읽었다)은 세로로 늘려 가운데에 두고, 절 자리가\n * 비는 것(항목이 정의되지 않았다)은 **다른 절과 같은 카드**로 둔다. 절 하나가 없다고 해서\n * 화면 높이를 다 쓰면 그 아래의 타임라인이 화면 밖으로 밀린다.\n */\n section[empty] {\n display: flex;\n flex: none;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 28px 14px;\n text-align: center;\n color: var(--ui-muted, #57676f);\n }\n\n div[empty] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: var(--spacing-large, 16px);\n text-align: center;\n color: var(--ui-muted, #57676f);\n }\n\n [empty] .title {\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--ui-ink, #17242c);\n }\n\n [empty] .why {\n max-width: 40em;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-faint, #8a959c);\n word-break: keep-all;\n }\n`\n\n/**\n * 화면에 낼 항목을 고른다.\n *\n * hidden 을 지킨다. 전에는 이 선언을 보지 않았다 — 액티비티가 「이 항목은 보이지 않는다」고 적어\n * 두어도 화면에 나왔다. 편집기가 없는 타입(object 등)이라 조용히 빠졌을 뿐이고, 편집기가 있는\n * 타입이면 그대로 보였다. 값을 표로 그리기 시작하면서 실제로 드러났다 — 처분 승인 화면에\n * disposition 객체의 JSON 원문이 나왔다.\n */\nfunction specsOf(model: any[] | null | undefined, inouts: string[]) {\n return (model || [])\n .filter(item => !item.hidden)\n .filter(item => inouts.includes(item.inout))\n .map(item => ({\n ...item,\n name: item.tag,\n label: item.tKey || item.name,\n property: item.options\n }))\n}\n\n/** 받은 값 — 업무를 시작할 때 함께 온 것. */\nexport function inputSpecOf(model: any[] | null | undefined) {\n return specsOf(model, ['in', 'inout'])\n}\n\n/** 적어 낼 값 — 수행자가 채우는 것. */\nexport function outputSpecOf(model: any[] | null | undefined) {\n return specsOf(model, ['out', 'inout'])\n}\n\n/**\n * 값을 **표로** 보인다. 입력칸으로 그리지 않는다.\n *\n * 전에는 읽기만 하는 절도 ox-properties-dynamic-view 로 그렸다. 그러면 값마다 흰 입력 상자가\n * 생기고 사용자는 고쳐 쓸 수 있는 것으로 읽는다. 그런데 그 절에는 변경을 받는 자리가 없다 —\n * 결재 화면은 아예 output 을 mutation 에 싣지 않는다(approveActivityApproval 은 id 와 comment 만\n * 보낸다). 「적을 수 있게 보이는데 적히지 않는 칸」이라 가장 나쁜 쪽이다.\n */\nexport function renderGiven(specs: any[], values: any) {\n return html`\n <dl class=\"given\">\n ${specs.map(\n spec => html`\n <dt>${fieldLabel(spec)}</dt>\n <dd>${fieldValue(values?.[spec.name])}</dd>\n `\n )}\n </dl>\n `\n}\n\n/**\n * 항목 이름 — ox-property-editor 와 같은 규칙으로 찾는다(그쪽은 label. 접두사를 스스로 붙인다).\n *\n * 번역이 없으면 **키를 그대로 보이지 않는다.** 전에는 label.because 가 없어서 화면에\n * Label.Because 가 나왔다. 사용자에게 뜻이 없는 글자다.\n *\n * description 으로 물러나지도 않는다 — 액티비티의 description 은 개발자가 영어로 적어 둔 설명이다\n * (예: \"The disposition waiting for approval\"). 한국어 화면에 영어 문장이 나오는 것보다 항목\n * 이름이 낫다.\n */\nexport function fieldLabel(spec: any): string {\n const key = String(spec?.label ?? spec?.name ?? '')\n const full = key.indexOf('label.') >= 0 ? key : 'label.' + key\n\n return i18next.t(full, { defaultValue: spec?.name || key })\n}\n\n/**\n * 선언이 준 이름 — `tKey` 로 찾되, **없으면 키를 보이지 않는다.**\n *\n * 찾는 축(`searchKeys`)과 판단 자리(`decisionKeys`) 둘 다 이것을 쓴다. 두 선언이 같은 모양으로\n * 이름을 나른다 — `tKey` 가 있으면 그 키로, 없으면 `name` 으로.\n *\n * `fieldLabel` 과 규칙이 하나 다르다. 그쪽은 `label.` 을 스스로 붙이지만 여기는 안 붙인다 — 이 두\n * 선언의 `tKey` 는 선언한 모듈이 온전한 키로 적는다(plant 는 `field.lot`, twin 은 `twin.instance`).\n * 접두사를 붙이면 맞게 적은 키가 오히려 안 찾아진다.\n *\n * ── 왜 물러날 자리가 필요한가 (2026-09-16) ────────────────────────────────\n * i18next 는 없는 키를 **키 그대로** 돌려준다. 선언된 tKey 를 세어 보니 plant 는 서른 중 열아홉이,\n * twin 은 셋 다 다섯 언어 어디에도 없었다. 그래서 그 표가 「어느 로트」 자리에 `lotName` 을, 「누가\n * 냈나」 자리에 `twin.command-origin` 을 그리고 있다. 사용자에게 뜻이 없는 글자다.\n *\n * 없는 번역을 이 자리에서 지어낼 수는 없다. 대신 선언이 이미 준 `name` 으로 물러난다 — 개발자가 적은\n * 영어지만 점과 낙타등이 섞인 키보다는 읽힌다. 같은 규율이 `fieldLabel` 에 이미 있었고, 이 규칙이\n * 다섯 자리에 흩어져 있어서 한 자리만 고쳐서는 나머지 넷이 그대로 남았다.\n */\nexport function declaredLabel(item: any): string {\n const name = String(item?.name ?? '')\n\n return item?.tKey ? i18next.t(item.tKey, { defaultValue: name || item.tKey }) : name\n}\n\n/**\n * 얼마나 깊이 펼치나.\n *\n * 이 화면들이 받는 값은 액티비티가 정하므로 얼마나 깊을지 알 수 없다. 트윈의 작동 명령은 네 단이다\n * (twinNow → attentions[0] → params → 값). 끝없이 펼치면 어떤 페이로드에서 화면이 통째로 페이로드가\n * 되므로, 이 깊이를 넘으면 원문을 그대로 보인다 — 접는 것이 아니라 **다른 방식으로 보이는 것**이다.\n */\nconst MAX_DEPTH = 4\n\n/**\n * 값 한 칸.\n *\n * ── 오브젝트를 한 줄 JSON 으로 내지 않는다 (2026-09-07) ─────────────────────\n * `JSON.stringify` 한 줄이었다. 트윈의 작동 명령을 열면 그 한 줄이 700자가 넘어서, 「업무 내용」이\n * 읽을 수 없는 글자 덩어리 하나가 됐다. 값은 다 있는데 아무것도 읽히지 않는다.\n *\n * 화면은 이미 이름과 값을 두 칸으로 보이는 말을 갖고 있다(dl.given). 오브젝트도 그 말로 편다 —\n * 새 표현을 만들지 않고, 안쪽이든 바깥쪽이든 같은 모양으로 읽힌다.\n *\n * 값이 없으면 빈 칸으로 두지 않는다 — 빈 칸은 값이 없는 것인지 화면이 못 낸 것인지 모른다.\n */\nexport function fieldValue(value: any, depth = 0): unknown {\n if (value === null || value === undefined || value === '') {\n return html`<span class=\"none\">${i18next.t('text.no-value')}</span>`\n }\n\n if (typeof value === 'boolean') {\n return i18next.t(value ? 'label.yes' : 'label.no', { defaultValue: String(value) })\n }\n\n if (typeof value !== 'object') {\n return String(value)\n }\n\n /* 너무 깊으면 펼치기를 그만두고 원문을 보인다 — 없는 척하지 않는다. */\n if (depth >= MAX_DEPTH) {\n return html`<code>${JSON.stringify(value)}</code>`\n }\n\n const laid = layOut(value, depth)\n\n /*\n * 맨 바깥 값에만 복사를 붙인다.\n *\n * 안쪽 단마다 달면 네 단짜리 페이로드에 단추가 열 개 넘게 붙어, 정작 읽으라고 편 값이 단추에\n * 묻힌다. 사람이 가져가려는 것은 「이 항목」이다.\n */\n return depth === 0 ? html`<span class=\"payload\">${copyButton(value)}${laid}</span>` : laid\n}\n\n/**\n * 이 값을 원문 그대로 가져간다.\n *\n * 화면이 편 글자가 아니라 JSON 을 준다 — 붙여 넣는 쪽(질의 · 로그 검색 · 문의 글)이 기대하는 것은\n * 원문이다. 들여쓰기를 넣어 그대로 읽을 수 있게 한다.\n *\n * 눌러도 화면이 안 바뀌면 눌린 줄을 모른다. 그래서 잠깐 표시를 남긴다 — 이 조작에는 그것 말고\n * 대답이 없다.\n */\nfunction copyButton(value: any) {\n const onCopy = async (e: Event) => {\n const button = e.currentTarget as HTMLElement\n\n const mark = (attr: 'done' | 'failed') => {\n button.toggleAttribute(attr, true)\n setTimeout(() => button.toggleAttribute(attr, false), 1500)\n }\n\n try {\n await navigator.clipboard.writeText(JSON.stringify(value, null, 2))\n } catch {\n /*\n * 클립보드는 브라우저가 막을 수 있다(보안 컨텍스트가 아니거나 권한이 없으면). 안 된 것을 된\n * 것처럼 말하지 않고, 성공과 같은 크기로 보인다 — title 만 바꾸면 누른 사람 눈에는 아무 일도\n * 안 일어난 것과 같다.\n */\n button.setAttribute('title', String(i18next.t('text.copy-failed')))\n mark('failed')\n return\n }\n\n mark('done')\n }\n\n return html`\n <button class=\"copy\" type=\"button\" title=${String(i18next.t('text.copy-payload'))} @click=${onCopy}>\n <md-icon>content_copy</md-icon>${i18next.t('button.copy')}\n </button>\n `\n}\n\n/** 값을 이름·값 두 칸으로 편다. 복사 단추는 여기 끼지 않는다 — 편 결과만 만든다. */\nfunction layOut(value: any, depth: number): unknown {\n if (Array.isArray(value)) {\n /* 빈 배열은 「값 없음」이 아니다 — 「비어 있다」는 그 자체로 사실이다. */\n if (value.length === 0) {\n return html`<span class=\"none\">${i18next.t('text.empty-list')}</span>`\n }\n\n /*\n * 배열은 깊이를 먹지 않는다 — **같은 것이 여러 개**이지 새로운 단이 아니다.\n *\n * 배열도 한 단으로 세었더니 트윈 작동 명령의 마지막 줄이 잘렸다: twinNow → attentions →\n * attentions[0] → recommendedActions → { code } 에서 recommendedActions 의 항목이 다섯 번째가\n * 되어, 정작 가장 단순한 { code: 'advice.raise-priority' } 하나가 JSON 원문으로 남았다.\n * 사람이 세는 단은 넷이다.\n */\n return html`\n <ol class=\"nested\">\n ${value.map(item => html`<li>${fieldValue(item, depth)}</li>`)}\n </ol>\n `\n }\n\n const keys = Object.keys(value)\n\n if (keys.length === 0) {\n return html`<span class=\"none\">${i18next.t('text.empty-object')}</span>`\n }\n\n /*\n * 안쪽 이름은 번역하지 않는다. 액티비티가 정한 키라 번역이 있을 리 없고, 없는 번역을 찾으면\n * 「Label.MaterialDefinitionId」 같은 글자가 나온다 — 사용자에게 뜻이 없는 글자다.\n */\n return html`\n <dl class=\"given nested\">\n ${keys.map(\n key => html`\n <dt>${key}</dt>\n <dd>${fieldValue(value[key], depth + 1)}</dd>\n `\n )}\n </dl>\n `\n}\n"]}
|
|
1
|
+
{"version":3,"file":"activity-fields.js","sourceRoot":"","sources":["../../client/components/activity-fields.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html } from 'lit'\n\nimport { i18next } from '@operato/i18n'\nimport { entity360Name } from '@operato/layout'\n\n/**\n * 업무 항목을 화면에 내는 방식 — **두 화면이 한 벌을 쓴다.**\n *\n * 업무 상세(activity-thread-page)와 결재 상세(activity-approval-page)는 거의 복사본이다. 항목을\n * 고르는 함수, 화면 종류에 따라 그리는 분기, 절을 그리는 부분이 두 파일에 같은 코드로 있었다.\n * 그래서 한쪽만 고치면 나머지가 그대로 남는다 — 실제로 그랬다(2026-09-06, 상세 화면의\n * Input · Output 영문 legend 를 고쳤는데 결재 화면에는 그대로 있었다).\n *\n * 화면마다 다른 것은 남긴다 — 상세는 결과를 적고, 결재는 읽기만 한다.\n */\n\n/** 절과 값 표의 모양. 카드 값은 SurfaceStyles 의 .ui-card 와 같다. */\nexport const ActivityFieldsStyles = css`\n section {\n margin: var(--spacing-large, 16px) var(--spacing-large, 16px) 0;\n padding: 14px;\n background: var(--ui-panel, #ffffff);\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r, 10px);\n }\n\n section > h2 {\n margin: 0 0 10px;\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--ui-ink, #17242c);\n }\n\n /* 읽기만 하는 절 — 값을 바꿀 수 없다는 것이 보여야 한다. */\n section[readonly] > h2 {\n color: var(--ui-muted, #57676f);\n }\n\n /* 받은 값 — 이름과 값을 두 칸으로. 이름은 오른쪽 정렬이라 값의 왼쪽 끝이 한 줄로 선다. */\n dl.given {\n display: grid;\n grid-template-columns: minmax(80px, max-content) 1fr;\n gap: 7px var(--spacing-large, 16px);\n margin: 0;\n }\n\n dl.given dt {\n text-align: right;\n color: var(--ui-muted, #57676f);\n word-break: keep-all;\n }\n\n dl.given dd {\n margin: 0;\n color: var(--ui-ink, #17242c);\n word-break: break-word;\n }\n\n dl.given .none {\n color: var(--ui-faint, #8a959c);\n }\n\n dl.given code {\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-small, 11.5px);\n }\n\n /*\n * 안쪽으로 펼친 오브젝트 · 배열.\n *\n * 바깥과 같은 두 칸 구조를 쓰되 **한 단 들여쓰고 왼쪽에 선을 긋는다.** 선이 없으면 세 단이 겹칠 때\n * 어느 이름이 어느 값에 딸린 것인지 눈으로 못 가른다.\n *\n * 안쪽 이름은 왼쪽 정렬이다. 바깥은 오른쪽 정렬이라 값의 왼쪽 끝이 한 줄로 서는데(dl.given dt),\n * 안쪽까지 그러면 단이 깊어질수록 이름이 값 쪽으로 밀려 붙는다.\n */\n dl.given .nested {\n margin: 2px 0 0;\n padding-left: 10px;\n border-left: 1px solid var(--ui-line, #d5dce0);\n }\n\n dl.given dl.nested {\n display: grid;\n grid-template-columns: minmax(60px, max-content) 1fr;\n gap: 4px var(--spacing-medium, 8px);\n }\n\n dl.given dl.nested dt {\n text-align: left;\n color: var(--ui-faint, #8a959c);\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-small, 11.5px);\n }\n\n /*\n * 페이로드를 그대로 가져가는 자리.\n *\n * 화면은 값을 읽기 좋게 펴 놓지만, 이 값을 **다른 도구에 넣어 볼 일**이 있다 — 트윈 명령 하나가\n * 네 단짜리 오브젝트이고, 그것을 손으로 옮겨 적을 수는 없다. 화면이 편 글자가 아니라 원문 JSON 을\n * 준다. 붙여 넣는 쪽이 기대하는 것은 원문이다.\n *\n * 값 위에 얹지 않고 오른쪽 위 구석에 둔다 — 값이 한 줄일 때도 두 단 깊을 때도 자리가 같다.\n */\n dl.given dd:has(> .payload) {\n position: relative;\n }\n\n .copy {\n position: absolute;\n top: 0;\n right: 0;\n display: inline-flex;\n align-items: center;\n gap: 3px;\n padding: 1px 5px;\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: 999px;\n background: var(--ui-panel, #ffffff);\n color: var(--ui-muted, #57676f);\n font-size: var(--ui-t-micro, 11px);\n line-height: 16px;\n cursor: pointer;\n }\n\n .copy md-icon {\n font-size: 13px;\n width: 13px;\n height: 13px;\n }\n\n .copy:hover {\n color: var(--ui-ink, #17242c);\n border-color: var(--ui-muted, #57676f);\n }\n\n .copy:focus-visible {\n outline: 2px solid var(--ui-accent, #00727e);\n outline-offset: 1px;\n }\n\n /* 복사한 직후 — 눌린 것이 보여야 한다. 소리도 움직임도 없는 조작이라 이 표시가 유일한 대답이다. */\n .copy[done] {\n border-color: var(--ui-good, #297d39);\n color: var(--ui-good, #297d39);\n }\n\n /*\n * 못 했을 때도 **같은 크기로** 말한다.\n *\n * 처음에는 실패를 title 로만 적었다. 그러면 누른 사람 눈에는 아무 일도 안 일어난 것과 같고,\n * 이유를 알려면 그 자리에 마우스를 올려 두어야 한다 — 손가락으로 쓰는 화면에는 그 길이 없다.\n * 클립보드는 브라우저가 막을 수 있는 자리라(보안 컨텍스트가 아니거나 권한이 없으면) 실제로 나는 일이다.\n */\n .copy[failed] {\n border-color: var(--ui-crit, #d14946);\n color: var(--ui-crit, #d14946);\n }\n\n /* 배열 — 번호로 몇 번째인지 말한다. 순서가 뜻을 갖는 값이 온다(주의 목록 · 권장 조치). */\n dl.given ol.nested {\n list-style: decimal;\n padding-left: 24px;\n }\n\n dl.given ol.nested li {\n margin: 2px 0;\n }\n\n dl.given ol.nested li::marker {\n color: var(--ui-faint, #8a959c);\n font-size: var(--ui-t-small, 11.5px);\n }\n\n /*\n * 빈 화면은 가장 중요한 자리다. 없다고만 하지 않고 왜 비어 있는지 적는다.\n *\n * 두 가지가 있다. 화면 전체가 비는 것(건을 못 읽었다)은 세로로 늘려 가운데에 두고, 절 자리가\n * 비는 것(항목이 정의되지 않았다)은 **다른 절과 같은 카드**로 둔다. 절 하나가 없다고 해서\n * 화면 높이를 다 쓰면 그 아래의 타임라인이 화면 밖으로 밀린다.\n */\n section[empty] {\n display: flex;\n flex: none;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 28px 14px;\n text-align: center;\n color: var(--ui-muted, #57676f);\n }\n\n div[empty] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: var(--spacing-large, 16px);\n text-align: center;\n color: var(--ui-muted, #57676f);\n }\n\n [empty] .title {\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--ui-ink, #17242c);\n }\n\n [empty] .why {\n max-width: 40em;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-faint, #8a959c);\n word-break: keep-all;\n }\n`\n\n/**\n * 화면에 낼 항목을 고른다.\n *\n * hidden 을 지킨다. 전에는 이 선언을 보지 않았다 — 액티비티가 「이 항목은 보이지 않는다」고 적어\n * 두어도 화면에 나왔다. 편집기가 없는 타입(object 등)이라 조용히 빠졌을 뿐이고, 편집기가 있는\n * 타입이면 그대로 보였다. 값을 표로 그리기 시작하면서 실제로 드러났다 — 처분 승인 화면에\n * disposition 객체의 JSON 원문이 나왔다.\n */\nfunction specsOf(model: any[] | null | undefined, inouts: string[]) {\n return (model || [])\n .filter(item => !item.hidden)\n .filter(item => inouts.includes(item.inout))\n .map(item => ({\n ...item,\n name: item.tag,\n label: item.tKey || item.name,\n property: item.options\n }))\n}\n\n/** 받은 값 — 업무를 시작할 때 함께 온 것. */\nexport function inputSpecOf(model: any[] | null | undefined) {\n return specsOf(model, ['in', 'inout'])\n}\n\n/** 적어 낼 값 — 수행자가 채우는 것. */\nexport function outputSpecOf(model: any[] | null | undefined) {\n return specsOf(model, ['out', 'inout'])\n}\n\n/**\n * 값을 **표로** 보인다. 입력칸으로 그리지 않는다.\n *\n * 전에는 읽기만 하는 절도 ox-properties-dynamic-view 로 그렸다. 그러면 값마다 흰 입력 상자가\n * 생기고 사용자는 고쳐 쓸 수 있는 것으로 읽는다. 그런데 그 절에는 변경을 받는 자리가 없다 —\n * 결재 화면은 아예 output 을 mutation 에 싣지 않는다(approveActivityApproval 은 id 와 comment 만\n * 보낸다). 「적을 수 있게 보이는데 적히지 않는 칸」이라 가장 나쁜 쪽이다.\n */\nexport function renderGiven(specs: any[], values: any) {\n return html`\n <dl class=\"given\">\n ${specs.map(\n spec => html`\n <dt>${fieldLabel(spec)}</dt>\n <dd>${fieldValue(values?.[spec.name])}</dd>\n `\n )}\n </dl>\n `\n}\n\n/**\n * 항목 이름 — ox-property-editor 와 같은 규칙으로 찾는다(그쪽은 label. 접두사를 스스로 붙인다).\n *\n * 번역이 없으면 **키를 그대로 보이지 않는다.** 전에는 label.because 가 없어서 화면에\n * Label.Because 가 나왔다. 사용자에게 뜻이 없는 글자다.\n *\n * description 으로 물러나지도 않는다 — 액티비티의 description 은 개발자가 영어로 적어 둔 설명이다\n * (예: \"The disposition waiting for approval\"). 한국어 화면에 영어 문장이 나오는 것보다 항목\n * 이름이 낫다.\n */\nexport function fieldLabel(spec: any): string {\n const key = String(spec?.label ?? spec?.name ?? '')\n const full = key.indexOf('label.') >= 0 ? key : 'label.' + key\n\n return i18next.t(full, { defaultValue: spec?.name || key })\n}\n\n/**\n * 선언이 준 이름 — `tKey` 로 찾되, **없으면 키를 보이지 않는다.**\n *\n * 찾는 축(`searchKeys`)과 판단 자리(`decisionKeys`) 둘 다 이것을 쓴다. 두 선언이 같은 모양으로\n * 이름을 나른다 — `tKey` 가 있으면 그 키로, 없으면 `name` 으로.\n *\n * `fieldLabel` 과 규칙이 하나 다르다. 그쪽은 `label.` 을 스스로 붙이지만 여기는 안 붙인다 — 이 두\n * 선언의 `tKey` 는 선언한 모듈이 온전한 키로 적는다(plant 는 `field.lot`, twin 은 `twin.instance`).\n * 접두사를 붙이면 맞게 적은 키가 오히려 안 찾아진다.\n *\n * ── 왜 물러날 자리가 필요한가 (2026-09-16) ────────────────────────────────\n * i18next 는 없는 키를 **키 그대로** 돌려준다. 선언된 tKey 를 세어 보니 plant 는 서른 중 열아홉이,\n * twin 은 셋 다 다섯 언어 어디에도 없었다. 그래서 그 표가 「어느 로트」 자리에 `lotName` 을, 「누가\n * 냈나」 자리에 `twin.command-origin` 을 그리고 있다. 사용자에게 뜻이 없는 글자다.\n *\n * 없는 번역을 이 자리에서 지어낼 수는 없다. 대신 선언이 이미 준 `name` 으로 물러난다 — 개발자가 적은\n * 영어지만 점과 낙타등이 섞인 키보다는 읽힌다. 같은 규율이 `fieldLabel` 에 이미 있었고, 이 규칙이\n * 다섯 자리에 흩어져 있어서 한 자리만 고쳐서는 나머지 넷이 그대로 남았다.\n */\nexport function declaredLabel(item: any): string {\n const name = String(item?.name ?? '')\n\n return item?.tKey ? i18next.t(item.tKey, { defaultValue: name || item.tKey }) : name\n}\n\n/**\n * 얼마나 깊이 펼치나.\n *\n * 이 화면들이 받는 값은 액티비티가 정하므로 얼마나 깊을지 알 수 없다. 트윈의 작동 명령은 네 단이다\n * (twinNow → attentions[0] → params → 값). 끝없이 펼치면 어떤 페이로드에서 화면이 통째로 페이로드가\n * 되므로, 이 깊이를 넘으면 원문을 그대로 보인다 — 접는 것이 아니라 **다른 방식으로 보이는 것**이다.\n */\nconst MAX_DEPTH = 4\n\n/**\n * 값 한 칸.\n *\n * ── 오브젝트를 한 줄 JSON 으로 내지 않는다 (2026-09-07) ─────────────────────\n * `JSON.stringify` 한 줄이었다. 트윈의 작동 명령을 열면 그 한 줄이 700자가 넘어서, 「업무 내용」이\n * 읽을 수 없는 글자 덩어리 하나가 됐다. 값은 다 있는데 아무것도 읽히지 않는다.\n *\n * 화면은 이미 이름과 값을 두 칸으로 보이는 말을 갖고 있다(dl.given). 오브젝트도 그 말로 편다 —\n * 새 표현을 만들지 않고, 안쪽이든 바깥쪽이든 같은 모양으로 읽힌다.\n *\n * 값이 없으면 빈 칸으로 두지 않는다 — 빈 칸은 값이 없는 것인지 화면이 못 낸 것인지 모른다.\n */\nexport function fieldValue(value: any, depth = 0): unknown {\n if (value === null || value === undefined || value === '') {\n return html`<span class=\"none\">${i18next.t('text.no-value')}</span>`\n }\n\n if (typeof value === 'boolean') {\n return i18next.t(value ? 'label.yes' : 'label.no', { defaultValue: String(value) })\n }\n\n if (typeof value !== 'object') {\n return String(value)\n }\n\n /* 너무 깊으면 펼치기를 그만두고 원문을 보인다 — 없는 척하지 않는다. */\n if (depth >= MAX_DEPTH) {\n return html`<code>${JSON.stringify(value)}</code>`\n }\n\n const laid = layOut(value, depth)\n\n /*\n * 맨 바깥 값에만 복사를 붙인다.\n *\n * 안쪽 단마다 달면 네 단짜리 페이로드에 단추가 열 개 넘게 붙어, 정작 읽으라고 편 값이 단추에\n * 묻힌다. 사람이 가져가려는 것은 「이 항목」이다.\n */\n return depth === 0 ? html`<span class=\"payload\">${copyButton(value)}${laid}</span>` : laid\n}\n\n/**\n * 이 값을 원문 그대로 가져간다.\n *\n * 화면이 편 글자가 아니라 JSON 을 준다 — 붙여 넣는 쪽(질의 · 로그 검색 · 문의 글)이 기대하는 것은\n * 원문이다. 들여쓰기를 넣어 그대로 읽을 수 있게 한다.\n *\n * 눌러도 화면이 안 바뀌면 눌린 줄을 모른다. 그래서 잠깐 표시를 남긴다 — 이 조작에는 그것 말고\n * 대답이 없다.\n */\nfunction copyButton(value: any) {\n const onCopy = async (e: Event) => {\n const button = e.currentTarget as HTMLElement\n\n const mark = (attr: 'done' | 'failed') => {\n button.toggleAttribute(attr, true)\n setTimeout(() => button.toggleAttribute(attr, false), 1500)\n }\n\n try {\n await navigator.clipboard.writeText(JSON.stringify(value, null, 2))\n } catch {\n /*\n * 클립보드는 브라우저가 막을 수 있다(보안 컨텍스트가 아니거나 권한이 없으면). 안 된 것을 된\n * 것처럼 말하지 않고, 성공과 같은 크기로 보인다 — title 만 바꾸면 누른 사람 눈에는 아무 일도\n * 안 일어난 것과 같다.\n */\n button.setAttribute('title', String(i18next.t('text.copy-failed')))\n mark('failed')\n return\n }\n\n mark('done')\n }\n\n return html`\n <button class=\"copy\" type=\"button\" title=${String(i18next.t('text.copy-payload'))} @click=${onCopy}>\n <md-icon>content_copy</md-icon>${i18next.t('button.copy')}\n </button>\n `\n}\n\n/** 값을 이름·값 두 칸으로 편다. 복사 단추는 여기 끼지 않는다 — 편 결과만 만든다. */\nfunction layOut(value: any, depth: number): unknown {\n if (Array.isArray(value)) {\n /* 빈 배열은 「값 없음」이 아니다 — 「비어 있다」는 그 자체로 사실이다. */\n if (value.length === 0) {\n return html`<span class=\"none\">${i18next.t('text.empty-list')}</span>`\n }\n\n /*\n * 배열은 깊이를 먹지 않는다 — **같은 것이 여러 개**이지 새로운 단이 아니다.\n *\n * 배열도 한 단으로 세었더니 트윈 작동 명령의 마지막 줄이 잘렸다: twinNow → attentions →\n * attentions[0] → recommendedActions → { code } 에서 recommendedActions 의 항목이 다섯 번째가\n * 되어, 정작 가장 단순한 { code: 'advice.raise-priority' } 하나가 JSON 원문으로 남았다.\n * 사람이 세는 단은 넷이다.\n */\n return html`\n <ol class=\"nested\">\n ${value.map(item => html`<li>${fieldValue(item, depth)}</li>`)}\n </ol>\n `\n }\n\n const keys = Object.keys(value)\n\n if (keys.length === 0) {\n return html`<span class=\"none\">${i18next.t('text.empty-object')}</span>`\n }\n\n /*\n * 안쪽 이름은 번역하지 않는다. 액티비티가 정한 키라 번역이 있을 리 없고, 없는 번역을 찾으면\n * 「Label.MaterialDefinitionId」 같은 글자가 나온다 — 사용자에게 뜻이 없는 글자다.\n */\n return html`\n <dl class=\"given nested\">\n ${keys.map(\n key => html`\n <dt>${key}</dt>\n <dd>${fieldValue(value[key], depth + 1)}</dd>\n `\n )}\n </dl>\n `\n}\n\n/**\n * A person as a value - their name opens their 360 (ADR-0067 decision 1), and no one says so as\n * `fieldValue` would. `entity360Name` draws plain text until the person kind is registered.\n */\nexport function personValue(person?: { name?: string } | null): unknown {\n return person?.name ? entity360Name(person) : fieldValue(undefined)\n}\n"]}
|
|
@@ -5,7 +5,7 @@ import '../templates/activity-instance-context-template.js';
|
|
|
5
5
|
import { LitElement } from 'lit';
|
|
6
6
|
declare const ActivityInstanceRibon_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
7
7
|
export declare class ActivityInstanceRibon extends ActivityInstanceRibon_base {
|
|
8
|
-
static styles: import("lit").CSSResult[];
|
|
8
|
+
static styles: import("lit").CSSResult[][];
|
|
9
9
|
activityInstance: any;
|
|
10
10
|
render(): import("lit-html").TemplateResult<1>;
|
|
11
11
|
/**
|
|
@@ -6,7 +6,7 @@ import '../templates/activity-instance-context-template.js';
|
|
|
6
6
|
import { html, LitElement } from 'lit';
|
|
7
7
|
import { customElement, property } from 'lit/decorators.js';
|
|
8
8
|
import { i18next, localize } from '@operato/i18n';
|
|
9
|
-
import { openPopup } from '@operato/layout';
|
|
9
|
+
import { entity360Name, openPopup } from '@operato/layout';
|
|
10
10
|
import { StateRibonStyles } from './state-ribon.js';
|
|
11
11
|
let ActivityInstanceRibon = class ActivityInstanceRibon extends localize(i18next)(LitElement) {
|
|
12
12
|
constructor() {
|
|
@@ -42,7 +42,7 @@ let ActivityInstanceRibon = class ActivityInstanceRibon extends localize(i18next
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
${startingType == 'issue' && assignee
|
|
45
|
-
? html `<span class="who"><span class="who-label">${i18next.t('field.assignee')}</span>${assignee
|
|
45
|
+
? html `<span class="who"><span class="who-label">${i18next.t('field.assignee')}</span>${entity360Name(assignee)}</span>`
|
|
46
46
|
: ''}
|
|
47
47
|
<!--
|
|
48
48
|
결재선의 현재 단계를 넘기지 않는다 — 이 ribbon 이 쓰이는 곳은 업무를 시작하는 화면
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-instance-ribon.js","sourceRoot":"","sources":["../../client/components/activity-instance-ribon.ts"],"names":[],"mappings":";AAAA,OAAO,yCAAyC,CAAA;AAChD,OAAO,wDAAwD,CAAA;AAC/D,OAAO,2EAA2E,CAAA;AAClF,OAAO,oDAAoD,CAAA;AAE3D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"activity-instance-ribon.js","sourceRoot":"","sources":["../../client/components/activity-instance-ribon.ts"],"names":[],"mappings":";AAAA,OAAO,yCAAyC,CAAA;AAChD,OAAO,wDAAwD,CAAA;AAC/D,OAAO,2EAA2E,CAAA;AAClF,OAAO,oDAAoD,CAAA;AAE3D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAG5C,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAAjE;;QAGuB,qBAAgB,GAAQ,EAAE,CAAA;IA0ExD,CAAC;aA5EQ,WAAM,GAAG,CAAC,gBAAgB,CAAC,AAArB,CAAqB;IAIlC,MAAM;QACJ,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,gBAAgB,IAAI,EAAE,CAAA;QAC5F,MAAM,EAAE,YAAY,EAAE,GAAG,QAAQ,IAAI,EAAE,CAAA;QAEvC,OAAO,IAAI,CAAA;;;UAGL,OAAO,CAAC,CAAC,CAAC,uBAAuB,GAAG,KAAK,CAAC;;;;;;;gBAOpC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;iBACrC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;mBACxB,CAAC,CAAgB,EAAE,EAAE;YAC9B,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBACvC,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;YACtB,CAAC;QACH,CAAC;;;;iCAIwB,WAAW,IAAI,EAAE,IAAI,WAAW,IAAI,EAAE;;;QAG/D,YAAY,IAAI,OAAO,IAAI,QAAQ;YACnC,CAAC,CAAC,IAAI,CAAA,6CAA6C,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,aAAa,CAAC,QAAQ,CAAC,SAAS;YACxH,CAAC,CAAC,EAAE;;;;;;QAMJ,YAAY,CAAC,CAAC,CAAC,IAAI,CAAA,+BAA+B,YAAY,yBAAyB,CAAC,CAAC,CAAC,EAAE;KAC/F,CAAA;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACK,QAAQ,CAAC,KAAa;QAC5B,SAAS,CACP,IAAI,CAAA,yDAAyD,IAAI,CAAC,gBAAgB,UAAU,KAAK,wCAAwC,EACzI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,EAAE,CAC1C,CAAA;IACH,CAAC;IAED,OAAO,CAAC,OAAO;QACb,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC7D,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAA;YAEvC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;;AAzE2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;+DAA2B;AAH3C,qBAAqB;IADjC,aAAa,CAAC,yBAAyB,CAAC;GAC5B,qBAAqB,CA6EjC","sourcesContent":["import '@material/web/button/elevated-button.js'\nimport '@operato/property-editor/ox-properties-dynamic-view.js'\nimport '@things-factory/organization/dist-client/component/approval-line-brief.js'\nimport '../templates/activity-instance-context-template.js'\n\nimport { html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { entity360Name, openPopup } from '@operato/layout'\n\nimport { StateRibonStyles } from './state-ribon.js'\n\n@customElement('activity-instance-ribon')\nexport class ActivityInstanceRibon extends localize(i18next)(LitElement) {\n static styles = [StateRibonStyles]\n\n @property({ type: Object }) activityInstance: any = {}\n\n render() {\n const { description, state, assignee, approvalLine, activity } = this.activityInstance || {}\n const { startingType } = activity || {}\n\n return html`\n <span class=\"state\">\n <i class=\"dot\"></i>\n ${i18next.t('label.activity-state-' + state)}\n </span>\n\n <md-icon\n class=\"info\"\n role=\"button\"\n tabindex=\"0\"\n title=${String(i18next.t('label.thread-info'))}\n @click=${() => this.openInfo(state)}\n @keydown=${(e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.openInfo(state)\n }\n }}\n >info</md-icon\n >\n\n <span class=\"desc\" title=${description || ''}>${description || ''}</span>\n\n\n ${startingType == 'issue' && assignee\n ? html`<span class=\"who\"><span class=\"who-label\">${i18next.t('field.assignee')}</span>${entity360Name(assignee)}</span>`\n : ''}\n <!--\n 결재선의 현재 단계를 넘기지 않는다 — 이 ribbon 이 쓰이는 곳은 업무를 시작하는 화면\n 하나뿐이고(activity-instance-start-page), 거기서는 아직 아무도 판단하지 않았다.\n approval-line-brief 는 값이 없으면 기안자를 현재로 표시하는데, 이 화면에서는 그것이 맞다.\n -->\n ${approvalLine ? html`<approval-line-brief .model=${approvalLine}></approval-line-brief>` : ''}\n `\n }\n\n /**\n * 이 건의 기본 정보 — **팝업으로 연다.**\n *\n * ── 왜 aside 가 아닌가 (2026-09-06) ────────────────────────────────────\n * 전에는 화면 오른쪽에서 나오는 aside 판이었다. 두 가지가 걸린다.\n *\n * 자리를 다툰다 화면 오른쪽 세로 판은 operato-twin 의 twin-entity360 이 쓰는 자리다\n * (position: fixed · right: 0 · height: 100vh). 같은 자리에 둘이 선다.\n * 전역 슬롯이다 VIEWPART_POSITION.ASIDEBAR 은 앱이 하나만 갖는다. 열 개가 넘는 패키지가\n * 그 자리를 쓰고 있어(twin 의 AI 협의 도크 · board-ui · help · more-ui …),\n * 이 판을 열면 그중 열려 있던 것이 닫힌다.\n *\n * 그리고 자리의 뜻이 다르다. 오른쪽 드로어는 **보고 있는 대상을 따라다니는 상세**이고(360 이\n * 그것이다 — 포커스가 바뀌면 내용이 바뀌고 화면을 옮겨도 남는다), 이 판은 **이 화면의 이 건**에\n * 대한 것이라 화면을 벗어나면 뜻이 없다. 페이지에 매인 것을 앱 전역 자리에 두지 않는다.\n *\n * 내용도 팝업 크기다 — 값 여덟 줄과 접힌 타임라인이라 세로 100vh 를 쓸 일이 없었다.\n */\n private openInfo(state: string) {\n openPopup(\n html`<activity-instance-context-template .activityInstance=${this.activityInstance} state=${state}></activity-instance-context-template>`,\n { title: i18next.t('label.thread-info') }\n )\n }\n\n updated(changes) {\n if (changes.has('activityInstance') && this.activityInstance) {\n const { state } = this.activityInstance\n\n this.setAttribute('state', state)\n }\n }\n}\n"]}
|
|
@@ -11,7 +11,7 @@ declare const ActivityThreadRibon_base: (new (...args: any[]) => LitElement) & t
|
|
|
11
11
|
* 여기서는 이 화면이 무엇을 보일지만 고른다.
|
|
12
12
|
*/
|
|
13
13
|
export declare class ActivityThreadRibon extends ActivityThreadRibon_base {
|
|
14
|
-
static styles: import("lit").CSSResult[];
|
|
14
|
+
static styles: import("lit").CSSResult[][];
|
|
15
15
|
activityThread: any;
|
|
16
16
|
render(): import("lit-html").TemplateResult<1>;
|
|
17
17
|
/**
|
|
@@ -6,7 +6,7 @@ import '../templates/activity-thread-context-template.js';
|
|
|
6
6
|
import { html, LitElement } from 'lit';
|
|
7
7
|
import { customElement, property } from 'lit/decorators.js';
|
|
8
8
|
import { i18next, localize } from '@operato/i18n';
|
|
9
|
-
import { openPopup } from '@operato/layout';
|
|
9
|
+
import { entity360Name, openPopup } from '@operato/layout';
|
|
10
10
|
import { currentApprovalOrder } from '../approval-progress.js';
|
|
11
11
|
import { StateRibonStyles } from './state-ribon.js';
|
|
12
12
|
/**
|
|
@@ -51,7 +51,7 @@ let ActivityThreadRibon = class ActivityThreadRibon extends localize(i18next)(Li
|
|
|
51
51
|
></approval-line-brief>`
|
|
52
52
|
: assignee
|
|
53
53
|
? html `<span class="who"
|
|
54
|
-
><span class="who-label">${i18next.t('field.assignee')}</span>${assignee
|
|
54
|
+
><span class="who-label">${i18next.t('field.assignee')}</span>${entity360Name(assignee)}</span
|
|
55
55
|
>`
|
|
56
56
|
: ''}
|
|
57
57
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-thread-ribon.js","sourceRoot":"","sources":["../../client/components/activity-thread-ribon.ts"],"names":[],"mappings":";AAAA,OAAO,yCAAyC,CAAA;AAChD,OAAO,wDAAwD,CAAA;AAC/D,OAAO,2EAA2E,CAAA;AAClF,OAAO,kDAAkD,CAAA;AAEzD,OAAO,EAAO,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"activity-thread-ribon.js","sourceRoot":"","sources":["../../client/components/activity-thread-ribon.ts"],"names":[],"mappings":";AAAA,OAAO,yCAAyC,CAAA;AAChD,OAAO,wDAAwD,CAAA;AAC/D,OAAO,2EAA2E,CAAA;AAClF,OAAO,kDAAkD,CAAA;AAEzD,OAAO,EAAO,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAEnD;;;;;GAKG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;aAC7D,WAAM,GAAG,CAAC,gBAAgB,CAAC,AAArB,CAAqB;IAIlC,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC,cAAc,CAAA;QACpF,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,gBAAgB,IAAI,EAAE,CAAA;QAE5D,OAAO,IAAI,CAAA;;;UAGL,OAAO,CAAC,CAAC,CAAC,uBAAuB,GAAG,KAAK,CAAC;;;;;;;gBAOpC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;iBACrC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;mBACxB,CAAC,CAAgB,EAAE,EAAE;YAC9B,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBACvC,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;YACtB,CAAC;QACH,CAAC;;;;iCAIwB,WAAW,IAAI,EAAE,IAAI,WAAW,IAAI,EAAE;;;QAG/D,YAAY;YACZ,CAAC,CAAC,IAAI,CAAA;qBACO,YAAY;uBACV,oBAAoB,CAAC,iBAAiB,CAAC;kCAC5B;YAC1B,CAAC,CAAC,QAAQ;gBACR,CAAC,CAAC,IAAI,CAAA;yCACyB,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,aAAa,CAAC,QAAQ,CAAC;cACvF;gBACJ,CAAC,CAAC,EAAE;KACT,CAAA;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACK,QAAQ,CAAC,KAAa;QAC5B,SAAS,CACP,IAAI,CAAA,qDAAqD,IAAI,CAAC,cAAc,UAAU,KAAK,sCAAsC,EACjI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,EAAE,CAC1C,CAAA;IACH,CAAC;IAED,OAAO,CAAC,OAAO;QACb,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACzD,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,cAAc,CAAA;YAErC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;;AA1E2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;2DAAoB;AAHpC,mBAAmB;IAD/B,aAAa,CAAC,uBAAuB,CAAC;GAC1B,mBAAmB,CA8E/B","sourcesContent":["import '@material/web/button/elevated-button.js'\nimport '@operato/property-editor/ox-properties-dynamic-view.js'\nimport '@things-factory/organization/dist-client/component/approval-line-brief.js'\nimport '../templates/activity-thread-context-template.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { entity360Name, openPopup } from '@operato/layout'\n\nimport { currentApprovalOrder } from '../approval-progress.js'\nimport { StateRibonStyles } from './state-ribon.js'\n\n/**\n * 업무 하나의 머리 — **지금 어떤 상태이고, 누구 손에 있나.**\n *\n * 모양은 `state-ribon.ts` 가 정한다(그 파일의 머리말에 왜 채운 띠를 걷었는지 적어 두었다).\n * 여기서는 이 화면이 무엇을 보일지만 고른다.\n */\n@customElement('activity-thread-ribon')\nexport class ActivityThreadRibon extends localize(i18next)(LitElement) {\n static styles = [StateRibonStyles]\n\n @property({ type: Object }) activityThread: any\n\n render() {\n const { state, assignee, activityInstance, activityApprovals } = this.activityThread\n const { description, approvalLine } = activityInstance || {}\n\n return html`\n <span class=\"state\">\n <i class=\"dot\"></i>\n ${i18next.t('label.activity-state-' + state)}\n </span>\n\n <md-icon\n class=\"info\"\n role=\"button\"\n tabindex=\"0\"\n title=${String(i18next.t('label.thread-info'))}\n @click=${() => this.openInfo(state)}\n @keydown=${(e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.openInfo(state)\n }\n }}\n >info</md-icon\n >\n\n <span class=\"desc\" title=${description || ''}>${description || ''}</span>\n\n\n ${approvalLine\n ? html`<approval-line-brief\n .model=${approvalLine}\n .current=${currentApprovalOrder(activityApprovals)}\n ></approval-line-brief>`\n : assignee\n ? html`<span class=\"who\"\n ><span class=\"who-label\">${i18next.t('field.assignee')}</span>${entity360Name(assignee)}</span\n >`\n : ''}\n `\n }\n\n /**\n * 이 건의 기본 정보 — **팝업으로 연다.**\n *\n * ── 왜 aside 가 아닌가 (2026-09-06) ────────────────────────────────────\n * 전에는 화면 오른쪽에서 나오는 aside 판이었다. 두 가지가 걸린다.\n *\n * 자리를 다툰다 화면 오른쪽 세로 판은 operato-twin 의 twin-entity360 이 쓰는 자리다\n * (position: fixed · right: 0 · height: 100vh). 같은 자리에 둘이 선다.\n * 전역 슬롯이다 VIEWPART_POSITION.ASIDEBAR 은 앱이 하나만 갖는다. 열 개가 넘는 패키지가\n * 그 자리를 쓰고 있어(twin 의 AI 협의 도크 · board-ui · help · more-ui …),\n * 이 판을 열면 그중 열려 있던 것이 닫힌다.\n *\n * 그리고 자리의 뜻이 다르다. 오른쪽 드로어는 **보고 있는 대상을 따라다니는 상세**이고(360 이\n * 그것이다 — 포커스가 바뀌면 내용이 바뀌고 화면을 옮겨도 남는다), 이 판은 **이 화면의 이 건**에\n * 대한 것이라 화면을 벗어나면 뜻이 없다. 페이지에 매인 것을 앱 전역 자리에 두지 않는다.\n *\n * 내용도 팝업 크기다 — 값 여덟 줄과 접힌 타임라인이라 세로 100vh 를 쓸 일이 없었다.\n */\n private openInfo(state: string) {\n openPopup(\n html`<activity-thread-context-template .activityThread=${this.activityThread} state=${state}></activity-thread-context-template>`,\n { title: i18next.t('label.thread-info') }\n )\n }\n\n updated(changes) {\n if (changes.has('activityThread') && this.activityThread) {\n const { state } = this.activityThread\n\n this.setAttribute('state', state)\n }\n }\n}\n"]}
|
|
@@ -6,6 +6,7 @@ import { customElement, property } from 'lit/decorators.js';
|
|
|
6
6
|
import { i18next } from '@operato/i18n';
|
|
7
7
|
import { whenText } from '../when.js';
|
|
8
8
|
import { ActivityThread } from '../types/activity-thread.js';
|
|
9
|
+
import { entity360Name } from '@operato/layout';
|
|
9
10
|
/** 그 줄이 화면에 내보이는 시각. 정렬도 이 값으로 한다. */
|
|
10
11
|
function whenOf(item) {
|
|
11
12
|
const at = item?.terminatedAt ?? item?.updatedAt ?? item?.createdAt;
|
|
@@ -269,7 +270,6 @@ let ActivityThreadTimeline = class ActivityThreadTimeline extends LitElement {
|
|
|
269
270
|
}
|
|
270
271
|
renderActivityThreadHistory(activityThreadHistory) {
|
|
271
272
|
const { updater, updatedAt, state, reason, output, transaction } = activityThreadHistory;
|
|
272
|
-
const { name } = updater || {};
|
|
273
273
|
return html `
|
|
274
274
|
<li>
|
|
275
275
|
<span date>${whenText(updatedAt)}</span>
|
|
@@ -277,7 +277,7 @@ let ActivityThreadTimeline = class ActivityThreadTimeline extends LitElement {
|
|
|
277
277
|
|
|
278
278
|
<span info>
|
|
279
279
|
<span what>${i18next.t('label.activity-transaction-' + transaction)}</span>
|
|
280
|
-
${name ? html `<span who>${
|
|
280
|
+
${updater?.name ? html `<span who>${entity360Name(updater)}</span>` : nothing}
|
|
281
281
|
${this.renderSaid(reason || (output ? this.renderOutput(output) : ''))}
|
|
282
282
|
</span>
|
|
283
283
|
</li>
|
|
@@ -285,14 +285,13 @@ let ActivityThreadTimeline = class ActivityThreadTimeline extends LitElement {
|
|
|
285
285
|
}
|
|
286
286
|
renderActivityApproval(activityApproval) {
|
|
287
287
|
const { judgment, approver, comment, createdAt, terminatedAt } = activityApproval;
|
|
288
|
-
const { name } = approver || {};
|
|
289
288
|
return html `
|
|
290
289
|
<li>
|
|
291
290
|
<span date>${whenText(terminatedAt ?? createdAt)}</span>
|
|
292
291
|
<span status></span>
|
|
293
292
|
<span info>
|
|
294
293
|
<span what>${i18next.t('label.activity-state-' + judgment)}</span>
|
|
295
|
-
${name ? html `<span who>${
|
|
294
|
+
${approver?.name ? html `<span who>${entity360Name(approver)}</span>` : nothing}
|
|
296
295
|
${this.renderSaid(comment)}
|
|
297
296
|
</span>
|
|
298
297
|
</li>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-thread-timeline.js","sourceRoot":"","sources":["../../client/components/activity-thread-timeline.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@things-factory/organization/dist-client/component/approval-line-view.js'\n\nimport { html, css, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { whenText } from '../when.js'\n\nimport { ActivityThread } from '../types/activity-thread.js'\nimport { ActivityThreadHistory } from '../types/activity-thread-history.js'\nimport { ActivityApproval } from '../types/activity-approval.js'\n\n\n/** 그 줄이 화면에 내보이는 시각. 정렬도 이 값으로 한다. */\nfunction whenOf(item: any): number {\n const at = item?.terminatedAt ?? item?.updatedAt ?? item?.createdAt\n\n const ms = at === null || at === undefined || at === '' ? NaN : Date.parse(String(at))\n\n return Number.isFinite(ms) ? ms : 0\n}\n\n@customElement('activity-thread-timeline')\nexport class ActivityThreadTimeline extends LitElement {\n static styles = [\n css`\n /*\n * 업무 하나가 지나온 자취 — **언제 · 무슨 일이 · 누구에 의해.**\n *\n * ── 무엇을 고쳤나 (2026-09-06) ─────────────────────────────────────\n * 1. 절 제목이 단추처럼 생겼었다. border: 2px 에 border-radius: 15px 인 알약이라\n * 누를 수 있는 것으로 보인다. 이 화면에서 알약은 거르개(chip)의 모양이다.\n * 2. 말풍선 꼬리에 #f4f4f4 가, 표 선에 lightgray 가 하드코딩되어 있었다. 어두운\n * 화면에서 밝은 회색이 그대로 나온다.\n * 3. 시각이 Tuesday, September 1, 2026 at 11:51 AM 이었다. when.ts 머리말 참조.\n *\n * ── 내용이 없는 줄에 상자를 그리지 않는다 ─────────────────────────\n * 의견이나 결과가 없는 줄에도 회색 상자가 min-height: 20px 으로 깔려 있었다. 저장 ·\n * 게시처럼 적을 것이 없는 기록이 대부분이라, 화면의 절반이 빈 회색 상자였다.\n */\n /*\n * 절 하나이므로 다른 절과 같은 카드로 둔다. 전에는 카드 밖에 그냥 놓여 있어서 「업무 정보」\n * 카드와 위계가 어긋났다 — 같은 층의 두 절인데 하나만 면 위에 있었다.\n */\n :host {\n display: block;\n padding: 14px;\n background: var(--ui-panel, #ffffff);\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r, 10px);\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-body, 13px);\n }\n\n /* 그릴 것이 없으면 빈 카드를 남기지 않는다. */\n :host(:not(:has(ol))) {\n display: none;\n }\n\n /*\n * ── 접어 둔다 (2026-09-06) ────────────────────────────────────────\n * 이력은 참고다. 결재자가 먼저 읽는 것은 「무엇을 승인하나」이고, 이력은 그것으로 판단이\n * 안 설 때 펴 보는 것이다. 그런데 이 카드가 내용 절과 똑같은 무게로 서 있었다.\n *\n * 접어 두되 **마지막에 무슨 일이 있었는지 한 줄은 남긴다.** 「이력 4건」만 있으면 펴 볼지\n * 말지를 정할 수가 없다.\n *\n * 펴 두어야 하는 화면이 있다 — 지난 일을 보러 온 화면(완료 조회), 그리고 보일 항목이 하나도\n * 없어서 「타임라인의 상신 사유를 보고 판단하세요」라고 안내한 경우다. 그때 접혀 있으면 그\n * 안내가 거짓말이 된다. 그래서 여는 쪽은 담는 화면이 정한다(open 속성).\n */\n h2 {\n margin: 0;\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--ui-ink, #17242c);\n }\n\n summary {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-medium, 8px);\n cursor: pointer;\n list-style: none;\n }\n\n summary::-webkit-details-marker {\n display: none;\n }\n\n /* 펴고 접는 표시. 색만으로 말하지 않으려고 방향이 바뀐다. */\n .chevron {\n flex: none;\n font-size: 18px;\n color: var(--ui-muted, #57676f);\n transition: transform 0.12s ease;\n }\n\n details[open] .chevron {\n transform: rotate(90deg);\n }\n\n /* 접혔을 때 보이는 한 줄 — 마지막에 무슨 일이 있었나. */\n .last {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n details[open] .last {\n display: none;\n }\n\n .count {\n flex: none;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n font-variant-numeric: tabular-nums;\n }\n\n ol {\n list-style: none;\n margin: var(--spacing-medium, 8px) 0 0;\n padding: 0;\n }\n\n li {\n display: grid;\n grid-template-columns: max-content 16px 1fr;\n align-items: start;\n column-gap: var(--spacing-small, 4px);\n }\n\n /* 시각 — 자릿수를 맞춰 위아래를 바로 비교할 수 있게. */\n [date] {\n padding-top: 1px;\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-small, 11.5px);\n font-variant-numeric: tabular-nums;\n color: var(--ui-faint, #8a959c);\n white-space: nowrap;\n }\n\n /*\n * 점과 그 아래로 이어지는 세로선.\n *\n * 칸을 세로로 늘려(align-self: stretch) 그 안에 점과 선을 그린다. 전에는 칸이 9px 이라\n * 선 높이를 calc(100% + 100px) 으로 짐작해서 늘렸고, 줄 높이가 그보다 낮으면 선이 다음\n * 줄을 지나 카드 아래까지 흘러내렸다.\n */\n [status] {\n position: relative;\n align-self: stretch;\n width: 9px;\n margin-left: 3px;\n }\n\n [status]::after {\n content: '';\n position: absolute;\n top: 4px;\n left: 0;\n width: 9px;\n height: 9px;\n border-radius: 50%;\n background-color: var(--ui-accent, #00727e);\n }\n\n [status]::before {\n content: '';\n position: absolute;\n top: 15px;\n bottom: 0;\n left: 3px;\n width: 2px;\n background-color: var(--ui-line, #d5dce0);\n }\n\n li:last-child [status]::before {\n display: none;\n }\n\n [info] {\n min-width: 0;\n padding-bottom: var(--spacing-large, 16px);\n }\n\n li:last-child [info] {\n padding-bottom: 0;\n }\n\n /* 무슨 일이 있었나 — 이 줄에서 먼저 읽혀야 하는 값. */\n [what] {\n font-weight: 600;\n }\n\n /* 누가 — 곁말이다. 아이콘은 두지 않는다(이름 앞의 원형 아이콘이 값처럼 보였다). */\n [who] {\n margin-left: var(--spacing-small, 4px);\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n /* 적힌 말 — 의견 · 사유 · 결과. 없으면 이 요소 자체를 그리지 않는다. */\n p {\n /* 줄을 바꿔 아래에 두되(block), 폭은 내용만큼만 — 짧은 의견에 화면 폭짜리 상자가 생겼다. */\n display: block;\n width: fit-content;\n max-width: 100%;\n margin: 5px 0 0;\n padding: 7px 10px;\n background: var(--ui-panel-2, #eef1f3);\n border-radius: var(--ui-r-small, 6px);\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-small, 11.5px);\n line-height: 1.5;\n word-break: keep-all;\n }\n\n @media only screen and (max-width: 460px) {\n li {\n grid-template-columns: 16px 1fr;\n }\n\n /* 좁은 화면에서는 시각을 내용 아래로 내린다 — 한 줄의 절반을 시각에 내줄 수 없다. */\n [date] {\n grid-column: 2;\n grid-row: 2;\n padding: 0 0 var(--spacing-medium, 8px);\n }\n }\n `\n ]\n\n @property({ type: Object }) activityThread?: ActivityThread\n @property({ type: String }) subtitle?: string\n @property({ type: Boolean, attribute: 'short-form' }) shortForm?: boolean\n\n /**\n * 펴 둘 것인가 — **담는 화면이 정한다.**\n *\n * 기본은 접는다. 펴는 곳은 둘이다.\n * 완료 조회 지난 일을 보러 온 화면이라 이력이 곧 본문이다\n * 보일 항목 없음 화면이 「타임라인의 상신 사유를 보고 판단하세요」로 여기를 가리킨다\n */\n @property({ type: Boolean }) open?: boolean\n\n render() {\n const { activityApprovals = [], activityThreadHistories = [] } = this.activityThread || {}\n\n /*\n * **정렬 키와 표시 키가 같아야 한다.**\n *\n * 전에는 둘 다 updatedAt 으로 정렬해 놓고, 결재 줄만 terminatedAt 을 보여 주었다. 그래서\n * 시각이 오르내리는 목록이 나왔다 — 11:51 승인됨 · 11:50 제출 · 11:50 게시 · 11:51 상신.\n * 시간 순으로 읽으라고 만든 화면인데 시간 순이 아니었다.\n */\n const timeline = [\n ...activityThreadHistories.filter(\n item => item.transaction && (!this.shortForm || !/^.*(reject|approve|save|restart).*$/.test(item.transaction))\n ),\n ...activityApprovals.filter(item => item.judgment)\n ].sort((a, b) => (whenOf(a) > whenOf(b) ? 1 : -1))\n\n if (!timeline.length) {\n return nothing\n }\n\n return html`\n <details ?open=${this.open}>\n <summary>\n <md-icon class=\"chevron\">chevron_right</md-icon>\n <h2>${this.subtitle || i18next.t('label.timeline')}</h2>\n <span class=\"count\">${i18next.t('text.timeline-count', { count: timeline.length })}</span>\n <span class=\"last\">${this.lastLine(timeline[timeline.length - 1])}</span>\n </summary>\n\n <ol>\n ${timeline.map(item =>\n 'reason' in item\n ? this.renderActivityThreadHistory(item as ActivityThreadHistory)\n : this.renderActivityApproval(item as ActivityApproval)\n )}\n </ol>\n </details>\n `\n }\n\n /** 접혔을 때 보이는 한 줄 — 마지막 기록의 「무엇을 · 누가 · 언제」. */\n lastLine(item: any): string {\n const what =\n 'reason' in item\n ? i18next.t('label.activity-transaction-' + item.transaction)\n : i18next.t('label.activity-state-' + item.judgment)\n\n const who = item?.updater?.name || item?.approver?.name || ''\n\n return [what, who, whenText(whenOf(item))].filter(Boolean).join(' · ')\n }\n\n renderActivityThreadHistory(activityThreadHistory: ActivityThreadHistory) {\n const { updater, updatedAt, state, reason, output, transaction } = activityThreadHistory\n const { name } = updater || {}\n\n return html`\n <li>\n <span date>${whenText(updatedAt)}</span>\n <span status></span>\n\n <span info>\n <span what>${i18next.t('label.activity-transaction-' + transaction)}</span>\n ${name ? html`<span who>${name}</span>` : nothing}\n ${this.renderSaid(reason || (output ? this.renderOutput(output) : ''))}\n </span>\n </li>\n `\n }\n\n renderActivityApproval(activityApproval: ActivityApproval) {\n const { judgment, approver, comment, createdAt, terminatedAt } = activityApproval\n const { name } = approver || {}\n\n return html`\n <li>\n <span date>${whenText(terminatedAt ?? createdAt)}</span>\n <span status></span>\n <span info>\n <span what>${i18next.t('label.activity-state-' + judgment)}</span>\n ${name ? html`<span who>${name}</span>` : nothing}\n ${this.renderSaid(comment)}\n </span>\n </li>\n `\n }\n\n /** 적힌 말이 없으면 상자를 그리지 않는다 — 빈 회색 상자는 내용이 있는 줄과 구별되지 않는다. */\n renderSaid(said?: string) {\n return said ? html`<p>${said}</p>` : nothing\n }\n\n renderOutput(output: any) {\n const keys = Object.keys(output)\n if (keys.length === 0) {\n return ''\n }\n\n const firstKey = keys[0]\n let value = output[firstKey]\n\n // 값이 객체이거나 긴 문자열인 경우 처리\n if (typeof value === 'object') {\n value = Array.isArray(value) ? '[...]' : '{...}'\n } else if (typeof value === 'string' && value.length > 50) {\n value = value.substring(0, 47) + '...'\n }\n\n let result = `${firstKey}: ${value}`\n\n // 추가 속성이 있는 경우 표시\n if (keys.length > 1) {\n result += ' ' + i18next.t('text.and-n-more-fields', { n: keys.length - 1 })\n }\n\n return result\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"activity-thread-timeline.js","sourceRoot":"","sources":["../../client/components/activity-thread-timeline.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@things-factory/organization/dist-client/component/approval-line-view.js'\n\nimport { html, css, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { whenText } from '../when.js'\n\nimport { ActivityThread } from '../types/activity-thread.js'\nimport { ActivityThreadHistory } from '../types/activity-thread-history.js'\nimport { ActivityApproval } from '../types/activity-approval.js'\nimport { entity360Name } from '@operato/layout'\n\n\n/** 그 줄이 화면에 내보이는 시각. 정렬도 이 값으로 한다. */\nfunction whenOf(item: any): number {\n const at = item?.terminatedAt ?? item?.updatedAt ?? item?.createdAt\n\n const ms = at === null || at === undefined || at === '' ? NaN : Date.parse(String(at))\n\n return Number.isFinite(ms) ? ms : 0\n}\n\n@customElement('activity-thread-timeline')\nexport class ActivityThreadTimeline extends LitElement {\n static styles = [\n css`\n /*\n * 업무 하나가 지나온 자취 — **언제 · 무슨 일이 · 누구에 의해.**\n *\n * ── 무엇을 고쳤나 (2026-09-06) ─────────────────────────────────────\n * 1. 절 제목이 단추처럼 생겼었다. border: 2px 에 border-radius: 15px 인 알약이라\n * 누를 수 있는 것으로 보인다. 이 화면에서 알약은 거르개(chip)의 모양이다.\n * 2. 말풍선 꼬리에 #f4f4f4 가, 표 선에 lightgray 가 하드코딩되어 있었다. 어두운\n * 화면에서 밝은 회색이 그대로 나온다.\n * 3. 시각이 Tuesday, September 1, 2026 at 11:51 AM 이었다. when.ts 머리말 참조.\n *\n * ── 내용이 없는 줄에 상자를 그리지 않는다 ─────────────────────────\n * 의견이나 결과가 없는 줄에도 회색 상자가 min-height: 20px 으로 깔려 있었다. 저장 ·\n * 게시처럼 적을 것이 없는 기록이 대부분이라, 화면의 절반이 빈 회색 상자였다.\n */\n /*\n * 절 하나이므로 다른 절과 같은 카드로 둔다. 전에는 카드 밖에 그냥 놓여 있어서 「업무 정보」\n * 카드와 위계가 어긋났다 — 같은 층의 두 절인데 하나만 면 위에 있었다.\n */\n :host {\n display: block;\n padding: 14px;\n background: var(--ui-panel, #ffffff);\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r, 10px);\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-body, 13px);\n }\n\n /* 그릴 것이 없으면 빈 카드를 남기지 않는다. */\n :host(:not(:has(ol))) {\n display: none;\n }\n\n /*\n * ── 접어 둔다 (2026-09-06) ────────────────────────────────────────\n * 이력은 참고다. 결재자가 먼저 읽는 것은 「무엇을 승인하나」이고, 이력은 그것으로 판단이\n * 안 설 때 펴 보는 것이다. 그런데 이 카드가 내용 절과 똑같은 무게로 서 있었다.\n *\n * 접어 두되 **마지막에 무슨 일이 있었는지 한 줄은 남긴다.** 「이력 4건」만 있으면 펴 볼지\n * 말지를 정할 수가 없다.\n *\n * 펴 두어야 하는 화면이 있다 — 지난 일을 보러 온 화면(완료 조회), 그리고 보일 항목이 하나도\n * 없어서 「타임라인의 상신 사유를 보고 판단하세요」라고 안내한 경우다. 그때 접혀 있으면 그\n * 안내가 거짓말이 된다. 그래서 여는 쪽은 담는 화면이 정한다(open 속성).\n */\n h2 {\n margin: 0;\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--ui-ink, #17242c);\n }\n\n summary {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-medium, 8px);\n cursor: pointer;\n list-style: none;\n }\n\n summary::-webkit-details-marker {\n display: none;\n }\n\n /* 펴고 접는 표시. 색만으로 말하지 않으려고 방향이 바뀐다. */\n .chevron {\n flex: none;\n font-size: 18px;\n color: var(--ui-muted, #57676f);\n transition: transform 0.12s ease;\n }\n\n details[open] .chevron {\n transform: rotate(90deg);\n }\n\n /* 접혔을 때 보이는 한 줄 — 마지막에 무슨 일이 있었나. */\n .last {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n details[open] .last {\n display: none;\n }\n\n .count {\n flex: none;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n font-variant-numeric: tabular-nums;\n }\n\n ol {\n list-style: none;\n margin: var(--spacing-medium, 8px) 0 0;\n padding: 0;\n }\n\n li {\n display: grid;\n grid-template-columns: max-content 16px 1fr;\n align-items: start;\n column-gap: var(--spacing-small, 4px);\n }\n\n /* 시각 — 자릿수를 맞춰 위아래를 바로 비교할 수 있게. */\n [date] {\n padding-top: 1px;\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-small, 11.5px);\n font-variant-numeric: tabular-nums;\n color: var(--ui-faint, #8a959c);\n white-space: nowrap;\n }\n\n /*\n * 점과 그 아래로 이어지는 세로선.\n *\n * 칸을 세로로 늘려(align-self: stretch) 그 안에 점과 선을 그린다. 전에는 칸이 9px 이라\n * 선 높이를 calc(100% + 100px) 으로 짐작해서 늘렸고, 줄 높이가 그보다 낮으면 선이 다음\n * 줄을 지나 카드 아래까지 흘러내렸다.\n */\n [status] {\n position: relative;\n align-self: stretch;\n width: 9px;\n margin-left: 3px;\n }\n\n [status]::after {\n content: '';\n position: absolute;\n top: 4px;\n left: 0;\n width: 9px;\n height: 9px;\n border-radius: 50%;\n background-color: var(--ui-accent, #00727e);\n }\n\n [status]::before {\n content: '';\n position: absolute;\n top: 15px;\n bottom: 0;\n left: 3px;\n width: 2px;\n background-color: var(--ui-line, #d5dce0);\n }\n\n li:last-child [status]::before {\n display: none;\n }\n\n [info] {\n min-width: 0;\n padding-bottom: var(--spacing-large, 16px);\n }\n\n li:last-child [info] {\n padding-bottom: 0;\n }\n\n /* 무슨 일이 있었나 — 이 줄에서 먼저 읽혀야 하는 값. */\n [what] {\n font-weight: 600;\n }\n\n /* 누가 — 곁말이다. 아이콘은 두지 않는다(이름 앞의 원형 아이콘이 값처럼 보였다). */\n [who] {\n margin-left: var(--spacing-small, 4px);\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n /* 적힌 말 — 의견 · 사유 · 결과. 없으면 이 요소 자체를 그리지 않는다. */\n p {\n /* 줄을 바꿔 아래에 두되(block), 폭은 내용만큼만 — 짧은 의견에 화면 폭짜리 상자가 생겼다. */\n display: block;\n width: fit-content;\n max-width: 100%;\n margin: 5px 0 0;\n padding: 7px 10px;\n background: var(--ui-panel-2, #eef1f3);\n border-radius: var(--ui-r-small, 6px);\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-small, 11.5px);\n line-height: 1.5;\n word-break: keep-all;\n }\n\n @media only screen and (max-width: 460px) {\n li {\n grid-template-columns: 16px 1fr;\n }\n\n /* 좁은 화면에서는 시각을 내용 아래로 내린다 — 한 줄의 절반을 시각에 내줄 수 없다. */\n [date] {\n grid-column: 2;\n grid-row: 2;\n padding: 0 0 var(--spacing-medium, 8px);\n }\n }\n `\n ]\n\n @property({ type: Object }) activityThread?: ActivityThread\n @property({ type: String }) subtitle?: string\n @property({ type: Boolean, attribute: 'short-form' }) shortForm?: boolean\n\n /**\n * 펴 둘 것인가 — **담는 화면이 정한다.**\n *\n * 기본은 접는다. 펴는 곳은 둘이다.\n * 완료 조회 지난 일을 보러 온 화면이라 이력이 곧 본문이다\n * 보일 항목 없음 화면이 「타임라인의 상신 사유를 보고 판단하세요」로 여기를 가리킨다\n */\n @property({ type: Boolean }) open?: boolean\n\n render() {\n const { activityApprovals = [], activityThreadHistories = [] } = this.activityThread || {}\n\n /*\n * **정렬 키와 표시 키가 같아야 한다.**\n *\n * 전에는 둘 다 updatedAt 으로 정렬해 놓고, 결재 줄만 terminatedAt 을 보여 주었다. 그래서\n * 시각이 오르내리는 목록이 나왔다 — 11:51 승인됨 · 11:50 제출 · 11:50 게시 · 11:51 상신.\n * 시간 순으로 읽으라고 만든 화면인데 시간 순이 아니었다.\n */\n const timeline = [\n ...activityThreadHistories.filter(\n item => item.transaction && (!this.shortForm || !/^.*(reject|approve|save|restart).*$/.test(item.transaction))\n ),\n ...activityApprovals.filter(item => item.judgment)\n ].sort((a, b) => (whenOf(a) > whenOf(b) ? 1 : -1))\n\n if (!timeline.length) {\n return nothing\n }\n\n return html`\n <details ?open=${this.open}>\n <summary>\n <md-icon class=\"chevron\">chevron_right</md-icon>\n <h2>${this.subtitle || i18next.t('label.timeline')}</h2>\n <span class=\"count\">${i18next.t('text.timeline-count', { count: timeline.length })}</span>\n <span class=\"last\">${this.lastLine(timeline[timeline.length - 1])}</span>\n </summary>\n\n <ol>\n ${timeline.map(item =>\n 'reason' in item\n ? this.renderActivityThreadHistory(item as ActivityThreadHistory)\n : this.renderActivityApproval(item as ActivityApproval)\n )}\n </ol>\n </details>\n `\n }\n\n /** 접혔을 때 보이는 한 줄 — 마지막 기록의 「무엇을 · 누가 · 언제」. */\n lastLine(item: any): string {\n const what =\n 'reason' in item\n ? i18next.t('label.activity-transaction-' + item.transaction)\n : i18next.t('label.activity-state-' + item.judgment)\n\n const who = item?.updater?.name || item?.approver?.name || ''\n\n return [what, who, whenText(whenOf(item))].filter(Boolean).join(' · ')\n }\n\n renderActivityThreadHistory(activityThreadHistory: ActivityThreadHistory) {\n const { updater, updatedAt, state, reason, output, transaction } = activityThreadHistory\n\n return html`\n <li>\n <span date>${whenText(updatedAt)}</span>\n <span status></span>\n\n <span info>\n <span what>${i18next.t('label.activity-transaction-' + transaction)}</span>\n ${updater?.name ? html`<span who>${entity360Name(updater)}</span>` : nothing}\n ${this.renderSaid(reason || (output ? this.renderOutput(output) : ''))}\n </span>\n </li>\n `\n }\n\n renderActivityApproval(activityApproval: ActivityApproval) {\n const { judgment, approver, comment, createdAt, terminatedAt } = activityApproval\n\n return html`\n <li>\n <span date>${whenText(terminatedAt ?? createdAt)}</span>\n <span status></span>\n <span info>\n <span what>${i18next.t('label.activity-state-' + judgment)}</span>\n ${approver?.name ? html`<span who>${entity360Name(approver)}</span>` : nothing}\n ${this.renderSaid(comment)}\n </span>\n </li>\n `\n }\n\n /** 적힌 말이 없으면 상자를 그리지 않는다 — 빈 회색 상자는 내용이 있는 줄과 구별되지 않는다. */\n renderSaid(said?: string) {\n return said ? html`<p>${said}</p>` : nothing\n }\n\n renderOutput(output: any) {\n const keys = Object.keys(output)\n if (keys.length === 0) {\n return ''\n }\n\n const firstKey = keys[0]\n let value = output[firstKey]\n\n // 값이 객체이거나 긴 문자열인 경우 처리\n if (typeof value === 'object') {\n value = Array.isArray(value) ? '[...]' : '{...}'\n } else if (typeof value === 'string' && value.length > 50) {\n value = value.substring(0, 47) + '...'\n }\n\n let result = `${firstKey}: ${value}`\n\n // 추가 속성이 있는 경우 표시\n if (keys.length > 1) {\n result += ' ' + i18next.t('text.and-n-more-fields', { n: keys.length - 1 })\n }\n\n return result\n }\n}\n"]}
|
|
@@ -37,4 +37,12 @@
|
|
|
37
37
|
*
|
|
38
38
|
* 색만으로 뜻을 전하지 않는다 — 상태 이름을 항상 함께 적는다.
|
|
39
39
|
*/
|
|
40
|
-
|
|
40
|
+
/**
|
|
41
|
+
* The state → colour mapping above, alone - for anything that shows a worklist state, not only the
|
|
42
|
+
* ribbon. Set on `:host([state=...])`; read `--worklist-state-color`. The activity instance view kept
|
|
43
|
+
* a fourth copy of the old eight hex colours after the ribbons moved to meanings; three of them were
|
|
44
|
+
* under 3:1 on a light surface (started 2.62 · rejected 2.73 · ended 2.65, measured on :4000
|
|
45
|
+
* 2026-09-24) and `unassigned` had none, so it drew tomato.
|
|
46
|
+
*/
|
|
47
|
+
export declare const WorklistStateColors: import("lit").CSSResult;
|
|
48
|
+
export declare const StateRibonStyles: import("lit").CSSResult[];
|