@things-factory/worklist 10.1.255 → 10.1.257
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/activity-state-counts.d.ts +3 -3
- package/dist-client/activity-state-counts.js +6 -5
- package/dist-client/activity-state-counts.js.map +1 -1
- package/dist-client/chip-counts.d.ts +1 -1
- package/dist-client/chip-counts.js +1 -1
- package/dist-client/chip-counts.js.map +1 -1
- package/dist-client/chip-filters.d.ts +3 -2
- package/dist-client/chip-filters.js +5 -3
- package/dist-client/chip-filters.js.map +1 -1
- package/dist-client/components/activity-/bowner-view.js +26 -40
- package/dist-client/components/activity-/bowner-view.js.map +1 -1
- package/dist-client/components/activity-instance-preview.js +37 -49
- package/dist-client/components/activity-instance-preview.js.map +1 -1
- package/dist-client/components/activity-intro-view.js +26 -40
- package/dist-client/components/activity-intro-view.js.map +1 -1
- package/dist-client/components/activity-reporter-view.js +26 -40
- package/dist-client/components/activity-reporter-view.js.map +1 -1
- package/dist-client/components/activity-starter-form.d.ts +2 -3
- package/dist-client/components/activity-starter-form.js +52 -64
- package/dist-client/components/activity-starter-form.js.map +1 -1
- package/dist-client/components/mutation-outcome.d.ts +6 -0
- package/dist-client/components/mutation-outcome.js +9 -0
- package/dist-client/components/mutation-outcome.js.map +1 -1
- package/dist-client/components/say-outcome.d.ts +3 -0
- package/dist-client/components/say-outcome.js +3 -0
- package/dist-client/components/say-outcome.js.map +1 -1
- package/dist-client/entity360/person-open-work-section.d.ts +2 -3
- package/dist-client/entity360/person-open-work-section.js +17 -31
- package/dist-client/entity360/person-open-work-section.js.map +1 -1
- package/dist-client/pages/activity/activity-list-page.d.ts +4 -2
- package/dist-client/pages/activity/activity-list-page.js +6 -11
- package/dist-client/pages/activity/activity-list-page.js.map +1 -1
- package/dist-client/pages/activity/activity-page.d.ts +13 -7
- package/dist-client/pages/activity/activity-page.js +111 -167
- package/dist-client/pages/activity/activity-page.js.map +1 -1
- package/dist-client/pages/activity-approval/activity-approval-page.d.ts +14 -8
- package/dist-client/pages/activity-approval/activity-approval-page.js +107 -123
- package/dist-client/pages/activity-approval/activity-approval-page.js.map +1 -1
- package/dist-client/pages/activity-catalog/activity-catalog-page.d.ts +10 -9
- package/dist-client/pages/activity-catalog/activity-catalog-page.js +38 -70
- package/dist-client/pages/activity-catalog/activity-catalog-page.js.map +1 -1
- package/dist-client/pages/activity-catalog/activity-detail-page.d.ts +6 -6
- package/dist-client/pages/activity-catalog/activity-detail-page.js +30 -57
- package/dist-client/pages/activity-catalog/activity-detail-page.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-list-page.js +2 -2
- package/dist-client/pages/activity-instance/activity-instance-list-page.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-search-page.d.ts +6 -3
- package/dist-client/pages/activity-instance/activity-instance-search-page.js +27 -29
- package/dist-client/pages/activity-instance/activity-instance-search-page.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-start-page.d.ts +15 -8
- package/dist-client/pages/activity-instance/activity-instance-start-page.js +130 -239
- package/dist-client/pages/activity-instance/activity-instance-start-page.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-view.d.ts +2 -2
- package/dist-client/pages/activity-instance/activity-instance-view.js +109 -118
- package/dist-client/pages/activity-instance/activity-instance-view.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-page.d.ts +15 -6
- package/dist-client/pages/activity-thread/activity-thread-page.js +128 -131
- package/dist-client/pages/activity-thread/activity-thread-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-view-page.d.ts +6 -6
- package/dist-client/pages/activity-thread/activity-thread-view-page.js +28 -53
- package/dist-client/pages/activity-thread/activity-thread-view-page.js.map +1 -1
- package/dist-client/pages/todo/approval-done-list-page.d.ts +3 -3
- package/dist-client/pages/todo/approval-done-list-page.js +5 -14
- package/dist-client/pages/todo/approval-done-list-page.js.map +1 -1
- package/dist-client/pages/todo/approval-pending-list-page.d.ts +19 -9
- package/dist-client/pages/todo/approval-pending-list-page.js +46 -59
- package/dist-client/pages/todo/approval-pending-list-page.js.map +1 -1
- package/dist-client/pages/todo/draft-list-page.d.ts +8 -2
- package/dist-client/pages/todo/draft-list-page.js +24 -23
- package/dist-client/pages/todo/draft-list-page.js.map +1 -1
- package/dist-client/pages/todo/pickable-list-page.d.ts +3 -3
- package/dist-client/pages/todo/pickable-list-page.js +5 -14
- package/dist-client/pages/todo/pickable-list-page.js.map +1 -1
- package/dist-client/pages/todo/todo-list-page.d.ts +11 -5
- package/dist-client/pages/todo/todo-list-page.js +33 -36
- package/dist-client/pages/todo/todo-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -12
|
@@ -16,8 +16,6 @@ export declare class TodoListPage extends TodoListPage_base {
|
|
|
16
16
|
grist: DataGrist;
|
|
17
17
|
gristConfig?: any;
|
|
18
18
|
filters?: string[];
|
|
19
|
-
/** How many each chip would show under the list's other conditions, counted by the list's own door. */
|
|
20
|
-
chipCounts: Map<string, number>;
|
|
21
19
|
/**
|
|
22
20
|
* 지금 어느 묶음을 보고 있나 — 할 것 · 제출한 것 · 완료 (pages/todo/work-box.ts).
|
|
23
21
|
*
|
|
@@ -27,6 +25,14 @@ export declare class TodoListPage extends TodoListPage_base {
|
|
|
27
25
|
private group;
|
|
28
26
|
/** 기다리는 두 묶음의 건수. 끝난 묶음은 이력이라 세지 않는다. */
|
|
29
27
|
private counts;
|
|
28
|
+
/**
|
|
29
|
+
* 안 보고 있는 기다리는 묶음의 건수 — 한 줄만 받아 total 을 읽는다(ADR-0190). 묶음이 주소에서 정해진 뒤에
|
|
30
|
+
* 묻는다.
|
|
31
|
+
*/
|
|
32
|
+
private otherCount;
|
|
33
|
+
private get otherGroup();
|
|
34
|
+
/** 보고 있는 묶음의 수는 목록 조회가, 나머지는 otherCount 가 안다. 못 읽은 수는 0 이 아니다. */
|
|
35
|
+
private get shownCounts();
|
|
30
36
|
get context(): {
|
|
31
37
|
title: string;
|
|
32
38
|
search: {
|
|
@@ -45,13 +51,13 @@ export declare class TodoListPage extends TodoListPage_base {
|
|
|
45
51
|
/** 주소만 바꾼다 — 목록도 설정도 pageUpdated 가 한 벌로 다시 세운다. */
|
|
46
52
|
private selectGroup;
|
|
47
53
|
private syncGroup;
|
|
48
|
-
/** 안 보고 있는 기다리는 묶음의 건수 — 한 줄만 받아 total 을 읽는다. 못 읽은 수는 0 이 아니다. */
|
|
49
|
-
private countOther;
|
|
50
54
|
pageInitialized(lifecycle: any): Promise<void>;
|
|
51
55
|
private applyConfig;
|
|
52
56
|
pageUpdated(changes: any, lifecycle: any, changedBefore: any): Promise<void>;
|
|
53
57
|
private counted;
|
|
54
|
-
|
|
58
|
+
countHandler({ filters, signal }: FetchOption): Promise<{
|
|
59
|
+
chips: Record<string, number>;
|
|
60
|
+
}>;
|
|
55
61
|
fetchHandler(options: FetchOption): Promise<{
|
|
56
62
|
total: any;
|
|
57
63
|
records: any;
|
|
@@ -7,7 +7,7 @@ import gql from 'graphql-tag';
|
|
|
7
7
|
import { css, html } from 'lit';
|
|
8
8
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
9
9
|
import { DataGrist, getRenderer } from '@operato/data-grist';
|
|
10
|
-
import { client } from '@operato/graphql';
|
|
10
|
+
import { client, QueryController } from '@operato/graphql';
|
|
11
11
|
import { countChips, dueSoonChip, importantChip, withChips } from '../../chip-filters.js';
|
|
12
12
|
import { i18next, localize } from '@operato/i18n';
|
|
13
13
|
import { navigate, PageView } from '@operato/shell';
|
|
@@ -30,8 +30,6 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
30
30
|
constructor() {
|
|
31
31
|
super(...arguments);
|
|
32
32
|
this.mode = isMobileDevice() ? 'CARD' : 'GRID';
|
|
33
|
-
/** How many each chip would show under the list's other conditions, counted by the list's own door. */
|
|
34
|
-
this.chipCounts = new Map();
|
|
35
33
|
/**
|
|
36
34
|
* 지금 어느 묶음을 보고 있나 — 할 것 · 제출한 것 · 완료 (pages/todo/work-box.ts).
|
|
37
35
|
*
|
|
@@ -41,6 +39,22 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
41
39
|
this.group = 'todo';
|
|
42
40
|
/** 기다리는 두 묶음의 건수. 끝난 묶음은 이력이라 세지 않는다. */
|
|
43
41
|
this.counts = {};
|
|
42
|
+
/**
|
|
43
|
+
* 안 보고 있는 기다리는 묶음의 건수 — 한 줄만 받아 total 을 읽는다(ADR-0190). 묶음이 주소에서 정해진 뒤에
|
|
44
|
+
* 묻는다.
|
|
45
|
+
*/
|
|
46
|
+
this.otherCount = new QueryController(this, gql `
|
|
47
|
+
query ($pagination: Pagination, $todo: Boolean!) {
|
|
48
|
+
todo: todoList(pagination: $pagination) @include(if: $todo) {
|
|
49
|
+
total
|
|
50
|
+
}
|
|
51
|
+
submitted: submittedList(pagination: $pagination) @skip(if: $todo) {
|
|
52
|
+
total
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`, {
|
|
56
|
+
variables: () => this.gristConfig ? { todo: this.otherGroup === 'todo', pagination: { page: 1, limit: 1 } } : undefined
|
|
57
|
+
});
|
|
44
58
|
}
|
|
45
59
|
static { this.styles = [
|
|
46
60
|
/* The work surface frame for a list screen (ADR-0060 decision 8). */
|
|
@@ -84,6 +98,14 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
84
98
|
|
|
85
99
|
`
|
|
86
100
|
]; }
|
|
101
|
+
get otherGroup() {
|
|
102
|
+
return this.group === 'submitted' ? 'todo' : 'submitted';
|
|
103
|
+
}
|
|
104
|
+
/** 보고 있는 묶음의 수는 목록 조회가, 나머지는 otherCount 가 안다. 못 읽은 수는 0 이 아니다. */
|
|
105
|
+
get shownCounts() {
|
|
106
|
+
const total = this.otherCount.data?.[this.otherGroup]?.total;
|
|
107
|
+
return typeof total == 'number' ? { ...this.counts, [this.otherGroup]: total } : this.counts;
|
|
108
|
+
}
|
|
87
109
|
get context() {
|
|
88
110
|
return {
|
|
89
111
|
title: i18next.t('title.todo list'),
|
|
@@ -131,7 +153,7 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
131
153
|
['todo', 'label.group-todo', 'text.group-todo-hint'],
|
|
132
154
|
['submitted', 'label.group-submitted', 'text.group-submitted-hint'],
|
|
133
155
|
['completed', 'label.group-completed', 'text.group-done-hint']
|
|
134
|
-
].map(([key, label, hint]) => ({ key, label, hint, count: countsOnTab(key) ? this.
|
|
156
|
+
].map(([key, label, hint]) => ({ key, label, hint, count: countsOnTab(key) ? this.shownCounts[key] : undefined }))}
|
|
135
157
|
.selected=${this.group}
|
|
136
158
|
@select=${(e) => this.selectGroup(e.detail)}
|
|
137
159
|
></work-box-groups>
|
|
@@ -140,6 +162,8 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
140
162
|
.mode=${mode}
|
|
141
163
|
.config=${this.gristConfig}
|
|
142
164
|
.fetchHandler=${this.fetchHandler.bind(this)}
|
|
165
|
+
.countHandler=${this.countHandler.bind(this)}
|
|
166
|
+
@counts-change=${() => this.requestUpdate()}
|
|
143
167
|
.personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')}
|
|
144
168
|
>
|
|
145
169
|
<div slot="headroom" class="header">
|
|
@@ -194,28 +218,9 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
194
218
|
this.filters = [];
|
|
195
219
|
this.applyConfig();
|
|
196
220
|
}
|
|
197
|
-
/** 안 보고 있는 기다리는 묶음의 건수 — 한 줄만 받아 total 을 읽는다. 못 읽은 수는 0 이 아니다. */
|
|
198
|
-
async countOther() {
|
|
199
|
-
const other = this.group === 'submitted' ? 'todo' : 'submitted';
|
|
200
|
-
const response = await client.query({
|
|
201
|
-
query: gql `
|
|
202
|
-
query ($pagination: Pagination) {
|
|
203
|
-
responses: ${QUERY_OF[other]}(pagination: $pagination) {
|
|
204
|
-
total
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
`,
|
|
208
|
-
variables: { pagination: { page: 1, limit: 1 } },
|
|
209
|
-
fetchPolicy: 'no-cache'
|
|
210
|
-
});
|
|
211
|
-
const total = response.errors?.length ? undefined : response.data?.responses?.total;
|
|
212
|
-
if (typeof total == 'number')
|
|
213
|
-
this.counts = { ...this.counts, [other]: total };
|
|
214
|
-
}
|
|
215
221
|
async pageInitialized(lifecycle) {
|
|
216
222
|
/* pageUpdated 가 이 함수보다 먼저 지나가므로 주소로 바로 들어온 경우는 여기서 잡힌다. */
|
|
217
223
|
this.syncGroup(lifecycle?.resourceId);
|
|
218
|
-
await this.countOther();
|
|
219
224
|
}
|
|
220
225
|
applyConfig() {
|
|
221
226
|
const completed = this.group === 'completed';
|
|
@@ -381,7 +386,6 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
381
386
|
async pageUpdated(changes, lifecycle, changedBefore) {
|
|
382
387
|
if (lifecycle?.resourceId !== changedBefore?.resourceId) {
|
|
383
388
|
this.syncGroup(lifecycle?.resourceId);
|
|
384
|
-
await this.countOther();
|
|
385
389
|
}
|
|
386
390
|
if (this.active) {
|
|
387
391
|
this.grist?.fetch();
|
|
@@ -389,13 +393,11 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
389
393
|
}
|
|
390
394
|
/* 읽은 것에 그림이 하나도 없으면 카드마다 빈 그림 칸을 세우지 않는다(§ list-thumbnail.ts). */
|
|
391
395
|
counted(chips) {
|
|
392
|
-
return chips.map(chip => ({ ...chip, count: this.
|
|
396
|
+
return chips.map(chip => ({ ...chip, count: this.grist?.counts?.chips?.[chip.value] ?? 0 }));
|
|
393
397
|
}
|
|
394
|
-
/* The chips' numbers - asked beside
|
|
395
|
-
|
|
396
|
-
countChips(QUERY_OF[this.group], this.chips().map(chip => chip.value), filters)
|
|
397
|
-
.then(counts => (this.chipCounts = counts))
|
|
398
|
-
.catch(e => console.error(e));
|
|
398
|
+
/* The chips' numbers - asked beside each read, on the list's filters before any chip is added. */
|
|
399
|
+
async countHandler({ filters = [], signal }) {
|
|
400
|
+
return { chips: await countChips(QUERY_OF[this.group], this.chips().map(chip => chip.value), filters, signal) };
|
|
399
401
|
}
|
|
400
402
|
async fetchHandler(options) {
|
|
401
403
|
const fetched = await this.fetchRows(options);
|
|
@@ -403,7 +405,6 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
403
405
|
return fetched;
|
|
404
406
|
}
|
|
405
407
|
async fetchRows({ page, limit, sortings = [], filters = [] }) {
|
|
406
|
-
this.countChips(filters);
|
|
407
408
|
filters = withChips(filters, this.filters);
|
|
408
409
|
const response = await client.query({
|
|
409
410
|
query: gql `
|
|
@@ -443,7 +444,7 @@ let TodoListPage = class TodoListPage extends p13n(localize(i18next)(PageView))
|
|
|
443
444
|
variables: { filters, pagination: { page, limit }, sortings }
|
|
444
445
|
});
|
|
445
446
|
const total = response.data.responses.total || 0;
|
|
446
|
-
/* 보고 있는 묶음의 수는 이 조회가 알려 준다. 나머지는
|
|
447
|
+
/* 보고 있는 묶음의 수는 이 조회가 알려 준다. 나머지는 otherCount 가 센다. */
|
|
447
448
|
if (countsOnTab(this.group))
|
|
448
449
|
this.counts = { ...this.counts, [this.group]: total };
|
|
449
450
|
return {
|
|
@@ -484,10 +485,6 @@ __decorate([
|
|
|
484
485
|
state(),
|
|
485
486
|
__metadata("design:type", Array)
|
|
486
487
|
], TodoListPage.prototype, "filters", void 0);
|
|
487
|
-
__decorate([
|
|
488
|
-
state(),
|
|
489
|
-
__metadata("design:type", Object)
|
|
490
|
-
], TodoListPage.prototype, "chipCounts", void 0);
|
|
491
488
|
__decorate([
|
|
492
489
|
state(),
|
|
493
490
|
__metadata("design:type", String)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"todo-list-page.js","sourceRoot":"","sources":["../../../client/pages/todo/todo-list-page.ts"],"names":[],"mappings":";AAAA,OAAO,qBAAqB,CAAA;AAC5B,OAAO,wCAAwC,CAAA;AAC/C,OAAO,2CAA2C,CAAA;AAClD,OAAO,qCAAqC,CAAA;AAE5C,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,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACzF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAa,MAAM,eAAe,CAAA;AAC1F,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAC/G,OAAO,EAAE,cAAc,EAAS,MAAM,gBAAgB,CAAA;AAEtD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAEpC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAG5E,0CAA0C;AAC1C,MAAM,QAAQ,GAA8B;IAC1C,IAAI,EAAE,UAAU;IAChB,SAAS,EAAE,eAAe;IAC1B,SAAS,EAAE,UAAU;CACtB,CAAA;AAED,MAAM,GAAG,GAAG,EAAE,CAAA;AACd,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,CAAA;AACrB,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAA;AAGd,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;IAA5D;;QA4CuB,SAAI,GAA6B,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;QAO/F,uGAAuG;QAC9F,eAAU,GAAG,IAAI,GAAG,EAAkB,CAAA;QAE/C;;;;;WAKG;QACc,UAAK,GAAc,MAAM,CAAA;QAE1C,yCAAyC;QACxB,WAAM,GAAuC,EAAE,CAAA;IA2ZlE,CAAC;aAzdQ,WAAM,GAAG;QACd,qEAAqE;QACrE,qBAAqB;QACrB,eAAe;QACf,iBAAiB;QACjB,kBAAkB;QAClB,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkCF;KACF,AAzCY,CAyCZ;IAuBD,IAAI,OAAO;QACT,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC;YACnC;;;;;;;;;;;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;gBACN,OAAO,EAAE,GAAG,EAAE;oBACZ,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAA;gBAC7B,CAAC;aACF;YACD,IAAI,EAAE,yBAAyB;YAC/B,OAAO,EAAE,EAAE;SACZ,CAAA;IACH,CAAC;IAED,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAE9D,OAAO,IAAI,CAAA;;kBAGL;YACE,CAAC,MAAM,EAAE,kBAAkB,EAAE,sBAAsB,CAAC;YACpD,CAAC,WAAW,EAAE,uBAAuB,EAAE,2BAA2B,CAAC;YACnE,CAAC,WAAW,EAAE,uBAAuB,EAAE,sBAAsB,CAAC;SAEjE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;oBACjG,IAAI,CAAC,KAAK;kBACZ,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;;;;gBAIhD,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,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;wBAC3B,CAAC,CAAc,EAAE,EAAE;YAC3B,IAAI,CAAC,OAAO,GAAI,CAAC,CAAC,aAAqB,EAAE,KAAK,CAAA;YAC9C,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;;;;;;;;;KASV,CAAA;IACH,CAAC;IAED,6CAA6C;IACrC,KAAK;QACX,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,MAAM;gBACT,OAAO,CAAC,aAAa,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAA;YACjH,KAAK,WAAW;gBACd,OAAO,CAAC,aAAa,EAAE,EAAE,WAAW,EAAE,CAAC,CAAA;YACzC,KAAK,WAAW;gBACd,OAAO;oBACL,aAAa,EAAE;oBACf,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE;oBAC9D,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE;iBACjE,CAAA;QACL,CAAC;IACH,CAAC;IAED,mDAAmD;IAC3C,WAAW,CAAC,KAAgB;QAClC,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YACzB,OAAM;QACR,CAAC;QAED,QAAQ,CAAC,YAAY,CAAE,IAAY,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAA;IAC3E,CAAC;IAEO,SAAS,CAAC,GAAY;QAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;QAEvC,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,OAAO,GAAG,EAAE,CAAA;QACjB,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAED,kEAAkE;IAC1D,KAAK,CAAC,UAAU;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAA;QAC/D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;uBAEO,QAAQ,CAAC,KAAK,CAAC;;;;OAI/B;YACD,SAAS,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAChD,WAAW,EAAE,UAAU;SACxB,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAA;QACnF,IAAI,OAAO,KAAK,IAAI,QAAQ;YAAE,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAA;IAChF,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAS;QAC7B,2DAA2D;QAC3D,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;QACrC,MAAM,IAAI,CAAC,UAAU,EAAE,CAAA;IACzB,CAAC;IAEO,WAAW;QACjB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,KAAK,WAAW,CAAA;QAE5C,IAAI,CAAC,WAAW,GAAG;YACjB,IAAI,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE;YAChG,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,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC;oBACrC,KAAK,EAAE,IAAI;oBACX,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,WAAW,EAAE;qBACxB;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,EAAE;iBACV;gBACD;;;;;;;;mBAQG;gBACH;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;oBACjC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;oBACnG,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,QAAQ,EAAE,IAAI;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,KAAK,EAAE,QAAQ;wBACf,OAAO,EAAE;4BACP,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,iCAAiC,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE;4BAC9E,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,+BAA+B,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE;4BAC1E,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE;4BACxE,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;4BAC5E,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,gCAAgC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE;4BAC5E,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,+BAA+B,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE;4BAC1E,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE;4BACpE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE;yBACzE;qBACF;oBACD,KAAK,EAAE,EAAE;iBACV;gBACD,GAAG,CAAC,SAAS;oBACX,CAAC,CAAC;wBACE;4BACE,IAAI,EAAE,UAAU;4BAChB,IAAI,EAAE,WAAW;4BACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC;4BACxC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;4BAC3B,QAAQ,EAAE,IAAI;4BACd,KAAK,EAAE,GAAG;yBACX;wBACD;4BACE,IAAI,EAAE,UAAU;4BAChB,IAAI,EAAE,cAAc;4BACpB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;4BAC3C,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;4BAC3B,QAAQ,EAAE,IAAI;4BACd,KAAK,EAAE,GAAG;yBACX;qBACF;oBACH,CAAC,CAAC,EAAE,CAAC;gBACP;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,QAAQ;oBACd,IAAI,EAAE,UAAU;oBAChB,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,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAA;4BAClD,IAAI,IAAI,GAAG,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAA;4BAClD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAA;wBACrE,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;iBACb;aACF;YACD,IAAI,EAAE;gBACJ,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,KAAK;gBACf,QAAQ,EAAE;oBACR,KAAK,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;wBACjD,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAA;oBACjC,CAAC;iBACF;aACF;YACD,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YAC9F,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE;SACrD,CAAA;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa;QACjD,IAAI,SAAS,EAAE,UAAU,KAAK,aAAa,EAAE,UAAU,EAAE,CAAC;YACxD,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;YACrC,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,kEAAkE;IAC1D,OAAO,CAAC,KAA0D;QACxE,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;IACtF,CAAC;IAED,iGAAiG;IACzF,UAAU,CAAC,OAAc;QAC/B,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;aAC5E,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC;aAC1C,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACjC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAAoB;QACrC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;QAE7C,IAAI,CAAC,WAAW,GAAG,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC,CAAA;QAE9E,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,EAAe;QACvE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;QACxB,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAE1C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;uBAEO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BpC;YACD,SAAS,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE;SAC9D,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAA;QAChD,qDAAqD;QACrD,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAA;QAElF,OAAO;YACL,KAAK;YACL,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBACxD,OAAO;oBACL,GAAG,IAAI,CAAC,gBAAgB;oBACxB,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,YAAY,EAAE,IAAI,CAAC,YAAY;iBAChC,CAAA;YACH,CAAC,CAAC;SACH,CAAA;IACH,CAAC;IAED,wDAAwD;IACxD,kBAAkB,CAAC,cAAc;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,iBAAiB,CAAA;QACpF,QAAQ,CAAC,GAAG,IAAI,IAAI,cAAc,CAAC,EAAE,UAAU,cAAc,CAAC,IAAI,EAAE,CAAC,CAAA;IACvE,CAAC;;AA5a2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;0CAAoE;AAE5E;IAAlB,KAAK,CAAC,UAAU,CAAC;8BAAS,SAAS;2CAAA;AAE3B;IAAR,KAAK,EAAE;;iDAAkB;AACjB;IAAR,KAAK,EAAE;;6CAAmB;AAGlB;IAAR,KAAK,EAAE;;gDAAuC;AAQ9B;IAAhB,KAAK,EAAE;;2CAAkC;AAGzB;IAAhB,KAAK,EAAE;;4CAAwD;AA/DrD,YAAY;IADxB,aAAa,CAAC,gBAAgB,CAAC;GACnB,YAAY,CA0dxB","sourcesContent":["import '@operato/data-grist'\nimport '@operato/data-grist/ox-filters-form.js'\nimport '@operato/input/ox-input-select-buttons.js'\nimport '../../components/work-box-groups.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 { countChips, dueSoonChip, importantChip, withChips } from '../../chip-filters.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { listWithThumbnail } from './list-thumbnail.js'\nimport { countsOnTab, groupAddress, groupAt, TODO_GROUPS, TodoGroup } from './work-box.js'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice, sleep } from '@operato/utils'\n\nimport { p13n } from '@operato/p13n'\n\nimport { dueCell, priorityCell, subjectCell } from '../../activity-cells.js'\n\n\n/** 묶음마다 묻는 목록 — 서버의 세 질의가 같은 줄 모양을 준다. */\nconst QUERY_OF: Record<TodoGroup, string> = {\n todo: 'todoList',\n submitted: 'submittedList',\n completed: 'doneList'\n}\n\nconst MIN = 60\nconst HOUR = 60 * MIN\nconst DAY = 24 * HOUR\n\n@customElement('todo-list-page')\nexport class TodoListPage extends p13n(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 :host {\n --grid-record-emphasized-background-color: #8b0000;\n --grid-record-emphasized-color: #ff6b6b;\n }\n\n /*\n * 제목은 헤드룸에 두지 않는다.\n *\n * MDI 를 켜면 제목이 이미 두 곳에 있다 — 헤더 바와 탭. 여기에 또 두면 한 화면에 같은 글자가\n * 셋이 된다. 그래서 표준 배치의 title 줄을 쓰지 않고 한 줄로 둔다.\n */\n .header {\n 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\n @property({ type: String }) mode: 'GRID' | 'LIST' | 'CARD' = isMobileDevice() ? 'CARD' : 'GRID'\n\n @query('ox-grist') grist!: DataGrist\n\n @state() gristConfig?: any\n @state() filters?: string[]\n\n /** How many each chip would show under the list's other conditions, counted by the list's own door. */\n @state() chipCounts = new Map<string, number>()\n\n /**\n * 지금 어느 묶음을 보고 있나 — 할 것 · 제출한 것 · 완료 (pages/todo/work-box.ts).\n *\n * 내가 올린 건은 내 차례일 때만 여기 있다. 제출하고 결재를 기다리는 동안과 끝난 뒤에는 상신함이\n * 따라간다 — 서버의 myThreadsOfOthersWhere 가 그 경계다.\n */\n @state() private group: TodoGroup = 'todo'\n\n /** 기다리는 두 묶음의 건수. 끝난 묶음은 이력이라 세지 않는다. */\n @state() private counts: Partial<Record<TodoGroup, number>> = {}\n\n get context() {\n return {\n title: i18next.t('title.todo 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: {\n handler: () => {\n this.grist.toggleHeadroom()\n }\n },\n help: 'worklist/todo-list-page',\n actions: []\n }\n }\n\n render() {\n const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')\n\n return html`\n <work-box-groups\n .groups=${(\n [\n ['todo', 'label.group-todo', 'text.group-todo-hint'],\n ['submitted', 'label.group-submitted', 'text.group-submitted-hint'],\n ['completed', 'label.group-completed', 'text.group-done-hint']\n ] as const\n ).map(([key, label, hint]) => ({ key, label, hint, count: countsOnTab(key) ? this.counts[key] : undefined }))}\n .selected=${this.group}\n @select=${(e: CustomEvent) => this.selectGroup(e.detail)}\n ></work-box-groups>\n\n <ox-grist class=\"page-fill\"\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=${this.counted(this.chips())}\n @change=${(e: CustomEvent) => {\n 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 </div>\n\n <ox-grist-personalizer slot=\"setting\"></ox-grist-personalizer>\n </ox-grist>\n `\n }\n\n /** 묶음마다 좁히는 칩이 다르다 — 끝난 일에 「만기」는 물음이 아니다. */\n private chips() {\n switch (this.group) {\n case 'todo':\n return [importantChip(), dueSoonChip(), { display: i18next.t('label.filter-proceeding'), value: 'proceeding' }]\n case 'submitted':\n return [importantChip(), dueSoonChip()]\n case 'completed':\n return [\n importantChip(),\n { display: i18next.t('label.filter-recent'), value: 'recent' },\n { display: i18next.t('label.filter-aborted'), value: 'aborted' }\n ]\n }\n }\n\n /** 주소만 바꾼다 — 목록도 설정도 pageUpdated 가 한 벌로 다시 세운다. */\n private selectGroup(group: TodoGroup) {\n if (this.group === group) {\n return\n }\n\n navigate(groupAddress((this as any).lifecycle?.page, group, TODO_GROUPS))\n }\n\n private syncGroup(key?: string) {\n const group = groupAt(key, TODO_GROUPS)\n\n if (this.group === group && this.gristConfig) {\n return\n }\n\n this.group = group\n this.filters = []\n this.applyConfig()\n }\n\n /** 안 보고 있는 기다리는 묶음의 건수 — 한 줄만 받아 total 을 읽는다. 못 읽은 수는 0 이 아니다. */\n private async countOther() {\n const other = this.group === 'submitted' ? 'todo' : 'submitted'\n const response = await client.query({\n query: gql`\n query ($pagination: Pagination) {\n responses: ${QUERY_OF[other]}(pagination: $pagination) {\n total\n }\n }\n `,\n variables: { pagination: { page: 1, limit: 1 } },\n fetchPolicy: 'no-cache'\n })\n\n const total = response.errors?.length ? undefined : response.data?.responses?.total\n if (typeof total == 'number') this.counts = { ...this.counts, [other]: total }\n }\n\n async pageInitialized(lifecycle) {\n /* pageUpdated 가 이 함수보다 먼저 지나가므로 주소로 바로 들어온 경우는 여기서 잡힌다. */\n this.syncGroup(lifecycle?.resourceId)\n await this.countOther()\n }\n\n private applyConfig() {\n const completed = this.group === 'completed'\n\n this.gristConfig = {\n list: { thumbnail: 'thumbnail', fields: ['name', 'description'], details: ['createdAt', 'due'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence', fixed: true },\n {\n type: 'string',\n name: 'name',\n header: i18next.t('field.wl-subject'),\n fixed: true,\n record: {\n editable: false,\n renderer: subjectCell()\n },\n filter: 'search',\n sortable: true,\n width: 250\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: 85\n },\n /*\n * 기한 — 끝난 일에서는 **다른 날짜 열과 같이** 시각만 적는다.\n *\n * dueCell 은 Date.now() 를 기준으로 「4일 13시간 지남」을 내고 1분마다 다시 센다. 아직 안 끝난\n * 일에는 그게 물음이다 — 지금부터 얼마 남았나. 끝난 일에는 그 물음이 없고, 그대로 두면 **거짓을\n * 말한다.** 기한 안에 끝낸 건도 오늘 날짜가 흐르면 빨간 「N일 지남」으로 바뀐다.\n *\n * 「기한을 지켰나」는 줄마다가 아니라 모아서 보는 물음이다 — activity-stats 의 자리다.\n */\n {\n type: 'datetime',\n name: 'dueAt',\n header: i18next.t('field.wl-due'),\n record: completed ? { editable: false } : { editable: false, renderer: dueCell(() => this.active) },\n sortable: true,\n width: 180\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 sortable: true,\n width: 70\n },\n {\n type: 'select',\n name: 'state',\n header: i18next.t('field.wl-state'),\n record: {\n editable: false,\n align: 'center',\n options: [\n { display: i18next.t('label.activity-state-unassigned'), value: 'unassigned' },\n { display: i18next.t('label.activity-state-assigned'), value: 'assigned' },\n { display: i18next.t('label.activity-state-started'), value: 'started' },\n { display: i18next.t('label.activity-state-delegated'), value: 'delegated' },\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-rejected'), value: 'rejected' },\n { display: i18next.t('label.activity-state-ended'), value: 'ended' },\n { display: i18next.t('label.activity-state-aborted'), value: 'aborted' }\n ]\n },\n width: 85\n },\n ...(completed\n ? [\n {\n type: 'datetime',\n name: 'startedAt',\n header: i18next.t('field.wl-started-at'),\n record: { editable: false },\n sortable: true,\n width: 180\n },\n {\n type: 'datetime',\n name: 'terminatedAt',\n header: i18next.t('field.wl-terminated-at'),\n record: { editable: false },\n sortable: true,\n width: 180\n }\n ]\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: 180\n },\n {\n type: 'string',\n name: 'uiSource',\n header: i18next.t('field.wl-view-source'),\n record: {\n editable: false,\n renderer: function (value, column, record, rowIndex, field) {\n const { uiType, uiSource } = record.activity || {}\n var type = uiType !== 'board' ? 'string' : 'board'\n return getRenderer(type)(uiSource, 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 }\n ],\n rows: {\n appendable: false,\n editable: false,\n handlers: {\n click: (columns, data, column, record, rowIndex) => {\n this.openActivityThread(record)\n }\n }\n },\n sorters: completed ? [{ name: 'terminatedAt', desc: true }] : [{ name: 'dueAt', desc: false }],\n pagination: { limit: 50, pages: [20, 50, 100, 500] }\n }\n }\n\n async pageUpdated(changes, lifecycle, changedBefore) {\n if (lifecycle?.resourceId !== changedBefore?.resourceId) {\n this.syncGroup(lifecycle?.resourceId)\n await this.countOther()\n }\n\n if (this.active) {\n this.grist?.fetch()\n }\n }\n\n /* 읽은 것에 그림이 하나도 없으면 카드마다 빈 그림 칸을 세우지 않는다(§ list-thumbnail.ts). */\n private counted(chips: { display: string; value: string; hint?: string }[]) {\n return chips.map(chip => ({ ...chip, count: this.chipCounts.get(chip.value) ?? 0 }))\n }\n\n /* The chips' numbers - asked beside the list, on the list's filters before any chip is added. */\n private countChips(filters: any[]) {\n countChips(QUERY_OF[this.group], this.chips().map(chip => chip.value), filters)\n .then(counts => (this.chipCounts = counts))\n .catch(e => console.error(e))\n }\n\n async fetchHandler(options: FetchOption) {\n const fetched = await this.fetchRows(options)\n\n this.gristConfig = listWithThumbnail(this.gristConfig, fetched?.records || [])\n\n return fetched\n }\n\n async fetchRows({ page, limit, sortings = [], filters = [] }: FetchOption) {\n this.countChips(filters)\n filters = withChips(filters, this.filters)\n\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n responses: ${QUERY_OF[this.group]}(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items {\n id\n activityInstance {\n activityId: id\n name\n description\n priority\n input\n refBy\n activityType\n uiType\n uiSource\n thumbnail\n updatedAt\n createdAt\n creator {\n id\n name\n }\n }\n state\n output\n dueAt\n assignedAt\n startedAt\n terminatedAt\n }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings }\n })\n\n const total = response.data.responses.total || 0\n /* 보고 있는 묶음의 수는 이 조회가 알려 준다. 나머지는 countOther 가 센다. */\n if (countsOnTab(this.group)) this.counts = { ...this.counts, [this.group]: total }\n\n return {\n total,\n records: (response.data.responses.items || []).map(item => {\n return {\n ...item.activityInstance,\n id: item.id,\n state: item.state,\n output: item.output,\n dueAt: item.dueAt,\n assignedAt: item.assignedAt,\n startedAt: item.startedAt,\n terminatedAt: item.terminatedAt\n }\n })\n }\n }\n\n /** 끝난 일은 보기 화면으로 연다 — 고칠 것이 없는 화면에 저장 · 제출이 서면 안 된다. */\n openActivityThread(activityThread) {\n const page = this.group === 'completed' ? 'activity-thread-view' : 'activity-thread'\n navigate(`${page}/${activityThread.id}?title=${activityThread.name}`)\n }\n\n}\n"]}
|
|
1
|
+
{"version":3,"file":"todo-list-page.js","sourceRoot":"","sources":["../../../client/pages/todo/todo-list-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 '../../components/work-box-groups.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, QueryController } from '@operato/graphql'\nimport { countChips, dueSoonChip, importantChip, withChips } from '../../chip-filters.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { listWithThumbnail } from './list-thumbnail.js'\nimport { countsOnTab, groupAddress, groupAt, TODO_GROUPS, TodoGroup } from './work-box.js'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice, sleep } from '@operato/utils'\n\nimport { p13n } from '@operato/p13n'\n\nimport { dueCell, priorityCell, subjectCell } from '../../activity-cells.js'\n\n\n/** 묶음마다 묻는 목록 — 서버의 세 질의가 같은 줄 모양을 준다. */\nconst QUERY_OF: Record<TodoGroup, string> = {\n todo: 'todoList',\n submitted: 'submittedList',\n completed: 'doneList'\n}\n\nconst MIN = 60\nconst HOUR = 60 * MIN\nconst DAY = 24 * HOUR\n\n@customElement('todo-list-page')\nexport class TodoListPage extends p13n(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 :host {\n --grid-record-emphasized-background-color: #8b0000;\n --grid-record-emphasized-color: #ff6b6b;\n }\n\n /*\n * 제목은 헤드룸에 두지 않는다.\n *\n * MDI 를 켜면 제목이 이미 두 곳에 있다 — 헤더 바와 탭. 여기에 또 두면 한 화면에 같은 글자가\n * 셋이 된다. 그래서 표준 배치의 title 줄을 쓰지 않고 한 줄로 둔다.\n */\n .header {\n 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\n @property({ type: String }) mode: 'GRID' | 'LIST' | 'CARD' = isMobileDevice() ? 'CARD' : 'GRID'\n\n @query('ox-grist') grist!: DataGrist\n\n @state() gristConfig?: any\n @state() filters?: string[]\n\n /**\n * 지금 어느 묶음을 보고 있나 — 할 것 · 제출한 것 · 완료 (pages/todo/work-box.ts).\n *\n * 내가 올린 건은 내 차례일 때만 여기 있다. 제출하고 결재를 기다리는 동안과 끝난 뒤에는 상신함이\n * 따라간다 — 서버의 myThreadsOfOthersWhere 가 그 경계다.\n */\n @state() private group: TodoGroup = 'todo'\n\n /** 기다리는 두 묶음의 건수. 끝난 묶음은 이력이라 세지 않는다. */\n @state() private counts: Partial<Record<TodoGroup, number>> = {}\n\n /**\n * 안 보고 있는 기다리는 묶음의 건수 — 한 줄만 받아 total 을 읽는다(ADR-0190). 묶음이 주소에서 정해진 뒤에\n * 묻는다.\n */\n private otherCount = new QueryController(\n this,\n gql`\n query ($pagination: Pagination, $todo: Boolean!) {\n todo: todoList(pagination: $pagination) @include(if: $todo) {\n total\n }\n submitted: submittedList(pagination: $pagination) @skip(if: $todo) {\n total\n }\n }\n `,\n {\n variables: () =>\n this.gristConfig ? { todo: this.otherGroup === 'todo', pagination: { page: 1, limit: 1 } } : undefined\n }\n )\n\n private get otherGroup(): TodoGroup {\n return this.group === 'submitted' ? 'todo' : 'submitted'\n }\n\n /** 보고 있는 묶음의 수는 목록 조회가, 나머지는 otherCount 가 안다. 못 읽은 수는 0 이 아니다. */\n private get shownCounts(): Partial<Record<TodoGroup, number>> {\n const total = this.otherCount.data?.[this.otherGroup]?.total\n return typeof total == 'number' ? { ...this.counts, [this.otherGroup]: total } : this.counts\n }\n\n get context() {\n return {\n title: i18next.t('title.todo 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: {\n handler: () => {\n this.grist.toggleHeadroom()\n }\n },\n help: 'worklist/todo-list-page',\n actions: []\n }\n }\n\n render() {\n const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')\n\n return html`\n <work-box-groups\n .groups=${(\n [\n ['todo', 'label.group-todo', 'text.group-todo-hint'],\n ['submitted', 'label.group-submitted', 'text.group-submitted-hint'],\n ['completed', 'label.group-completed', 'text.group-done-hint']\n ] as const\n ).map(([key, label, hint]) => ({ key, label, hint, count: countsOnTab(key) ? this.shownCounts[key] : undefined }))}\n .selected=${this.group}\n @select=${(e: CustomEvent) => this.selectGroup(e.detail)}\n ></work-box-groups>\n\n <ox-grist class=\"page-fill\"\n .mode=${mode}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .countHandler=${this.countHandler.bind(this)}\n @counts-change=${() => this.requestUpdate()}\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=${this.counted(this.chips())}\n @change=${(e: CustomEvent) => {\n 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 </div>\n\n <ox-grist-personalizer slot=\"setting\"></ox-grist-personalizer>\n </ox-grist>\n `\n }\n\n /** 묶음마다 좁히는 칩이 다르다 — 끝난 일에 「만기」는 물음이 아니다. */\n private chips() {\n switch (this.group) {\n case 'todo':\n return [importantChip(), dueSoonChip(), { display: i18next.t('label.filter-proceeding'), value: 'proceeding' }]\n case 'submitted':\n return [importantChip(), dueSoonChip()]\n case 'completed':\n return [\n importantChip(),\n { display: i18next.t('label.filter-recent'), value: 'recent' },\n { display: i18next.t('label.filter-aborted'), value: 'aborted' }\n ]\n }\n }\n\n /** 주소만 바꾼다 — 목록도 설정도 pageUpdated 가 한 벌로 다시 세운다. */\n private selectGroup(group: TodoGroup) {\n if (this.group === group) {\n return\n }\n\n navigate(groupAddress((this as any).lifecycle?.page, group, TODO_GROUPS))\n }\n\n private syncGroup(key?: string) {\n const group = groupAt(key, TODO_GROUPS)\n\n if (this.group === group && this.gristConfig) {\n return\n }\n\n this.group = group\n this.filters = []\n this.applyConfig()\n }\n\n async pageInitialized(lifecycle) {\n /* pageUpdated 가 이 함수보다 먼저 지나가므로 주소로 바로 들어온 경우는 여기서 잡힌다. */\n this.syncGroup(lifecycle?.resourceId)\n }\n\n private applyConfig() {\n const completed = this.group === 'completed'\n\n this.gristConfig = {\n list: { thumbnail: 'thumbnail', fields: ['name', 'description'], details: ['createdAt', 'due'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence', fixed: true },\n {\n type: 'string',\n name: 'name',\n header: i18next.t('field.wl-subject'),\n fixed: true,\n record: {\n editable: false,\n renderer: subjectCell()\n },\n filter: 'search',\n sortable: true,\n width: 250\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: 85\n },\n /*\n * 기한 — 끝난 일에서는 **다른 날짜 열과 같이** 시각만 적는다.\n *\n * dueCell 은 Date.now() 를 기준으로 「4일 13시간 지남」을 내고 1분마다 다시 센다. 아직 안 끝난\n * 일에는 그게 물음이다 — 지금부터 얼마 남았나. 끝난 일에는 그 물음이 없고, 그대로 두면 **거짓을\n * 말한다.** 기한 안에 끝낸 건도 오늘 날짜가 흐르면 빨간 「N일 지남」으로 바뀐다.\n *\n * 「기한을 지켰나」는 줄마다가 아니라 모아서 보는 물음이다 — activity-stats 의 자리다.\n */\n {\n type: 'datetime',\n name: 'dueAt',\n header: i18next.t('field.wl-due'),\n record: completed ? { editable: false } : { editable: false, renderer: dueCell(() => this.active) },\n sortable: true,\n width: 180\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 sortable: true,\n width: 70\n },\n {\n type: 'select',\n name: 'state',\n header: i18next.t('field.wl-state'),\n record: {\n editable: false,\n align: 'center',\n options: [\n { display: i18next.t('label.activity-state-unassigned'), value: 'unassigned' },\n { display: i18next.t('label.activity-state-assigned'), value: 'assigned' },\n { display: i18next.t('label.activity-state-started'), value: 'started' },\n { display: i18next.t('label.activity-state-delegated'), value: 'delegated' },\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-rejected'), value: 'rejected' },\n { display: i18next.t('label.activity-state-ended'), value: 'ended' },\n { display: i18next.t('label.activity-state-aborted'), value: 'aborted' }\n ]\n },\n width: 85\n },\n ...(completed\n ? [\n {\n type: 'datetime',\n name: 'startedAt',\n header: i18next.t('field.wl-started-at'),\n record: { editable: false },\n sortable: true,\n width: 180\n },\n {\n type: 'datetime',\n name: 'terminatedAt',\n header: i18next.t('field.wl-terminated-at'),\n record: { editable: false },\n sortable: true,\n width: 180\n }\n ]\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: 180\n },\n {\n type: 'string',\n name: 'uiSource',\n header: i18next.t('field.wl-view-source'),\n record: {\n editable: false,\n renderer: function (value, column, record, rowIndex, field) {\n const { uiType, uiSource } = record.activity || {}\n var type = uiType !== 'board' ? 'string' : 'board'\n return getRenderer(type)(uiSource, 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 }\n ],\n rows: {\n appendable: false,\n editable: false,\n handlers: {\n click: (columns, data, column, record, rowIndex) => {\n this.openActivityThread(record)\n }\n }\n },\n sorters: completed ? [{ name: 'terminatedAt', desc: true }] : [{ name: 'dueAt', desc: false }],\n pagination: { limit: 50, pages: [20, 50, 100, 500] }\n }\n }\n\n async pageUpdated(changes, lifecycle, changedBefore) {\n if (lifecycle?.resourceId !== changedBefore?.resourceId) {\n this.syncGroup(lifecycle?.resourceId)\n }\n\n if (this.active) {\n this.grist?.fetch()\n }\n }\n\n /* 읽은 것에 그림이 하나도 없으면 카드마다 빈 그림 칸을 세우지 않는다(§ list-thumbnail.ts). */\n private counted(chips: { display: string; value: string; hint?: string }[]) {\n return chips.map(chip => ({ ...chip, count: this.grist?.counts?.chips?.[chip.value] ?? 0 }))\n }\n\n /* The chips' numbers - asked beside each read, on the list's filters before any chip is added. */\n async countHandler({ filters = [], signal }: FetchOption) {\n return { chips: await countChips(QUERY_OF[this.group], this.chips().map(chip => chip.value), filters, signal) }\n }\n\n async fetchHandler(options: FetchOption) {\n const fetched = await this.fetchRows(options)\n\n this.gristConfig = listWithThumbnail(this.gristConfig, fetched?.records || [])\n\n return fetched\n }\n\n async fetchRows({ page, limit, sortings = [], filters = [] }: FetchOption) {\n filters = withChips(filters, this.filters)\n\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n responses: ${QUERY_OF[this.group]}(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items {\n id\n activityInstance {\n activityId: id\n name\n description\n priority\n input\n refBy\n activityType\n uiType\n uiSource\n thumbnail\n updatedAt\n createdAt\n creator {\n id\n name\n }\n }\n state\n output\n dueAt\n assignedAt\n startedAt\n terminatedAt\n }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings }\n })\n\n const total = response.data.responses.total || 0\n /* 보고 있는 묶음의 수는 이 조회가 알려 준다. 나머지는 otherCount 가 센다. */\n if (countsOnTab(this.group)) this.counts = { ...this.counts, [this.group]: total }\n\n return {\n total,\n records: (response.data.responses.items || []).map(item => {\n return {\n ...item.activityInstance,\n id: item.id,\n state: item.state,\n output: item.output,\n dueAt: item.dueAt,\n assignedAt: item.assignedAt,\n startedAt: item.startedAt,\n terminatedAt: item.terminatedAt\n }\n })\n }\n }\n\n /** 끝난 일은 보기 화면으로 연다 — 고칠 것이 없는 화면에 저장 · 제출이 서면 안 된다. */\n openActivityThread(activityThread) {\n const page = this.group === 'completed' ? 'activity-thread-view' : 'activity-thread'\n navigate(`${page}/${activityThread.id}?title=${activityThread.name}`)\n }\n\n}\n"]}
|