@things-factory/worklist 10.1.259 → 10.1.261

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist-client/chip-counts.d.ts +13 -0
  2. package/dist-client/chip-counts.js +11 -0
  3. package/dist-client/chip-counts.js.map +1 -1
  4. package/dist-client/chip-filters.d.ts +29 -0
  5. package/dist-client/chip-filters.js +17 -1
  6. package/dist-client/chip-filters.js.map +1 -1
  7. package/dist-client/pages/approval-progress/approval-progress-page.d.ts +0 -4
  8. package/dist-client/pages/approval-progress/approval-progress-page.js +37 -31
  9. package/dist-client/pages/approval-progress/approval-progress-page.js.map +1 -1
  10. package/dist-client/pages/todo/approval-pending-list-page.d.ts +2 -3
  11. package/dist-client/pages/todo/approval-pending-list-page.js +33 -47
  12. package/dist-client/pages/todo/approval-pending-list-page.js.map +1 -1
  13. package/dist-client/pages/todo/draft-list-page.js +13 -10
  14. package/dist-client/pages/todo/draft-list-page.js.map +1 -1
  15. package/dist-client/pages/todo/todo-list-page.d.ts +0 -3
  16. package/dist-client/pages/todo/todo-list-page.js +30 -42
  17. package/dist-client/pages/todo/todo-list-page.js.map +1 -1
  18. package/dist-client/tsconfig.tsbuildinfo +1 -1
  19. package/dist-server/controllers/activity-installation/activity-installation-controller.js +1 -0
  20. package/dist-server/controllers/activity-installation/activity-installation-controller.js.map +1 -1
  21. package/dist-server/controllers/activity-installation/installable-to-activity.d.ts +10 -0
  22. package/dist-server/controllers/activity-installation/installable-to-activity.js +22 -1
  23. package/dist-server/controllers/activity-installation/installable-to-activity.js.map +1 -1
  24. package/dist-server/service/approval-progress/approval-progress-query.js +4 -0
  25. package/dist-server/service/approval-progress/approval-progress-query.js.map +1 -1
  26. package/dist-server/service/approval-progress/approval-progress-rule.d.ts +4 -0
  27. package/dist-server/service/approval-progress/approval-progress-rule.js +8 -0
  28. package/dist-server/service/approval-progress/approval-progress-rule.js.map +1 -1
  29. package/dist-server/service/approval-progress/approval-progress-type.d.ts +1 -0
  30. package/dist-server/service/approval-progress/approval-progress-type.js +4 -0
  31. package/dist-server/service/approval-progress/approval-progress-type.js.map +1 -1
  32. package/dist-server/service/index.d.ts +3 -3
  33. package/dist-server/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +12 -12
  35. package/translations/en.json +3 -2
  36. package/translations/es.json +3 -2
  37. package/translations/ja.json +3 -2
  38. package/translations/ko.json +3 -2
  39. package/translations/ms.json +3 -2
  40. package/translations/zh.json +3 -2
@@ -5,3 +5,16 @@
5
5
  * makes and the door that totals the sets.
6
6
  */
7
7
  export declare function chipCounts<F>(keys: readonly string[], filtersFor: (key: string) => F[], totals: (sets: F[][]) => Promise<number[]>): Promise<Record<string, number>>;
8
+ /** The grid column whose filter holds the chips pressed - kept in the address with the list's other conditions. */
9
+ export declare const CHIPS_FILTER = "chips";
10
+ /**
11
+ * The grid's filters split into the chips pressed (the `chips` filter, one or several) and the list's other
12
+ * conditions, which the chips' named conditions are added to. Pure.
13
+ */
14
+ export declare function splitChips<F extends {
15
+ name: string;
16
+ value?: unknown;
17
+ }>(filters: readonly F[]): {
18
+ chips: string[];
19
+ filters: F[];
20
+ };
@@ -8,4 +8,15 @@ export async function chipCounts(keys, filtersFor, totals) {
8
8
  const counted = await totals(keys.map(key => filtersFor(key)));
9
9
  return Object.fromEntries(keys.map((key, at) => [key, counted[at] ?? 0]));
10
10
  }
11
+ /** The grid column whose filter holds the chips pressed - kept in the address with the list's other conditions. */
12
+ export const CHIPS_FILTER = 'chips';
13
+ /**
14
+ * The grid's filters split into the chips pressed (the `chips` filter, one or several) and the list's other
15
+ * conditions, which the chips' named conditions are added to. Pure.
16
+ */
17
+ export function splitChips(filters) {
18
+ const chosen = filters.find(one => one.name === CHIPS_FILTER)?.value;
19
+ const chips = (Array.isArray(chosen) ? chosen : chosen ? [chosen] : []).map(String);
20
+ return { chips, filters: filters.filter(one => one.name !== CHIPS_FILTER) };
21
+ }
11
22
  //# sourceMappingURL=chip-counts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chip-counts.js","sourceRoot":"","sources":["../client/chip-counts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAI,IAAuB,EAAE,UAAgC,EAAE,MAA0C;IACvI,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAC9D,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;AAC3E,CAAC","sourcesContent":["/**\n * The numbers beside a list's condition chips (「중요」, 「만기」 ...): each is how many the list would show with that\n * chip pressed under the list's other conditions - the list's own door asked for its total, one set of filters per\n * chip, so the number is counted by the server exactly as the list is. Pure: the caller gives the filters each chip\n * makes and the door that totals the sets.\n */\nexport async function chipCounts<F>(keys: readonly string[], filtersFor: (key: string) => F[], totals: (sets: F[][]) => Promise<number[]>): Promise<Record<string, number>> {\n const counted = await totals(keys.map(key => filtersFor(key)))\n return Object.fromEntries(keys.map((key, at) => [key, counted[at] ?? 0]))\n}\n"]}
1
+ {"version":3,"file":"chip-counts.js","sourceRoot":"","sources":["../client/chip-counts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAI,IAAuB,EAAE,UAAgC,EAAE,MAA0C;IACvI,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAC9D,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;AAC3E,CAAC;AAED,mHAAmH;AACnH,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAA;AAEnC;;;GAGG;AACH,MAAM,UAAU,UAAU,CAA8C,OAAqB;IAC3F,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,KAAK,CAAA;IACpE,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACnF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,CAAA;AAC7E,CAAC","sourcesContent":["/**\n * The numbers beside a list's condition chips (「중요」, 「만기」 ...): each is how many the list would show with that\n * chip pressed under the list's other conditions - the list's own door asked for its total, one set of filters per\n * chip, so the number is counted by the server exactly as the list is. Pure: the caller gives the filters each chip\n * makes and the door that totals the sets.\n */\nexport async function chipCounts<F>(keys: readonly string[], filtersFor: (key: string) => F[], totals: (sets: F[][]) => Promise<number[]>): Promise<Record<string, number>> {\n const counted = await totals(keys.map(key => filtersFor(key)))\n return Object.fromEntries(keys.map((key, at) => [key, counted[at] ?? 0]))\n}\n\n/** The grid column whose filter holds the chips pressed - kept in the address with the list's other conditions. */\nexport const CHIPS_FILTER = 'chips'\n\n/**\n * The grid's filters split into the chips pressed (the `chips` filter, one or several) and the list's other\n * conditions, which the chips' named conditions are added to. Pure.\n */\nexport function splitChips<F extends { name: string; value?: unknown }>(filters: readonly F[]): { chips: string[]; filters: F[] } {\n const chosen = filters.find(one => one.name === CHIPS_FILTER)?.value\n const chips = (Array.isArray(chosen) ? chosen : chosen ? [chosen] : []).map(String)\n return { chips, filters: filters.filter(one => one.name !== CHIPS_FILTER) }\n}\n"]}
@@ -59,3 +59,32 @@ export declare function withChips(filters: any[], chosen?: string | string[] | n
59
59
  * read's `signal`.
60
60
  */
61
61
  export declare function countChips(query: string, keys: string[], filters: any[], signal?: AbortSignal): Promise<Record<string, number>>;
62
+ /**
63
+ * The chips as the grid's own filter (FOOTER-BUTTON-REVIEW G09): a column no one sees whose filter is the chips, so the
64
+ * pressed ones live in the address with search, sort and page - a shared link, coming back and reloading read the same
65
+ * list, and pressing one reads page 1 again. Their numbers are the grid's counts for this column (`countHandler`
66
+ * answering `{ chips: ... }`). The chips need no label: each names its condition.
67
+ */
68
+ export declare function chipsColumn(chips: {
69
+ display: string;
70
+ value: string;
71
+ hint?: string;
72
+ }[]): {
73
+ type: string;
74
+ name: string;
75
+ header: string;
76
+ hidden: boolean;
77
+ record: {
78
+ editable: boolean;
79
+ };
80
+ filter: {
81
+ type: string;
82
+ operator: string;
83
+ label: string;
84
+ options: {
85
+ display: string;
86
+ value: string;
87
+ hint?: string;
88
+ }[];
89
+ };
90
+ };
@@ -2,7 +2,7 @@ import gql from 'graphql-tag';
2
2
  import { client } from '@operato/graphql';
3
3
  import { i18next } from '@operato/i18n';
4
4
  import { adjustFilters } from '@operato/utils';
5
- import { chipCounts } from './chip-counts.js';
5
+ import { chipCounts, CHIPS_FILTER } from './chip-counts.js';
6
6
  import { ActivityThreadStatus } from './types/activity-thread.js';
7
7
  /**
8
8
  * 목록 위의 칩이 무엇을 걸러 내는가 — **한 곳에서 정한다.**
@@ -101,4 +101,20 @@ export async function countChips(query, keys, filters, signal) {
101
101
  return sets.map((_, at) => response.data?.[`c${at}`]?.total ?? 0);
102
102
  });
103
103
  }
104
+ /**
105
+ * The chips as the grid's own filter (FOOTER-BUTTON-REVIEW G09): a column no one sees whose filter is the chips, so the
106
+ * pressed ones live in the address with search, sort and page - a shared link, coming back and reloading read the same
107
+ * list, and pressing one reads page 1 again. Their numbers are the grid's counts for this column (`countHandler`
108
+ * answering `{ chips: ... }`). The chips need no label: each names its condition.
109
+ */
110
+ export function chipsColumn(chips) {
111
+ return {
112
+ type: 'string',
113
+ name: CHIPS_FILTER,
114
+ header: i18next.t('label.filter'),
115
+ hidden: true,
116
+ record: { editable: false },
117
+ filter: { type: 'select-buttons', operator: 'in', label: '', options: chips }
118
+ };
119
+ }
104
120
  //# sourceMappingURL=chip-filters.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chip-filters.js","sourceRoot":"","sources":["../client/chip-filters.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\n\nimport { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { adjustFilters } from '@operato/utils'\n\nimport { chipCounts } from './chip-counts.js'\nimport { ActivityThreadStatus } from './types/activity-thread.js'\n\n/**\n * 목록 위의 칩이 무엇을 걸러 내는가 — **한 곳에서 정한다.**\n *\n * ── 왜 모았나 (2026-09-02) ─────────────────────────────────────────────────\n * 「중요」와 「만기」가 다섯 화면에 있는데, 그 뜻이 다섯 곳에 복사돼 있었다.\n *\n * priority gte 2 5곳\n * dueAt lte (지금 + 24 * 3600 * 1000) 4곳\n *\n * 한 화면에서 「중요」를 우선순위 3 이상으로 바꾸면 나머지 넷은 2로 남는다. 그러면 **같은 이름의 칩이\n * 화면마다 다른 것을 걸러 낸다.** 사용자는 그것을 알 방법이 없다.\n *\n * ── 칩은 컬럼 필터가 아니다 ────────────────────────────────────────────────\n * `ox-filters-form` 은 칸 하나와 연산자 하나를 그린다. 이 칩들은 **이름 붙인 조건 묶음**이다 —\n * 「중요」는 `priority = ?` 가 아니라 `priority >= 2` 이고, 「만기」는 지금 시각에서 계산한다.\n *\n * 그래서 사용자가 스스로 만들 수 없다. 「중요 = 우선순위 2 이상」은 제품이 아는 사실이다.\n *\n * ── 그러니 그 뜻을 화면이 말해야 한다 ──────────────────────────────────────\n * 이름만 보이면 눌러 보고 결과를 되짚어야 한다. 칩마다 `hint` 를 함께 낸다(마우스를 올리면 보인다).\n */\n\n/** 「중요」의 경계. 이 값 이상이 중요하다. */\nexport const IMPORTANT_FROM = 2\n\n/** 「만기」의 창. 이 시간 안에 만기가 오는 것. */\nexport const DUE_WITHIN_HOURS = 24\n\n/*\n * 연산자를 리터럴로 좁힌다(`as const`). `adjustFilters` 가 받는 타입은 연산자가 정해진 낱말 중\n * 하나인데, 그 타입이 내보내지지 않아 여기서 가져올 수 없다. `string` 으로 두면 넘길 때 거절된다.\n */\n\n/** 우선순위가 경계 이상인 것. */\nexport function importantFilters() {\n return [{ name: 'priority', operator: 'gte' as const, value: IMPORTANT_FROM }]\n}\n\n/**\n * 만기가 창 안에 드는 것.\n *\n * 부를 때마다 지금 시각으로 계산한다 — 화면을 열어 둔 채 시간이 지나면 「만기 임박」의 범위도 함께\n * 움직여야 한다.\n */\nexport function dueSoonFilters() {\n const until = new Date(Date.now() + DUE_WITHIN_HOURS * 3600 * 1000).toISOString().replace('T', ' ')\n\n return [{ name: 'dueAt', operator: 'lte' as const, value: until }]\n}\n\n/** 칩 하나 — 이름과 뜻. */\nexport function importantChip() {\n return {\n display: i18next.t('label.filter-important'),\n value: 'important',\n hint: i18next.t('text.filter-important-hint', { from: IMPORTANT_FROM })\n }\n}\n\nexport function dueSoonChip() {\n return {\n display: i18next.t('label.filter-due'),\n value: 'due',\n hint: i18next.t('text.filter-due-hint', { hours: DUE_WITHIN_HOURS })\n }\n}\n\n/*\n * 칩마다 거는 조건 — 화면마다 if 로 따로 적던 것을 여기 한 곳에 둔다. 같은 이름의 칩은 어느 화면에서나 같은 것을\n * 걸러 낸다(위 「왜 모았나」). 시각에서 계산하는 것은 부를 때마다 지금으로 계산한다.\n */\nconst CONDITIONS: Record<string, () => { name: string; operator: any; value: any }[]> = {\n important: importantFilters,\n due: dueSoonFilters,\n proceeding: () => [{ name: 'state', operator: 'eq', value: ActivityThreadStatus.Started }],\n /* 최근 24시간 이내에 끝낸 일 */\n recent: () => [{ name: 'terminatedAt', operator: 'gte', value: new Date(Date.now() - 24 * 3600 * 1000).toISOString().replace('T', ' ') }],\n aborted: () => [{ name: 'state', operator: 'eq', value: ActivityThreadStatus.Aborted }],\n resubmitted: () => [{ name: 'round', operator: 'gte', value: 2 }],\n rejected: () => [{ name: 'judgment', operator: 'eq', value: 'rejected' }]\n}\n\n/** The list's filters with the pressed chips' conditions added. */\nexport function withChips(filters: any[], chosen?: string | string[] | null): any[] {\n const keys = chosen instanceof Array ? chosen : chosen ? [chosen] : []\n return keys.reduce((narrowed, key) => (CONDITIONS[key] ? adjustFilters(narrowed, CONDITIONS[key]()) : narrowed), filters)\n}\n\n/**\n * How many each chip would show under the list's other conditions (`filters`, without the pressed chip) - the list\n * door `query` asked for its total once per chip, in one request. A list's `countHandler` asks it under the grid\n * read's `signal`.\n */\nexport async function countChips(query: string, keys: string[], filters: any[], signal?: AbortSignal): Promise<Record<string, number>> {\n return chipCounts(\n keys,\n key => withChips(filters, key),\n async sets => {\n const response = await client.query({\n query: gql`\n query (${sets.map((_, at) => `$f${at}: [Filter!]`).join(', ')}) {\n ${sets.map((_, at) => `c${at}: ${query}(filters: $f${at}, pagination: { page: 1, limit: 1 }) { total }`).join('\\n')}\n }\n `,\n variables: Object.fromEntries(sets.map((set, at) => [`f${at}`, set])),\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n return sets.map((_, at) => response.data?.[`c${at}`]?.total ?? 0)\n }\n )\n}\n"]}
1
+ {"version":3,"file":"chip-filters.js","sourceRoot":"","sources":["../client/chip-filters.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\n\nimport { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { adjustFilters } from '@operato/utils'\n\nimport { chipCounts, CHIPS_FILTER } from './chip-counts.js'\nimport { ActivityThreadStatus } from './types/activity-thread.js'\n\n/**\n * 목록 위의 칩이 무엇을 걸러 내는가 — **한 곳에서 정한다.**\n *\n * ── 왜 모았나 (2026-09-02) ─────────────────────────────────────────────────\n * 「중요」와 「만기」가 다섯 화면에 있는데, 그 뜻이 다섯 곳에 복사돼 있었다.\n *\n * priority gte 2 5곳\n * dueAt lte (지금 + 24 * 3600 * 1000) 4곳\n *\n * 한 화면에서 「중요」를 우선순위 3 이상으로 바꾸면 나머지 넷은 2로 남는다. 그러면 **같은 이름의 칩이\n * 화면마다 다른 것을 걸러 낸다.** 사용자는 그것을 알 방법이 없다.\n *\n * ── 칩은 컬럼 필터가 아니다 ────────────────────────────────────────────────\n * `ox-filters-form` 은 칸 하나와 연산자 하나를 그린다. 이 칩들은 **이름 붙인 조건 묶음**이다 —\n * 「중요」는 `priority = ?` 가 아니라 `priority >= 2` 이고, 「만기」는 지금 시각에서 계산한다.\n *\n * 그래서 사용자가 스스로 만들 수 없다. 「중요 = 우선순위 2 이상」은 제품이 아는 사실이다.\n *\n * ── 그러니 그 뜻을 화면이 말해야 한다 ──────────────────────────────────────\n * 이름만 보이면 눌러 보고 결과를 되짚어야 한다. 칩마다 `hint` 를 함께 낸다(마우스를 올리면 보인다).\n */\n\n/** 「중요」의 경계. 이 값 이상이 중요하다. */\nexport const IMPORTANT_FROM = 2\n\n/** 「만기」의 창. 이 시간 안에 만기가 오는 것. */\nexport const DUE_WITHIN_HOURS = 24\n\n/*\n * 연산자를 리터럴로 좁힌다(`as const`). `adjustFilters` 가 받는 타입은 연산자가 정해진 낱말 중\n * 하나인데, 그 타입이 내보내지지 않아 여기서 가져올 수 없다. `string` 으로 두면 넘길 때 거절된다.\n */\n\n/** 우선순위가 경계 이상인 것. */\nexport function importantFilters() {\n return [{ name: 'priority', operator: 'gte' as const, value: IMPORTANT_FROM }]\n}\n\n/**\n * 만기가 창 안에 드는 것.\n *\n * 부를 때마다 지금 시각으로 계산한다 — 화면을 열어 둔 채 시간이 지나면 「만기 임박」의 범위도 함께\n * 움직여야 한다.\n */\nexport function dueSoonFilters() {\n const until = new Date(Date.now() + DUE_WITHIN_HOURS * 3600 * 1000).toISOString().replace('T', ' ')\n\n return [{ name: 'dueAt', operator: 'lte' as const, value: until }]\n}\n\n/** 칩 하나 — 이름과 뜻. */\nexport function importantChip() {\n return {\n display: i18next.t('label.filter-important'),\n value: 'important',\n hint: i18next.t('text.filter-important-hint', { from: IMPORTANT_FROM })\n }\n}\n\nexport function dueSoonChip() {\n return {\n display: i18next.t('label.filter-due'),\n value: 'due',\n hint: i18next.t('text.filter-due-hint', { hours: DUE_WITHIN_HOURS })\n }\n}\n\n/*\n * 칩마다 거는 조건 — 화면마다 if 로 따로 적던 것을 여기 한 곳에 둔다. 같은 이름의 칩은 어느 화면에서나 같은 것을\n * 걸러 낸다(위 「왜 모았나」). 시각에서 계산하는 것은 부를 때마다 지금으로 계산한다.\n */\nconst CONDITIONS: Record<string, () => { name: string; operator: any; value: any }[]> = {\n important: importantFilters,\n due: dueSoonFilters,\n proceeding: () => [{ name: 'state', operator: 'eq', value: ActivityThreadStatus.Started }],\n /* 최근 24시간 이내에 끝낸 일 */\n recent: () => [{ name: 'terminatedAt', operator: 'gte', value: new Date(Date.now() - 24 * 3600 * 1000).toISOString().replace('T', ' ') }],\n aborted: () => [{ name: 'state', operator: 'eq', value: ActivityThreadStatus.Aborted }],\n resubmitted: () => [{ name: 'round', operator: 'gte', value: 2 }],\n rejected: () => [{ name: 'judgment', operator: 'eq', value: 'rejected' }]\n}\n\n/** The list's filters with the pressed chips' conditions added. */\nexport function withChips(filters: any[], chosen?: string | string[] | null): any[] {\n const keys = chosen instanceof Array ? chosen : chosen ? [chosen] : []\n return keys.reduce((narrowed, key) => (CONDITIONS[key] ? adjustFilters(narrowed, CONDITIONS[key]()) : narrowed), filters)\n}\n\n/**\n * How many each chip would show under the list's other conditions (`filters`, without the pressed chip) - the list\n * door `query` asked for its total once per chip, in one request. A list's `countHandler` asks it under the grid\n * read's `signal`.\n */\nexport async function countChips(query: string, keys: string[], filters: any[], signal?: AbortSignal): Promise<Record<string, number>> {\n return chipCounts(\n keys,\n key => withChips(filters, key),\n async sets => {\n const response = await client.query({\n query: gql`\n query (${sets.map((_, at) => `$f${at}: [Filter!]`).join(', ')}) {\n ${sets.map((_, at) => `c${at}: ${query}(filters: $f${at}, pagination: { page: 1, limit: 1 }) { total }`).join('\\n')}\n }\n `,\n variables: Object.fromEntries(sets.map((set, at) => [`f${at}`, set])),\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n return sets.map((_, at) => response.data?.[`c${at}`]?.total ?? 0)\n }\n )\n}\n\n/**\n * The chips as the grid's own filter (FOOTER-BUTTON-REVIEW G09): a column no one sees whose filter is the chips, so the\n * pressed ones live in the address with search, sort and page - a shared link, coming back and reloading read the same\n * list, and pressing one reads page 1 again. Their numbers are the grid's counts for this column (`countHandler`\n * answering `{ chips: ... }`). The chips need no label: each names its condition.\n */\nexport function chipsColumn(chips: { display: string; value: string; hint?: string }[]) {\n return {\n type: 'string',\n name: CHIPS_FILTER,\n header: i18next.t('label.filter'),\n hidden: true,\n record: { editable: false },\n filter: { type: 'select-buttons', operator: 'in', label: '', options: chips }\n }\n}\n"]}
@@ -1,6 +1,5 @@
1
1
  import '@operato/data-grist';
2
2
  import '@operato/data-grist/ox-filters-form.js';
3
- import '@operato/input/ox-input-select-buttons.js';
4
3
  import '../activity-instance/activity-instance-view.js';
5
4
  import { DataGrist, FetchOption } from '@operato/data-grist';
6
5
  import { PageView } from '@operato/shell';
@@ -16,9 +15,6 @@ export declare class ApprovalProgressPage extends ApprovalProgressPage_base {
16
15
  grist: DataGrist;
17
16
  gristConfig?: any;
18
17
  mode: 'GRID' | 'LIST' | 'CARD';
19
- /** The chips pressed — states, and 기한 넘김. */
20
- chips: string[];
21
- counts: Map<string, number>;
22
18
  get context(): {
23
19
  title: string;
24
20
  search: {
@@ -1,7 +1,6 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import '@operato/data-grist';
3
3
  import '@operato/data-grist/ox-filters-form.js';
4
- import '@operato/input/ox-input-select-buttons.js';
5
4
  import '../activity-instance/activity-instance-view.js';
6
5
  import gql from 'graphql-tag';
7
6
  import { css, html } from 'lit';
@@ -18,6 +17,8 @@ import { waitedParts } from '../../approval-progress.js';
18
17
  import { whenShortText } from '../../when.js';
19
18
  const STATES = ['PENDING', 'APPROVED', 'REJECTED', 'ABORTED'];
20
19
  const OVERDUE = 'OVERDUE';
20
+ /** The grid column whose filter holds the chips pressed. */
21
+ const CHIPS = 'chips';
21
22
  /**
22
23
  * 결재 현황 — every approval of the domain, whatever its kind, and where each stands (ADR-0183).
23
24
  *
@@ -28,9 +29,6 @@ let ApprovalProgressPage = class ApprovalProgressPage extends localize(i18next)(
28
29
  constructor() {
29
30
  super(...arguments);
30
31
  this.mode = isMobileDevice() ? 'CARD' : 'GRID';
31
- /** The chips pressed — states, and 기한 넘김. */
32
- this.chips = [STATES[0]];
33
- this.counts = new Map();
34
32
  }
35
33
  static { this.styles = [
36
34
  /* The work surface frame for a list screen (ADR-0060 decision 8). */
@@ -67,25 +65,17 @@ let ApprovalProgressPage = class ApprovalProgressPage extends localize(i18next)(
67
65
  }
68
66
  };
69
67
  }
68
+ /*
69
+ * The kind, department, raised days, the chips (states and 기한 넘김), search and page are the grid's own, kept in
70
+ * the address (FOOTER-BUTTON-REVIEW G09): a shared link, coming back and reloading read the same list, and a change of
71
+ * them reads page 1 again. `mode` is only how the rows are drawn, never a condition.
72
+ */
70
73
  render() {
71
74
  return html `
72
- <ox-grist class="page-fill" .mode=${this.mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
75
+ <ox-grist class="page-fill" .mode=${this.mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)} ?url-params-sensitive=${this.active}>
73
76
  <div slot="headroom" class="header">
74
77
  <div class="filters">
75
78
  <ox-filters-form class="filter" without-search></ox-filters-form>
76
- <ox-input-select-buttons
77
- multiple
78
- .value=${this.chips}
79
- .options=${[...STATES, OVERDUE].map(value => ({
80
- value,
81
- display: i18next.t(`label.approval-progress-${value.toLowerCase()}`),
82
- count: this.counts.get(value) ?? 0
83
- }))}
84
- @change=${(e) => {
85
- this.chips = e.currentTarget?.value || [];
86
- this.grist.fetch();
87
- }}
88
- ></ox-input-select-buttons>
89
79
  </div>
90
80
  </div>
91
81
  </ox-grist>
@@ -127,7 +117,7 @@ let ApprovalProgressPage = class ApprovalProgressPage extends localize(i18next)(
127
117
  { type: 'string', name: 'departmentName', header: i18next.t('field.approval-department'), record: { editable: false }, width: 100 },
128
118
  { type: 'string', name: 'stateLabel', header: i18next.t('label.status'), record: { editable: false, align: 'center' }, width: 70 },
129
119
  { type: 'string', name: 'stepText', header: i18next.t('field.approval-step'), record: { editable: false, align: 'center' }, width: 60 },
130
- { type: 'string', name: 'waiting', header: i18next.t('field.approval-waiting-on'), record: { editable: false }, width: 180 },
120
+ { type: 'string', name: 'waiting', header: i18next.t('field.approval-approver'), record: { editable: false }, width: 180 },
131
121
  { type: 'string', name: 'waited', header: i18next.t('field.approval-waited'), record: { editable: false }, width: 100 },
132
122
  {
133
123
  type: 'datetime',
@@ -137,7 +127,22 @@ let ApprovalProgressPage = class ApprovalProgressPage extends localize(i18next)(
137
127
  width: 130
138
128
  },
139
129
  { type: 'date', name: 'raisedAt', header: i18next.t('field.approval-raised-at'), record: { editable: false, renderer: value => whenShortText(value) }, filter: { operator: 'between' }, width: 140 },
140
- { type: 'datetime', name: 'endedAt', header: i18next.t('field.approval-ended-at'), record: { editable: false, renderer: value => whenShortText(value) }, width: 140 }
130
+ { type: 'datetime', name: 'endedAt', header: i18next.t('field.approval-ended-at'), record: { editable: false, renderer: value => whenShortText(value) }, width: 140 },
131
+ {
132
+ /* The chips - the states and 기한 넘김, waiting ones first; each names itself, so no label. Their numbers are the read's. */
133
+ type: 'string',
134
+ name: CHIPS,
135
+ header: i18next.t('label.status'),
136
+ hidden: true,
137
+ record: { editable: false },
138
+ filter: {
139
+ type: 'select-buttons',
140
+ operator: 'in',
141
+ label: '',
142
+ value: [STATES[0]],
143
+ options: [...STATES, OVERDUE].map(value => ({ value, display: i18next.t(`label.approval-progress-${value.toLowerCase()}`) }))
144
+ }
145
+ }
141
146
  ],
142
147
  rows: { appendable: false, selectable: false },
143
148
  sorters: []
@@ -190,14 +195,16 @@ let ApprovalProgressPage = class ApprovalProgressPage extends localize(i18next)(
190
195
  const kinds = valueOf('activityName');
191
196
  const [from, to] = valueOf('raisedAt') || [];
192
197
  const search = filters.find(one => one.operator === 'search')?.value?.replace(/^%|%$/g, '');
198
+ const pressed = valueOf(CHIPS);
199
+ const chips = pressed ? [].concat(pressed) : [];
193
200
  return {
194
201
  activityNames: kinds ? [].concat(kinds) : undefined,
195
202
  departmentIds: valueOf('departmentId') ? [valueOf('departmentId')] : undefined,
196
203
  from: from ? new Date(from) : undefined,
197
204
  /* The range's last day counts whole - the server's `to` is exclusive. */
198
205
  to: to ? new Date(new Date(to).getTime() + 24 * 3600 * 1000) : undefined,
199
- states: this.chips.filter(chip => chip !== OVERDUE),
200
- overdueOnly: this.chips.includes(OVERDUE) || undefined,
206
+ states: chips.filter(chip => chip !== OVERDUE),
207
+ overdueOnly: chips.includes(OVERDUE) || undefined,
201
208
  search: search || undefined
202
209
  };
203
210
  }
@@ -218,6 +225,7 @@ let ApprovalProgressPage = class ApprovalProgressPage extends localize(i18next)(
218
225
  step
219
226
  steps
220
227
  waitingOn
228
+ decidedBy
221
229
  waitingRole
222
230
  waitingSince
223
231
  dueAt
@@ -238,7 +246,10 @@ let ApprovalProgressPage = class ApprovalProgressPage extends localize(i18next)(
238
246
  fetchPolicy: 'no-cache'
239
247
  });
240
248
  const result = response.data?.approvalProgress;
241
- this.counts = new Map([...(result?.counts || []).map((one) => [one.state, one.count]), [OVERDUE, result?.overdue ?? 0]]);
249
+ /* The chips' numbers come with the rows - handed to the grid as its counts for the chips column, which the form draws. */
250
+ const counts = { [CHIPS]: Object.fromEntries([...(result?.counts || []).map((one) => [one.state, one.count]), [OVERDUE, result?.overdue ?? 0]]) };
251
+ this.grist.counts = counts;
252
+ this.grist.dispatchEvent(new CustomEvent('counts-change', { detail: counts }));
242
253
  return {
243
254
  total: result?.total || 0,
244
255
  records: (result?.items || []).map((item) => ({
@@ -251,7 +262,10 @@ let ApprovalProgressPage = class ApprovalProgressPage extends localize(i18next)(
251
262
  }))
252
263
  };
253
264
  }
265
+ /* Who decides it: whom a waiting case waits on, who approved last or rejected a decided one. */
254
266
  waitingText(item) {
267
+ if (item.state !== 'PENDING')
268
+ return (item.decidedBy || []).join(', ');
255
269
  const names = (item.waitingOn || []).join(', ');
256
270
  return item.waitingRole ? `${item.waitingRole}${names ? ` (${names})` : ''}` : names;
257
271
  }
@@ -279,14 +293,6 @@ __decorate([
279
293
  state(),
280
294
  __metadata("design:type", String)
281
295
  ], ApprovalProgressPage.prototype, "mode", void 0);
282
- __decorate([
283
- state(),
284
- __metadata("design:type", Array)
285
- ], ApprovalProgressPage.prototype, "chips", void 0);
286
- __decorate([
287
- state(),
288
- __metadata("design:type", Object)
289
- ], ApprovalProgressPage.prototype, "counts", void 0);
290
296
  ApprovalProgressPage = __decorate([
291
297
  customElement('approval-progress-page')
292
298
  ], ApprovalProgressPage);
@@ -1 +1 @@
1
- {"version":3,"file":"approval-progress-page.js","sourceRoot":"","sources":["../../../client/pages/approval-progress/approval-progress-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist'\nimport '@operato/data-grist/ox-filters-form.js'\nimport '@operato/input/ox-input-select-buttons.js'\nimport '../activity-instance/activity-instance-view.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n\nimport { dueCell } from '../../activity-cells.js'\nimport { waitedParts } from '../../approval-progress.js'\nimport { whenShortText } from '../../when.js'\n\nconst STATES = ['PENDING', 'APPROVED', 'REJECTED', 'ABORTED'] as const\nconst OVERDUE = 'OVERDUE'\n\n/**\n * 결재 현황 — every approval of the domain, whatever its kind, and where each stands (ADR-0183).\n *\n * The rows, the step and whom it waits on are the server's (`approvalProgress`); this page only asks with the\n * conditions pressed and draws. A row opens the case's own view, whose door checks the reader again.\n */\n@customElement('approval-progress-page')\nexport class ApprovalProgressPage extends localize(i18next)(PageView) {\n static styles = [\n /* The work surface frame for a list screen (ADR-0060 decision 8). */\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n css`\n .header {\n grid-template-areas: 'filters actions';\n }\n\n .header .filters {\n padding: 0;\n background: none;\n border-radius: 0;\n gap: 12px;\n }\n `\n ]\n\n @query('ox-grist') grist!: DataGrist\n\n @state() gristConfig?: any\n @state() mode: 'GRID' | 'LIST' | 'CARD' = isMobileDevice() ? 'CARD' : 'GRID'\n /** The chips pressed — states, and 기한 넘김. */\n @state() chips: string[] = [STATES[0]]\n @state() counts = new Map<string, number>()\n\n get context() {\n return {\n title: i18next.t('title.approval progress'),\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n },\n filter: {\n handler: () => {\n this.grist.toggleHeadroom()\n }\n }\n }\n }\n\n render() {\n return html`\n <ox-grist class=\"page-fill\" .mode=${this.mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <ox-filters-form class=\"filter\" without-search></ox-filters-form>\n <ox-input-select-buttons\n multiple\n .value=${this.chips}\n .options=${[...STATES, OVERDUE].map(value => ({\n value,\n display: i18next.t(`label.approval-progress-${value.toLowerCase()}`),\n count: this.counts.get(value) ?? 0\n }))}\n @change=${(e: CustomEvent) => {\n this.chips = (e.currentTarget as any)?.value || []\n this.grist.fetch()\n }}\n ></ox-input-select-buttons>\n </div>\n </div>\n </ox-grist>\n `\n }\n\n async pageInitialized() {\n this.gristConfig = {\n pagination: { pages: [50, 100, 200] },\n list: { fields: ['headline', 'activityLabel'], details: ['requesterName', 'waiting', 'waited'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n {\n type: 'gutter',\n gutterName: 'button',\n icon: 'assignment',\n iconOnly: false,\n title: i18next.t('button.view'),\n width: 72,\n handlers: { click: (_columns, _data, _column, record) => this.openCase(record) }\n },\n {\n /* The kind filter - the kinds that have a case here, as the server names them. */\n type: 'select',\n name: 'activityName',\n header: i18next.t('field.approval-kind'),\n hidden: true,\n filter: { operator: 'in', options: () => this.kindOptions() }\n },\n { type: 'string', name: 'activityLabel', header: i18next.t('field.approval-kind'), record: { editable: false }, width: 140 },\n { type: 'string', name: 'headline', header: i18next.t('field.approval-headline'), record: { editable: false }, filter: 'search', width: 260 },\n { type: 'string', name: 'requesterName', header: i18next.t('field.approval-requester'), record: { editable: false }, filter: 'search', width: 90 },\n {\n type: 'select',\n name: 'departmentId',\n header: i18next.t('field.approval-department'),\n hidden: true,\n filter: { operator: 'eq', options: () => this.departmentOptions() }\n },\n { type: 'string', name: 'departmentName', header: i18next.t('field.approval-department'), record: { editable: false }, width: 100 },\n { type: 'string', name: 'stateLabel', header: i18next.t('label.status'), record: { editable: false, align: 'center' }, width: 70 },\n { type: 'string', name: 'stepText', header: i18next.t('field.approval-step'), record: { editable: false, align: 'center' }, width: 60 },\n { type: 'string', name: 'waiting', header: i18next.t('field.approval-waiting-on'), record: { editable: false }, width: 180 },\n { type: 'string', name: 'waited', header: i18next.t('field.approval-waited'), record: { editable: false }, width: 100 },\n {\n type: 'datetime',\n name: 'dueAt',\n header: i18next.t('field.approval-due-at'),\n record: { editable: false, renderer: dueCell(() => this.active) },\n width: 130\n },\n { type: 'date', name: 'raisedAt', header: i18next.t('field.approval-raised-at'), record: { editable: false, renderer: value => whenShortText(value) }, filter: { operator: 'between' }, width: 140 },\n { type: 'datetime', name: 'endedAt', header: i18next.t('field.approval-ended-at'), record: { editable: false, renderer: value => whenShortText(value) }, width: 140 }\n ],\n rows: { appendable: false, selectable: false },\n sorters: []\n }\n }\n\n private openCase(record: any) {\n openPopup(html`<activity-instance-view activity-instance-id=${record.id}></activity-instance-view>`, {\n backdrop: true,\n size: 'large',\n title: record.headline\n })\n }\n\n private async kindOptions() {\n const response = await client.query({\n query: gql`\n query {\n approvalProgress(pagination: { page: 1, limit: 1 }) {\n kinds {\n name\n label\n labelTKey\n }\n }\n }\n `\n })\n return (response.data?.approvalProgress?.kinds || []).map((kind: any) => ({\n value: kind.name,\n display: String(i18next.t(kind.labelTKey, { defaultValue: kind.label }))\n }))\n }\n\n private async departmentOptions() {\n const response = await client.query({\n query: gql`\n query {\n departments(pagination: { page: 1, limit: 500 }, sortings: [{ name: \"name\" }]) {\n items {\n id\n name\n }\n }\n }\n `\n })\n return (response.data?.departments?.items || []).map((one: any) => ({ value: one.id, display: one.name }))\n }\n\n /** The grid's conditions as the server's filter - the column filters, the search box and the chips. */\n private filterOf(filters: any[]) {\n const valueOf = (name: string) => filters.find(one => one.name === name && one.operator !== 'search')?.value\n const kinds = valueOf('activityName')\n const [from, to] = valueOf('raisedAt') || []\n const search = filters.find(one => one.operator === 'search')?.value?.replace(/^%|%$/g, '')\n\n return {\n activityNames: kinds ? [].concat(kinds) : undefined,\n departmentIds: valueOf('departmentId') ? [valueOf('departmentId')] : undefined,\n from: from ? new Date(from) : undefined,\n /* The range's last day counts whole - the server's `to` is exclusive. */\n to: to ? new Date(new Date(to).getTime() + 24 * 3600 * 1000) : undefined,\n states: this.chips.filter(chip => chip !== OVERDUE),\n overdueOnly: this.chips.includes(OVERDUE) || undefined,\n search: search || undefined\n }\n }\n\n async fetchHandler({ page, limit, filters = [] }: FetchOption) {\n const response = await client.query({\n query: gql`\n query ($filter: ApprovalProgressFilter, $pagination: Pagination) {\n approvalProgress(filter: $filter, pagination: $pagination) {\n items {\n id\n activityName\n activityLabel\n activityLabelTKey\n headline\n requesterName\n departmentName\n state\n step\n steps\n waitingOn\n waitingRole\n waitingSince\n dueAt\n overdue\n raisedAt\n endedAt\n }\n total\n counts {\n state\n count\n }\n overdue\n }\n }\n `,\n variables: { filter: this.filterOf(filters), pagination: { page, limit } },\n fetchPolicy: 'no-cache'\n })\n\n const result = response.data?.approvalProgress\n this.counts = new Map([...(result?.counts || []).map((one: any) => [one.state, one.count]), [OVERDUE, result?.overdue ?? 0]])\n\n return {\n total: result?.total || 0,\n records: (result?.items || []).map((item: any) => ({\n ...item,\n activityLabel: item.activityLabelTKey ? String(i18next.t(item.activityLabelTKey, { defaultValue: item.activityLabel })) : item.activityLabel,\n stateLabel: i18next.t(`label.approval-progress-${String(item.state).toLowerCase()}`),\n stepText: item.step ? `${item.step} / ${item.steps}` : '',\n waiting: this.waitingText(item),\n waited: this.waitedText(item)\n }))\n }\n }\n\n private waitingText(item: any) {\n const names = (item.waitingOn || []).join(', ')\n return item.waitingRole ? `${item.waitingRole}${names ? ` (${names})` : ''}` : names\n }\n\n private waitedText(item: any) {\n const parts = item.state === 'PENDING' ? waitedParts(item.waitingSince) : undefined\n if (!parts) return ''\n /* Past due is the due column's to say (dueCell) - said here too, it would be said twice. */\n return parts.days\n ? i18next.t('text.waited-days', parts)\n : parts.hours\n ? i18next.t('text.waited-hours', parts)\n : i18next.t('text.waited-minutes', parts)\n }\n}\n"]}
1
+ {"version":3,"file":"approval-progress-page.js","sourceRoot":"","sources":["../../../client/pages/approval-progress/approval-progress-page.ts"],"names":[],"mappings":";AAAA,OAAO,qBAAqB,CAAA;AAC5B,OAAO,wCAAwC,CAAA;AAC/C,OAAO,gDAAgD,CAAA;AAEvD,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAe,MAAM,qBAAqB,CAAA;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAC/G,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAE7C,MAAM,MAAM,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAU,CAAA;AACtE,MAAM,OAAO,GAAG,SAAS,CAAA;AACzB,4DAA4D;AAC5D,MAAM,KAAK,GAAG,OAAO,CAAA;AAErB;;;;;GAKG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;IAA9D;;QAwBI,SAAI,GAA6B,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;IAmP9E,CAAC;aA1QQ,WAAM,GAAG;QACd,qEAAqE;QACrE,qBAAqB;QACrB,eAAe;QACf,iBAAiB;QACjB,kBAAkB;QAClB,GAAG,CAAA;;;;;;;;;;;KAWF;KACF,AAlBY,CAkBZ;IAOD,IAAI,OAAO;QACT,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC;YAC3C,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;gBACN,OAAO,EAAE,GAAG,EAAE;oBACZ,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAA;gBAC7B,CAAC;aACF;SACF,CAAA;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,OAAO,IAAI,CAAA;0CAC2B,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,WAAW,kBAAkB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B,IAAI,CAAC,MAAM;;;;;;;KAO7J,CAAA;IACH,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC,WAAW,GAAG;YACjB,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE;YACrC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE;YAChG,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE;gBAC1C;oBACE,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,QAAQ;oBACpB,IAAI,EAAE,YAAY;oBAClB,QAAQ,EAAE,KAAK;oBACf,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC;oBAC/B,KAAK,EAAE,EAAE;oBACT,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;iBACjF;gBACD;oBACE,kFAAkF;oBAClF,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,cAAc;oBACpB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC;oBACxC,MAAM,EAAE,IAAI;oBACZ,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE;iBAC9D;gBACD,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC5H,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC7I,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;gBAClJ;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,cAAc;oBACpB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC;oBAC9C,MAAM,EAAE,IAAI;oBACZ,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE;iBACpE;gBACD,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBACnI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;gBAClI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;gBACvI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC1H,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBACvH;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;oBAC1C,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;oBACjE,KAAK,EAAE,GAAG;iBACX;gBACD,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBACpM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBACrK;oBACE,yHAAyH;oBACzH,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,KAAK;oBACX,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;oBACjC,MAAM,EAAE,IAAI;oBACZ,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,MAAM,EAAE;wBACN,IAAI,EAAE,gBAAgB;wBACtB,QAAQ,EAAE,IAAI;wBACd,KAAK,EAAE,EAAE;wBACT,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;wBAClB,OAAO,EAAE,CAAC,GAAG,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,2BAA2B,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;qBAC9H;iBACF;aACF;YACD,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;YAC9C,OAAO,EAAE,EAAE;SACZ,CAAA;IACH,CAAC;IAEO,QAAQ,CAAC,MAAW;QAC1B,SAAS,CAAC,IAAI,CAAA,gDAAgD,MAAM,CAAC,EAAE,4BAA4B,EAAE;YACnG,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,MAAM,CAAC,QAAQ;SACvB,CAAC,CAAA;IACJ,CAAC;IAEO,KAAK,CAAC,WAAW;QACvB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;OAUT;SACF,CAAC,CAAA;QACF,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;YACxE,KAAK,EAAE,IAAI,CAAC,IAAI;YAChB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;SACzE,CAAC,CAAC,CAAA;IACL,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC7B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;OAST;SACF,CAAC,CAAA;QACF,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IAC5G,CAAC;IAED,uGAAuG;IAC/F,QAAQ,CAAC,OAAc;QAC7B,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,EAAE,KAAK,CAAA;QAC5G,MAAM,KAAK,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;QACrC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAA;QAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;QAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;QAC9B,MAAM,KAAK,GAAa,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAEzD,OAAO;YACL,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;YACnD,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;YAC9E,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YACvC,yEAAyE;YACzE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YACxE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC;YAC9C,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,SAAS;YACjD,MAAM,EAAE,MAAM,IAAI,SAAS;SAC5B,CAAA;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,GAAG,EAAE,EAAe;QAC3D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BT;YACD,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YAC1E,WAAW,EAAE,UAAU;SACxB,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;QAC9C,0HAA0H;QAC1H,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACtJ,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;QAC1B,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;QAE9E,OAAO;YACL,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;YACzB,OAAO,EAAE,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;gBACjD,GAAG,IAAI;gBACP,aAAa,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa;gBAC5I,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,2BAA2B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;gBACpF,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE;gBACzD,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;gBAC/B,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;aAC9B,CAAC,CAAC;SACJ,CAAA;IACH,CAAC;IAED,gGAAgG;IACxF,WAAW,CAAC,IAAS;QAC3B,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtE,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/C,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;IACtF,CAAC;IAEO,UAAU,CAAC,IAAS;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QACnF,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAA;QACrB,4FAA4F;QAC5F,OAAO,KAAK,CAAC,IAAI;YACf,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,kBAAkB,EAAE,KAAK,CAAC;YACtC,CAAC,CAAC,KAAK,CAAC,KAAK;gBACX,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,EAAE,KAAK,CAAC;gBACvC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAA;IAC/C,CAAC;;AArPkB;IAAlB,KAAK,CAAC,UAAU,CAAC;8BAAS,SAAS;mDAAA;AAE3B;IAAR,KAAK,EAAE;;yDAAkB;AACjB;IAAR,KAAK,EAAE;;kDAAoE;AAxBjE,oBAAoB;IADhC,aAAa,CAAC,wBAAwB,CAAC;GAC3B,oBAAoB,CA2QhC","sourcesContent":["import '@operato/data-grist'\nimport '@operato/data-grist/ox-filters-form.js'\nimport '../activity-instance/activity-instance-view.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n\nimport { dueCell } from '../../activity-cells.js'\nimport { waitedParts } from '../../approval-progress.js'\nimport { whenShortText } from '../../when.js'\n\nconst STATES = ['PENDING', 'APPROVED', 'REJECTED', 'ABORTED'] as const\nconst OVERDUE = 'OVERDUE'\n/** The grid column whose filter holds the chips pressed. */\nconst CHIPS = 'chips'\n\n/**\n * 결재 현황 — every approval of the domain, whatever its kind, and where each stands (ADR-0183).\n *\n * The rows, the step and whom it waits on are the server's (`approvalProgress`); this page only asks with the\n * conditions pressed and draws. A row opens the case's own view, whose door checks the reader again.\n */\n@customElement('approval-progress-page')\nexport class ApprovalProgressPage extends localize(i18next)(PageView) {\n static styles = [\n /* The work surface frame for a list screen (ADR-0060 decision 8). */\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n css`\n .header {\n grid-template-areas: 'filters actions';\n }\n\n .header .filters {\n padding: 0;\n background: none;\n border-radius: 0;\n gap: 12px;\n }\n `\n ]\n\n @query('ox-grist') grist!: DataGrist\n\n @state() gristConfig?: any\n @state() mode: 'GRID' | 'LIST' | 'CARD' = isMobileDevice() ? 'CARD' : 'GRID'\n\n get context() {\n return {\n title: i18next.t('title.approval progress'),\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n },\n filter: {\n handler: () => {\n this.grist.toggleHeadroom()\n }\n }\n }\n }\n\n /*\n * The kind, department, raised days, the chips (states and 기한 넘김), search and page are the grid's own, kept in\n * the address (FOOTER-BUTTON-REVIEW G09): a shared link, coming back and reloading read the same list, and a change of\n * them reads page 1 again. `mode` is only how the rows are drawn, never a condition.\n */\n render() {\n return html`\n <ox-grist class=\"page-fill\" .mode=${this.mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)} ?url-params-sensitive=${this.active}>\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <ox-filters-form class=\"filter\" without-search></ox-filters-form>\n </div>\n </div>\n </ox-grist>\n `\n }\n\n async pageInitialized() {\n this.gristConfig = {\n pagination: { pages: [50, 100, 200] },\n list: { fields: ['headline', 'activityLabel'], details: ['requesterName', 'waiting', 'waited'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n {\n type: 'gutter',\n gutterName: 'button',\n icon: 'assignment',\n iconOnly: false,\n title: i18next.t('button.view'),\n width: 72,\n handlers: { click: (_columns, _data, _column, record) => this.openCase(record) }\n },\n {\n /* The kind filter - the kinds that have a case here, as the server names them. */\n type: 'select',\n name: 'activityName',\n header: i18next.t('field.approval-kind'),\n hidden: true,\n filter: { operator: 'in', options: () => this.kindOptions() }\n },\n { type: 'string', name: 'activityLabel', header: i18next.t('field.approval-kind'), record: { editable: false }, width: 140 },\n { type: 'string', name: 'headline', header: i18next.t('field.approval-headline'), record: { editable: false }, filter: 'search', width: 260 },\n { type: 'string', name: 'requesterName', header: i18next.t('field.approval-requester'), record: { editable: false }, filter: 'search', width: 90 },\n {\n type: 'select',\n name: 'departmentId',\n header: i18next.t('field.approval-department'),\n hidden: true,\n filter: { operator: 'eq', options: () => this.departmentOptions() }\n },\n { type: 'string', name: 'departmentName', header: i18next.t('field.approval-department'), record: { editable: false }, width: 100 },\n { type: 'string', name: 'stateLabel', header: i18next.t('label.status'), record: { editable: false, align: 'center' }, width: 70 },\n { type: 'string', name: 'stepText', header: i18next.t('field.approval-step'), record: { editable: false, align: 'center' }, width: 60 },\n { type: 'string', name: 'waiting', header: i18next.t('field.approval-approver'), record: { editable: false }, width: 180 },\n { type: 'string', name: 'waited', header: i18next.t('field.approval-waited'), record: { editable: false }, width: 100 },\n {\n type: 'datetime',\n name: 'dueAt',\n header: i18next.t('field.approval-due-at'),\n record: { editable: false, renderer: dueCell(() => this.active) },\n width: 130\n },\n { type: 'date', name: 'raisedAt', header: i18next.t('field.approval-raised-at'), record: { editable: false, renderer: value => whenShortText(value) }, filter: { operator: 'between' }, width: 140 },\n { type: 'datetime', name: 'endedAt', header: i18next.t('field.approval-ended-at'), record: { editable: false, renderer: value => whenShortText(value) }, width: 140 },\n {\n /* The chips - the states and 기한 넘김, waiting ones first; each names itself, so no label. Their numbers are the read's. */\n type: 'string',\n name: CHIPS,\n header: i18next.t('label.status'),\n hidden: true,\n record: { editable: false },\n filter: {\n type: 'select-buttons',\n operator: 'in',\n label: '',\n value: [STATES[0]],\n options: [...STATES, OVERDUE].map(value => ({ value, display: i18next.t(`label.approval-progress-${value.toLowerCase()}`) }))\n }\n }\n ],\n rows: { appendable: false, selectable: false },\n sorters: []\n }\n }\n\n private openCase(record: any) {\n openPopup(html`<activity-instance-view activity-instance-id=${record.id}></activity-instance-view>`, {\n backdrop: true,\n size: 'large',\n title: record.headline\n })\n }\n\n private async kindOptions() {\n const response = await client.query({\n query: gql`\n query {\n approvalProgress(pagination: { page: 1, limit: 1 }) {\n kinds {\n name\n label\n labelTKey\n }\n }\n }\n `\n })\n return (response.data?.approvalProgress?.kinds || []).map((kind: any) => ({\n value: kind.name,\n display: String(i18next.t(kind.labelTKey, { defaultValue: kind.label }))\n }))\n }\n\n private async departmentOptions() {\n const response = await client.query({\n query: gql`\n query {\n departments(pagination: { page: 1, limit: 500 }, sortings: [{ name: \"name\" }]) {\n items {\n id\n name\n }\n }\n }\n `\n })\n return (response.data?.departments?.items || []).map((one: any) => ({ value: one.id, display: one.name }))\n }\n\n /** The grid's conditions as the server's filter - the column filters, the search box and the chips. */\n private filterOf(filters: any[]) {\n const valueOf = (name: string) => filters.find(one => one.name === name && one.operator !== 'search')?.value\n const kinds = valueOf('activityName')\n const [from, to] = valueOf('raisedAt') || []\n const search = filters.find(one => one.operator === 'search')?.value?.replace(/^%|%$/g, '')\n const pressed = valueOf(CHIPS)\n const chips: string[] = pressed ? [].concat(pressed) : []\n\n return {\n activityNames: kinds ? [].concat(kinds) : undefined,\n departmentIds: valueOf('departmentId') ? [valueOf('departmentId')] : undefined,\n from: from ? new Date(from) : undefined,\n /* The range's last day counts whole - the server's `to` is exclusive. */\n to: to ? new Date(new Date(to).getTime() + 24 * 3600 * 1000) : undefined,\n states: chips.filter(chip => chip !== OVERDUE),\n overdueOnly: chips.includes(OVERDUE) || undefined,\n search: search || undefined\n }\n }\n\n async fetchHandler({ page, limit, filters = [] }: FetchOption) {\n const response = await client.query({\n query: gql`\n query ($filter: ApprovalProgressFilter, $pagination: Pagination) {\n approvalProgress(filter: $filter, pagination: $pagination) {\n items {\n id\n activityName\n activityLabel\n activityLabelTKey\n headline\n requesterName\n departmentName\n state\n step\n steps\n waitingOn\n decidedBy\n waitingRole\n waitingSince\n dueAt\n overdue\n raisedAt\n endedAt\n }\n total\n counts {\n state\n count\n }\n overdue\n }\n }\n `,\n variables: { filter: this.filterOf(filters), pagination: { page, limit } },\n fetchPolicy: 'no-cache'\n })\n\n const result = response.data?.approvalProgress\n /* The chips' numbers come with the rows - handed to the grid as its counts for the chips column, which the form draws. */\n const counts = { [CHIPS]: Object.fromEntries([...(result?.counts || []).map((one: any) => [one.state, one.count]), [OVERDUE, result?.overdue ?? 0]]) }\n this.grist.counts = counts\n this.grist.dispatchEvent(new CustomEvent('counts-change', { detail: counts }))\n\n return {\n total: result?.total || 0,\n records: (result?.items || []).map((item: any) => ({\n ...item,\n activityLabel: item.activityLabelTKey ? String(i18next.t(item.activityLabelTKey, { defaultValue: item.activityLabel })) : item.activityLabel,\n stateLabel: i18next.t(`label.approval-progress-${String(item.state).toLowerCase()}`),\n stepText: item.step ? `${item.step} / ${item.steps}` : '',\n waiting: this.waitingText(item),\n waited: this.waitedText(item)\n }))\n }\n }\n\n /* Who decides it: whom a waiting case waits on, who approved last or rejected a decided one. */\n private waitingText(item: any) {\n if (item.state !== 'PENDING') return (item.decidedBy || []).join(', ')\n const names = (item.waitingOn || []).join(', ')\n return item.waitingRole ? `${item.waitingRole}${names ? ` (${names})` : ''}` : names\n }\n\n private waitedText(item: any) {\n const parts = item.state === 'PENDING' ? waitedParts(item.waitingSince) : undefined\n if (!parts) return ''\n /* Past due is the due column's to say (dueCell) - said here too, it would be said twice. */\n return parts.days\n ? i18next.t('text.waited-days', parts)\n : parts.hours\n ? i18next.t('text.waited-hours', parts)\n : i18next.t('text.waited-minutes', parts)\n }\n}\n"]}
@@ -1,6 +1,5 @@
1
1
  import '@operato/data-grist';
2
2
  import '@operato/data-grist/ox-filters-form.js';
3
- import '@operato/input/ox-input-select-buttons.js';
4
3
  import '../../components/work-box-groups.js';
5
4
  import { DataGrist, FetchOption } from '@operato/data-grist';
6
5
  import { PageView } from '@operato/shell';
@@ -52,7 +51,6 @@ export declare class ApprovalPendingListPage extends ApprovalPendingListPage_bas
52
51
  private get shownCounts();
53
52
  mode: 'GRID' | 'LIST' | 'CARD';
54
53
  grist: DataGrist;
55
- filters?: string[];
56
54
  get context(): {
57
55
  title: string;
58
56
  search: {
@@ -71,8 +69,9 @@ export declare class ApprovalPendingListPage extends ApprovalPendingListPage_bas
71
69
  private syncGroup;
72
70
  pageInitialized(lifecycle: any): Promise<void>;
73
71
  private applyConfig;
72
+ /** The chips that narrow every group - each a named condition (chip-filters). */
73
+ private chips;
74
74
  pageUpdated(changes: any, lifecycle: any, changedBefore: any): Promise<void>;
75
- private counted;
76
75
  countHandler({ filters, signal }: FetchOption): Promise<{
77
76
  chips: Record<string, number>;
78
77
  }>;
@@ -1,14 +1,15 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import '@operato/data-grist';
3
3
  import '@operato/data-grist/ox-filters-form.js';
4
- import '@operato/input/ox-input-select-buttons.js';
5
4
  import '../../components/work-box-groups.js';
6
5
  import gql from 'graphql-tag';
7
6
  import { css, html } from 'lit';
8
7
  import { customElement, property, query, state } from 'lit/decorators.js';
8
+ import { keyed } from 'lit/directives/keyed.js';
9
9
  import { DataGrist, getRenderer } from '@operato/data-grist';
10
10
  import { client, QueryController } from '@operato/graphql';
11
- import { countChips, dueSoonChip, importantChip, withChips } from '../../chip-filters.js';
11
+ import { splitChips } from '../../chip-counts.js';
12
+ import { chipsColumn, countChips, dueSoonChip, importantChip, withChips } from '../../chip-filters.js';
12
13
  import { i18next, localize } from '@operato/i18n';
13
14
  import { navigate, PageView } from '@operato/shell';
14
15
  import { listWithThumbnail } from './list-thumbnail.js';
@@ -198,40 +199,28 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
198
199
  @select=${(e) => this.selectGroup(e.detail)}
199
200
  ></work-box-groups>
200
201
 
201
- <ox-grist
202
- class="page-fill"
203
- .mode=${mode}
204
- .config=${this.gristConfig}
205
- .fetchHandler=${this.fetchHandler.bind(this)}
206
- .countHandler=${this.countHandler.bind(this)}
207
- @counts-change=${() => this.requestUpdate()}
208
- .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')}
209
- >
210
- <div slot="headroom" class="header">
211
- <div class="filters">
212
- <!-- 좁은 화면에서는 상단 바가 검색을 갖는다(위 context 주석 참조). -->
213
- <ox-filters-form class="filter" without-search></ox-filters-form>
214
- <ox-input-select-buttons
215
- .value=${this.filters}
216
- .options=${this.counted([
217
- importantChip(),
218
- dueSoonChip(),
219
- { display: i18next.t('label.filter-resubmitted'),
220
- value: 'resubmitted'
221
- }
222
- ])}
223
- @change=${(e) => {
224
- this.filters = e.currentTarget?.value;
225
- this.grist.fetch();
226
- }}
227
- ></ox-input-select-buttons>
228
- <!-- <ox-filters-form class="filter" autofocus without-search></ox-filters-form> -->
202
+ <!--
203
+ The group is a different list, named by the path; the list's own query - the chips, search, sort, page - is the
204
+ grid's, kept in the address (FOOTER-BUTTON-REVIEW G09). A group is its own grid, bound to its own path.
205
+ -->
206
+ ${keyed(this.group, html `<ox-grist
207
+ class="page-fill"
208
+ .mode=${mode}
209
+ .config=${this.gristConfig}
210
+ .fetchHandler=${this.fetchHandler.bind(this)}
211
+ .countHandler=${this.countHandler.bind(this)}
212
+ .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')}
213
+ ?url-params-sensitive=${this.active}
214
+ >
215
+ <div slot="headroom" class="header">
216
+ <div class="filters">
217
+ <!-- 좁은 화면에서는 상단 바가 검색을 갖는다(위 context 주석 참조). 칩도 이 폼이 그린다(chipsColumn). -->
218
+ <ox-filters-form class="filter" without-search></ox-filters-form>
219
+ </div>
229
220
  </div>
230
221
 
231
- </div>
232
-
233
- <ox-grist-personalizer slot="setting"></ox-grist-personalizer>
234
- </ox-grist>
222
+ <ox-grist-personalizer slot="setting"></ox-grist-personalizer>
223
+ </ox-grist>`)}
235
224
  `;
236
225
  }
237
226
  /** 주소만 바꾼다 — 목록도 설정도 pageUpdated 가 한 벌로 다시 세운다. */
@@ -543,30 +532,31 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
543
532
  }
544
533
  ];
545
534
  }
535
+ this.gristConfig.columns = [...this.gristConfig.columns, chipsColumn(this.chips())];
536
+ }
537
+ /** The chips that narrow every group - each a named condition (chip-filters). */
538
+ chips() {
539
+ return [importantChip(), dueSoonChip(), { display: i18next.t('label.filter-resubmitted'), value: 'resubmitted' }];
546
540
  }
541
+ /* The grid re-reads by its own address when the page comes back - nothing to ask here. */
547
542
  async pageUpdated(changes, lifecycle, changedBefore) {
548
543
  if (lifecycle?.resourceId !== changedBefore?.resourceId) {
549
544
  await this.syncGroup(lifecycle?.resourceId);
550
545
  }
551
- if (this.active) {
552
- this.grist?.fetch();
553
- }
554
- }
555
- /* 읽은 것에 그림이 하나도 없으면 카드마다 빈 그림 칸을 세우지 않는다(§ list-thumbnail.ts). */
556
- counted(chips) {
557
- return chips.map(chip => ({ ...chip, count: this.grist?.counts?.chips?.[chip.value] ?? 0 }));
558
546
  }
559
547
  /* The chips' numbers - asked beside each read, on the list's filters before any chip is added. */
560
548
  async countHandler({ filters = [], signal }) {
561
- return { chips: await countChips(this.group === 'reference' ? 'approvalReferenceList' : this.group === 'completed' ? 'approvalCompletedList' : 'approvalPendingList', ['important', 'due', 'resubmitted'], filters, signal) };
549
+ return { chips: await countChips(this.group === 'reference' ? 'approvalReferenceList' : this.group === 'completed' ? 'approvalCompletedList' : 'approvalPendingList', ['important', 'due', 'resubmitted'], splitChips(filters).filters, signal) };
562
550
  }
551
+ /* 읽은 것에 그림이 하나도 없으면 카드마다 빈 그림 칸을 세우지 않는다(§ list-thumbnail.ts). */
563
552
  async fetchHandler(options) {
564
553
  const fetched = await this.fetchRows(options);
565
554
  this.gristConfig = listWithThumbnail(this.gristConfig, fetched?.records || []);
566
555
  return fetched;
567
556
  }
568
557
  async fetchRows({ page, limit, sortings = [], filters = [] }) {
569
- filters = withChips(filters, this.filters);
558
+ const { chips, filters: others } = splitChips(filters);
559
+ filters = withChips(others, chips);
570
560
  const response = await client.query({ query: gql `
571
561
  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
572
562
  round
@@ -659,10 +649,6 @@ __decorate([
659
649
  query('ox-grist'),
660
650
  __metadata("design:type", DataGrist)
661
651
  ], ApprovalPendingListPage.prototype, "grist", void 0);
662
- __decorate([
663
- state(),
664
- __metadata("design:type", Array)
665
- ], ApprovalPendingListPage.prototype, "filters", void 0);
666
652
  ApprovalPendingListPage = __decorate([
667
653
  customElement('approval-pending-list-page')
668
654
  ], ApprovalPendingListPage);