@things-factory/worklist 10.1.39 → 10.1.41
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/components/activity-starter-form.js +1 -1
- package/dist-client/components/activity-starter-form.js.map +1 -1
- package/dist-client/components/activity-thread-timeline.d.ts +2 -2
- package/dist-client/pages/activity-catalog/activity-detail-page.js +10 -0
- package/dist-client/pages/activity-catalog/activity-detail-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-view.d.ts +1 -1
- package/dist-client/pages/work-door/door-sentence.d.ts +63 -0
- package/dist-client/pages/work-door/door-sentence.js +109 -0
- package/dist-client/pages/work-door/door-sentence.js.map +1 -0
- package/dist-client/pages/work-door/door-words.d.ts +7 -0
- package/dist-client/pages/work-door/door-words.js +30 -0
- package/dist-client/pages/work-door/door-words.js.map +1 -1
- package/dist-client/pages/work-door/work-doors-page.d.ts +1 -1
- package/dist-client/pages/work-door/work-doors-page.js +49 -17
- package/dist-client/pages/work-door/work-doors-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/index.d.ts +3 -3
- package/dist-server/service/work-door/work-door-declaration.d.ts +11 -5
- package/dist-server/service/work-door/work-door-declaration.js +5 -3
- package/dist-server/service/work-door/work-door-declaration.js.map +1 -1
- package/dist-server/service/work-door/work-door-resolver.js +1 -1
- package/dist-server/service/work-door/work-door-resolver.js.map +1 -1
- package/dist-server/service/work-door/work-door-service.js +3 -14
- package/dist-server/service/work-door/work-door-service.js.map +1 -1
- package/dist-server/service/work-door/work-door-type.d.ts +43 -6
- package/dist-server/service/work-door/work-door-type.js +102 -26
- package/dist-server/service/work-door/work-door-type.js.map +1 -1
- package/dist-server/service/work-door/work-door.d.ts +65 -16
- package/dist-server/service/work-door/work-door.js +53 -90
- package/dist-server/service/work-door/work-door.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/tests/work-door-door-words.test.ts +35 -1
- package/tests/work-door-not-used-refuse.test.ts +6 -6
- package/tests/work-door-sentence.test.ts +199 -0
- package/tests/work-door.test.ts +63 -56
- package/translations/en.json +26 -1
- package/translations/ja.json +26 -1
- package/translations/ko.json +26 -1
- package/translations/ms.json +26 -1
- package/translations/zh.json +26 -1
|
@@ -8,7 +8,8 @@ import { i18next, localize } from '@operato/i18n';
|
|
|
8
8
|
import { PageView } from '@operato/shell';
|
|
9
9
|
import { ProseStyles, SurfaceStyles } from '@things-factory/styles';
|
|
10
10
|
import { detailAddress, doorsToSetHere, opensDetail } from './door-links.js';
|
|
11
|
-
import {
|
|
11
|
+
import { doorLabel, doorSentence, doorsSummary, whenNoLineTag } from './door-sentence.js';
|
|
12
|
+
import { kindWord, reasonWord, standingWord, whenNoLineBecauseWord, whenNoLineWord } from './door-words.js';
|
|
12
13
|
/**
|
|
13
14
|
* 결재 문 현황 — **이 공장에서 어느 승인 단계가 실제로 서 있나.**
|
|
14
15
|
*
|
|
@@ -22,9 +23,10 @@ import { kindWord, standingWord } from './door-words.js';
|
|
|
22
23
|
* 실적 정정도 폐기도 승인 없이 반영되는데, 화면에는 문이 있는 것처럼 보였다.
|
|
23
24
|
*
|
|
24
25
|
* ── 비어 있는 것을 「승인 없음」으로 그리지 않는다 ─────────────────────────
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* 빈 결재선의 결과는 **문마다 선언돼 있다**(ADR-0077). `run` 문은 올린 사람이 자기 건을 승인한 것이
|
|
27
|
+
* 되고, `refuse` 문은 올리는 것 자체가 거절된다. 서버는 그 사실을 값으로 보내고 **문장은 이 화면이
|
|
28
|
+
* 번역으로 짓는다**(§`door-sentence.ts`) — 서버가 한국어로 지어 보내던 때는 다른 언어 사용자도 한국어를
|
|
29
|
+
* 읽었고, 거절하는 문에도 「자기 건을 승인한다」가 적혔다.
|
|
28
30
|
*
|
|
29
31
|
* ── 할 일이 있는 것부터 ────────────────────────────────────────────────────
|
|
30
32
|
* 선언 순서로 늘어놓으면 서 있는 것과 빈 것이 섞이고, 읽는 사람이 열한 줄을 훑어야 한다. **정할
|
|
@@ -34,18 +36,26 @@ import { kindWord, standingWord } from './door-words.js';
|
|
|
34
36
|
const DOORS = gql `
|
|
35
37
|
{
|
|
36
38
|
workDoors {
|
|
37
|
-
summary
|
|
38
39
|
todo
|
|
40
|
+
on
|
|
41
|
+
off
|
|
42
|
+
missing
|
|
43
|
+
notUsed
|
|
39
44
|
seatsStanding
|
|
40
45
|
seatsOffered
|
|
41
46
|
doors {
|
|
42
47
|
name
|
|
43
|
-
|
|
48
|
+
labelKey
|
|
44
49
|
kind
|
|
45
50
|
standing
|
|
51
|
+
reason
|
|
46
52
|
approvers
|
|
47
53
|
from
|
|
48
|
-
|
|
54
|
+
whenNoLine
|
|
55
|
+
whenNoLineBecause
|
|
56
|
+
followsLabelKey
|
|
57
|
+
assigneeRole
|
|
58
|
+
notUsedBecause
|
|
49
59
|
}
|
|
50
60
|
}
|
|
51
61
|
}
|
|
@@ -60,7 +70,7 @@ const ORDER = { missing: 0, off: 1, on: 2, 'not-used': 3 };
|
|
|
60
70
|
let WorkDoorsPage = class WorkDoorsPage extends localize(i18next)(PageView) {
|
|
61
71
|
constructor() {
|
|
62
72
|
super(...arguments);
|
|
63
|
-
this.
|
|
73
|
+
this.counts = { on: 0, off: 0, missing: 0, notUsed: 0, todo: 0 };
|
|
64
74
|
this.todo = 0;
|
|
65
75
|
this.seatsStanding = 0;
|
|
66
76
|
this.seatsOffered = 0;
|
|
@@ -181,6 +191,14 @@ let WorkDoorsPage = class WorkDoorsPage extends localize(i18next)(PageView) {
|
|
|
181
191
|
opacity: 0.85;
|
|
182
192
|
}
|
|
183
193
|
|
|
194
|
+
/* 빈 결재선에서 이 문이 어떻게 하나 — 선언이다(ADR-0077). 문장 아래 한 줄로. */
|
|
195
|
+
.when {
|
|
196
|
+
display: block;
|
|
197
|
+
margin-top: 2px;
|
|
198
|
+
font-size: 0.85em;
|
|
199
|
+
opacity: 0.75;
|
|
200
|
+
}
|
|
201
|
+
|
|
184
202
|
.from {
|
|
185
203
|
flex: 0 0 auto;
|
|
186
204
|
font-size: 0.85em;
|
|
@@ -234,10 +252,20 @@ let WorkDoorsPage = class WorkDoorsPage extends localize(i18next)(PageView) {
|
|
|
234
252
|
this.doors = (response.data?.workDoors?.doors ?? []).map((one) => ({
|
|
235
253
|
...one,
|
|
236
254
|
standing: standingWord(one.standing) ?? one.standing,
|
|
237
|
-
kind: kindWord(one.kind) ?? one.kind
|
|
255
|
+
kind: kindWord(one.kind) ?? one.kind,
|
|
256
|
+
reason: reasonWord(one.reason) ?? one.reason,
|
|
257
|
+
whenNoLine: whenNoLineWord(one.whenNoLine),
|
|
258
|
+
whenNoLineBecause: whenNoLineBecauseWord(one.whenNoLineBecause)
|
|
238
259
|
}));
|
|
239
|
-
|
|
240
|
-
this.todo =
|
|
260
|
+
const read = response.data?.workDoors ?? {};
|
|
261
|
+
this.todo = read.todo ?? 0;
|
|
262
|
+
this.counts = {
|
|
263
|
+
on: read.on ?? 0,
|
|
264
|
+
off: read.off ?? 0,
|
|
265
|
+
missing: read.missing ?? 0,
|
|
266
|
+
notUsed: read.notUsed ?? 0,
|
|
267
|
+
todo: this.todo
|
|
268
|
+
};
|
|
241
269
|
this.seatsStanding = response.data?.workDoors?.seatsStanding ?? 0;
|
|
242
270
|
this.seatsOffered = response.data?.workDoors?.seatsOffered ?? 0;
|
|
243
271
|
}
|
|
@@ -284,7 +312,7 @@ let WorkDoorsPage = class WorkDoorsPage extends localize(i18next)(PageView) {
|
|
|
284
312
|
${toSet.length
|
|
285
313
|
? html `<li>
|
|
286
314
|
${i18next.t('text.work-doors-next-line', { count: toSet.length })}
|
|
287
|
-
${toSet.map((door, index) => html `${index ? ' · ' : ''}<a href=${detailAddress(door.name)}>${i18next.t
|
|
315
|
+
${toSet.map((door, index) => html `${index ? ' · ' : ''}<a href=${detailAddress(door.name)}>${doorLabel(i18next.t, door)}</a>`)}
|
|
288
316
|
</li>`
|
|
289
317
|
: ''}
|
|
290
318
|
</ol>
|
|
@@ -312,7 +340,7 @@ let WorkDoorsPage = class WorkDoorsPage extends localize(i18next)(PageView) {
|
|
|
312
340
|
|
|
313
341
|
<section class="sheet ui-card">
|
|
314
342
|
<span class="ui-label">${i18next.t('title.work-doors')}</span>
|
|
315
|
-
<p class="state">${this.
|
|
343
|
+
<p class="state">${doorsSummary(i18next.t, this.counts)}</p>
|
|
316
344
|
${sorted.map(door => this.renderDoor(door))}
|
|
317
345
|
</section>
|
|
318
346
|
|
|
@@ -320,14 +348,18 @@ let WorkDoorsPage = class WorkDoorsPage extends localize(i18next)(PageView) {
|
|
|
320
348
|
`;
|
|
321
349
|
}
|
|
322
350
|
renderDoor(door) {
|
|
351
|
+
const label = doorLabel(i18next.t, door);
|
|
352
|
+
const when = whenNoLineTag(i18next.t, door);
|
|
323
353
|
return html `
|
|
324
354
|
<div class="door ${door.standing}">
|
|
325
355
|
${opensDetail(door)
|
|
326
|
-
? html `<a class="name" href=${detailAddress(door.name)}>${
|
|
327
|
-
: html `<span class="name">${
|
|
356
|
+
? html `<a class="name" href=${detailAddress(door.name)}>${label}</a>`
|
|
357
|
+
: html `<span class="name">${label}</span>`}
|
|
328
358
|
<span class="kind">${i18next.t(`text.door-kind-${door.kind}`)}</span>
|
|
329
359
|
<span class="mark ${door.standing}">${i18next.t(`text.door-standing-${door.standing}`)}</span>
|
|
330
|
-
<span class="why"
|
|
360
|
+
<span class="why"
|
|
361
|
+
>${doorSentence(i18next.t, door)}${when ? html `<span class="when">${when}</span>` : ''}</span
|
|
362
|
+
>
|
|
331
363
|
${
|
|
332
364
|
/*
|
|
333
365
|
* 자기 것이 아닌 양식을 쓰는 것은 정상이지만, 안 말하면 「정한 적 없는데」가 된다.
|
|
@@ -355,7 +387,7 @@ __decorate([
|
|
|
355
387
|
__decorate([
|
|
356
388
|
state(),
|
|
357
389
|
__metadata("design:type", Object)
|
|
358
|
-
], WorkDoorsPage.prototype, "
|
|
390
|
+
], WorkDoorsPage.prototype, "counts", void 0);
|
|
359
391
|
__decorate([
|
|
360
392
|
state(),
|
|
361
393
|
__metadata("design:type", Object)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-doors-page.js","sourceRoot":"","sources":["../../../client/pages/work-door/work-doors-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/text-button.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { PageView } from '@operato/shell'\nimport { ProseStyles, SurfaceStyles } from '@things-factory/styles'\n\nimport { detailAddress, doorsToSetHere, opensDetail } from './door-links.js'\nimport { kindWord, standingWord } from './door-words.js'\n\n/**\n * 결재 문 현황 — **이 공장에서 어느 승인 단계가 실제로 서 있나.**\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── 왜 이 화면이 있나 ──────────────────────────────────────────────────────\n * 양식은 제품이 세우고 **결재선은 그 공장이 채운다.** 채운 것이 「우리는 이 단계를 쓴다」는 선언이라,\n * 비어 있는 것은 결함이 아니라 선택이다. 그런데 **그 선택이 어디에도 안 보였다.** 업무 카탈로그는\n * 「제품이 이 문을 만들었나」를 보여 주고, 이 물음에는 답하지 않는다.\n *\n * 실측(2026-09-11): 미라텍에 양식 11개가 다 서 있고 **11개 전부 결재자가 없었다.** 그 공장에서는\n * 실적 정정도 폐기도 승인 없이 반영되는데, 화면에는 문이 있는 것처럼 보였다.\n *\n * ── 비어 있는 것을 「승인 없음」으로 그리지 않는다 ─────────────────────────\n * 워크리스트는 결재선이 비면 **상신을 곧 완료로 만든다.** 그래서 실제 모습은 「아무도 승인하지\n * 않는다」가 아니라 **「올린 사람이 자기 건을 승인한다」**다. 그 차이를 문장이 말한다 — 문장은\n * 서버가 만든다(§`work-door.ts`). 화면이 상태를 보고 문장을 다시 지으면 두 벌이 된다.\n *\n * ── 할 일이 있는 것부터 ────────────────────────────────────────────────────\n * 선언 순서로 늘어놓으면 서 있는 것과 빈 것이 섞이고, 읽는 사람이 열한 줄을 훑어야 한다. **정할\n * 것이 남은 것을 위로** 올린다 — 이 화면을 여는 이유가 그것이다.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\nconst DOORS = gql`\n {\n workDoors {\n summary\n todo\n seatsStanding\n seatsOffered\n doors {\n name\n label\n kind\n standing\n approvers\n from\n because\n }\n }\n }\n`\n\ntype Door = {\n name: string\n label: string\n kind: 'approval' | 'assignment'\n standing: 'on' | 'off' | 'missing' | 'not-used'\n approvers: number\n from?: string\n because: string\n}\n\n/**\n * 조치할 것 · 서 있는 것 · 안 쓰기로 한 것 순. 같은 무리 안에서는 선언 순서를 지킨다.\n *\n * 안 쓰기로 한 것을 맨 아래 두는 이유는 **그것이 할 일이 아니어서**다. 위에 섞으면 이 화면을 여는\n * 이유(무엇을 채워야 하나)가 안 보인다.\n */\nconst ORDER: Record<Door['standing'], number> = { missing: 0, off: 1, on: 2, 'not-used': 3 }\n\n@customElement('work-doors-page')\nexport class WorkDoorsPage extends localize(i18next)(PageView) {\n static styles = [\n ProseStyles,\n SurfaceStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 12px;\n overflow: auto;\n }\n\n /*\n * These classes were used here but defined only inside disposition-policy-page's shadow root,\n * so this screen drew them unstyled. Shadow roots do not share rules; each page states its own.\n */\n .sheet {\n max-width: var(--ui-measure, min(1080px, 100%));\n }\n\n .lead {\n margin: 0;\n color: var(--ui-muted, #57676f);\n font-size: var(--ui-t-small, 11.5px);\n max-width: 60ch;\n }\n\n .state {\n margin: 0 0 4px;\n color: var(--ui-ink, #17242c);\n font-weight: 600;\n }\n\n .none {\n margin: 0;\n color: var(--ui-muted, #57676f);\n }\n\n .blocked {\n border-left: 3px solid var(--ui-crit, #c93f30);\n padding: 10px 12px;\n background: var(--ui-line-soft, #eef1f3);\n border-radius: var(--ui-r-small, 6px);\n }\n\n .blocked .why {\n margin-top: 4px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n white-space: pre-wrap;\n }\n\n .rule-actions {\n margin-top: var(--spacing-small, 4px);\n }\n\n .door {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 10px 0;\n border-bottom: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));\n }\n\n .door:last-child {\n border-bottom: none;\n }\n\n .name {\n flex: 0 0 auto;\n font-weight: 600;\n }\n\n /* 상태는 글자로도 말한다 — 색만으로 가르면 색을 못 보는 사람에게 아무 말도 안 한다. */\n .mark {\n flex: 0 0 auto;\n font-size: 0.85em;\n padding: 1px 8px;\n border-radius: 10px;\n border: 1px solid currentColor;\n }\n\n .mark.on {\n color: var(--md-sys-color-primary, #3f6b3f);\n }\n\n .mark.off {\n color: var(--md-sys-color-tertiary, #7a5b12);\n }\n\n .mark.missing {\n color: var(--md-sys-color-error, #a33);\n }\n\n /* 할 일이 아니므로 눈에 덜 띈다. 지우지는 않는다 — 껐다는 사실 자체가 읽혀야 한다. */\n .mark.not-used {\n color: var(--md-sys-color-outline, #777);\n }\n\n .door.not-used {\n opacity: 0.7;\n }\n\n /* 종류는 낱말로 말한다 — 결재와 배정은 비었을 때 결과가 반대다. */\n .kind {\n flex: 0 0 auto;\n font-size: 0.8em;\n opacity: 0.65;\n }\n\n .why {\n flex: 1 1 auto;\n opacity: 0.85;\n }\n\n .from {\n flex: 0 0 auto;\n font-size: 0.85em;\n opacity: 0.7;\n }\n\n ol.next {\n margin: 0;\n padding-left: 20px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n ol.next li {\n line-height: 1.5;\n }\n\n ol.next a {\n margin-left: 4px;\n color: var(--md-sys-color-primary, #3f6cd4);\n }\n `\n ]\n\n @state() private doors?: Door[]\n @state() private summary = ''\n @state() private todo = 0\n @state() private seatsStanding = 0\n @state() private seatsOffered = 0\n @state() private readFailure = ''\n\n get context() {\n return { title: i18next.t('menu.work-doors') }\n }\n\n async pageInitialized() {\n await this.load()\n }\n\n async pageUpdated(changes: any, lifecycle: any) {\n if (lifecycle.active && !this.doors) {\n await this.load()\n }\n }\n\n private async load() {\n this.readFailure = ''\n\n const response = await client.query({ query: DOORS, fetchPolicy: 'no-cache' })\n\n /*\n * 못 읽은 것을 빈 목록으로 그리지 않는다. 「결재 문이 하나도 없습니다」와 「못 읽었습니다」는\n * 사람이 할 일이 다르다 — 앞은 설치를 보러 갈 일이고 뒤는 다시 눌러 볼 일이다.\n */\n if (response.errors?.length) {\n this.readFailure = response.errors[0].message\n return\n }\n\n /*\n * 전선의 철자를 여기서 **한 번** 옮긴다(§`door-words`). 안 옮기면 i18n 키도 CSS 클래스도\n * 어긋나는데 **오류가 하나도 안 난다** — 키 글자가 화면에 뜨고 상태 색이 투명해질 뿐이다.\n */\n this.doors = (response.data?.workDoors?.doors ?? []).map((one: any) => ({\n ...one,\n standing: standingWord(one.standing) ?? one.standing,\n kind: kindWord(one.kind) ?? one.kind\n }))\n this.summary = response.data?.workDoors?.summary ?? ''\n this.todo = response.data?.workDoors?.todo ?? 0\n this.seatsStanding = response.data?.workDoors?.seatsStanding ?? 0\n this.seatsOffered = response.data?.workDoors?.seatsOffered ?? 0\n }\n\n /**\n * 다음에 할 일 — **순서대로.**\n *\n * ── 문제만 말하는 화면이었다 ──────────────────────────────────────────\n * 열두 줄이 「비었습니다」라고 적혀 있고, 어디로 가야 채우는지는 어디에도 없었다. 이 저장소에서\n * 가장 자주 나는 결함을 내 화면이 하고 있었다 — **자리는 있고 길이 없다.**\n *\n * ── 순서를 지키는 이유 ────────────────────────────────────────────────\n * 결재선은 역할을 가리킨다. 역할이 하나도 없는 공장에서 양식 화면으로 가면 **결재선 칸이 빈\n * 목록**이고, 사람은 헛걸음한 채 무엇이 잘못됐는지 모르고 돌아온다. 그래서 자리가 안 섰으면\n * 자리부터 말한다.\n *\n * 여기서 고치게 하지 않는다. 역할도 업무 양식도 **남의 화면이 임자**이고, 같은 일을 하는 자리를\n * 둘 만들면 한쪽이 낡는다. 이 화면이 하는 일은 **가리키는 것**이다.\n */\n private renderNext() {\n if (!this.todo) {\n return ''\n }\n\n const seatsShort = this.seatsStanding < this.seatsOffered\n const toSet = doorsToSetHere(this.doors || [])\n\n /* 남은 것이 전부 남의 양식이거나 배정 양식이면 여기서 가리킬 곳이 없다 — 빈 칸을 세우지 않는다. */\n if (!seatsShort && !toSet.length) {\n return ''\n }\n\n return html`\n <section class=\"sheet ui-card\">\n <span class=\"ui-label\">${i18next.t('title.work-doors-next')}</span>\n <ol class=\"next\">\n ${seatsShort\n ? html`<li>\n ${i18next.t('text.work-doors-next-seats', {\n standing: this.seatsStanding,\n offered: this.seatsOffered\n })}\n <a href=\"roles\">${i18next.t('field.roles')}</a>\n <!-- field.roles 는 auth-ui 설치본의 키다. 내가 menu.roles 를 썼다가 고쳤다 —\n 그 키는 어디에도 없어서 화면에 키 글자가 그대로 떴을 것이다. -->\n </li>`\n : ''}\n ${toSet.length\n ? html`<li>\n ${i18next.t('text.work-doors-next-line', { count: toSet.length })}\n ${toSet.map(\n (door, index) =>\n html`${index ? ' · ' : ''}<a href=${detailAddress(door.name)}>${i18next.t(door.label)}</a>`\n )}\n </li>`\n : ''}\n </ol>\n </section>\n `\n }\n\n render() {\n if (this.readFailure) {\n return html`<div class=\"sheet blocked\">\n ${i18next.t('text.work-doors-read-failed')}\n <div class=\"why\">${this.readFailure}</div>\n <div class=\"rule-actions\">\n <md-text-button @click=${() => this.load()}>${i18next.t('button.try-again')}</md-text-button>\n </div>\n </div>`\n }\n\n if (!this.doors) {\n return html`<div class=\"none\">${i18next.t('text.loading')}</div>`\n }\n\n const sorted = [...this.doors].sort((a, b) => ORDER[a.standing] - ORDER[b.standing])\n\n return html`\n <div class=\"sheet\">\n <p class=\"lead\">${i18next.t('text.work-doors-lead')}</p>\n </div>\n\n <section class=\"sheet ui-card\">\n <span class=\"ui-label\">${i18next.t('title.work-doors')}</span>\n <p class=\"state\">${this.summary}</p>\n ${sorted.map(door => this.renderDoor(door))}\n </section>\n\n ${this.renderNext()}\n `\n }\n\n private renderDoor(door: Door) {\n return html`\n <div class=\"door ${door.standing}\">\n ${opensDetail(door)\n ? html`<a class=\"name\" href=${detailAddress(door.name)}>${i18next.t(door.label)}</a>`\n : html`<span class=\"name\">${i18next.t(door.label)}</span>`}\n <span class=\"kind\">${i18next.t(`text.door-kind-${door.kind}`)}</span>\n <span class=\"mark ${door.standing}\">${i18next.t(`text.door-standing-${door.standing}`)}</span>\n <span class=\"why\">${door.because}</span>\n ${\n /*\n * 자기 것이 아닌 양식을 쓰는 것은 정상이지만, 안 말하면 「정한 적 없는데」가 된다.\n *\n * ⚠ **차 있는 것과 비어 있는 것은 다른 말이다.** 「본사에서 정했습니다」를 결재자가 없는\n * 줄에 달면 한 줄이 스스로 모순된다 — 정한 것이 없는데 정했다고 적힌다. 비어 있으면\n * 말할 수 있는 것은 **양식의 임자**뿐이고, 그 사실은 「여기서는 못 고칩니다」를 뜻해서\n * 오히려 더 중요하다.\n */\n door.from\n ? html`<span class=\"from\"\n >${door.standing === 'on'\n ? i18next.t('text.door-filled-from', { name: door.from })\n : i18next.t('text.door-form-from', { name: door.from })}</span\n >`\n : ''\n }\n </div>\n `\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"work-doors-page.js","sourceRoot":"","sources":["../../../client/pages/work-door/work-doors-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/text-button.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { PageView } from '@operato/shell'\nimport { ProseStyles, SurfaceStyles } from '@things-factory/styles'\n\nimport { detailAddress, doorsToSetHere, opensDetail } from './door-links.js'\nimport { DoorCounts, DoorFacts, doorLabel, doorSentence, doorsSummary, whenNoLineTag } from './door-sentence.js'\nimport { kindWord, reasonWord, standingWord, whenNoLineBecauseWord, whenNoLineWord } from './door-words.js'\n\n/**\n * 결재 문 현황 — **이 공장에서 어느 승인 단계가 실제로 서 있나.**\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── 왜 이 화면이 있나 ──────────────────────────────────────────────────────\n * 양식은 제품이 세우고 **결재선은 그 공장이 채운다.** 채운 것이 「우리는 이 단계를 쓴다」는 선언이라,\n * 비어 있는 것은 결함이 아니라 선택이다. 그런데 **그 선택이 어디에도 안 보였다.** 업무 카탈로그는\n * 「제품이 이 문을 만들었나」를 보여 주고, 이 물음에는 답하지 않는다.\n *\n * 실측(2026-09-11): 미라텍에 양식 11개가 다 서 있고 **11개 전부 결재자가 없었다.** 그 공장에서는\n * 실적 정정도 폐기도 승인 없이 반영되는데, 화면에는 문이 있는 것처럼 보였다.\n *\n * ── 비어 있는 것을 「승인 없음」으로 그리지 않는다 ─────────────────────────\n * 빈 결재선의 결과는 **문마다 선언돼 있다**(ADR-0077). `run` 문은 올린 사람이 자기 건을 승인한 것이\n * 되고, `refuse` 문은 올리는 것 자체가 거절된다. 서버는 그 사실을 값으로 보내고 **문장은 이 화면이\n * 번역으로 짓는다**(§`door-sentence.ts`) — 서버가 한국어로 지어 보내던 때는 다른 언어 사용자도 한국어를\n * 읽었고, 거절하는 문에도 「자기 건을 승인한다」가 적혔다.\n *\n * ── 할 일이 있는 것부터 ────────────────────────────────────────────────────\n * 선언 순서로 늘어놓으면 서 있는 것과 빈 것이 섞이고, 읽는 사람이 열한 줄을 훑어야 한다. **정할\n * 것이 남은 것을 위로** 올린다 — 이 화면을 여는 이유가 그것이다.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\nconst DOORS = gql`\n {\n workDoors {\n todo\n on\n off\n missing\n notUsed\n seatsStanding\n seatsOffered\n doors {\n name\n labelKey\n kind\n standing\n reason\n approvers\n from\n whenNoLine\n whenNoLineBecause\n followsLabelKey\n assigneeRole\n notUsedBecause\n }\n }\n }\n`\n\ntype Door = DoorFacts & {\n kind: 'approval' | 'assignment'\n standing: 'on' | 'off' | 'missing' | 'not-used'\n from?: string\n}\n\n/**\n * 조치할 것 · 서 있는 것 · 안 쓰기로 한 것 순. 같은 무리 안에서는 선언 순서를 지킨다.\n *\n * 안 쓰기로 한 것을 맨 아래 두는 이유는 **그것이 할 일이 아니어서**다. 위에 섞으면 이 화면을 여는\n * 이유(무엇을 채워야 하나)가 안 보인다.\n */\nconst ORDER: Record<Door['standing'], number> = { missing: 0, off: 1, on: 2, 'not-used': 3 }\n\n@customElement('work-doors-page')\nexport class WorkDoorsPage extends localize(i18next)(PageView) {\n static styles = [\n ProseStyles,\n SurfaceStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 12px;\n overflow: auto;\n }\n\n /*\n * These classes were used here but defined only inside disposition-policy-page's shadow root,\n * so this screen drew them unstyled. Shadow roots do not share rules; each page states its own.\n */\n .sheet {\n max-width: var(--ui-measure, min(1080px, 100%));\n }\n\n .lead {\n margin: 0;\n color: var(--ui-muted, #57676f);\n font-size: var(--ui-t-small, 11.5px);\n max-width: 60ch;\n }\n\n .state {\n margin: 0 0 4px;\n color: var(--ui-ink, #17242c);\n font-weight: 600;\n }\n\n .none {\n margin: 0;\n color: var(--ui-muted, #57676f);\n }\n\n .blocked {\n border-left: 3px solid var(--ui-crit, #c93f30);\n padding: 10px 12px;\n background: var(--ui-line-soft, #eef1f3);\n border-radius: var(--ui-r-small, 6px);\n }\n\n .blocked .why {\n margin-top: 4px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n white-space: pre-wrap;\n }\n\n .rule-actions {\n margin-top: var(--spacing-small, 4px);\n }\n\n .door {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 10px 0;\n border-bottom: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.12));\n }\n\n .door:last-child {\n border-bottom: none;\n }\n\n .name {\n flex: 0 0 auto;\n font-weight: 600;\n }\n\n /* 상태는 글자로도 말한다 — 색만으로 가르면 색을 못 보는 사람에게 아무 말도 안 한다. */\n .mark {\n flex: 0 0 auto;\n font-size: 0.85em;\n padding: 1px 8px;\n border-radius: 10px;\n border: 1px solid currentColor;\n }\n\n .mark.on {\n color: var(--md-sys-color-primary, #3f6b3f);\n }\n\n .mark.off {\n color: var(--md-sys-color-tertiary, #7a5b12);\n }\n\n .mark.missing {\n color: var(--md-sys-color-error, #a33);\n }\n\n /* 할 일이 아니므로 눈에 덜 띈다. 지우지는 않는다 — 껐다는 사실 자체가 읽혀야 한다. */\n .mark.not-used {\n color: var(--md-sys-color-outline, #777);\n }\n\n .door.not-used {\n opacity: 0.7;\n }\n\n /* 종류는 낱말로 말한다 — 결재와 배정은 비었을 때 결과가 반대다. */\n .kind {\n flex: 0 0 auto;\n font-size: 0.8em;\n opacity: 0.65;\n }\n\n .why {\n flex: 1 1 auto;\n opacity: 0.85;\n }\n\n /* 빈 결재선에서 이 문이 어떻게 하나 — 선언이다(ADR-0077). 문장 아래 한 줄로. */\n .when {\n display: block;\n margin-top: 2px;\n font-size: 0.85em;\n opacity: 0.75;\n }\n\n .from {\n flex: 0 0 auto;\n font-size: 0.85em;\n opacity: 0.7;\n }\n\n ol.next {\n margin: 0;\n padding-left: 20px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n ol.next li {\n line-height: 1.5;\n }\n\n ol.next a {\n margin-left: 4px;\n color: var(--md-sys-color-primary, #3f6cd4);\n }\n `\n ]\n\n @state() private doors?: Door[]\n @state() private counts: DoorCounts = { on: 0, off: 0, missing: 0, notUsed: 0, todo: 0 }\n @state() private todo = 0\n @state() private seatsStanding = 0\n @state() private seatsOffered = 0\n @state() private readFailure = ''\n\n get context() {\n return { title: i18next.t('menu.work-doors') }\n }\n\n async pageInitialized() {\n await this.load()\n }\n\n async pageUpdated(changes: any, lifecycle: any) {\n if (lifecycle.active && !this.doors) {\n await this.load()\n }\n }\n\n private async load() {\n this.readFailure = ''\n\n const response = await client.query({ query: DOORS, fetchPolicy: 'no-cache' })\n\n /*\n * 못 읽은 것을 빈 목록으로 그리지 않는다. 「결재 문이 하나도 없습니다」와 「못 읽었습니다」는\n * 사람이 할 일이 다르다 — 앞은 설치를 보러 갈 일이고 뒤는 다시 눌러 볼 일이다.\n */\n if (response.errors?.length) {\n this.readFailure = response.errors[0].message\n return\n }\n\n /*\n * 전선의 철자를 여기서 **한 번** 옮긴다(§`door-words`). 안 옮기면 i18n 키도 CSS 클래스도\n * 어긋나는데 **오류가 하나도 안 난다** — 키 글자가 화면에 뜨고 상태 색이 투명해질 뿐이다.\n */\n this.doors = (response.data?.workDoors?.doors ?? []).map((one: any) => ({\n ...one,\n standing: standingWord(one.standing) ?? one.standing,\n kind: kindWord(one.kind) ?? one.kind,\n reason: reasonWord(one.reason) ?? one.reason,\n whenNoLine: whenNoLineWord(one.whenNoLine),\n whenNoLineBecause: whenNoLineBecauseWord(one.whenNoLineBecause)\n }))\n\n const read = response.data?.workDoors ?? {}\n\n this.todo = read.todo ?? 0\n this.counts = {\n on: read.on ?? 0,\n off: read.off ?? 0,\n missing: read.missing ?? 0,\n notUsed: read.notUsed ?? 0,\n todo: this.todo\n }\n this.seatsStanding = response.data?.workDoors?.seatsStanding ?? 0\n this.seatsOffered = response.data?.workDoors?.seatsOffered ?? 0\n }\n\n /**\n * 다음에 할 일 — **순서대로.**\n *\n * ── 문제만 말하는 화면이었다 ──────────────────────────────────────────\n * 열두 줄이 「비었습니다」라고 적혀 있고, 어디로 가야 채우는지는 어디에도 없었다. 이 저장소에서\n * 가장 자주 나는 결함을 내 화면이 하고 있었다 — **자리는 있고 길이 없다.**\n *\n * ── 순서를 지키는 이유 ────────────────────────────────────────────────\n * 결재선은 역할을 가리킨다. 역할이 하나도 없는 공장에서 양식 화면으로 가면 **결재선 칸이 빈\n * 목록**이고, 사람은 헛걸음한 채 무엇이 잘못됐는지 모르고 돌아온다. 그래서 자리가 안 섰으면\n * 자리부터 말한다.\n *\n * 여기서 고치게 하지 않는다. 역할도 업무 양식도 **남의 화면이 임자**이고, 같은 일을 하는 자리를\n * 둘 만들면 한쪽이 낡는다. 이 화면이 하는 일은 **가리키는 것**이다.\n */\n private renderNext() {\n if (!this.todo) {\n return ''\n }\n\n const seatsShort = this.seatsStanding < this.seatsOffered\n const toSet = doorsToSetHere(this.doors || [])\n\n /* 남은 것이 전부 남의 양식이거나 배정 양식이면 여기서 가리킬 곳이 없다 — 빈 칸을 세우지 않는다. */\n if (!seatsShort && !toSet.length) {\n return ''\n }\n\n return html`\n <section class=\"sheet ui-card\">\n <span class=\"ui-label\">${i18next.t('title.work-doors-next')}</span>\n <ol class=\"next\">\n ${seatsShort\n ? html`<li>\n ${i18next.t('text.work-doors-next-seats', {\n standing: this.seatsStanding,\n offered: this.seatsOffered\n })}\n <a href=\"roles\">${i18next.t('field.roles')}</a>\n <!-- field.roles 는 auth-ui 설치본의 키다. 내가 menu.roles 를 썼다가 고쳤다 —\n 그 키는 어디에도 없어서 화면에 키 글자가 그대로 떴을 것이다. -->\n </li>`\n : ''}\n ${toSet.length\n ? html`<li>\n ${i18next.t('text.work-doors-next-line', { count: toSet.length })}\n ${toSet.map(\n (door, index) =>\n html`${index ? ' · ' : ''}<a href=${detailAddress(door.name)}>${doorLabel(i18next.t, door)}</a>`\n )}\n </li>`\n : ''}\n </ol>\n </section>\n `\n }\n\n render() {\n if (this.readFailure) {\n return html`<div class=\"sheet blocked\">\n ${i18next.t('text.work-doors-read-failed')}\n <div class=\"why\">${this.readFailure}</div>\n <div class=\"rule-actions\">\n <md-text-button @click=${() => this.load()}>${i18next.t('button.try-again')}</md-text-button>\n </div>\n </div>`\n }\n\n if (!this.doors) {\n return html`<div class=\"none\">${i18next.t('text.loading')}</div>`\n }\n\n const sorted = [...this.doors].sort((a, b) => ORDER[a.standing] - ORDER[b.standing])\n\n return html`\n <div class=\"sheet\">\n <p class=\"lead\">${i18next.t('text.work-doors-lead')}</p>\n </div>\n\n <section class=\"sheet ui-card\">\n <span class=\"ui-label\">${i18next.t('title.work-doors')}</span>\n <p class=\"state\">${doorsSummary(i18next.t, this.counts)}</p>\n ${sorted.map(door => this.renderDoor(door))}\n </section>\n\n ${this.renderNext()}\n `\n }\n\n private renderDoor(door: Door) {\n const label = doorLabel(i18next.t, door)\n const when = whenNoLineTag(i18next.t, door)\n\n return html`\n <div class=\"door ${door.standing}\">\n ${opensDetail(door)\n ? html`<a class=\"name\" href=${detailAddress(door.name)}>${label}</a>`\n : html`<span class=\"name\">${label}</span>`}\n <span class=\"kind\">${i18next.t(`text.door-kind-${door.kind}`)}</span>\n <span class=\"mark ${door.standing}\">${i18next.t(`text.door-standing-${door.standing}`)}</span>\n <span class=\"why\"\n >${doorSentence(i18next.t, door)}${when ? html`<span class=\"when\">${when}</span>` : ''}</span\n >\n ${\n /*\n * 자기 것이 아닌 양식을 쓰는 것은 정상이지만, 안 말하면 「정한 적 없는데」가 된다.\n *\n * ⚠ **차 있는 것과 비어 있는 것은 다른 말이다.** 「본사에서 정했습니다」를 결재자가 없는\n * 줄에 달면 한 줄이 스스로 모순된다 — 정한 것이 없는데 정했다고 적힌다. 비어 있으면\n * 말할 수 있는 것은 **양식의 임자**뿐이고, 그 사실은 「여기서는 못 고칩니다」를 뜻해서\n * 오히려 더 중요하다.\n */\n door.from\n ? html`<span class=\"from\"\n >${door.standing === 'on'\n ? i18next.t('text.door-filled-from', { name: door.from })\n : i18next.t('text.door-form-from', { name: door.from })}</span\n >`\n : ''\n }\n </div>\n `\n }\n}\n"]}
|
package/dist-client/route.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function route(page: string): "
|
|
1
|
+
export default function route(page: string): "activity" | "draft-list" | "todo-list" | "done-list" | "approval-pending-list" | "done-list-calendar" | "activity-catalog" | "activity-detail" | "work-doors-page" | "approval-done-list" | "pickable-list" | "starter-list" | "reporter-list" | "activity-list" | "activity-instance-start" | "activity-thread" | "activity-thread-view" | "activity-approval" | "activity-instance-list" | "activity-instance-search" | "activity-template-list" | "activity-store" | "activity-thread-list" | "activity-approval-list" | "installable-activity-list" | "activity-stats-list" | undefined;
|