@things-factory/worklist 10.1.7 → 10.1.13

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.
@@ -17,12 +17,12 @@ export declare class ActivityThreadTimeline extends LitElement {
17
17
  * 보일 항목 없음 화면이 「타임라인의 상신 사유를 보고 판단하세요」로 여기를 가리킨다
18
18
  */
19
19
  open?: boolean;
20
- render(): typeof nothing | import("lit-html").TemplateResult<1>;
20
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
21
21
  /** 접혔을 때 보이는 한 줄 — 마지막 기록의 「무엇을 · 누가 · 언제」. */
22
22
  lastLine(item: any): string;
23
23
  renderActivityThreadHistory(activityThreadHistory: ActivityThreadHistory): import("lit-html").TemplateResult<1>;
24
24
  renderActivityApproval(activityApproval: ActivityApproval): import("lit-html").TemplateResult<1>;
25
25
  /** 적힌 말이 없으면 상자를 그리지 않는다 — 빈 회색 상자는 내용이 있는 줄과 구별되지 않는다. */
26
- renderSaid(said?: string): typeof nothing | import("lit-html").TemplateResult<1>;
26
+ renderSaid(said?: string): import("lit-html").TemplateResult<1> | typeof nothing;
27
27
  renderOutput(output: any): string;
28
28
  }
@@ -61,7 +61,7 @@ export declare class ActivityApprovalPage extends ActivityApprovalPage_base {
61
61
  action: () => void;
62
62
  }[];
63
63
  };
64
- render(): typeof nothing | TemplateResult<1>;
64
+ render(): TemplateResult<1> | typeof nothing;
65
65
  /** 화면에 낼 항목이 하나라도 있나. 타임라인을 펴 둘지 정하는 자리가 쓴다. */
66
66
  hasFields(): boolean;
67
67
  activityContent(): DirectiveResult<import("lit-html/directive.js").DirectiveClass>;
@@ -45,7 +45,7 @@ export declare class ActivityThreadPage extends ActivityThreadPage_base {
45
45
  action: () => void;
46
46
  })[];
47
47
  };
48
- render(): typeof nothing | import("lit-html").TemplateResult<1>;
48
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
49
49
  /** 화면에 낼 항목이 하나라도 있나. 타임라인을 펴 둘지 정하는 자리가 쓴다. */
50
50
  hasFields(): boolean;
51
51
  activityContent(): DirectiveResult<import("lit-html/directive.js").DirectiveClass>;
@@ -42,7 +42,7 @@ export declare class ActivityThreadViewPage extends ActivityThreadViewPage_base
42
42
  action: () => void;
43
43
  }[];
44
44
  };
45
- render(): typeof nothing | import("lit-html").TemplateResult<1>;
45
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
46
46
  activityContent(): DirectiveResult<import("lit-html/directive.js").DirectiveClass>;
47
47
  /**
48
48
  * 화면 종류가 달라도 같은 자리에 같은 모양으로 놓인다(activity-approval-page 의 같은 이름 참조).
@@ -26,6 +26,6 @@ declare const ActivityThreadView_base: (new (...args: any[]) => LitElement) & ty
26
26
  export declare class ActivityThreadView extends ActivityThreadView_base {
27
27
  static styles: import("lit").CSSResult[];
28
28
  activityThread?: ActivityThread;
29
- render(): typeof nothing | import("lit-html").TemplateResult<1>;
29
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
30
30
  }
31
31
  export {};
@@ -16,23 +16,23 @@ export declare class ApprovalPendingListPage extends ApprovalPendingListPage_bas
16
16
  /**
17
17
  * 지금 어느 묶음을 보고 있나.
18
18
  *
19
- * ── 왜 한 화면에 두 묶음인가 ─────────────────────────────────────────────
19
+ * ── 왜 한 화면에 세 묶음인가 ─────────────────────────────────────────────
20
20
  * 참조를 별도 화면으로 만들면 **아무도 열지 않는다.** 참조는 안 봐도 아무 일이 안 일어나므로,
21
21
  * 아침에 업무 앱을 열면 「승인 대기」를 보지 「참조」를 따로 열지 않는다. 참조를 만든 이유가 의견을
22
22
  * 듣는 것인데 안 여는 화면에 넣으면 의견이 안 온다.
23
23
  *
24
- * 그래서 한 화면에 두고, **두 수를 함께 보인다.** 한 곳만 열면 둘 다 눈에 든다.
24
+ * 그래서 한 화면에 두고, 완료 이력도 같은 자리에서 다시 찾을 수 있게 한다.
25
25
  *
26
26
  * ── 그렇다고 한 목록에 섞지 않는다 ────────────────────────────────────────
27
- * 두 묶음의 물음이 다르다 — 「내가 결정할 것」과 「내가 알아야 할 것」. 섞으면 참조가 「내가 승인해야
28
- * 하나」로 읽히고, 배지에 12가 뜨는데 결정할 것은 3건이 된다. 그러면 그 수를 안 믿게 된다.
27
+ * 세 묶음의 물음이 다르다 — 「내가 결정할 것」, 「내가 알아야 할 것」, 「끝난 참조 이력」. 섞으면
28
+ * 참조가 「내가 승인해야 하나」로 읽히고, 배지에 12가 뜨는데 결정할 것은 3건이 된다.
29
29
  *
30
30
  * ── 주소가 주인이다 ──────────────────────────────────────────────────────
31
31
  * `.../approval-pending-list/reference` 로 붙여 보낼 수 있다. 대상이 바뀌는 것이므로 필터와 달리
32
32
  * 주소에 담는다.
33
33
  */
34
34
  private group;
35
- /** 두 묶음의 건수. 탭에 함께 적는다 — 안 눌러도 얼마나 있는지 보여야 한다. */
35
+ /** 세 묶음의 건수. 탭에 함께 적는다 — 안 눌러도 얼마나 있는지 보여야 한다. */
36
36
  private counts;
37
37
  mode: 'GRID' | 'LIST' | 'CARD';
38
38
  grist: DataGrist;
@@ -57,7 +57,7 @@ export declare class ApprovalPendingListPage extends ApprovalPendingListPage_bas
57
57
  /**
58
58
  * 안 보고 있는 묶음의 건수를 따로 센다 — 한 줄만 받아 `total` 을 읽는다.
59
59
  *
60
- * 보고 있는 쪽의 수는 목록 조회가 이미 알려 준다(fetchHandler). 그래서 여기서는 반대쪽만 묻는다.
60
+ * 보고 있는 쪽의 수는 목록 조회가 이미 알려 준다(fetchHandler). 그래서 여기서는 나머지 둘만 묻는다.
61
61
  */
62
62
  private countOther;
63
63
  pageInitialized(lifecycle: any): Promise<void>;
@@ -24,23 +24,23 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
24
24
  /**
25
25
  * 지금 어느 묶음을 보고 있나.
26
26
  *
27
- * ── 왜 한 화면에 두 묶음인가 ─────────────────────────────────────────────
27
+ * ── 왜 한 화면에 세 묶음인가 ─────────────────────────────────────────────
28
28
  * 참조를 별도 화면으로 만들면 **아무도 열지 않는다.** 참조는 안 봐도 아무 일이 안 일어나므로,
29
29
  * 아침에 업무 앱을 열면 「승인 대기」를 보지 「참조」를 따로 열지 않는다. 참조를 만든 이유가 의견을
30
30
  * 듣는 것인데 안 여는 화면에 넣으면 의견이 안 온다.
31
31
  *
32
- * 그래서 한 화면에 두고, **두 수를 함께 보인다.** 한 곳만 열면 둘 다 눈에 든다.
32
+ * 그래서 한 화면에 두고, 완료 이력도 같은 자리에서 다시 찾을 수 있게 한다.
33
33
  *
34
34
  * ── 그렇다고 한 목록에 섞지 않는다 ────────────────────────────────────────
35
- * 두 묶음의 물음이 다르다 — 「내가 결정할 것」과 「내가 알아야 할 것」. 섞으면 참조가 「내가 승인해야
36
- * 하나」로 읽히고, 배지에 12가 뜨는데 결정할 것은 3건이 된다. 그러면 그 수를 안 믿게 된다.
35
+ * 세 묶음의 물음이 다르다 — 「내가 결정할 것」, 「내가 알아야 할 것」, 「끝난 참조 이력」. 섞으면
36
+ * 참조가 「내가 승인해야 하나」로 읽히고, 배지에 12가 뜨는데 결정할 것은 3건이 된다.
37
37
  *
38
38
  * ── 주소가 주인이다 ──────────────────────────────────────────────────────
39
39
  * `.../approval-pending-list/reference` 로 붙여 보낼 수 있다. 대상이 바뀌는 것이므로 필터와 달리
40
40
  * 주소에 담는다.
41
41
  */
42
42
  this.group = 'approval';
43
- /** 두 묶음의 건수. 탭에 함께 적는다 — 안 눌러도 얼마나 있는지 보여야 한다. */
43
+ /** 세 묶음의 건수. 탭에 함께 적는다 — 안 눌러도 얼마나 있는지 보여야 한다. */
44
44
  this.counts = {};
45
45
  this.mode = isMobileDevice() ? 'CARD' : 'GRID';
46
46
  }
@@ -105,7 +105,7 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
105
105
  }
106
106
 
107
107
  /*
108
- * 두 묶음 — 표 위. 지금 보는 쪽은 밑줄과 굵기로 말하고, 옆에 건수를 적는다.
108
+ * 세 묶음 — 표 위. 지금 보는 쪽은 밑줄과 굵기로 말하고, 옆에 건수를 적는다.
109
109
  *
110
110
  * 건수를 안 눌러도 보이게 두는 것이 요점이다. 참조에 몇 건이 있는지 모르면 열어 볼 이유가 없다.
111
111
  *
@@ -213,7 +213,7 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
213
213
  const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID');
214
214
  return html `
215
215
  <!--
216
- 두 묶음 — 표 **바깥** 위에 둔다.
216
+ 세 묶음 — 표 **바깥** 위에 둔다.
217
217
 
218
218
  헤드룸에 두면 필터를 접을 때 함께 사라지고, 그러면 참조로 가는 유일한 길이 없어진다.
219
219
  필터는 접혀도 되지만(풀면 전체가 돌아온다) 이것은 접히면 안 된다.
@@ -221,7 +221,8 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
221
221
  <div class="groups">
222
222
  ${[
223
223
  ['approval', 'label.group-approval', 'text.group-approval-hint'],
224
- ['reference', 'label.group-reference', 'text.group-reference-hint']
224
+ ['reference', 'label.group-reference', 'text.group-reference-hint'],
225
+ ['completed', 'label.group-completed', 'text.group-completed-hint']
225
226
  ].map(([key, label, hint]) => html `
226
227
  <button
227
228
  ?data-selected=${this.group === key}
@@ -229,9 +230,9 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
229
230
  @click=${() => this.selectGroup(key)}
230
231
  >
231
232
  ${i18next.t(label)}
232
- ${this.counts[key] === undefined
233
- ? ''
234
- : html `<span class="n">${this.counts[key].toLocaleString(i18next.language || undefined)}</span>`}
233
+ ${key !== 'completed' && this.counts[key] !== undefined
234
+ ? html `<span class="n">${this.counts[key].toLocaleString(i18next.language || undefined)}</span>`
235
+ : ''}
235
236
  </button>
236
237
  `)}
237
238
  </div>
@@ -279,7 +280,7 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
279
280
  navigate(group === 'approval' ? `${page}` : `${page}/${group}`);
280
281
  }
281
282
  async syncGroup(key) {
282
- const group = key === 'reference' ? 'reference' : 'approval';
283
+ const group = key === 'reference' || key === 'completed' ? key : 'approval';
283
284
  if (this.group === group && this.gristConfig) {
284
285
  return;
285
286
  }
@@ -289,22 +290,28 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
289
290
  /**
290
291
  * 안 보고 있는 묶음의 건수를 따로 센다 — 한 줄만 받아 `total` 을 읽는다.
291
292
  *
292
- * 보고 있는 쪽의 수는 목록 조회가 이미 알려 준다(fetchHandler). 그래서 여기서는 반대쪽만 묻는다.
293
+ * 보고 있는 쪽의 수는 목록 조회가 이미 알려 준다(fetchHandler). 그래서 여기서는 나머지 둘만 묻는다.
293
294
  */
294
295
  async countOther() {
295
- const other = this.group === 'approval' ? 'reference' : 'approval';
296
- const query = other === 'approval' ? 'approvalPendingList' : 'approvalReferenceList';
297
- const response = await client.query({
298
- query: gql `
299
- query ($pagination: Pagination) {
300
- responses: ${query}(pagination: $pagination) {
301
- total
302
- }
303
- }
304
- `,
305
- variables: { pagination: { page: 1, limit: 1 } },
306
- fetchPolicy: 'no-cache'
307
- });
296
+ const queries = {
297
+ approval: 'approvalPendingList',
298
+ reference: 'approvalReferenceList'
299
+ };
300
+ const others = Object.keys(queries).filter(group => group !== this.group);
301
+ const results = await Promise.all(others.map(async (group) => ({
302
+ group,
303
+ response: await client.query({
304
+ query: gql `
305
+ query ($pagination: Pagination) {
306
+ responses: ${queries[group]}(pagination: $pagination) {
307
+ total
308
+ }
309
+ }
310
+ `,
311
+ variables: { pagination: { page: 1, limit: 1 } },
312
+ fetchPolicy: 'no-cache'
313
+ })
314
+ })));
308
315
  /*
309
316
  * A count we could not fetch is not zero.
310
317
  *
@@ -313,11 +320,13 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
313
320
  * 「0」, which says "nothing is waiting on you". The render already draws nothing when the count
314
321
  * is undefined, so leaving it unknown is both honest and already handled.
315
322
  */
316
- const total = response.errors?.length ? undefined : response.data?.responses?.total;
317
- if (typeof total != 'number') {
318
- return;
323
+ const counts = { ...this.counts };
324
+ for (const { group, response } of results) {
325
+ const total = response.errors?.length ? undefined : response.data?.responses?.total;
326
+ if (typeof total == 'number')
327
+ counts[group] = total;
319
328
  }
320
- this.counts = { ...this.counts, [other]: total };
329
+ this.counts = counts;
321
330
  }
322
331
  async pageInitialized(lifecycle) {
323
332
  /*
@@ -347,11 +356,17 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
347
356
  title: i18next.t('text.no-reference-title'),
348
357
  description: i18next.t('text.no-reference-description')
349
358
  }
350
- : {
351
- icon: 'task_alt',
352
- title: i18next.t('text.no-approval-title'),
353
- description: i18next.t('text.no-approval-description')
354
- },
359
+ : this.group === 'completed'
360
+ ? {
361
+ icon: 'task_alt',
362
+ title: i18next.t('text.no-completed-approval-title'),
363
+ description: i18next.t('text.no-completed-approval-description')
364
+ }
365
+ : {
366
+ icon: 'task_alt',
367
+ title: i18next.t('text.no-approval-title'),
368
+ description: i18next.t('text.no-approval-description')
369
+ },
355
370
  /*
356
371
  * ── 승인자가 먼저 묻는 순서대로 둔다 (2026-09-01) ─────────────────────
357
372
  *
@@ -461,7 +476,8 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
461
476
  editable: false,
462
477
  options: [
463
478
  { display: i18next.t('label.activity-state-submitted'), value: 'submitted' },
464
- { display: i18next.t('label.activity-state-escalated'), value: 'escalated' }
479
+ { display: i18next.t('label.activity-state-escalated'), value: 'escalated' },
480
+ { display: i18next.t('label.activity-state-ended'), value: 'ended' }
465
481
  ]
466
482
  },
467
483
  width: 90
@@ -547,23 +563,9 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
547
563
  if (this.group === 'reference') {
548
564
  /*
549
565
  * ── 내 참조가 어떤 상태인가 ─────────────────────────────────────────
550
- * 참조 묶음에는 **끝난 건도 나온다.** 참조는 결재를 막지 않으므로 결재가 끝난 뒤에 의견이 달릴
551
- * 수 있고, 그래서 목록에서 끝난 것을 걸러내지 않는다. 화면이 그것을 구별해야 한다 — 안 그러면
552
- * 이미 끝난 건을 열어 의견을 쓰고 그것이 반영된 줄로 안다.
553
- *
554
- * **의견 없이 끝난 것을 빈 칸으로 두지 않는다.** 침묵은 동의가 아니다. 품질팀장이 아무 말 안
555
- * 하고 끝났으면 「반대하지 않았다」가 아니라 「말할 기회에 말하지 않았다」다. 나중에 사고 조사에서
556
- * 그 둘은 다르다.
557
- *
558
- * ── 「결정 후 의견」은 commentedAt 으로 판정한다 ────────────────────────
559
- * 처음에 `updatedAt > terminatedAt` 으로 추론하려 했다. **그 부등호는 언제나 참이다** —
560
- * 닫는 코드가 `terminatedAt: new Date()` 를 저장 **전에** 만들고, `updatedAt` 은
561
- * `@UpdateDateColumn` 이 DB 에 쓰는 순간 찍는다. 두 값이 몇 밀리초 어긋나서, 닫힌 참조가
562
- * 전부 「결정 후 의견」으로 보였을 것이다.
566
+ * 이 묶음은 진행 중인 참조만 보인다. 끝난 건은 별도 「완료」 탭에서 결재 이력과 함께 확인한다.
563
567
  *
564
- * 그래서 사실에 집을 줬다 — `ActivityApproval.commentedAt`(2eaceace25). 의견을 언제 말했나는
565
- * 파생으로 알 수 없는 값이다. **빈 의견에는 시각을 찍지 않는다**(서버 `stampedComment`), 그래서
566
- * 시각의 유무가 곧 침묵과 발언의 구별이다.
568
+ * 닫힘 표시는, 이미 읽어 둔 목록 응답이 새로고침 직전에 도착한 경우를 위한 방어 표시다.
567
569
  */
568
570
  this.gristConfig.columns = [
569
571
  ...this.gristConfig.columns,
@@ -603,6 +605,24 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
603
605
  }
604
606
  ];
605
607
  }
608
+ if (this.group === 'completed') {
609
+ this.gristConfig.columns = [
610
+ ...this.gristConfig.columns,
611
+ {
612
+ type: 'select',
613
+ name: 'participation',
614
+ header: i18next.t('field.wl-participation'),
615
+ record: {
616
+ editable: false,
617
+ options: [
618
+ { display: i18next.t('label.participation-approval'), value: 'approval' },
619
+ { display: i18next.t('label.participation-reference'), value: 'reference' }
620
+ ]
621
+ },
622
+ width: 80
623
+ }
624
+ ];
625
+ }
606
626
  }
607
627
  async pageUpdated(changes, lifecycle, changedBefore) {
608
628
  if (lifecycle?.resourceId !== changedBefore?.resourceId) {
@@ -624,11 +644,12 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
624
644
  filters = adjustFilters(filters, [{ name: 'round', operator: 'gte', value: 2 }]);
625
645
  }
626
646
  const response = await client.query({ query: gql `
627
- query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) { responses: ${this.group === 'reference' ? 'approvalReferenceList' : 'approvalPendingList'}(filters: $filters, pagination: $pagination, sortings: $sortings) { items { id
647
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) { responses: ${this.group === 'reference' ? 'approvalReferenceList' : this.group === 'completed' ? 'approvalCompletedList' : 'approvalPendingList'}(filters: $filters, pagination: $pagination, sortings: $sortings) { items { id
628
648
  round
629
649
  comment
630
650
  commentedAt
631
651
  judgment
652
+ participation
632
653
  terminatedAt
633
654
  activityThread { activityInstance { activityId: id
634
655
  name
@@ -666,8 +687,9 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
666
687
  }
667
688
  });
668
689
  const total = response.data.responses.total || 0;
669
- /* 보고 있는 묶음의 수는 이 조회가 알려 준다. 반대쪽만 따로 센다(countOther). */
670
- this.counts = { ...this.counts, [this.group]: total };
690
+ /* 보고 있는 묶음의 수는 이 조회가 알려 준다. 나머지 둘만 따로 센다(countOther). */
691
+ if (this.group !== 'completed')
692
+ this.counts = { ...this.counts, [this.group]: total };
671
693
  return { total,
672
694
  records: (response.data.responses.items || []).map(item => {
673
695
  let thread = item.activityThread;
@@ -681,6 +703,7 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
681
703
  round: item.round,
682
704
  comment: item.comment,
683
705
  judgment: item.judgment,
706
+ participation: item.participation || 'approval',
684
707
  commentedAt: item.commentedAt,
685
708
  terminatedAt: item.terminatedAt,
686
709
  updatedAt: item.updatedAt
@@ -1 +1 @@
1
- {"version":3,"file":"approval-pending-list-page.js","sourceRoot":"","sources":["../../../client/pages/todo/approval-pending-list-page.ts"],"names":[],"mappings":";AAAA,OAAO,qBAAqB,CAAA;AAC5B,OAAO,wCAAwC,CAAA;AAC/C,OAAO,6CAA6C,CAAA;AACpD,OAAO,2CAA2C,CAAA;AAElD,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEzE,OAAO,EAAE,SAAS,EAAe,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACpG,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACxF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAS,MAAM,gBAAgB,CAAA;AAErE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAEpC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE3F,MAAM,GAAG,GAAG,EAAE,CAAA;AACd,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,CAAA;AACrB,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAA;AAGd,IAAM,uBAAuB,GAA7B,MAAM,uBAAwB,SAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;IAAvE;;QAmIL;;;;;;;;;;;;;;;;;WAiBG;QACc,UAAK,GAA6B,UAAU,CAAA;QAE7D,kDAAkD;QACjC,WAAM,GAA8C,EAAE,CAAA;QAC3C,SAAI,GAA6B,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;IAqiBjG,CAAC;aA9rByF,WAAM,GAAG;QAC/F,eAAe;QACf,iBAAiB;QACjB,kBAAkB;QAClB,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0HF;KACF,AA/H6F,CA+H7F;IAgCD,IAAI,OAAO;QAAS,OAAO,EAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;YAC9E;;;;;;;;;;;eAWG;YACH;;;;;;;;eAQG;YACH,MAAM,EAAE;gBACN,OAAO,EAAE,CAAC,MAAc,EAAE,EAAE;oBAC1B,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAA;gBAChC,CAAC;gBACD,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,IAAI,EAAE;aACpC;YACD,MAAM,EAAE,EAAU,OAAO,EAAE,GAAG,EAAE;oBAAa,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAA;gBAC7E,CAAC;aACA;YACI,IAAI,EAAE,qCAAqC;YAC3C,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,KAAK,EAClB,CAAA;IACD,CAAC;IAEA,MAAM;QAAS,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAE3E,OAAO,IAAI,CAAA;;;;;;;;UASL;YACE,CAAC,UAAU,EAAE,sBAAsB,EAAE,0BAA0B,CAAC;YAChE,CAAC,WAAW,EAAE,uBAAuB,EAAE,2BAA2B,CAAC;SAEtE,CAAC,GAAG,CACH,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA;;+BAEP,IAAI,CAAC,KAAK,KAAK,GAAG;sBAC3B,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;uBACd,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;;gBAElC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;gBAChB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS;YAC9B,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,IAAI,CAAA,mBAAmB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAE,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC,SAAS;;WAEtG,CACF;;;;gBAIO,IAAI;kBACF,IAAI,CAAC,WAAW;wBACV,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;kCAClB,IAAI,CAAC,yBAAyB,CAAC,UAAU,CAAE;;;;;;;uBAOtD,IAAI,CAAC,OAAO;yBACV;YACT,aAAa,EAAE;YACf,WAAW,EAAE;YACb,EAAoB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;gBAChE,KAAK,EAAE,aAAa;aACpC;SACa;wBACS,CAAC,CAAc,EAAE,EAAE;YAAmB,IAAI,CAAC,OAAO,GAAI,CAAC,CAAC,aAAqB,EAAE,KAAK,CAAA;YAC5F,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QACjC,CAAC;;;;;8DAK4D,IAAI,CAAC,OAAO;;;;;KAKrE,CAAA;IACJ,CAAC;IAEA,mDAAmD;IAC3C,WAAW,CAAC,KAA+B;QACjD,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YACzB,OAAM;QACR,CAAC;QAED,MAAM,IAAI,GAAI,IAAY,CAAC,SAAS,EAAE,IAAI,CAAA;QAC1C,QAAQ,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC,CAAA;IACjE,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,GAAY;QAClC,MAAM,KAAK,GAAG,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAA;QAE5D,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC7C,OAAM;QACR,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,UAAU;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAA;QAClE,MAAM,KAAK,GAAG,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,uBAAuB,CAAA;QAEpF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;uBAEO,KAAK;;;;OAIrB;YACD,SAAS,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAChD,WAAW,EAAE,UAAU;SACxB,CAAC,CAAA;QAEF;;;;;;;WAOG;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAA;QAEnF,IAAI,OAAO,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,OAAM;QACR,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAA;IAClD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAS;QAC7B;;;WAGG;QACH,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;QAC3C,MAAM,IAAI,CAAC,UAAU,EAAE,CAAA;IACzB,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC,WAAW,GAAG,EAAQ,IAAI,EAAE,EAAU,SAAS,EAAE,WAAW;gBAC7D,MAAM,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC;gBAC/B,OAAO,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC;aACnC;YACI;;;;;;;;eAQG;YACH,SAAS,EACP,IAAI,CAAC,KAAK,KAAK,WAAW;gBACxB,CAAC,CAAC;oBACE,IAAI,EAAE,YAAY;oBAClB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC;oBAC3C,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,+BAA+B,CAAC;iBACxD;gBACH,CAAC,CAAC;oBACE,IAAI,EAAE,UAAU;oBAChB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;oBAC1C,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;iBACvD;YACP;;;;;;;;;;;;;;;;;;;eAmBG;YACH,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE;gBACvD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,MAAM;oBACZ,KAAK,EAAE,IAAI;oBACX,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC;oBACrC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf;;;2BAGG;wBACH,QAAQ,EAAE,WAAW,CAAC,aAAa,CAAC;qBACrC;oBACD,MAAM,EAAE,QAAQ;oBAChB,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE;;;;;;;;;;uBAUG;oBACH,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,aAAa;oBACnB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC;oBACtC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,MAAM,EAAE,QAAQ;oBAChB,MAAM,EAAE,IAAI;iBACb;gBACD;oBACE,IAAI,EAAE,iBAAiB;oBACvB,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC;oBACrC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,iBAAiB;oBACvB,IAAI,EAAE,UAAU;oBAChB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;oBACnC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE;;;;;uBAKG;oBACH,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;oBACjC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC;qBACrC;oBACD,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,UAAU;oBAChB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC;oBACtC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,KAAK,EAAE,QAAQ;wBACf,QAAQ,EAAE,YAAY,EAAE;qBACzB;oBACD,KAAK,EAAE,EAAE;iBACV;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;oBACnC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE;4BACP,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,gCAAgC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE;4BAC5E,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,gCAAgC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE;yBAC7E;qBACF;oBACD,KAAK,EAAE,EAAE;iBACV;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;oBACnC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE;oBAC3C,KAAK,EAAE,EAAE;iBACV;gBACD;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;oBAC1C,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,KAAK;oBACb,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC;oBACxC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,KAAK;oBACb,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,YAAY;oBAClB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC;oBACzC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,UAAU,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK;4BACxD,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAA;4BACtD,IAAI,IAAI,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAA;4BACpD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAA;wBACvE,CAAC;qBACF;oBACD,KAAK,EAAE,GAAG;oBACV,MAAM,EAAE,IAAI;iBACb;gBACD;oBACE,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC;oBACpC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,MAAM,EAAE,IAAI;oBACZ,QAAQ,EAAE;wBACR,KAAK,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;4BACjD,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAA;wBACnC,CAAC;qBACF;iBACF;aACF;YACD,IAAI,EAAE,EAAU,UAAU,EAAE,KAAK;gBAC/B,QAAQ,EAAE,KAAK;gBACf,QAAQ,EAAE,EAAY,KAAK,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;wBAAe,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAA;oBAChI,CAAC;iBACA;aACA;YACI;;;;;;;;;;;eAWG;YACH,OAAO,EAAE;gBACP,EAAY,IAAI,EAAE,OAAO;oBACvB,IAAI,EAAE,KAAK;iBACnB;aACK;SACF,CAAA;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC/B;;;;;;;;;;;;;;;;;;;eAmBG;YACH,IAAI,CAAC,WAAW,CAAC,OAAO,GAAG;gBACzB,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO;gBAC3B;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,aAAa;oBACnB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;oBACvC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,CAAC,KAAU,EAAE,MAAW,EAAE,MAAW,EAAE,EAAE;4BACjD,MAAM,MAAM,GAAG,MAAM,EAAE,QAAQ,KAAK,QAAQ,CAAA;4BAC5C,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;4BAE9C,IAAI,CAAC,MAAM,EAAE,CAAC;gCACZ,OAAO,IAAI,CAAA,uBAAuB,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,SAAS,CAAA;4BAC7E,CAAC;4BAED,IAAI,CAAC,OAAO,EAAE,CAAC;gCACb,OAAO,IAAI,CAAA;8CACmB,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;kBACrE,CAAA;4BACJ,CAAC;4BAED;;;+BAGG;4BACH,MAAM,KAAK,GACT,MAAM,EAAE,WAAW;gCACnB,MAAM,EAAE,YAAY;gCACpB,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAA;4BAElF,OAAO,IAAI,CAAA;;0BAEC,OAAO;oBACb,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,2BAA2B,OAAO,CAAC,CAAC,CAAC,+BAA+B,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;;eAEpG,CAAA;wBACH,CAAC;qBACF;oBACD,KAAK,EAAE,GAAG;iBACX;aACF,CAAA;QACH,CAAC;IACJ,CAAC;IAEA,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa;QACjD,IAAI,SAAS,EAAE,UAAU,KAAK,aAAa,EAAE,UAAU,EAAE,CAAC;YACxD,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;YAC3C,MAAM,IAAI,CAAC,UAAU,EAAE,CAAA;QACzB,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;QACrB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,EAAe;QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAAO,OAAO,GAAG,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAA;QAC1L,CAAC;QAEE,IAAI,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAAO,OAAO,GAAG,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CAAA;QAChG,CAAC;QAEE,IAAI,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YAAO,OAAO,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QACtI,CAAC;QAEE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,EAAQ,KAAK,EAAE,GAAG,CAAA;6GACmD,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmClL;YACD,SAAS,EAAE,EAAU,OAAO;gBAC1B,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;gBAC3B,QAAQ;aACd;SACA,CAAC,CAAA;QAEC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAA;QAChD,uDAAuD;QACvD,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAA;QAErD,OAAO,EAAQ,KAAK;YAClB,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBAAW,IAAI,MAAM,GAAG,IAAI,CAAC,cAAc,CAAA;gBACnG,OAAO,EAAY,EAAE,EAAE,IAAI,CAAC,EAAE;oBAC5B,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,GAAG,MAAM,CAAC,gBAAgB;oBAC1B,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,UAAU,EAAE,MAAM,CAAC,UAAU;oBAC7B,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;oBAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;iBACjC,CAAA;YACD,CAAC,CAAC;SACD,CAAA;IACD,CAAC;IAEA,oBAAoB,CAAC,gBAAgB;QAAQ,QAAQ,CAAC,qBAAqB,gBAAgB,CAAC,EAAE,UAAU,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAA;IACjI,CAAC;;AA5jB4B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;4DAAkB;AAoB5B;IAAhB,KAAK,EAAE;;sDAAqD;AAG5C;IAAhB,KAAK,EAAE;;uDAA+D;AAC3C;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;qDAAoE;AAE5E;IAAlB,KAAK,CAAC,UAAU,CAAC;8BAAS,SAAS;sDAAA;AAE3B;IAAR,KAAK,EAAE;;wDAAmB;AA7JhB,uBAAuB;IADnC,aAAa,CAAC,4BAA4B,CAAC;GAC/B,uBAAuB,CA8rBnC","sourcesContent":["import '@operato/data-grist'\nimport '@operato/data-grist/ox-filters-form.js'\nimport '@operato/context/ox-context-page-toolbar.js'\nimport '@operato/input/ox-input-select-buttons.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { asyncReplace } from 'lit/directives/async-replace.js'\nimport { DataGrist, FetchOption, getRenderer } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { dueSoonChip, dueSoonFilters, importantChip, importantFilters } from '../../chip-filters.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'\nimport { adjustFilters, isMobileDevice, sleep } from '@operato/utils'\n\nimport { p13n } from '@operato/p13n'\n\nimport { approvalMarks, dueCell, priorityCell, subjectCell } from '../../activity-cells.js'\n\nconst MIN = 60\nconst HOUR = 60 * MIN\nconst DAY = 24 * HOUR\n\n@customElement('approval-pending-list-page')\nexport class ApprovalPendingListPage extends p13n(localize(i18next)(PageView)) { static styles = [\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n css`\n /*\n * 세로로 쌓는다 — 묶음 고르는 줄이 표 **위**에 서야 한다.\n *\n * 방향을 빼면 가로로 배치되어, 그 줄이 표 왼쪽에 세로 띠로 서고 버튼 글자가 세로 가운데에\n * 떠서 표 밑처럼 보인다. 표도 그 띠만큼 밀려 오른쪽 칸이 잘린다. 전에는 자식이 ox-grist\n * 하나뿐이라 방향이 드러나지 않았다.\n */\n :host {\n display: flex;\n flex-direction: column;\n\n width: 100%;\n\n --grid-record-emphasized-background-color: #8b0000;\n --grid-record-emphasized-color: #ff6b6b;\n }\n\n ox-grist { overflow-y: auto;\n flex: 1;\n }\n\n .header { grid-template-areas: 'filters actions';\n }\n\n /*\n * ── 헤드룸의 왼쪽 기준선은 표와 같다 ─────────────────────────────────\n * CommonHeaderStyles 의 .filters 는 배경과 모서리와 여백을 가진 영역이다. 그 배경이\n * --md-sys-color-surface 인데 이 제품에서는 바탕색과 같은 값이라, **보이지 않는 영역이\n * 자리만 차지하고** 검색창을 여백만큼(12px) 안쪽으로 밀었다.\n *\n * 실측: 표 왼쪽 끝 212 · 필터 영역 212 · 검색창 224. 화면의 왼쪽 기준선이 둘이 됐다.\n *\n * 그 규칙들은 .header 안에 중첩되어 있어(.header .filters) 밑줄 없는 .filters 로는 이길 수\n * 없다 — 특정도가 0,1,0 대 0,2,0 이다.\n */\n .header .filters {\n padding: 0;\n background: none;\n border-radius: 0;\n gap: 12px;\n }\n\n /*\n * 이 화면에는 항목 필터가 없다 — 검색은 제목줄로 올라갔고 남은 것은 칩뿐이다. 그래도\n * ox-filters-form 은 폭 0으로 남아 자리를 지키고, 그 뒤에 gap 12px 이 붙어 칩들을 밀어냈다.\n *\n * 실측: 표 셀 212 · 필터 영역 212 · 칩 224. 왼쪽 기준선이 다시 둘이 됐다.\n *\n * 컴포넌트가 스스로 [empty] 를 달아 주므로 그것으로 접는다. 필터가 생기면 그 표식이 사라져\n * 다시 나온다.\n */\n .header .filters ox-filters-form[empty] {\n display: none;\n }\n\n /*\n * 두 묶음 — 표 위. 지금 보는 쪽은 밑줄과 굵기로 말하고, 옆에 건수를 적는다.\n *\n * 건수를 안 눌러도 보이게 두는 것이 요점이다. 참조에 몇 건이 있는지 모르면 열어 볼 이유가 없다.\n *\n * ── 필터 칩보다 무거워야 한다 ─────────────────────────────────────────────\n * 처음에 13px 맨글자로 두었더니, 바로 아래 「중요·만기·재상신」 칩이 테두리를 가져서 더 세게\n * 보였다. **보는 대상을 바꾸는 것이 대상을 좁히는 것보다 약해 보였다.** 그래서 글자를 14px 로\n * 올리고(칩은 13px) 세로 여백을 넓혀 제목 다음 자리를 준다.\n *\n * 좌우 여백은 품질 화면 탭에서 베끼지 않는다 — 그 화면은 --spacing-large 라 이 화면의 왼쪽\n * 기준선 212px(표 셀과 같은 값)을 깨뜨린다. 무게만 같게 하고 기준선은 화면이 정한다.\n */\n .groups {\n flex: none;\n display: flex;\n gap: var(--spacing-large);\n padding: 0 var(--spacing-medium);\n border-bottom: 1px solid var(--ui-line, #d5dce0);\n }\n\n .groups button {\n border: none;\n background: none;\n padding: var(--spacing-medium) 2px calc(var(--spacing-medium) - 2px);\n border-bottom: 2px solid transparent;\n font: inherit;\n font-size: 14px;\n color: var(--ui-muted, #57676f);\n cursor: pointer;\n }\n\n .groups button:hover:not([data-selected]) {\n color: var(--ui-ink, #17272f);\n }\n\n /*\n * ── 색은 밑줄이 말하고, 글자는 읽히는 색으로 ──────────────────────────────\n * 고른 탭의 글자를 --ui-accent 로 두었는데, 그 토큰이 브랜드 초록(#0e8a56)이 되면서 14px\n * 글자로는 안 읽히게 됐다.\n *\n * [확인함] 초록 #0e8a56 · 배경 rgb(240 242 244) → 대비 3.91. 본문 글자 기준 4.5 미달이다.\n * 흰 바탕 기준으로는 4.39 인데, **이 앱 배경은 흰색이 아니라** 더 나쁘다.\n *\n * --ui-ink 15.1 고른 탭 글자\n * --ui-muted 5.52 안 고른 탭 글자\n * --ui-accent 3.91 밑줄 — UI 요소는 3.0 기준이라 통과한다\n *\n * 밑줄은 선택을 가장 강하게 표시하는 자리다. 브랜드색이 거기 남으면 잃는 것이 없다.\n */\n .groups button[data-selected] {\n font-weight: 600;\n color: var(--ui-ink, #17272f);\n border-bottom-color: var(--ui-accent, #262e38);\n }\n\n .groups .n {\n margin-left: 4px;\n color: var(--ui-faint, #8a959c);\n }\n\n .groups button[data-selected] .n {\n color: inherit;\n }\n\n\n `\n ]\n\n @property({ type: Object }) gristConfig?: any\n\n /**\n * 지금 어느 묶음을 보고 있나.\n *\n * ── 왜 한 화면에 두 묶음인가 ─────────────────────────────────────────────\n * 참조를 별도 화면으로 만들면 **아무도 열지 않는다.** 참조는 안 봐도 아무 일이 안 일어나므로,\n * 아침에 업무 앱을 열면 「승인 대기」를 보지 「참조」를 따로 열지 않는다. 참조를 만든 이유가 의견을\n * 듣는 것인데 안 여는 화면에 넣으면 의견이 안 온다.\n *\n * 그래서 한 화면에 두고, **두 수를 함께 보인다.** 한 곳만 열면 둘 다 눈에 든다.\n *\n * ── 그렇다고 한 목록에 섞지 않는다 ────────────────────────────────────────\n * 두 묶음의 물음이 다르다 — 「내가 결정할 것」과 「내가 알아야 할 것」. 섞으면 참조가 「내가 승인해야\n * 하나」로 읽히고, 배지에 12가 뜨는데 결정할 것은 3건이 된다. 그러면 그 수를 안 믿게 된다.\n *\n * ── 주소가 주인이다 ──────────────────────────────────────────────────────\n * `.../approval-pending-list/reference` 로 붙여 보낼 수 있다. 대상이 바뀌는 것이므로 필터와 달리\n * 주소에 담는다.\n */\n @state() private group: 'approval' | 'reference' = 'approval'\n\n /** 두 묶음의 건수. 탭에 함께 적는다 — 안 눌러도 얼마나 있는지 보여야 한다. */\n @state() private counts: { approval?: number; reference?: number } = {}\n @property({ type: String }) mode: 'GRID' | 'LIST' | 'CARD' = isMobileDevice() ? 'CARD' : 'GRID'\n\n @query('ox-grist') grist!: DataGrist\n\n @state() filters?: string[]\n\n get context() { return { title: i18next.t('title.approval pending list'),\n /*\n * 검색의 자리는 **기기가 정한다.**\n *\n * PC 에서는 화면 안으로 내린다. 상단 바에 두면 거른 조건과 걸러진 결과가 화면의 위아래 끝으로\n * 갈라지고, 지금 무엇으로 걸렀는지가 목록 옆에서 보이지 않는다.\n *\n * 좁은 화면에서는 상단 바에 그대로 둔다. 헤드룸에 두면 검색창과 필터가 목록을 그만큼 밀어\n * 내리고, 카드 두 장이 화면 밖으로 나간다. 상단 바는 그 자리를 아끼려고 있는 것이다.\n *\n * 이 저장소에는 두 방식이 다 있다 — 헤드룸 50곳, 상단 바 51곳. 정형인 것은 구조\n * (.header / .filters / .filter)이고 검색의 자리는 화면이 정한다.\n */\n /*\n * 검색은 **페이지 제목 줄의 오른쪽 끝**에 둔다 — 다른 화면들과 같은 자리다.\n *\n * 전에는 좁은 화면에서만 여기에 넣고, 넓은 화면에서는 헤드룸의 ox-filters-form 이 검색을\n * 그렸다. 그래서 같은 앱 안에서 검색 상자의 자리가 화면마다 달랐다.\n *\n * 헤드룸에는 좁히는 칩만 남는다. 폼은 without-search 로 남겨 두어, 나중에 어느 칸에\n * filter 를 달면 그 UI 가 저절로 생긴다(비면 아무것도 그리지 않는다).\n */\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n },\n filter: { handler: () => { this.grist.toggleHeadroom()\n }\n },\n help: 'worklist/approval-pending-list-page',\n actions: [],\n toolbar: false\n }\n }\n\n render() { const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')\n\n return html`\n <!--\n 두 묶음 — 표 **바깥** 위에 둔다.\n\n 헤드룸에 두면 필터를 접을 때 함께 사라지고, 그러면 참조로 가는 유일한 길이 없어진다.\n 필터는 접혀도 되지만(풀면 전체가 돌아온다) 이것은 접히면 안 된다.\n -->\n <div class=\"groups\">\n ${(\n [\n ['approval', 'label.group-approval', 'text.group-approval-hint'],\n ['reference', 'label.group-reference', 'text.group-reference-hint']\n ] as const\n ).map(\n ([key, label, hint]) => html`\n <button\n ?data-selected=${this.group === key}\n title=${i18next.t(hint)}\n @click=${() => this.selectGroup(key)}\n >\n ${i18next.t(label)}\n ${this.counts[key] === undefined\n ? ''\n : html`<span class=\"n\">${this.counts[key]!.toLocaleString(i18next.language || undefined)}</span>`}\n </button>\n `\n )}\n </div>\n\n <ox-grist\n .mode=${mode}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}\n >\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <!-- 좁은 화면에서는 상단 바가 검색을 갖는다(위 context 주석 참조). -->\n <ox-filters-form class=\"filter\" without-search></ox-filters-form>\n <ox-input-select-buttons\n .value=${this.filters}\n .options=${[\n importantChip(),\n dueSoonChip(),\n { display: i18next.t('label.filter-resubmitted'),\n value: 'resubmitted'\n }\n ]}\n @change=${(e: CustomEvent) => { this.filters = (e.currentTarget as any)?.value\n this.grist.fetch()\n }}\n ></ox-input-select-buttons>\n <!-- <ox-filters-form class=\"filter\" autofocus without-search></ox-filters-form> -->\n </div>\n\n <ox-context-page-toolbar class=\"actions\" .context=${this.context}></ox-context-page-toolbar>\n </div>\n\n <ox-grist-personalizer slot=\"setting\"></ox-grist-personalizer>\n </ox-grist>\n `\n }\n\n /** 주소만 바꾼다 — 목록도 설정도 pageUpdated 가 한 벌로 다시 세운다. */\n private selectGroup(group: 'approval' | 'reference') {\n if (this.group === group) {\n return\n }\n\n const page = (this as any).lifecycle?.page\n navigate(group === 'approval' ? `${page}` : `${page}/${group}`)\n }\n\n private async syncGroup(key?: string) {\n const group = key === 'reference' ? 'reference' : 'approval'\n\n if (this.group === group && this.gristConfig) {\n return\n }\n\n this.group = group\n this.applyConfig()\n }\n\n /**\n * 안 보고 있는 묶음의 건수를 따로 센다 — 한 줄만 받아 `total` 을 읽는다.\n *\n * 보고 있는 쪽의 수는 목록 조회가 이미 알려 준다(fetchHandler). 그래서 여기서는 반대쪽만 묻는다.\n */\n private async countOther() {\n const other = this.group === 'approval' ? 'reference' : 'approval'\n const query = other === 'approval' ? 'approvalPendingList' : 'approvalReferenceList'\n\n const response = await client.query({\n query: gql`\n query ($pagination: Pagination) {\n responses: ${query}(pagination: $pagination) {\n total\n }\n }\n `,\n variables: { pagination: { page: 1, limit: 1 } },\n fetchPolicy: 'no-cache'\n })\n\n /*\n * A count we could not fetch is not zero.\n *\n * This read `?? 0`, and the client runs with errorPolicy: 'all' — a failed query comes back as\n * a resolved response carrying `errors`, not a rejection. So any failure drew the other tab as\n * 「0」, which says \"nothing is waiting on you\". The render already draws nothing when the count\n * is undefined, so leaving it unknown is both honest and already handled.\n */\n const total = response.errors?.length ? undefined : response.data?.responses?.total\n\n if (typeof total != 'number') {\n return\n }\n\n this.counts = { ...this.counts, [other]: total }\n }\n\n async pageInitialized(lifecycle) {\n /*\n * 주소가 묶음을 정한다. 주소로 바로 들어온 경우가 여기서 잡힌다 — pageUpdated 가 이 함수보다 먼저\n * 지나가므로 그것만 믿으면 `/reference` 링크가 결재 묶음을 보여준다.\n */\n await this.syncGroup(lifecycle?.resourceId)\n await this.countOther()\n }\n\n private applyConfig() {\n this.gristConfig = { list: { thumbnail: 'thumbnail',\n fields: ['name', 'description'],\n details: ['createdAt', 'due']\n },\n /*\n * 빈 화면이 참조를 가르친다.\n *\n * 참조를 처음 누른 사람은 여기서 그 개념을 만난다. 기본 문장 「해당하는 것이 없습니다」는\n * 참조가 무엇인지 말해 주지 않는다 — 내가 결정해야 하는 것인지, 안 봐도 되는 것인지 모른 채\n * 화면을 닫는다.\n *\n * 그래서 없을 때 그 묶음이 무엇인지 적는다.\n */\n emptyNote:\n this.group === 'reference'\n ? {\n icon: 'visibility',\n title: i18next.t('text.no-reference-title'),\n description: i18next.t('text.no-reference-description')\n }\n : {\n icon: 'task_alt',\n title: i18next.t('text.no-approval-title'),\n description: i18next.t('text.no-approval-description')\n },\n /*\n * ── 승인자가 먼저 묻는 순서대로 둔다 (2026-09-01) ─────────────────────\n *\n * 전에는 앞자리를 「열기」 버튼과 **모든 줄이 같은 값인 상태 칸**이 차지했고, 승인자가 가장 먼저\n * 묻는 「누가 올렸나」는 1200px 밖에 있었다. 실측으로 이름부터 담당까지 폭 합이 1287px 이었고\n * 보이는 폭은 1150px 이었다.\n *\n * 순서를 물음의 순서로 바꾼다 — 무엇을 · 누가 · 언제까지 · 얼마나 급한가.\n *\n * ── 「열기」 버튼을 없앤다 ─────────────────────────────────────────────\n * 줄을 누르면 이미 열린다(아래 rows.handlers.click). 같은 일을 하는 버튼이 줄마다 72px 을\n * 차지하고 있었다.\n *\n * 다만 그 버튼의 아이콘이 신호를 하나 들고 있었다 — `comment` 가 있으면 `label`, 없으면\n * `new_label`. **승인자가 보다가 메모를 남기고 미뤄 둔 것**이라는 뜻이다. 버리지 않고 이름 옆에\n * 말로 적는다. 태그 아이콘 두 개로는 아무도 그 뜻으로 읽지 않는다.\n *\n * 열람 여부는 담는 값이 없다 — `ActivityApproval` 에 `readAt` 이 없어서, 열어만 보고 나가면\n * 흔적이 남지 않는다. 지금 표시하는 것은 「메모를 남겨 뒀나」이지 「열어 봤나」가 아니다.\n */\n columns: [\n { type: 'gutter', gutterName: 'sequence', fixed: true },\n {\n type: 'string',\n name: 'name',\n fixed: true,\n header: i18next.t('field.wl-subject'),\n record: {\n editable: false,\n /*\n * 이름과 설명을 한 칸에 둔다. 둘은 함께 읽는 것인데 275px 짜리 칸으로 갈라 두면 눈이\n * 오가고, 그 사이를 우선순위·차수가 가로막고 있었다.\n */\n renderer: subjectCell(approvalMarks)\n },\n filter: 'search',\n sortable: true,\n width: 320\n },\n {\n /*\n * 설명은 제목 칸 안에서 두 줄로 보인다(activity-cells 의 subjectCell). 그래서 따로 보일 칸이\n * 필요하지 않다.\n *\n * 그런데 **칸을 없애면 설명으로 검색하는 기능이 함께 사라진다.** searchText 는\n * filter: 'search' 를 선언한 컬럼마다 필터를 만들고 hidden 은 보지 않는다\n * (data-grist.ts 의 set searchText). 실측으로 확인했다 — 컬럼을 없앤 뒤 설명에만 있는\n * 낱말로 검색하면 0건이었다.\n *\n * 그래서 감춘 칸으로 남겨 검색만 남긴다.\n */\n type: 'string',\n name: 'description',\n header: i18next.t('field.description'),\n record: { editable: false },\n filter: 'search',\n hidden: true\n },\n {\n type: 'resource-object',\n name: 'creator',\n header: i18next.t('field.wl-drafter'),\n record: { editable: false },\n sortable: true,\n width: 110\n },\n {\n type: 'resource-object',\n name: 'assignee',\n header: i18next.t('field.wl-owner'),\n record: { editable: false },\n width: 110\n },\n {\n /*\n * 기한 한 칸 — 남은 시간이 위, 절대 시각이 아래다.\n *\n * 전에는 「만기 시간」(180px)과 「남은 시간」(180px)이 따로 있었다. 사람이 묻는 것은 「얼마나\n * 남았나」 하나이고, 절대 시각은 그것을 확인할 때만 본다. 두 칸으로 360px 을 쓸 일이 아니다.\n */\n type: 'string',\n name: 'dueAt',\n header: i18next.t('field.wl-due'),\n record: {\n editable: false,\n renderer: dueCell(() => this.active)\n },\n sortable: true,\n width: 170\n },\n {\n type: 'number',\n name: 'priority',\n header: i18next.t('field.wl-priority'),\n record: {\n editable: false,\n align: 'center',\n renderer: priorityCell()\n },\n width: 80\n },\n {\n type: 'select',\n name: 'state',\n header: i18next.t('field.wl-state'),\n record: {\n editable: false,\n options: [\n { display: i18next.t('label.activity-state-submitted'), value: 'submitted' },\n { display: i18next.t('label.activity-state-escalated'), value: 'escalated' }\n ]\n },\n width: 90\n },\n {\n type: 'number',\n name: 'round',\n header: i18next.t('field.wl-round'),\n record: { editable: false, align: 'right' },\n width: 60\n },\n {\n type: 'datetime',\n name: 'createdAt',\n header: i18next.t('field.wl-submitted-at'),\n record: { editable: false },\n sortable: true,\n filter: false,\n width: 160\n },\n {\n type: 'datetime',\n name: 'updatedAt',\n header: i18next.t('field.wl-changed-at'),\n record: { editable: false },\n sortable: true,\n filter: false,\n width: 160\n },\n {\n type: 'string',\n name: 'viewSource',\n header: i18next.t('field.wl-view-source'),\n record: {\n editable: false,\n renderer: function (value, column, record, rowIndex, field) {\n const { viewType, viewSource } = record.activity || {}\n var type = viewType !== 'board' ? 'string' : 'board'\n return getRenderer(type)(viewSource, column, record, rowIndex, field)\n }\n },\n width: 140,\n hidden: true\n },\n {\n type: 'image',\n name: 'thumbnail',\n header: i18next.t('field.thumbnail'),\n record: { editable: false },\n hidden: true,\n handlers: {\n click: (columns, data, column, record, rowIndex) => {\n this.openActivityApproval(record)\n }\n }\n }\n ],\n rows: { appendable: false,\n editable: false,\n handlers: { click: (columns, data, column, record, rowIndex) => { this.openActivityApproval(record)\n }\n }\n },\n /*\n * 급한 것부터 — 「할 일」 목록과 같은 순서다.\n *\n * 최근 변경 내림차순이었다. 받은 편지함으로는 말이 되지만 이 목록의 물음은 「무엇을 먼저\n * 판단해야 하나」다. 그 정렬에서는 나흘 지난 건이 목록 중간에 묻힌다 — 기안자가 나흘째\n * 기다리는 동안.\n *\n * 두 목록은 같은 성격이다(내가 처리해야 할 것). 순서 기준이 다르면 화면을 옮길 때마다 다른\n * 순서를 본다.\n *\n * 기한이 없는 건은 뒤로 간다 — 서버가 그 자리를 정한다(applyApprovalSortings).\n */\n sorters: [\n { name: 'dueAt',\n desc: false\n }\n ]\n }\n\n if (this.group === 'reference') {\n /*\n * ── 내 참조가 어떤 상태인가 ─────────────────────────────────────────\n * 참조 묶음에는 **끝난 건도 나온다.** 참조는 결재를 막지 않으므로 결재가 끝난 뒤에 의견이 달릴\n * 수 있고, 그래서 목록에서 끝난 것을 걸러내지 않는다. 화면이 그것을 구별해야 한다 — 안 그러면\n * 이미 끝난 건을 열어 의견을 쓰고 그것이 반영된 줄로 안다.\n *\n * **의견 없이 끝난 것을 빈 칸으로 두지 않는다.** 침묵은 동의가 아니다. 품질팀장이 아무 말 안\n * 하고 끝났으면 「반대하지 않았다」가 아니라 「말할 기회에 말하지 않았다」다. 나중에 사고 조사에서\n * 그 둘은 다르다.\n *\n * ── 「결정 후 의견」은 commentedAt 으로 판정한다 ────────────────────────\n * 처음에 `updatedAt > terminatedAt` 으로 추론하려 했다. **그 부등호는 언제나 참이다** —\n * 닫는 코드가 `terminatedAt: new Date()` 를 저장 **전에** 만들고, `updatedAt` 은\n * `@UpdateDateColumn` 이 DB 에 쓰는 순간 찍는다. 두 값이 몇 밀리초 어긋나서, 닫힌 참조가\n * 전부 「결정 후 의견」으로 보였을 것이다.\n *\n * 그래서 사실에 집을 줬다 — `ActivityApproval.commentedAt`(2eaceace25). 의견을 언제 말했나는\n * 파생으로 알 수 없는 값이다. **빈 의견에는 시각을 찍지 않는다**(서버 `stampedComment`), 그래서\n * 시각의 유무가 곧 침묵과 발언의 구별이다.\n */\n this.gristConfig.columns = [\n ...this.gristConfig.columns,\n {\n type: 'string',\n name: 'myReference',\n header: i18next.t('field.my-reference'),\n record: {\n editable: false,\n renderer: (value: any, column: any, record: any) => {\n const closed = record?.judgment === 'closed'\n const comment = (record?.comment || '').trim()\n\n if (!closed) {\n return html`<span class=\"quiet\">${i18next.t('text.reference-open')}</span>`\n }\n\n if (!comment) {\n return html`<span class=\"pill quiet\"\n ><span class=\"dot\"></span>${i18next.t('text.reference-closed-silent')}</span\n >`\n }\n\n /*\n * 의견은 있는데 시각이 없으면 **언제인지 모른다** — commentedAt 이 생기기 전에 적힌\n * 줄이다. 모르는 것을 「결정 전」으로도 「결정 후」로도 적지 않고, 의견만 보인다.\n */\n const after =\n record?.commentedAt &&\n record?.terminatedAt &&\n new Date(record.commentedAt).getTime() > new Date(record.terminatedAt).getTime()\n\n return html`\n <span class=\"line\">\n <span>${comment}</span>\n ${after ? html`<span class=\"pill warn\">${i18next.t('text.reference-after-decision')}</span>` : ''}\n </span>\n `\n }\n },\n width: 260\n }\n ]\n }\n }\n\n async pageUpdated(changes, lifecycle, changedBefore) {\n if (lifecycle?.resourceId !== changedBefore?.resourceId) {\n await this.syncGroup(lifecycle?.resourceId)\n await this.countOther()\n }\n\n if (this.active) {\n this.grist?.fetch()\n }\n }\n\n async fetchHandler({ page, limit, sortings = [], filters = [] }: FetchOption) { if (this.filters?.includes('important')) { filters = adjustFilters(filters, importantFilters())\n }\n\n if (this.filters?.includes('due')) { filters = adjustFilters(filters, dueSoonFilters())\n }\n\n if (this.filters?.includes('resubmitted')) { filters = adjustFilters(filters, [{ name: 'round', operator: 'gte', value: 2 }])\n }\n\n const response = await client.query({ query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) { responses: ${this.group === 'reference' ? 'approvalReferenceList' : 'approvalPendingList'}(filters: $filters, pagination: $pagination, sortings: $sortings) { items { id\n round\n comment\n commentedAt\n judgment\n terminatedAt\n activityThread { activityInstance { activityId: id\n name\n description\n priority\n input\n refBy\n activityType\n viewType\n viewSource\n thumbnail\n updatedAt\n createdAt\n creator { id\n name\n }\n }\n state\n output\n dueAt\n assignee { id\n name\n }\n assignedAt\n }\n updatedAt\n }\n total\n }\n }\n `,\n variables: { filters,\n pagination: { page, limit },\n sortings\n }\n })\n\n const total = response.data.responses.total || 0\n /* 보고 있는 묶음의 수는 이 조회가 알려 준다. 반대쪽만 따로 센다(countOther). */\n this.counts = { ...this.counts, [this.group]: total }\n\n return { total,\n records: (response.data.responses.items || []).map(item => { let thread = item.activityThread\n return { id: item.id,\n state: thread.state,\n ...thread.activityInstance,\n dueAt: thread.dueAt,\n assignee: thread.assignee,\n assignedAt: thread.assignedAt,\n output: thread.output,\n round: item.round,\n comment: item.comment,\n judgment: item.judgment,\n commentedAt: item.commentedAt,\n terminatedAt: item.terminatedAt,\n updatedAt: item.updatedAt\n }\n })\n }\n }\n\n openActivityApproval(activityApproval) { navigate(`activity-approval/${activityApproval.id}?title=${activityApproval.name}`)\n }\n}\n"]}
1
+ {"version":3,"file":"approval-pending-list-page.js","sourceRoot":"","sources":["../../../client/pages/todo/approval-pending-list-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist'\nimport '@operato/data-grist/ox-filters-form.js'\nimport '@operato/context/ox-context-page-toolbar.js'\nimport '@operato/input/ox-input-select-buttons.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { asyncReplace } from 'lit/directives/async-replace.js'\nimport { DataGrist, FetchOption, getRenderer } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { dueSoonChip, dueSoonFilters, importantChip, importantFilters } from '../../chip-filters.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'\nimport { adjustFilters, isMobileDevice, sleep } from '@operato/utils'\n\nimport { p13n } from '@operato/p13n'\n\nimport { approvalMarks, dueCell, priorityCell, subjectCell } from '../../activity-cells.js'\n\nconst MIN = 60\nconst HOUR = 60 * MIN\nconst DAY = 24 * HOUR\n\n@customElement('approval-pending-list-page')\nexport class ApprovalPendingListPage extends p13n(localize(i18next)(PageView)) { static styles = [\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n css`\n /*\n * 세로로 쌓는다 — 묶음 고르는 줄이 표 **위**에 서야 한다.\n *\n * 방향을 빼면 가로로 배치되어, 그 줄이 표 왼쪽에 세로 띠로 서고 버튼 글자가 세로 가운데에\n * 떠서 표 밑처럼 보인다. 표도 그 띠만큼 밀려 오른쪽 칸이 잘린다. 전에는 자식이 ox-grist\n * 하나뿐이라 방향이 드러나지 않았다.\n */\n :host {\n display: flex;\n flex-direction: column;\n\n width: 100%;\n\n --grid-record-emphasized-background-color: #8b0000;\n --grid-record-emphasized-color: #ff6b6b;\n }\n\n ox-grist { overflow-y: auto;\n flex: 1;\n }\n\n .header { grid-template-areas: 'filters actions';\n }\n\n /*\n * ── 헤드룸의 왼쪽 기준선은 표와 같다 ─────────────────────────────────\n * CommonHeaderStyles 의 .filters 는 배경과 모서리와 여백을 가진 영역이다. 그 배경이\n * --md-sys-color-surface 인데 이 제품에서는 바탕색과 같은 값이라, **보이지 않는 영역이\n * 자리만 차지하고** 검색창을 여백만큼(12px) 안쪽으로 밀었다.\n *\n * 실측: 표 왼쪽 끝 212 · 필터 영역 212 · 검색창 224. 화면의 왼쪽 기준선이 둘이 됐다.\n *\n * 그 규칙들은 .header 안에 중첩되어 있어(.header .filters) 밑줄 없는 .filters 로는 이길 수\n * 없다 — 특정도가 0,1,0 대 0,2,0 이다.\n */\n .header .filters {\n padding: 0;\n background: none;\n border-radius: 0;\n gap: 12px;\n }\n\n /*\n * 이 화면에는 항목 필터가 없다 — 검색은 제목줄로 올라갔고 남은 것은 칩뿐이다. 그래도\n * ox-filters-form 은 폭 0으로 남아 자리를 지키고, 그 뒤에 gap 12px 이 붙어 칩들을 밀어냈다.\n *\n * 실측: 표 셀 212 · 필터 영역 212 · 칩 224. 왼쪽 기준선이 다시 둘이 됐다.\n *\n * 컴포넌트가 스스로 [empty] 를 달아 주므로 그것으로 접는다. 필터가 생기면 그 표식이 사라져\n * 다시 나온다.\n */\n .header .filters ox-filters-form[empty] {\n display: none;\n }\n\n /*\n * 세 묶음 — 표 위. 지금 보는 쪽은 밑줄과 굵기로 말하고, 옆에 건수를 적는다.\n *\n * 건수를 안 눌러도 보이게 두는 것이 요점이다. 참조에 몇 건이 있는지 모르면 열어 볼 이유가 없다.\n *\n * ── 필터 칩보다 무거워야 한다 ─────────────────────────────────────────────\n * 처음에 13px 맨글자로 두었더니, 바로 아래 「중요·만기·재상신」 칩이 테두리를 가져서 더 세게\n * 보였다. **보는 대상을 바꾸는 것이 대상을 좁히는 것보다 약해 보였다.** 그래서 글자를 14px 로\n * 올리고(칩은 13px) 세로 여백을 넓혀 제목 다음 자리를 준다.\n *\n * 좌우 여백은 품질 화면 탭에서 베끼지 않는다 — 그 화면은 --spacing-large 라 이 화면의 왼쪽\n * 기준선 212px(표 셀과 같은 값)을 깨뜨린다. 무게만 같게 하고 기준선은 화면이 정한다.\n */\n .groups {\n flex: none;\n display: flex;\n gap: var(--spacing-large);\n padding: 0 var(--spacing-medium);\n border-bottom: 1px solid var(--ui-line, #d5dce0);\n }\n\n .groups button {\n border: none;\n background: none;\n padding: var(--spacing-medium) 2px calc(var(--spacing-medium) - 2px);\n border-bottom: 2px solid transparent;\n font: inherit;\n font-size: 14px;\n color: var(--ui-muted, #57676f);\n cursor: pointer;\n }\n\n .groups button:hover:not([data-selected]) {\n color: var(--ui-ink, #17272f);\n }\n\n /*\n * ── 색은 밑줄이 말하고, 글자는 읽히는 색으로 ──────────────────────────────\n * 고른 탭의 글자를 --ui-accent 로 두었는데, 그 토큰이 브랜드 초록(#0e8a56)이 되면서 14px\n * 글자로는 안 읽히게 됐다.\n *\n * [확인함] 초록 #0e8a56 · 배경 rgb(240 242 244) → 대비 3.91. 본문 글자 기준 4.5 미달이다.\n * 흰 바탕 기준으로는 4.39 인데, **이 앱 배경은 흰색이 아니라** 더 나쁘다.\n *\n * --ui-ink 15.1 고른 탭 글자\n * --ui-muted 5.52 안 고른 탭 글자\n * --ui-accent 3.91 밑줄 — UI 요소는 3.0 기준이라 통과한다\n *\n * 밑줄은 선택을 가장 강하게 표시하는 자리다. 브랜드색이 거기 남으면 잃는 것이 없다.\n */\n .groups button[data-selected] {\n font-weight: 600;\n color: var(--ui-ink, #17272f);\n border-bottom-color: var(--ui-accent, #262e38);\n }\n\n .groups .n {\n margin-left: 4px;\n color: var(--ui-faint, #8a959c);\n }\n\n .groups button[data-selected] .n {\n color: inherit;\n }\n\n\n `\n ]\n\n @property({ type: Object }) gristConfig?: any\n\n /**\n * 지금 어느 묶음을 보고 있나.\n *\n * ── 왜 한 화면에 세 묶음인가 ─────────────────────────────────────────────\n * 참조를 별도 화면으로 만들면 **아무도 열지 않는다.** 참조는 안 봐도 아무 일이 안 일어나므로,\n * 아침에 업무 앱을 열면 「승인 대기」를 보지 「참조」를 따로 열지 않는다. 참조를 만든 이유가 의견을\n * 듣는 것인데 안 여는 화면에 넣으면 의견이 안 온다.\n *\n * 그래서 한 화면에 두고, 완료 이력도 같은 자리에서 다시 찾을 수 있게 한다.\n *\n * ── 그렇다고 한 목록에 섞지 않는다 ────────────────────────────────────────\n * 세 묶음의 물음이 다르다 — 「내가 결정할 것」, 「내가 알아야 할 것」, 「끝난 참조 이력」. 섞으면\n * 참조가 「내가 승인해야 하나」로 읽히고, 배지에 12가 뜨는데 결정할 것은 3건이 된다.\n *\n * ── 주소가 주인이다 ──────────────────────────────────────────────────────\n * `.../approval-pending-list/reference` 로 붙여 보낼 수 있다. 대상이 바뀌는 것이므로 필터와 달리\n * 주소에 담는다.\n */\n @state() private group: 'approval' | 'reference' | 'completed' = 'approval'\n\n /** 세 묶음의 건수. 탭에 함께 적는다 — 안 눌러도 얼마나 있는지 보여야 한다. */\n @state() private counts: { approval?: number; reference?: number } = {}\n @property({ type: String }) mode: 'GRID' | 'LIST' | 'CARD' = isMobileDevice() ? 'CARD' : 'GRID'\n\n @query('ox-grist') grist!: DataGrist\n\n @state() filters?: string[]\n\n get context() { return { title: i18next.t('title.approval pending list'),\n /*\n * 검색의 자리는 **기기가 정한다.**\n *\n * PC 에서는 화면 안으로 내린다. 상단 바에 두면 거른 조건과 걸러진 결과가 화면의 위아래 끝으로\n * 갈라지고, 지금 무엇으로 걸렀는지가 목록 옆에서 보이지 않는다.\n *\n * 좁은 화면에서는 상단 바에 그대로 둔다. 헤드룸에 두면 검색창과 필터가 목록을 그만큼 밀어\n * 내리고, 카드 두 장이 화면 밖으로 나간다. 상단 바는 그 자리를 아끼려고 있는 것이다.\n *\n * 이 저장소에는 두 방식이 다 있다 — 헤드룸 50곳, 상단 바 51곳. 정형인 것은 구조\n * (.header / .filters / .filter)이고 검색의 자리는 화면이 정한다.\n */\n /*\n * 검색은 **페이지 제목 줄의 오른쪽 끝**에 둔다 — 다른 화면들과 같은 자리다.\n *\n * 전에는 좁은 화면에서만 여기에 넣고, 넓은 화면에서는 헤드룸의 ox-filters-form 이 검색을\n * 그렸다. 그래서 같은 앱 안에서 검색 상자의 자리가 화면마다 달랐다.\n *\n * 헤드룸에는 좁히는 칩만 남는다. 폼은 without-search 로 남겨 두어, 나중에 어느 칸에\n * filter 를 달면 그 UI 가 저절로 생긴다(비면 아무것도 그리지 않는다).\n */\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n },\n filter: { handler: () => { this.grist.toggleHeadroom()\n }\n },\n help: 'worklist/approval-pending-list-page',\n actions: [],\n toolbar: false\n }\n }\n\n render() { const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')\n\n return html`\n <!--\n 세 묶음 — 표 **바깥** 위에 둔다.\n\n 헤드룸에 두면 필터를 접을 때 함께 사라지고, 그러면 참조로 가는 유일한 길이 없어진다.\n 필터는 접혀도 되지만(풀면 전체가 돌아온다) 이것은 접히면 안 된다.\n -->\n <div class=\"groups\">\n ${(\n [\n ['approval', 'label.group-approval', 'text.group-approval-hint'],\n ['reference', 'label.group-reference', 'text.group-reference-hint'],\n ['completed', 'label.group-completed', 'text.group-completed-hint']\n ] as const\n ).map(\n ([key, label, hint]) => html`\n <button\n ?data-selected=${this.group === key}\n title=${i18next.t(hint)}\n @click=${() => this.selectGroup(key)}\n >\n ${i18next.t(label)}\n ${key !== 'completed' && this.counts[key] !== undefined\n ? html`<span class=\"n\">${this.counts[key]!.toLocaleString(i18next.language || undefined)}</span>`\n : ''}\n </button>\n `\n )}\n </div>\n\n <ox-grist\n .mode=${mode}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}\n >\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <!-- 좁은 화면에서는 상단 바가 검색을 갖는다(위 context 주석 참조). -->\n <ox-filters-form class=\"filter\" without-search></ox-filters-form>\n <ox-input-select-buttons\n .value=${this.filters}\n .options=${[\n importantChip(),\n dueSoonChip(),\n { display: i18next.t('label.filter-resubmitted'),\n value: 'resubmitted'\n }\n ]}\n @change=${(e: CustomEvent) => { this.filters = (e.currentTarget as any)?.value\n this.grist.fetch()\n }}\n ></ox-input-select-buttons>\n <!-- <ox-filters-form class=\"filter\" autofocus without-search></ox-filters-form> -->\n </div>\n\n <ox-context-page-toolbar class=\"actions\" .context=${this.context}></ox-context-page-toolbar>\n </div>\n\n <ox-grist-personalizer slot=\"setting\"></ox-grist-personalizer>\n </ox-grist>\n `\n }\n\n /** 주소만 바꾼다 — 목록도 설정도 pageUpdated 가 한 벌로 다시 세운다. */\n private selectGroup(group: 'approval' | 'reference' | 'completed') {\n if (this.group === group) {\n return\n }\n\n const page = (this as any).lifecycle?.page\n navigate(group === 'approval' ? `${page}` : `${page}/${group}`)\n }\n\n private async syncGroup(key?: string) {\n const group = key === 'reference' || key === 'completed' ? key : 'approval'\n\n if (this.group === group && this.gristConfig) {\n return\n }\n\n this.group = group\n this.applyConfig()\n }\n\n /**\n * 안 보고 있는 묶음의 건수를 따로 센다 — 한 줄만 받아 `total` 을 읽는다.\n *\n * 보고 있는 쪽의 수는 목록 조회가 이미 알려 준다(fetchHandler). 그래서 여기서는 나머지 둘만 묻는다.\n */\n private async countOther() {\n const queries = {\n approval: 'approvalPendingList',\n reference: 'approvalReferenceList'\n } as const\n const others = (Object.keys(queries) as Array<keyof typeof queries>).filter(group => group !== this.group)\n const results = await Promise.all(\n others.map(async group => ({\n group,\n response: await client.query({\n query: gql`\n query ($pagination: Pagination) {\n responses: ${queries[group]}(pagination: $pagination) {\n total\n }\n }\n `,\n variables: { pagination: { page: 1, limit: 1 } },\n fetchPolicy: 'no-cache'\n })\n }))\n )\n\n /*\n * A count we could not fetch is not zero.\n *\n * This read `?? 0`, and the client runs with errorPolicy: 'all' — a failed query comes back as\n * a resolved response carrying `errors`, not a rejection. So any failure drew the other tab as\n * 「0」, which says \"nothing is waiting on you\". The render already draws nothing when the count\n * is undefined, so leaving it unknown is both honest and already handled.\n */\n const counts = { ...this.counts }\n for (const { group, response } of results) {\n const total = response.errors?.length ? undefined : response.data?.responses?.total\n if (typeof total == 'number') counts[group] = total\n }\n this.counts = counts\n }\n\n async pageInitialized(lifecycle) {\n /*\n * 주소가 묶음을 정한다. 주소로 바로 들어온 경우가 여기서 잡힌다 — pageUpdated 가 이 함수보다 먼저\n * 지나가므로 그것만 믿으면 `/reference` 링크가 결재 묶음을 보여준다.\n */\n await this.syncGroup(lifecycle?.resourceId)\n await this.countOther()\n }\n\n private applyConfig() {\n this.gristConfig = { list: { thumbnail: 'thumbnail',\n fields: ['name', 'description'],\n details: ['createdAt', 'due']\n },\n /*\n * 빈 화면이 참조를 가르친다.\n *\n * 참조를 처음 누른 사람은 여기서 그 개념을 만난다. 기본 문장 「해당하는 것이 없습니다」는\n * 참조가 무엇인지 말해 주지 않는다 — 내가 결정해야 하는 것인지, 안 봐도 되는 것인지 모른 채\n * 화면을 닫는다.\n *\n * 그래서 없을 때 그 묶음이 무엇인지 적는다.\n */\n emptyNote:\n this.group === 'reference'\n ? {\n icon: 'visibility',\n title: i18next.t('text.no-reference-title'),\n description: i18next.t('text.no-reference-description')\n }\n : this.group === 'completed'\n ? {\n icon: 'task_alt',\n title: i18next.t('text.no-completed-approval-title'),\n description: i18next.t('text.no-completed-approval-description')\n }\n : {\n icon: 'task_alt',\n title: i18next.t('text.no-approval-title'),\n description: i18next.t('text.no-approval-description')\n },\n /*\n * ── 승인자가 먼저 묻는 순서대로 둔다 (2026-09-01) ─────────────────────\n *\n * 전에는 앞자리를 「열기」 버튼과 **모든 줄이 같은 값인 상태 칸**이 차지했고, 승인자가 가장 먼저\n * 묻는 「누가 올렸나」는 1200px 밖에 있었다. 실측으로 이름부터 담당까지 폭 합이 1287px 이었고\n * 보이는 폭은 1150px 이었다.\n *\n * 순서를 물음의 순서로 바꾼다 — 무엇을 · 누가 · 언제까지 · 얼마나 급한가.\n *\n * ── 「열기」 버튼을 없앤다 ─────────────────────────────────────────────\n * 줄을 누르면 이미 열린다(아래 rows.handlers.click). 같은 일을 하는 버튼이 줄마다 72px 을\n * 차지하고 있었다.\n *\n * 다만 그 버튼의 아이콘이 신호를 하나 들고 있었다 — `comment` 가 있으면 `label`, 없으면\n * `new_label`. **승인자가 보다가 메모를 남기고 미뤄 둔 것**이라는 뜻이다. 버리지 않고 이름 옆에\n * 말로 적는다. 태그 아이콘 두 개로는 아무도 그 뜻으로 읽지 않는다.\n *\n * 열람 여부는 담는 값이 없다 — `ActivityApproval` 에 `readAt` 이 없어서, 열어만 보고 나가면\n * 흔적이 남지 않는다. 지금 표시하는 것은 「메모를 남겨 뒀나」이지 「열어 봤나」가 아니다.\n */\n columns: [\n { type: 'gutter', gutterName: 'sequence', fixed: true },\n {\n type: 'string',\n name: 'name',\n fixed: true,\n header: i18next.t('field.wl-subject'),\n record: {\n editable: false,\n /*\n * 이름과 설명을 한 칸에 둔다. 둘은 함께 읽는 것인데 275px 짜리 칸으로 갈라 두면 눈이\n * 오가고, 그 사이를 우선순위·차수가 가로막고 있었다.\n */\n renderer: subjectCell(approvalMarks)\n },\n filter: 'search',\n sortable: true,\n width: 320\n },\n {\n /*\n * 설명은 제목 칸 안에서 두 줄로 보인다(activity-cells 의 subjectCell). 그래서 따로 보일 칸이\n * 필요하지 않다.\n *\n * 그런데 **칸을 없애면 설명으로 검색하는 기능이 함께 사라진다.** searchText 는\n * filter: 'search' 를 선언한 컬럼마다 필터를 만들고 hidden 은 보지 않는다\n * (data-grist.ts 의 set searchText). 실측으로 확인했다 — 컬럼을 없앤 뒤 설명에만 있는\n * 낱말로 검색하면 0건이었다.\n *\n * 그래서 감춘 칸으로 남겨 검색만 남긴다.\n */\n type: 'string',\n name: 'description',\n header: i18next.t('field.description'),\n record: { editable: false },\n filter: 'search',\n hidden: true\n },\n {\n type: 'resource-object',\n name: 'creator',\n header: i18next.t('field.wl-drafter'),\n record: { editable: false },\n sortable: true,\n width: 110\n },\n {\n type: 'resource-object',\n name: 'assignee',\n header: i18next.t('field.wl-owner'),\n record: { editable: false },\n width: 110\n },\n {\n /*\n * 기한 한 칸 — 남은 시간이 위, 절대 시각이 아래다.\n *\n * 전에는 「만기 시간」(180px)과 「남은 시간」(180px)이 따로 있었다. 사람이 묻는 것은 「얼마나\n * 남았나」 하나이고, 절대 시각은 그것을 확인할 때만 본다. 두 칸으로 360px 을 쓸 일이 아니다.\n */\n type: 'string',\n name: 'dueAt',\n header: i18next.t('field.wl-due'),\n record: {\n editable: false,\n renderer: dueCell(() => this.active)\n },\n sortable: true,\n width: 170\n },\n {\n type: 'number',\n name: 'priority',\n header: i18next.t('field.wl-priority'),\n record: {\n editable: false,\n align: 'center',\n renderer: priorityCell()\n },\n width: 80\n },\n {\n type: 'select',\n name: 'state',\n header: i18next.t('field.wl-state'),\n record: {\n editable: false,\n options: [\n { display: i18next.t('label.activity-state-submitted'), value: 'submitted' },\n { display: i18next.t('label.activity-state-escalated'), value: 'escalated' },\n { display: i18next.t('label.activity-state-ended'), value: 'ended' }\n ]\n },\n width: 90\n },\n {\n type: 'number',\n name: 'round',\n header: i18next.t('field.wl-round'),\n record: { editable: false, align: 'right' },\n width: 60\n },\n {\n type: 'datetime',\n name: 'createdAt',\n header: i18next.t('field.wl-submitted-at'),\n record: { editable: false },\n sortable: true,\n filter: false,\n width: 160\n },\n {\n type: 'datetime',\n name: 'updatedAt',\n header: i18next.t('field.wl-changed-at'),\n record: { editable: false },\n sortable: true,\n filter: false,\n width: 160\n },\n {\n type: 'string',\n name: 'viewSource',\n header: i18next.t('field.wl-view-source'),\n record: {\n editable: false,\n renderer: function (value, column, record, rowIndex, field) {\n const { viewType, viewSource } = record.activity || {}\n var type = viewType !== 'board' ? 'string' : 'board'\n return getRenderer(type)(viewSource, column, record, rowIndex, field)\n }\n },\n width: 140,\n hidden: true\n },\n {\n type: 'image',\n name: 'thumbnail',\n header: i18next.t('field.thumbnail'),\n record: { editable: false },\n hidden: true,\n handlers: {\n click: (columns, data, column, record, rowIndex) => {\n this.openActivityApproval(record)\n }\n }\n }\n ],\n rows: { appendable: false,\n editable: false,\n handlers: { click: (columns, data, column, record, rowIndex) => { this.openActivityApproval(record)\n }\n }\n },\n /*\n * 급한 것부터 — 「할 일」 목록과 같은 순서다.\n *\n * 최근 변경 내림차순이었다. 받은 편지함으로는 말이 되지만 이 목록의 물음은 「무엇을 먼저\n * 판단해야 하나」다. 그 정렬에서는 나흘 지난 건이 목록 중간에 묻힌다 — 기안자가 나흘째\n * 기다리는 동안.\n *\n * 두 목록은 같은 성격이다(내가 처리해야 할 것). 순서 기준이 다르면 화면을 옮길 때마다 다른\n * 순서를 본다.\n *\n * 기한이 없는 건은 뒤로 간다 — 서버가 그 자리를 정한다(applyApprovalSortings).\n */\n sorters: [\n { name: 'dueAt',\n desc: false\n }\n ]\n }\n\n if (this.group === 'reference') {\n /*\n * ── 내 참조가 어떤 상태인가 ─────────────────────────────────────────\n * 이 묶음은 진행 중인 참조만 보인다. 끝난 건은 별도 「완료」 탭에서 결재 이력과 함께 확인한다.\n *\n * 닫힘 표시는, 이미 읽어 둔 목록 응답이 새로고침 직전에 도착한 경우를 위한 방어 표시다.\n */\n this.gristConfig.columns = [\n ...this.gristConfig.columns,\n {\n type: 'string',\n name: 'myReference',\n header: i18next.t('field.my-reference'),\n record: {\n editable: false,\n renderer: (value: any, column: any, record: any) => {\n const closed = record?.judgment === 'closed'\n const comment = (record?.comment || '').trim()\n\n if (!closed) {\n return html`<span class=\"quiet\">${i18next.t('text.reference-open')}</span>`\n }\n\n if (!comment) {\n return html`<span class=\"pill quiet\"\n ><span class=\"dot\"></span>${i18next.t('text.reference-closed-silent')}</span\n >`\n }\n\n /*\n * 의견은 있는데 시각이 없으면 **언제인지 모른다** — commentedAt 이 생기기 전에 적힌\n * 줄이다. 모르는 것을 「결정 전」으로도 「결정 후」로도 적지 않고, 의견만 보인다.\n */\n const after =\n record?.commentedAt &&\n record?.terminatedAt &&\n new Date(record.commentedAt).getTime() > new Date(record.terminatedAt).getTime()\n\n return html`\n <span class=\"line\">\n <span>${comment}</span>\n ${after ? html`<span class=\"pill warn\">${i18next.t('text.reference-after-decision')}</span>` : ''}\n </span>\n `\n }\n },\n width: 260\n }\n ]\n }\n\n if (this.group === 'completed') {\n this.gristConfig.columns = [\n ...this.gristConfig.columns,\n {\n type: 'select',\n name: 'participation',\n header: i18next.t('field.wl-participation'),\n record: {\n editable: false,\n options: [\n { display: i18next.t('label.participation-approval'), value: 'approval' },\n { display: i18next.t('label.participation-reference'), value: 'reference' }\n ]\n },\n width: 80\n }\n ]\n }\n }\n\n async pageUpdated(changes, lifecycle, changedBefore) {\n if (lifecycle?.resourceId !== changedBefore?.resourceId) {\n await this.syncGroup(lifecycle?.resourceId)\n await this.countOther()\n }\n\n if (this.active) {\n this.grist?.fetch()\n }\n }\n\n async fetchHandler({ page, limit, sortings = [], filters = [] }: FetchOption) { if (this.filters?.includes('important')) { filters = adjustFilters(filters, importantFilters())\n }\n\n if (this.filters?.includes('due')) { filters = adjustFilters(filters, dueSoonFilters())\n }\n\n if (this.filters?.includes('resubmitted')) { filters = adjustFilters(filters, [{ name: 'round', operator: 'gte', value: 2 }])\n }\n\n const response = await client.query({ query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) { responses: ${this.group === 'reference' ? 'approvalReferenceList' : this.group === 'completed' ? 'approvalCompletedList' : 'approvalPendingList'}(filters: $filters, pagination: $pagination, sortings: $sortings) { items { id\n round\n comment\n commentedAt\n judgment\n participation\n terminatedAt\n activityThread { activityInstance { activityId: id\n name\n description\n priority\n input\n refBy\n activityType\n viewType\n viewSource\n thumbnail\n updatedAt\n createdAt\n creator { id\n name\n }\n }\n state\n output\n dueAt\n assignee { id\n name\n }\n assignedAt\n }\n updatedAt\n }\n total\n }\n }\n `,\n variables: { filters,\n pagination: { page, limit },\n sortings\n }\n })\n\n const total = response.data.responses.total || 0\n /* 보고 있는 묶음의 수는 이 조회가 알려 준다. 나머지 둘만 따로 센다(countOther). */\n if (this.group !== 'completed') this.counts = { ...this.counts, [this.group]: total }\n\n return { total,\n records: (response.data.responses.items || []).map(item => { let thread = item.activityThread\n return { id: item.id,\n state: thread.state,\n ...thread.activityInstance,\n dueAt: thread.dueAt,\n assignee: thread.assignee,\n assignedAt: thread.assignedAt,\n output: thread.output,\n round: item.round,\n comment: item.comment,\n judgment: item.judgment,\n participation: item.participation || 'approval',\n commentedAt: item.commentedAt,\n terminatedAt: item.terminatedAt,\n updatedAt: item.updatedAt\n }\n })\n }\n }\n\n openActivityApproval(activityApproval) { navigate(`activity-approval/${activityApproval.id}?title=${activityApproval.name}`)\n }\n}\n"]}
@@ -1 +1 @@
1
- export default function route(page: string): "activity" | "draft-list" | "todo-list" | "activity-thread-view" | "done-list" | "approval-pending-list" | "done-list-calendar" | "activity-catalog" | "approval-done-list" | "pickable-list" | "starter-list" | "reporter-list" | "activity-list" | "activity-instance-start" | "activity-thread" | "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;
1
+ export default function route(page: string): "activity" | "activity-thread" | "activity-thread-view" | "activity-approval" | "draft-list" | "todo-list" | "done-list" | "approval-pending-list" | "done-list-calendar" | "activity-catalog" | "approval-done-list" | "pickable-list" | "starter-list" | "reporter-list" | "activity-list" | "activity-instance-start" | "activity-instance-list" | "activity-instance-search" | "activity-template-list" | "activity-store" | "activity-thread-list" | "activity-approval-list" | "installable-activity-list" | "activity-stats-list" | undefined;