@things-factory/worklist 10.0.19 → 10.1.0
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-cells.d.ts +21 -0
- package/dist-client/activity-cells.js +117 -0
- package/dist-client/activity-cells.js.map +1 -0
- package/dist-client/activity-summary-generator.js +57 -11
- package/dist-client/activity-summary-generator.js.map +1 -1
- package/dist-client/approval-progress.d.ts +18 -0
- package/dist-client/approval-progress.js +31 -0
- package/dist-client/approval-progress.js.map +1 -0
- package/dist-client/bootstrap.js +6 -11
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/chip-filters.d.ts +53 -0
- package/dist-client/chip-filters.js +60 -0
- package/dist-client/chip-filters.js.map +1 -0
- package/dist-client/components/activity-approval-ribon.d.ts +19 -0
- package/dist-client/components/activity-approval-ribon.js +50 -126
- package/dist-client/components/activity-approval-ribon.js.map +1 -1
- package/dist-client/components/activity-fields.d.ts +50 -0
- package/dist-client/components/activity-fields.js +393 -0
- package/dist-client/components/activity-fields.js.map +1 -0
- package/dist-client/components/activity-instance-ribon.d.ts +19 -0
- package/dist-client/components/activity-instance-ribon.js +55 -122
- package/dist-client/components/activity-instance-ribon.js.map +1 -1
- package/dist-client/components/activity-thread-ribon.d.ts +25 -0
- package/dist-client/components/activity-thread-ribon.js +64 -121
- package/dist-client/components/activity-thread-ribon.js.map +1 -1
- package/dist-client/components/activity-thread-timeline.d.ts +14 -2
- package/dist-client/components/activity-thread-timeline.js +234 -104
- package/dist-client/components/activity-thread-timeline.js.map +1 -1
- package/dist-client/components/state-ribon.d.ts +40 -0
- package/dist-client/components/state-ribon.js +200 -0
- package/dist-client/components/state-ribon.js.map +1 -0
- package/dist-client/due.d.ts +16 -0
- package/dist-client/due.js +78 -0
- package/dist-client/due.js.map +1 -0
- package/dist-client/pages/activity/activity-list-page.js +4 -6
- package/dist-client/pages/activity/activity-list-page.js.map +1 -1
- package/dist-client/pages/activity-approval/activity-approval-list-page.js +2 -1
- package/dist-client/pages/activity-approval/activity-approval-list-page.js.map +1 -1
- package/dist-client/pages/activity-approval/activity-approval-page.d.ts +52 -5
- package/dist-client/pages/activity-approval/activity-approval-page.js +265 -118
- package/dist-client/pages/activity-approval/activity-approval-page.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-list-page.d.ts +0 -1
- package/dist-client/pages/activity-instance/activity-instance-list-page.js +8 -24
- package/dist-client/pages/activity-instance/activity-instance-list-page.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-start-page.d.ts +1 -0
- package/dist-client/pages/activity-instance/activity-instance-start-page.js +7 -0
- package/dist-client/pages/activity-instance/activity-instance-start-page.js.map +1 -1
- package/dist-client/pages/activity-instance/activity-instance-view.js +1 -1
- package/dist-client/pages/activity-instance/activity-instance-view.js.map +1 -1
- package/dist-client/pages/activity-stats/activity-stats-list-page.js +2 -1
- package/dist-client/pages/activity-stats/activity-stats-list-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-page.d.ts +30 -4
- package/dist-client/pages/activity-thread/activity-thread-page.js +142 -66
- 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 +17 -6
- package/dist-client/pages/activity-thread/activity-thread-view-page.js +118 -65
- package/dist-client/pages/activity-thread/activity-thread-view-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-view.d.ts +24 -7
- package/dist-client/pages/activity-thread/activity-thread-view.js +84 -308
- package/dist-client/pages/activity-thread/activity-thread-view.js.map +1 -1
- package/dist-client/pages/installable-activity/installable-activity-list-page.d.ts +1 -1
- package/dist-client/pages/installable-activity/installable-activity-list-page.js +13 -9
- package/dist-client/pages/installable-activity/installable-activity-list-page.js.map +1 -1
- package/dist-client/pages/todo/approval-done-list-page.d.ts +1 -0
- package/dist-client/pages/todo/approval-done-list-page.js +111 -58
- package/dist-client/pages/todo/approval-done-list-page.js.map +1 -1
- package/dist-client/pages/todo/approval-pending-list-page.d.ts +32 -1
- package/dist-client/pages/todo/approval-pending-list-page.js +485 -130
- package/dist-client/pages/todo/approval-pending-list-page.js.map +1 -1
- package/dist-client/pages/todo/done-list-calendar-page.d.ts +2 -0
- package/dist-client/pages/todo/done-list-calendar-page.js +213 -29
- package/dist-client/pages/todo/done-list-calendar-page.js.map +1 -1
- package/dist-client/pages/todo/done-list-page.d.ts +1 -0
- package/dist-client/pages/todo/done-list-page.js +126 -62
- package/dist-client/pages/todo/done-list-page.js.map +1 -1
- package/dist-client/pages/todo/draft-list-page.d.ts +1 -0
- package/dist-client/pages/todo/draft-list-page.js +82 -34
- package/dist-client/pages/todo/draft-list-page.js.map +1 -1
- package/dist-client/pages/todo/pickable-list-page.js +6 -10
- package/dist-client/pages/todo/pickable-list-page.js.map +1 -1
- package/dist-client/pages/todo/todo-list-page.d.ts +1 -1
- package/dist-client/pages/todo/todo-list-page.js +102 -78
- package/dist-client/pages/todo/todo-list-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +0 -6
- package/dist-client/route.js.map +1 -1
- package/dist-client/templates/activity-approval-context-template.js +60 -138
- package/dist-client/templates/activity-approval-context-template.js.map +1 -1
- package/dist-client/templates/activity-instance-context-template.js +13 -172
- package/dist-client/templates/activity-instance-context-template.js.map +1 -1
- package/dist-client/templates/activity-thread-context-template.js +39 -131
- package/dist-client/templates/activity-thread-context-template.js.map +1 -1
- package/dist-client/templates/context-panel.d.ts +26 -0
- package/dist-client/templates/context-panel.js +182 -0
- package/dist-client/templates/context-panel.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/when.d.ts +4 -0
- package/dist-client/when.js +72 -0
- package/dist-client/when.js.map +1 -0
- package/dist-server/controllers/activity-approval/_abort.js +2 -1
- package/dist-server/controllers/activity-approval/_abort.js.map +1 -1
- package/dist-server/controllers/activity-approval/approval-line.d.ts +106 -0
- package/dist-server/controllers/activity-approval/approval-line.js +151 -0
- package/dist-server/controllers/activity-approval/approval-line.js.map +1 -0
- package/dist-server/controllers/activity-approval/approve.js +61 -51
- package/dist-server/controllers/activity-approval/approve.js.map +1 -1
- package/dist-server/controllers/activity-approval/claim.d.ts +42 -0
- package/dist-server/controllers/activity-approval/claim.js +77 -0
- package/dist-server/controllers/activity-approval/claim.js.map +1 -0
- package/dist-server/controllers/activity-approval/create-approval.d.ts +41 -0
- package/dist-server/controllers/activity-approval/create-approval.js +78 -0
- package/dist-server/controllers/activity-approval/create-approval.js.map +1 -0
- package/dist-server/controllers/activity-approval/delegate.d.ts +16 -0
- package/dist-server/controllers/activity-approval/delegate.js +43 -7
- package/dist-server/controllers/activity-approval/delegate.js.map +1 -1
- package/dist-server/controllers/activity-approval/opinion.d.ts +10 -0
- package/dist-server/controllers/activity-approval/opinion.js +43 -0
- package/dist-server/controllers/activity-approval/opinion.js.map +1 -0
- package/dist-server/controllers/activity-approval/reject.js +21 -1
- package/dist-server/controllers/activity-approval/reject.js.map +1 -1
- package/dist-server/controllers/activity-approval/save.js +26 -2
- package/dist-server/controllers/activity-approval/save.js.map +1 -1
- package/dist-server/controllers/activity-installation/activity-installation-controller.d.ts +26 -0
- package/dist-server/controllers/activity-installation/activity-installation-controller.js +45 -0
- package/dist-server/controllers/activity-installation/activity-installation-controller.js.map +1 -1
- package/dist-server/controllers/activity-instance/approval-expectation.d.ts +34 -0
- package/dist-server/controllers/activity-instance/approval-expectation.js +18 -0
- package/dist-server/controllers/activity-instance/approval-expectation.js.map +1 -0
- package/dist-server/controllers/activity-instance/issue.js +32 -0
- package/dist-server/controllers/activity-instance/issue.js.map +1 -1
- package/dist-server/controllers/activity-thread/restart.js +17 -1
- package/dist-server/controllers/activity-thread/restart.js.map +1 -1
- package/dist-server/controllers/activity-thread/submit.js +23 -43
- package/dist-server/controllers/activity-thread/submit.js.map +1 -1
- package/dist-server/controllers/common.js +46 -0
- package/dist-server/controllers/common.js.map +1 -1
- package/dist-server/controllers/index.d.ts +1 -0
- package/dist-server/controllers/index.js +8 -0
- package/dist-server/controllers/index.js.map +1 -1
- package/dist-server/service/activity/activity-mutation.js +25 -5
- package/dist-server/service/activity/activity-mutation.js.map +1 -1
- package/dist-server/service/activity/activity-type.d.ts +14 -0
- package/dist-server/service/activity/activity-type.js +8 -0
- package/dist-server/service/activity/activity-type.js.map +1 -1
- package/dist-server/service/activity/activity.d.ts +18 -0
- package/dist-server/service/activity/activity.js +5 -0
- package/dist-server/service/activity/activity.js.map +1 -1
- package/dist-server/service/activity-approval/activity-approval-mutation.js +3 -2
- package/dist-server/service/activity-approval/activity-approval-mutation.js.map +1 -1
- package/dist-server/service/activity-approval/activity-approval-query.d.ts +19 -1
- package/dist-server/service/activity-approval/activity-approval-query.js +186 -56
- package/dist-server/service/activity-approval/activity-approval-query.js.map +1 -1
- package/dist-server/service/activity-approval/activity-approval.d.ts +29 -1
- package/dist-server/service/activity-approval/activity-approval.js +19 -0
- package/dist-server/service/activity-approval/activity-approval.js.map +1 -1
- package/dist-server/service/activity-approval/my-pending-approvals.d.ts +21 -0
- package/dist-server/service/activity-approval/my-pending-approvals.js +40 -0
- package/dist-server/service/activity-approval/my-pending-approvals.js.map +1 -0
- package/dist-server/service/activity-instance/activity-instance-type.d.ts +20 -0
- package/dist-server/service/activity-instance/activity-instance-type.js +11 -0
- package/dist-server/service/activity-instance/activity-instance-type.js.map +1 -1
- package/dist-server/service/activity-instance/activity-instance.d.ts +6 -0
- package/dist-server/service/activity-instance/activity-instance.js +5 -0
- package/dist-server/service/activity-instance/activity-instance.js.map +1 -1
- package/dist-server/service/activity-stats/activity-stats-query.d.ts +5 -0
- package/dist-server/service/activity-stats/activity-stats-query.js +149 -11
- package/dist-server/service/activity-stats/activity-stats-query.js.map +1 -1
- package/dist-server/service/activity-stats/activity-stats-type.d.ts +19 -6
- package/dist-server/service/activity-stats/activity-stats-type.js +37 -10
- package/dist-server/service/activity-stats/activity-stats-type.js.map +1 -1
- package/dist-server/service/activity-stats/event-placement.d.ts +55 -0
- package/dist-server/service/activity-stats/event-placement.js +82 -0
- package/dist-server/service/activity-stats/event-placement.js.map +1 -0
- package/dist-server/service/activity-thread/activity-thread-query.js +48 -24
- package/dist-server/service/activity-thread/activity-thread-query.js.map +1 -1
- package/dist-server/service/activity-thread/my-open-threads.d.ts +15 -0
- package/dist-server/service/activity-thread/my-open-threads.js +40 -0
- package/dist-server/service/activity-thread/my-open-threads.js.map +1 -0
- package/dist-server/service/activity-thread/todo-order.d.ts +49 -0
- package/dist-server/service/activity-thread/todo-order.js +70 -0
- package/dist-server/service/activity-thread/todo-order.js.map +1 -0
- package/dist-server/service/installable-activity/installable-activity-mutation.d.ts +6 -0
- package/dist-server/service/installable-activity/installable-activity-mutation.js +7 -24
- package/dist-server/service/installable-activity/installable-activity-mutation.js.map +1 -1
- package/dist-server/service/nulls-last.d.ts +30 -0
- package/dist-server/service/nulls-last.js +37 -0
- package/dist-server/service/nulls-last.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/helps/dataset/activity-instance-search-page.ko.md +5 -0
- package/helps/dataset/activity-instance-search-page.md +13 -0
- package/helps/worklist/activity-approval.ko.md +5 -0
- package/helps/worklist/activity-approval.md +14 -0
- package/helps/worklist/activity-bank-page.ko.md +5 -0
- package/helps/worklist/activity-bank-page.md +13 -0
- package/helps/worklist/activity-reporter-list.ko.md +5 -0
- package/helps/worklist/activity-reporter-list.md +13 -0
- package/helps/worklist/activity-store.ko.md +5 -0
- package/helps/worklist/activity-store.md +13 -0
- package/helps/worklist/activity-template.ko.md +5 -0
- package/helps/worklist/activity-template.md +13 -0
- package/helps/worklist/installable-activity.ko.md +5 -0
- package/helps/worklist/installable-activity.md +13 -0
- package/package.json +13 -12
- package/things-factory.config.js +0 -5
- package/translations/en.json +129 -51
- package/translations/ja.json +130 -52
- package/translations/ko.json +82 -4
- package/translations/ms.json +129 -51
- package/translations/zh.json +132 -54
- package/dist-client/pages/dashboard/dashboard-home.d.ts +0 -1
- package/dist-client/pages/dashboard/dashboard-home.js +0 -22
- package/dist-client/pages/dashboard/dashboard-home.js.map +0 -1
- package/dist-client/pages/worklist-home.d.ts +0 -1
- package/dist-client/pages/worklist-home.js +0 -17
- package/dist-client/pages/worklist-home.js.map +0 -1
|
@@ -1,24 +1,47 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
2
|
import '@operato/data-grist';
|
|
3
|
+
import '@operato/data-grist/ox-filters-form.js';
|
|
3
4
|
import '@operato/context/ox-context-page-toolbar.js';
|
|
4
5
|
import '@operato/input/ox-input-select-buttons.js';
|
|
5
6
|
import gql from 'graphql-tag';
|
|
6
7
|
import { css, html } from 'lit';
|
|
7
8
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
8
|
-
import { asyncReplace } from 'lit/directives/async-replace.js';
|
|
9
9
|
import { DataGrist, getRenderer } from '@operato/data-grist';
|
|
10
10
|
import { client } from '@operato/graphql';
|
|
11
|
+
import { dueSoonChip, dueSoonFilters, importantChip, importantFilters } from '../../chip-filters.js';
|
|
11
12
|
import { i18next, localize } from '@operato/i18n';
|
|
12
13
|
import { navigate, PageView } from '@operato/shell';
|
|
13
14
|
import { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles } from '@operato/styles';
|
|
14
|
-
import { adjustFilters, isMobileDevice
|
|
15
|
+
import { adjustFilters, isMobileDevice } from '@operato/utils';
|
|
15
16
|
import { p13n } from '@operato/p13n';
|
|
17
|
+
import { approvalMarks, dueCell, priorityCell, subjectCell } from '../../activity-cells.js';
|
|
16
18
|
const MIN = 60;
|
|
17
19
|
const HOUR = 60 * MIN;
|
|
18
20
|
const DAY = 24 * HOUR;
|
|
19
21
|
let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localize(i18next)(PageView)) {
|
|
20
22
|
constructor() {
|
|
21
23
|
super(...arguments);
|
|
24
|
+
/**
|
|
25
|
+
* 지금 어느 묶음을 보고 있나.
|
|
26
|
+
*
|
|
27
|
+
* ── 왜 한 화면에 두 묶음인가 ─────────────────────────────────────────────
|
|
28
|
+
* 참조를 별도 화면으로 만들면 **아무도 열지 않는다.** 참조는 안 봐도 아무 일이 안 일어나므로,
|
|
29
|
+
* 아침에 업무 앱을 열면 「승인 대기」를 보지 「참조」를 따로 열지 않는다. 참조를 만든 이유가 의견을
|
|
30
|
+
* 듣는 것인데 안 여는 화면에 넣으면 의견이 안 온다.
|
|
31
|
+
*
|
|
32
|
+
* 그래서 한 화면에 두고, **두 수를 함께 보인다.** 한 곳만 열면 둘 다 눈에 든다.
|
|
33
|
+
*
|
|
34
|
+
* ── 그렇다고 한 목록에 섞지 않는다 ────────────────────────────────────────
|
|
35
|
+
* 두 묶음의 물음이 다르다 — 「내가 결정할 것」과 「내가 알아야 할 것」. 섞으면 참조가 「내가 승인해야
|
|
36
|
+
* 하나」로 읽히고, 배지에 12가 뜨는데 결정할 것은 3건이 된다. 그러면 그 수를 안 믿게 된다.
|
|
37
|
+
*
|
|
38
|
+
* ── 주소가 주인이다 ──────────────────────────────────────────────────────
|
|
39
|
+
* `.../approval-pending-list/reference` 로 붙여 보낼 수 있다. 대상이 바뀌는 것이므로 필터와 달리
|
|
40
|
+
* 주소에 담는다.
|
|
41
|
+
*/
|
|
42
|
+
this.group = 'approval';
|
|
43
|
+
/** 두 묶음의 건수. 탭에 함께 적는다 — 안 눌러도 얼마나 있는지 보여야 한다. */
|
|
44
|
+
this.counts = {};
|
|
22
45
|
this.mode = isMobileDevice() ? 'CARD' : 'GRID';
|
|
23
46
|
}
|
|
24
47
|
static { this.styles = [
|
|
@@ -26,7 +49,16 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
26
49
|
CommonGristStyles,
|
|
27
50
|
CommonHeaderStyles,
|
|
28
51
|
css `
|
|
29
|
-
|
|
52
|
+
/*
|
|
53
|
+
* 세로로 쌓는다 — 묶음 고르는 줄이 표 **위**에 서야 한다.
|
|
54
|
+
*
|
|
55
|
+
* 방향을 빼면 가로로 배치되어, 그 줄이 표 왼쪽에 세로 띠로 서고 버튼 글자가 세로 가운데에
|
|
56
|
+
* 떠서 표 밑처럼 보인다. 표도 그 띠만큼 밀려 오른쪽 칸이 잘린다. 전에는 자식이 ox-grist
|
|
57
|
+
* 하나뿐이라 방향이 드러나지 않았다.
|
|
58
|
+
*/
|
|
59
|
+
:host {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
30
62
|
|
|
31
63
|
width: 100%;
|
|
32
64
|
|
|
@@ -40,14 +72,135 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
40
72
|
|
|
41
73
|
.header { grid-template-areas: 'filters actions';
|
|
42
74
|
}
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* ── 헤드룸의 왼쪽 기준선은 표와 같다 ─────────────────────────────────
|
|
78
|
+
* CommonHeaderStyles 의 .filters 는 배경과 모서리와 여백을 가진 영역이다. 그 배경이
|
|
79
|
+
* --md-sys-color-surface 인데 이 제품에서는 바탕색과 같은 값이라, **보이지 않는 영역이
|
|
80
|
+
* 자리만 차지하고** 검색창을 여백만큼(12px) 안쪽으로 밀었다.
|
|
81
|
+
*
|
|
82
|
+
* 실측: 표 왼쪽 끝 212 · 필터 영역 212 · 검색창 224. 화면의 왼쪽 기준선이 둘이 됐다.
|
|
83
|
+
*
|
|
84
|
+
* 그 규칙들은 .header 안에 중첩되어 있어(.header .filters) 밑줄 없는 .filters 로는 이길 수
|
|
85
|
+
* 없다 — 특정도가 0,1,0 대 0,2,0 이다.
|
|
86
|
+
*/
|
|
87
|
+
.header .filters {
|
|
88
|
+
padding: 0;
|
|
89
|
+
background: none;
|
|
90
|
+
border-radius: 0;
|
|
91
|
+
gap: 12px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/*
|
|
95
|
+
* 이 화면에는 항목 필터가 없다 — 검색은 제목줄로 올라갔고 남은 것은 칩뿐이다. 그래도
|
|
96
|
+
* ox-filters-form 은 폭 0으로 남아 자리를 지키고, 그 뒤에 gap 12px 이 붙어 칩들을 밀어냈다.
|
|
97
|
+
*
|
|
98
|
+
* 실측: 표 셀 212 · 필터 영역 212 · 칩 224. 왼쪽 기준선이 다시 둘이 됐다.
|
|
99
|
+
*
|
|
100
|
+
* 컴포넌트가 스스로 [empty] 를 달아 주므로 그것으로 접는다. 필터가 생기면 그 표식이 사라져
|
|
101
|
+
* 다시 나온다.
|
|
102
|
+
*/
|
|
103
|
+
.header .filters ox-filters-form[empty] {
|
|
104
|
+
display: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/*
|
|
108
|
+
* 두 묶음 — 표 위. 지금 보는 쪽은 밑줄과 굵기로 말하고, 옆에 건수를 적는다.
|
|
109
|
+
*
|
|
110
|
+
* 건수를 안 눌러도 보이게 두는 것이 요점이다. 참조에 몇 건이 있는지 모르면 열어 볼 이유가 없다.
|
|
111
|
+
*
|
|
112
|
+
* ── 필터 칩보다 무거워야 한다 ─────────────────────────────────────────────
|
|
113
|
+
* 처음에 13px 맨글자로 두었더니, 바로 아래 「중요·만기·재상신」 칩이 테두리를 가져서 더 세게
|
|
114
|
+
* 보였다. **보는 대상을 바꾸는 것이 대상을 좁히는 것보다 약해 보였다.** 그래서 글자를 14px 로
|
|
115
|
+
* 올리고(칩은 13px) 세로 여백을 넓혀 제목 다음 자리를 준다.
|
|
116
|
+
*
|
|
117
|
+
* 좌우 여백은 품질 화면 탭에서 베끼지 않는다 — 그 화면은 --spacing-large 라 이 화면의 왼쪽
|
|
118
|
+
* 기준선 212px(표 셀과 같은 값)을 깨뜨린다. 무게만 같게 하고 기준선은 화면이 정한다.
|
|
119
|
+
*/
|
|
120
|
+
.groups {
|
|
121
|
+
flex: none;
|
|
122
|
+
display: flex;
|
|
123
|
+
gap: var(--spacing-large);
|
|
124
|
+
padding: 0 var(--spacing-medium);
|
|
125
|
+
border-bottom: 1px solid var(--ui-line, #d5dce0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.groups button {
|
|
129
|
+
border: none;
|
|
130
|
+
background: none;
|
|
131
|
+
padding: var(--spacing-medium) 2px calc(var(--spacing-medium) - 2px);
|
|
132
|
+
border-bottom: 2px solid transparent;
|
|
133
|
+
font: inherit;
|
|
134
|
+
font-size: 14px;
|
|
135
|
+
color: var(--ui-muted, #57676f);
|
|
136
|
+
cursor: pointer;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.groups button:hover:not([data-selected]) {
|
|
140
|
+
color: var(--ui-ink, #17272f);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/*
|
|
144
|
+
* ── 색은 밑줄이 말하고, 글자는 읽히는 색으로 ──────────────────────────────
|
|
145
|
+
* 고른 탭의 글자를 --ui-accent 로 두었는데, 그 토큰이 브랜드 초록(#0e8a56)이 되면서 14px
|
|
146
|
+
* 글자로는 안 읽히게 됐다.
|
|
147
|
+
*
|
|
148
|
+
* [확인함] 초록 #0e8a56 · 배경 rgb(240 242 244) → 대비 3.91. 본문 글자 기준 4.5 미달이다.
|
|
149
|
+
* 흰 바탕 기준으로는 4.39 인데, **이 앱 배경은 흰색이 아니라** 더 나쁘다.
|
|
150
|
+
*
|
|
151
|
+
* --ui-ink 15.1 고른 탭 글자
|
|
152
|
+
* --ui-muted 5.52 안 고른 탭 글자
|
|
153
|
+
* --ui-accent 3.91 밑줄 — UI 요소는 3.0 기준이라 통과한다
|
|
154
|
+
*
|
|
155
|
+
* 밑줄은 선택을 가장 강하게 표시하는 자리다. 브랜드색이 거기 남으면 잃는 것이 없다.
|
|
156
|
+
*/
|
|
157
|
+
.groups button[data-selected] {
|
|
158
|
+
font-weight: 600;
|
|
159
|
+
color: var(--ui-ink, #17272f);
|
|
160
|
+
border-bottom-color: var(--ui-accent, #262e38);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.groups .n {
|
|
164
|
+
margin-left: 4px;
|
|
165
|
+
color: var(--ui-faint, #8a959c);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.groups button[data-selected] .n {
|
|
169
|
+
color: inherit;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
43
173
|
`
|
|
44
174
|
]; }
|
|
45
175
|
get context() {
|
|
46
176
|
return { title: i18next.t('title.approval pending list'),
|
|
47
|
-
|
|
177
|
+
/*
|
|
178
|
+
* 검색의 자리는 **기기가 정한다.**
|
|
179
|
+
*
|
|
180
|
+
* PC 에서는 화면 안으로 내린다. 상단 바에 두면 거른 조건과 걸러진 결과가 화면의 위아래 끝으로
|
|
181
|
+
* 갈라지고, 지금 무엇으로 걸렀는지가 목록 옆에서 보이지 않는다.
|
|
182
|
+
*
|
|
183
|
+
* 좁은 화면에서는 상단 바에 그대로 둔다. 헤드룸에 두면 검색창과 필터가 목록을 그만큼 밀어
|
|
184
|
+
* 내리고, 카드 두 장이 화면 밖으로 나간다. 상단 바는 그 자리를 아끼려고 있는 것이다.
|
|
185
|
+
*
|
|
186
|
+
* 이 저장소에는 두 방식이 다 있다 — 헤드룸 50곳, 상단 바 51곳. 정형인 것은 구조
|
|
187
|
+
* (.header / .filters / .filter)이고 검색의 자리는 화면이 정한다.
|
|
188
|
+
*/
|
|
189
|
+
/*
|
|
190
|
+
* 검색은 **페이지 제목 줄의 오른쪽 끝**에 둔다 — 다른 화면들과 같은 자리다.
|
|
191
|
+
*
|
|
192
|
+
* 전에는 좁은 화면에서만 여기에 넣고, 넓은 화면에서는 헤드룸의 ox-filters-form 이 검색을
|
|
193
|
+
* 그렸다. 그래서 같은 앱 안에서 검색 상자의 자리가 화면마다 달랐다.
|
|
194
|
+
*
|
|
195
|
+
* 헤드룸에는 좁히는 칩만 남는다. 폼은 without-search 로 남겨 두어, 나중에 어느 칸에
|
|
196
|
+
* filter 를 달면 그 UI 가 저절로 생긴다(비면 아무것도 그리지 않는다).
|
|
197
|
+
*/
|
|
198
|
+
search: {
|
|
199
|
+
handler: (search) => {
|
|
48
200
|
this.grist.searchText = search;
|
|
49
201
|
},
|
|
50
|
-
value: this.grist?.searchText || ''
|
|
202
|
+
value: this.grist?.searchText || ''
|
|
203
|
+
},
|
|
51
204
|
filter: { handler: () => {
|
|
52
205
|
this.grist.toggleHeadroom();
|
|
53
206
|
}
|
|
@@ -59,6 +212,30 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
59
212
|
render() {
|
|
60
213
|
const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID');
|
|
61
214
|
return html `
|
|
215
|
+
<!--
|
|
216
|
+
두 묶음 — 표 **바깥** 위에 둔다.
|
|
217
|
+
|
|
218
|
+
헤드룸에 두면 필터를 접을 때 함께 사라지고, 그러면 참조로 가는 유일한 길이 없어진다.
|
|
219
|
+
필터는 접혀도 되지만(풀면 전체가 돌아온다) 이것은 접히면 안 된다.
|
|
220
|
+
-->
|
|
221
|
+
<div class="groups">
|
|
222
|
+
${[
|
|
223
|
+
['approval', 'label.group-approval', 'text.group-approval-hint'],
|
|
224
|
+
['reference', 'label.group-reference', 'text.group-reference-hint']
|
|
225
|
+
].map(([key, label, hint]) => html `
|
|
226
|
+
<button
|
|
227
|
+
?data-selected=${this.group === key}
|
|
228
|
+
title=${i18next.t(hint)}
|
|
229
|
+
@click=${() => this.selectGroup(key)}
|
|
230
|
+
>
|
|
231
|
+
${i18next.t(label)}
|
|
232
|
+
${this.counts[key] === undefined
|
|
233
|
+
? ''
|
|
234
|
+
: html `<span class="n">${this.counts[key].toLocaleString(i18next.language || undefined)}</span>`}
|
|
235
|
+
</button>
|
|
236
|
+
`)}
|
|
237
|
+
</div>
|
|
238
|
+
|
|
62
239
|
<ox-grist
|
|
63
240
|
.mode=${mode}
|
|
64
241
|
.config=${this.gristConfig}
|
|
@@ -67,15 +244,13 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
67
244
|
>
|
|
68
245
|
<div slot="headroom" class="header">
|
|
69
246
|
<div class="filters">
|
|
247
|
+
<!-- 좁은 화면에서는 상단 바가 검색을 갖는다(위 context 주석 참조). -->
|
|
248
|
+
<ox-filters-form class="filter" without-search></ox-filters-form>
|
|
70
249
|
<ox-input-select-buttons
|
|
71
250
|
.value=${this.filters}
|
|
72
251
|
.options=${[
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
},
|
|
76
|
-
{ display: i18next.t('label.filter-due'),
|
|
77
|
-
value: 'due'
|
|
78
|
-
},
|
|
252
|
+
importantChip(),
|
|
253
|
+
dueSoonChip(),
|
|
79
254
|
{ display: i18next.t('label.filter-resubmitted'),
|
|
80
255
|
value: 'resubmitted'
|
|
81
256
|
}
|
|
@@ -95,146 +270,250 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
95
270
|
</ox-grist>
|
|
96
271
|
`;
|
|
97
272
|
}
|
|
273
|
+
/** 주소만 바꾼다 — 목록도 설정도 pageUpdated 가 한 벌로 다시 세운다. */
|
|
274
|
+
selectGroup(group) {
|
|
275
|
+
if (this.group === group) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const page = this.lifecycle?.page;
|
|
279
|
+
navigate(group === 'approval' ? `${page}` : `${page}/${group}`);
|
|
280
|
+
}
|
|
281
|
+
async syncGroup(key) {
|
|
282
|
+
const group = key === 'reference' ? 'reference' : 'approval';
|
|
283
|
+
if (this.group === group && this.gristConfig) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
this.group = group;
|
|
287
|
+
this.applyConfig();
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* 안 보고 있는 묶음의 건수를 따로 센다 — 한 줄만 받아 `total` 을 읽는다.
|
|
291
|
+
*
|
|
292
|
+
* 보고 있는 쪽의 수는 목록 조회가 이미 알려 준다(fetchHandler). 그래서 여기서는 반대쪽만 묻는다.
|
|
293
|
+
*/
|
|
294
|
+
async countOther() {
|
|
295
|
+
const other = this.group === 'approval' ? 'reference' : 'approval';
|
|
296
|
+
const query = other === 'approval' ? 'approvalPendingList' : 'approvalReferenceList';
|
|
297
|
+
const response = await client.query({
|
|
298
|
+
query: gql `
|
|
299
|
+
query ($pagination: Pagination) {
|
|
300
|
+
responses: ${query}(pagination: $pagination) {
|
|
301
|
+
total
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
`,
|
|
305
|
+
variables: { pagination: { page: 1, limit: 1 } },
|
|
306
|
+
fetchPolicy: 'no-cache'
|
|
307
|
+
});
|
|
308
|
+
/*
|
|
309
|
+
* A count we could not fetch is not zero.
|
|
310
|
+
*
|
|
311
|
+
* This read `?? 0`, and the client runs with errorPolicy: 'all' — a failed query comes back as
|
|
312
|
+
* a resolved response carrying `errors`, not a rejection. So any failure drew the other tab as
|
|
313
|
+
* 「0」, which says "nothing is waiting on you". The render already draws nothing when the count
|
|
314
|
+
* is undefined, so leaving it unknown is both honest and already handled.
|
|
315
|
+
*/
|
|
316
|
+
const total = response.errors?.length ? undefined : response.data?.responses?.total;
|
|
317
|
+
if (typeof total != 'number') {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
this.counts = { ...this.counts, [other]: total };
|
|
321
|
+
}
|
|
98
322
|
async pageInitialized(lifecycle) {
|
|
323
|
+
/*
|
|
324
|
+
* 주소가 묶음을 정한다. 주소로 바로 들어온 경우가 여기서 잡힌다 — pageUpdated 가 이 함수보다 먼저
|
|
325
|
+
* 지나가므로 그것만 믿으면 `/reference` 링크가 결재 묶음을 보여준다.
|
|
326
|
+
*/
|
|
327
|
+
await this.syncGroup(lifecycle?.resourceId);
|
|
328
|
+
await this.countOther();
|
|
329
|
+
}
|
|
330
|
+
applyConfig() {
|
|
99
331
|
this.gristConfig = { list: { thumbnail: 'thumbnail',
|
|
100
332
|
fields: ['name', 'description'],
|
|
101
333
|
details: ['createdAt', 'due']
|
|
102
334
|
},
|
|
335
|
+
/*
|
|
336
|
+
* 빈 화면이 참조를 가르친다.
|
|
337
|
+
*
|
|
338
|
+
* 참조를 처음 누른 사람은 여기서 그 개념을 만난다. 기본 문장 「해당하는 것이 없습니다」는
|
|
339
|
+
* 참조가 무엇인지 말해 주지 않는다 — 내가 결정해야 하는 것인지, 안 봐도 되는 것인지 모른 채
|
|
340
|
+
* 화면을 닫는다.
|
|
341
|
+
*
|
|
342
|
+
* 그래서 없을 때 그 묶음이 무엇인지 적는다.
|
|
343
|
+
*/
|
|
344
|
+
emptyNote: this.group === 'reference'
|
|
345
|
+
? {
|
|
346
|
+
icon: 'visibility',
|
|
347
|
+
title: i18next.t('text.no-reference-title'),
|
|
348
|
+
description: i18next.t('text.no-reference-description')
|
|
349
|
+
}
|
|
350
|
+
: {
|
|
351
|
+
icon: 'task_alt',
|
|
352
|
+
title: i18next.t('text.no-approval-title'),
|
|
353
|
+
description: i18next.t('text.no-approval-description')
|
|
354
|
+
},
|
|
355
|
+
/*
|
|
356
|
+
* ── 승인자가 먼저 묻는 순서대로 둔다 (2026-09-01) ─────────────────────
|
|
357
|
+
*
|
|
358
|
+
* 전에는 앞자리를 「열기」 버튼과 **모든 줄이 같은 값인 상태 칸**이 차지했고, 승인자가 가장 먼저
|
|
359
|
+
* 묻는 「누가 올렸나」는 1200px 밖에 있었다. 실측으로 이름부터 담당까지 폭 합이 1287px 이었고
|
|
360
|
+
* 보이는 폭은 1150px 이었다.
|
|
361
|
+
*
|
|
362
|
+
* 순서를 물음의 순서로 바꾼다 — 무엇을 · 누가 · 언제까지 · 얼마나 급한가.
|
|
363
|
+
*
|
|
364
|
+
* ── 「열기」 버튼을 없앤다 ─────────────────────────────────────────────
|
|
365
|
+
* 줄을 누르면 이미 열린다(아래 rows.handlers.click). 같은 일을 하는 버튼이 줄마다 72px 을
|
|
366
|
+
* 차지하고 있었다.
|
|
367
|
+
*
|
|
368
|
+
* 다만 그 버튼의 아이콘이 신호를 하나 들고 있었다 — `comment` 가 있으면 `label`, 없으면
|
|
369
|
+
* `new_label`. **승인자가 보다가 메모를 남기고 미뤄 둔 것**이라는 뜻이다. 버리지 않고 이름 옆에
|
|
370
|
+
* 말로 적는다. 태그 아이콘 두 개로는 아무도 그 뜻으로 읽지 않는다.
|
|
371
|
+
*
|
|
372
|
+
* 열람 여부는 담는 값이 없다 — `ActivityApproval` 에 `readAt` 이 없어서, 열어만 보고 나가면
|
|
373
|
+
* 흔적이 남지 않는다. 지금 표시하는 것은 「메모를 남겨 뒀나」이지 「열어 봤나」가 아니다.
|
|
374
|
+
*/
|
|
103
375
|
columns: [
|
|
104
376
|
{ type: 'gutter', gutterName: 'sequence', fixed: true },
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
iconOnly: false,
|
|
108
|
-
title: i18next.t('button.open'),
|
|
109
|
-
width: 72,
|
|
110
|
-
fixed: true,
|
|
111
|
-
icon: record => (record && !record.comment ? 'new_label' : 'label')
|
|
112
|
-
},
|
|
113
|
-
{ type: 'string',
|
|
377
|
+
{
|
|
378
|
+
type: 'string',
|
|
114
379
|
name: 'name',
|
|
115
380
|
fixed: true,
|
|
116
|
-
header: i18next.t('field.
|
|
117
|
-
record: {
|
|
381
|
+
header: i18next.t('field.wl-subject'),
|
|
382
|
+
record: {
|
|
383
|
+
editable: false,
|
|
384
|
+
/*
|
|
385
|
+
* 이름과 설명을 한 칸에 둔다. 둘은 함께 읽는 것인데 275px 짜리 칸으로 갈라 두면 눈이
|
|
386
|
+
* 오가고, 그 사이를 우선순위·차수가 가로막고 있었다.
|
|
387
|
+
*/
|
|
388
|
+
renderer: subjectCell(approvalMarks)
|
|
118
389
|
},
|
|
119
390
|
filter: 'search',
|
|
120
391
|
sortable: true,
|
|
121
|
-
width:
|
|
392
|
+
width: 320
|
|
122
393
|
},
|
|
123
|
-
{
|
|
394
|
+
{
|
|
395
|
+
/*
|
|
396
|
+
* 설명은 제목 칸 안에서 두 줄로 보인다(activity-cells 의 subjectCell). 그래서 따로 보일 칸이
|
|
397
|
+
* 필요하지 않다.
|
|
398
|
+
*
|
|
399
|
+
* 그런데 **칸을 없애면 설명으로 검색하는 기능이 함께 사라진다.** searchText 는
|
|
400
|
+
* filter: 'search' 를 선언한 컬럼마다 필터를 만들고 hidden 은 보지 않는다
|
|
401
|
+
* (data-grist.ts 의 set searchText). 실측으로 확인했다 — 컬럼을 없앤 뒤 설명에만 있는
|
|
402
|
+
* 낱말로 검색하면 0건이었다.
|
|
403
|
+
*
|
|
404
|
+
* 그래서 감춘 칸으로 남겨 검색만 남긴다.
|
|
405
|
+
*/
|
|
406
|
+
type: 'string',
|
|
124
407
|
name: 'description',
|
|
125
408
|
header: i18next.t('field.description'),
|
|
126
|
-
record: { editable: false
|
|
127
|
-
},
|
|
409
|
+
record: { editable: false },
|
|
128
410
|
filter: 'search',
|
|
129
|
-
|
|
411
|
+
hidden: true
|
|
130
412
|
},
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
413
|
+
{
|
|
414
|
+
type: 'resource-object',
|
|
415
|
+
name: 'creator',
|
|
416
|
+
header: i18next.t('field.wl-drafter'),
|
|
417
|
+
record: { editable: false },
|
|
418
|
+
sortable: true,
|
|
419
|
+
width: 110
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
type: 'resource-object',
|
|
423
|
+
name: 'assignee',
|
|
424
|
+
header: i18next.t('field.wl-owner'),
|
|
425
|
+
record: { editable: false },
|
|
426
|
+
width: 110
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
/*
|
|
430
|
+
* 기한 한 칸 — 남은 시간이 위, 절대 시각이 아래다.
|
|
431
|
+
*
|
|
432
|
+
* 전에는 「만기 시간」(180px)과 「남은 시간」(180px)이 따로 있었다. 사람이 묻는 것은 「얼마나
|
|
433
|
+
* 남았나」 하나이고, 절대 시각은 그것을 확인할 때만 본다. 두 칸으로 360px 을 쓸 일이 아니다.
|
|
434
|
+
*/
|
|
435
|
+
type: 'string',
|
|
436
|
+
name: 'dueAt',
|
|
437
|
+
header: i18next.t('field.wl-due'),
|
|
438
|
+
record: {
|
|
439
|
+
editable: false,
|
|
440
|
+
renderer: dueCell(() => this.active)
|
|
140
441
|
},
|
|
141
|
-
|
|
142
|
-
|
|
442
|
+
sortable: true,
|
|
443
|
+
width: 170
|
|
143
444
|
},
|
|
144
|
-
{
|
|
445
|
+
{
|
|
446
|
+
type: 'number',
|
|
145
447
|
name: 'priority',
|
|
146
|
-
header: i18next.t('field.priority'),
|
|
147
|
-
record: {
|
|
448
|
+
header: i18next.t('field.wl-priority'),
|
|
449
|
+
record: {
|
|
450
|
+
editable: false,
|
|
148
451
|
align: 'center',
|
|
149
|
-
renderer:
|
|
150
|
-
const color = ['', '#98FB98', '#87CEEB', '#FF6B6B', '#8B0000'][value || 0];
|
|
151
|
-
const count = new Array(value > 4 ? 4 : value || 0).fill('');
|
|
152
|
-
return html `${count.map(() => html `<md-icon style="color:${color};">star</md-icon>`)}`;
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
width: 70
|
|
156
|
-
},
|
|
157
|
-
{ type: 'number',
|
|
158
|
-
name: 'round',
|
|
159
|
-
header: i18next.t('field.round'),
|
|
160
|
-
record: { editable: false,
|
|
161
|
-
align: 'right'
|
|
452
|
+
renderer: priorityCell()
|
|
162
453
|
},
|
|
163
|
-
width:
|
|
454
|
+
width: 80
|
|
164
455
|
},
|
|
165
|
-
{
|
|
456
|
+
{
|
|
457
|
+
type: 'select',
|
|
166
458
|
name: 'state',
|
|
167
|
-
header: i18next.t('field.
|
|
168
|
-
record: {
|
|
459
|
+
header: i18next.t('field.wl-state'),
|
|
460
|
+
record: {
|
|
461
|
+
editable: false,
|
|
169
462
|
options: [
|
|
170
463
|
{ display: i18next.t('label.activity-state-submitted'), value: 'submitted' },
|
|
171
464
|
{ display: i18next.t('label.activity-state-escalated'), value: 'escalated' }
|
|
172
465
|
]
|
|
173
466
|
},
|
|
174
|
-
width:
|
|
175
|
-
},
|
|
176
|
-
{ type: 'datetime',
|
|
177
|
-
name: 'dueAt',
|
|
178
|
-
header: i18next.t('field.due-at'),
|
|
179
|
-
record: { editable: false,
|
|
180
|
-
options: { year: 'numeric',
|
|
181
|
-
month: 'numeric',
|
|
182
|
-
day: 'numeric'
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
width: 180
|
|
186
|
-
},
|
|
187
|
-
{ type: 'string',
|
|
188
|
-
name: 'dueIn',
|
|
189
|
-
header: i18next.t('field.due-in'),
|
|
190
|
-
record: { editable: false,
|
|
191
|
-
renderer: (value, column, record, rowIndex, field) => {
|
|
192
|
-
return html ` <span>${asyncReplace(this.dueTimer(record.dueAt))}</span>`;
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
sortable: false,
|
|
196
|
-
width: 180
|
|
197
|
-
},
|
|
198
|
-
{ type: 'resource-object',
|
|
199
|
-
name: 'creator',
|
|
200
|
-
header: i18next.t('field.issuer'),
|
|
201
|
-
record: { editable: false
|
|
202
|
-
},
|
|
203
|
-
sortable: true,
|
|
204
|
-
width: 85
|
|
467
|
+
width: 90
|
|
205
468
|
},
|
|
206
|
-
{
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
},
|
|
211
|
-
width:
|
|
469
|
+
{
|
|
470
|
+
type: 'number',
|
|
471
|
+
name: 'round',
|
|
472
|
+
header: i18next.t('field.wl-round'),
|
|
473
|
+
record: { editable: false, align: 'right' },
|
|
474
|
+
width: 60
|
|
212
475
|
},
|
|
213
|
-
{
|
|
476
|
+
{
|
|
477
|
+
type: 'datetime',
|
|
214
478
|
name: 'createdAt',
|
|
215
|
-
header: i18next.t('field.
|
|
216
|
-
record: { editable: false
|
|
217
|
-
},
|
|
479
|
+
header: i18next.t('field.wl-submitted-at'),
|
|
480
|
+
record: { editable: false },
|
|
218
481
|
sortable: true,
|
|
219
482
|
filter: false,
|
|
220
|
-
width:
|
|
483
|
+
width: 160
|
|
221
484
|
},
|
|
222
|
-
{
|
|
485
|
+
{
|
|
486
|
+
type: 'datetime',
|
|
223
487
|
name: 'updatedAt',
|
|
224
|
-
header: i18next.t('field.
|
|
225
|
-
record: { editable: false
|
|
226
|
-
},
|
|
488
|
+
header: i18next.t('field.wl-changed-at'),
|
|
489
|
+
record: { editable: false },
|
|
227
490
|
sortable: true,
|
|
228
491
|
filter: false,
|
|
229
|
-
width:
|
|
492
|
+
width: 160
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
type: 'string',
|
|
496
|
+
name: 'viewSource',
|
|
497
|
+
header: i18next.t('field.wl-view-source'),
|
|
498
|
+
record: {
|
|
499
|
+
editable: false,
|
|
500
|
+
renderer: function (value, column, record, rowIndex, field) {
|
|
501
|
+
const { viewType, viewSource } = record.activity || {};
|
|
502
|
+
var type = viewType !== 'board' ? 'string' : 'board';
|
|
503
|
+
return getRenderer(type)(viewSource, column, record, rowIndex, field);
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
width: 140,
|
|
507
|
+
hidden: true
|
|
230
508
|
},
|
|
231
|
-
{
|
|
509
|
+
{
|
|
510
|
+
type: 'image',
|
|
232
511
|
name: 'thumbnail',
|
|
233
512
|
header: i18next.t('field.thumbnail'),
|
|
234
513
|
record: { editable: false },
|
|
235
514
|
hidden: true,
|
|
236
|
-
handlers: {
|
|
237
|
-
|
|
515
|
+
handlers: {
|
|
516
|
+
click: (columns, data, column, record, rowIndex) => {
|
|
238
517
|
this.openActivityApproval(record);
|
|
239
518
|
}
|
|
240
519
|
}
|
|
@@ -247,33 +526,110 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
247
526
|
}
|
|
248
527
|
}
|
|
249
528
|
},
|
|
529
|
+
/*
|
|
530
|
+
* 급한 것부터 — 「할 일」 목록과 같은 순서다.
|
|
531
|
+
*
|
|
532
|
+
* 최근 변경 내림차순이었다. 받은 편지함으로는 말이 되지만 이 목록의 물음은 「무엇을 먼저
|
|
533
|
+
* 판단해야 하나」다. 그 정렬에서는 나흘 지난 건이 목록 중간에 묻힌다 — 기안자가 나흘째
|
|
534
|
+
* 기다리는 동안.
|
|
535
|
+
*
|
|
536
|
+
* 두 목록은 같은 성격이다(내가 처리해야 할 것). 순서 기준이 다르면 화면을 옮길 때마다 다른
|
|
537
|
+
* 순서를 본다.
|
|
538
|
+
*
|
|
539
|
+
* 기한이 없는 건은 뒤로 간다 — 서버가 그 자리를 정한다(applyApprovalSortings).
|
|
540
|
+
*/
|
|
250
541
|
sorters: [
|
|
251
|
-
{ name: '
|
|
252
|
-
desc:
|
|
542
|
+
{ name: 'dueAt',
|
|
543
|
+
desc: false
|
|
253
544
|
}
|
|
254
545
|
]
|
|
255
546
|
};
|
|
547
|
+
if (this.group === 'reference') {
|
|
548
|
+
/*
|
|
549
|
+
* ── 내 참조가 어떤 상태인가 ─────────────────────────────────────────
|
|
550
|
+
* 참조 묶음에는 **끝난 건도 나온다.** 참조는 결재를 막지 않으므로 결재가 끝난 뒤에 의견이 달릴
|
|
551
|
+
* 수 있고, 그래서 목록에서 끝난 것을 걸러내지 않는다. 화면이 그것을 구별해야 한다 — 안 그러면
|
|
552
|
+
* 이미 끝난 건을 열어 의견을 쓰고 그것이 반영된 줄로 안다.
|
|
553
|
+
*
|
|
554
|
+
* **의견 없이 끝난 것을 빈 칸으로 두지 않는다.** 침묵은 동의가 아니다. 품질팀장이 아무 말 안
|
|
555
|
+
* 하고 끝났으면 「반대하지 않았다」가 아니라 「말할 기회에 말하지 않았다」다. 나중에 사고 조사에서
|
|
556
|
+
* 그 둘은 다르다.
|
|
557
|
+
*
|
|
558
|
+
* ── 「결정 후 의견」은 commentedAt 으로 판정한다 ────────────────────────
|
|
559
|
+
* 처음에 `updatedAt > terminatedAt` 으로 추론하려 했다. **그 부등호는 언제나 참이다** —
|
|
560
|
+
* 닫는 코드가 `terminatedAt: new Date()` 를 저장 **전에** 만들고, `updatedAt` 은
|
|
561
|
+
* `@UpdateDateColumn` 이 DB 에 쓰는 순간 찍는다. 두 값이 몇 밀리초 어긋나서, 닫힌 참조가
|
|
562
|
+
* 전부 「결정 후 의견」으로 보였을 것이다.
|
|
563
|
+
*
|
|
564
|
+
* 그래서 사실에 집을 줬다 — `ActivityApproval.commentedAt`(2eaceace25). 의견을 언제 말했나는
|
|
565
|
+
* 파생으로 알 수 없는 값이다. **빈 의견에는 시각을 찍지 않는다**(서버 `stampedComment`), 그래서
|
|
566
|
+
* 시각의 유무가 곧 침묵과 발언의 구별이다.
|
|
567
|
+
*/
|
|
568
|
+
this.gristConfig.columns = [
|
|
569
|
+
...this.gristConfig.columns,
|
|
570
|
+
{
|
|
571
|
+
type: 'string',
|
|
572
|
+
name: 'myReference',
|
|
573
|
+
header: i18next.t('field.my-reference'),
|
|
574
|
+
record: {
|
|
575
|
+
editable: false,
|
|
576
|
+
renderer: (value, column, record) => {
|
|
577
|
+
const closed = record?.judgment === 'closed';
|
|
578
|
+
const comment = (record?.comment || '').trim();
|
|
579
|
+
if (!closed) {
|
|
580
|
+
return html `<span class="quiet">${i18next.t('text.reference-open')}</span>`;
|
|
581
|
+
}
|
|
582
|
+
if (!comment) {
|
|
583
|
+
return html `<span class="pill quiet"
|
|
584
|
+
><span class="dot"></span>${i18next.t('text.reference-closed-silent')}</span
|
|
585
|
+
>`;
|
|
586
|
+
}
|
|
587
|
+
/*
|
|
588
|
+
* 의견은 있는데 시각이 없으면 **언제인지 모른다** — commentedAt 이 생기기 전에 적힌
|
|
589
|
+
* 줄이다. 모르는 것을 「결정 전」으로도 「결정 후」로도 적지 않고, 의견만 보인다.
|
|
590
|
+
*/
|
|
591
|
+
const after = record?.commentedAt &&
|
|
592
|
+
record?.terminatedAt &&
|
|
593
|
+
new Date(record.commentedAt).getTime() > new Date(record.terminatedAt).getTime();
|
|
594
|
+
return html `
|
|
595
|
+
<span class="line">
|
|
596
|
+
<span>${comment}</span>
|
|
597
|
+
${after ? html `<span class="pill warn">${i18next.t('text.reference-after-decision')}</span>` : ''}
|
|
598
|
+
</span>
|
|
599
|
+
`;
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
width: 260
|
|
603
|
+
}
|
|
604
|
+
];
|
|
605
|
+
}
|
|
256
606
|
}
|
|
257
607
|
async pageUpdated(changes, lifecycle, changedBefore) {
|
|
608
|
+
if (lifecycle?.resourceId !== changedBefore?.resourceId) {
|
|
609
|
+
await this.syncGroup(lifecycle?.resourceId);
|
|
610
|
+
await this.countOther();
|
|
611
|
+
}
|
|
258
612
|
if (this.active) {
|
|
259
613
|
this.grist?.fetch();
|
|
260
614
|
}
|
|
261
615
|
}
|
|
262
616
|
async fetchHandler({ page, limit, sortings = [], filters = [] }) {
|
|
263
617
|
if (this.filters?.includes('important')) {
|
|
264
|
-
filters = adjustFilters(filters,
|
|
618
|
+
filters = adjustFilters(filters, importantFilters());
|
|
265
619
|
}
|
|
266
620
|
if (this.filters?.includes('due')) {
|
|
267
|
-
|
|
268
|
-
filters = adjustFilters(filters, [{ name: 'dueAt', operator: 'lte', value: tomorrow }]);
|
|
621
|
+
filters = adjustFilters(filters, dueSoonFilters());
|
|
269
622
|
}
|
|
270
623
|
if (this.filters?.includes('resubmitted')) {
|
|
271
624
|
filters = adjustFilters(filters, [{ name: 'round', operator: 'gte', value: 2 }]);
|
|
272
625
|
}
|
|
273
626
|
const response = await client.query({ query: gql `
|
|
274
|
-
query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) { responses: approvalPendingList(filters: $filters, pagination: $pagination, sortings: $sortings) { items { id
|
|
627
|
+
query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) { responses: ${this.group === 'reference' ? 'approvalReferenceList' : 'approvalPendingList'}(filters: $filters, pagination: $pagination, sortings: $sortings) { items { id
|
|
275
628
|
round
|
|
276
629
|
comment
|
|
630
|
+
commentedAt
|
|
631
|
+
judgment
|
|
632
|
+
terminatedAt
|
|
277
633
|
activityThread { activityInstance { activityId: id
|
|
278
634
|
name
|
|
279
635
|
description
|
|
@@ -309,7 +665,10 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
309
665
|
sortings
|
|
310
666
|
}
|
|
311
667
|
});
|
|
312
|
-
|
|
668
|
+
const total = response.data.responses.total || 0;
|
|
669
|
+
/* 보고 있는 묶음의 수는 이 조회가 알려 준다. 반대쪽만 따로 센다(countOther). */
|
|
670
|
+
this.counts = { ...this.counts, [this.group]: total };
|
|
671
|
+
return { total,
|
|
313
672
|
records: (response.data.responses.items || []).map(item => {
|
|
314
673
|
let thread = item.activityThread;
|
|
315
674
|
return { id: item.id,
|
|
@@ -321,6 +680,9 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
321
680
|
output: thread.output,
|
|
322
681
|
round: item.round,
|
|
323
682
|
comment: item.comment,
|
|
683
|
+
judgment: item.judgment,
|
|
684
|
+
commentedAt: item.commentedAt,
|
|
685
|
+
terminatedAt: item.terminatedAt,
|
|
324
686
|
updatedAt: item.updatedAt
|
|
325
687
|
};
|
|
326
688
|
})
|
|
@@ -329,26 +691,19 @@ let ApprovalPendingListPage = class ApprovalPendingListPage extends p13n(localiz
|
|
|
329
691
|
openActivityApproval(activityApproval) {
|
|
330
692
|
navigate(`activity-approval/${activityApproval.id}?title=${activityApproval.name}`);
|
|
331
693
|
}
|
|
332
|
-
async *dueTimer(exp) {
|
|
333
|
-
while (exp && this.active) {
|
|
334
|
-
var secs = Math.round((Number(exp) - Date.now()) / 1000);
|
|
335
|
-
var positive = secs >= 0;
|
|
336
|
-
secs = Math.abs(secs);
|
|
337
|
-
const days = Math.floor(secs / DAY);
|
|
338
|
-
secs -= days * DAY;
|
|
339
|
-
const hours = Math.floor(secs / HOUR);
|
|
340
|
-
secs -= hours * HOUR;
|
|
341
|
-
const minutes = Math.floor(secs / MIN);
|
|
342
|
-
const seconds = secs - minutes * MIN;
|
|
343
|
-
yield `${positive ? '' : '- '}${days ? `${days}${i18next.t('label.days')} ` : ''}${hours ? `${hours}${i18next.t('label.hours')} ` : ''}${minutes ? `${minutes}${i18next.t('label.minutes')} ` : ''}`;
|
|
344
|
-
await sleep(60 * 1000);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
694
|
};
|
|
348
695
|
__decorate([
|
|
349
696
|
property({ type: Object }),
|
|
350
697
|
__metadata("design:type", Object)
|
|
351
698
|
], ApprovalPendingListPage.prototype, "gristConfig", void 0);
|
|
699
|
+
__decorate([
|
|
700
|
+
state(),
|
|
701
|
+
__metadata("design:type", String)
|
|
702
|
+
], ApprovalPendingListPage.prototype, "group", void 0);
|
|
703
|
+
__decorate([
|
|
704
|
+
state(),
|
|
705
|
+
__metadata("design:type", Object)
|
|
706
|
+
], ApprovalPendingListPage.prototype, "counts", void 0);
|
|
352
707
|
__decorate([
|
|
353
708
|
property({ type: String }),
|
|
354
709
|
__metadata("design:type", String)
|