@things-factory/organization 10.1.222 → 10.1.226
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/pages/employee-profile/employee-profile-form.d.ts +31 -0
- package/dist-client/pages/employee-profile/employee-profile-form.js +86 -0
- package/dist-client/pages/employee-profile/employee-profile-form.js.map +1 -0
- package/dist-client/pages/employee-profile/employee-profile-list-page.d.ts +38 -0
- package/dist-client/pages/employee-profile/employee-profile-list-page.js +164 -0
- package/dist-client/pages/employee-profile/employee-profile-list-page.js.map +1 -0
- package/dist-client/pages/employee-profile/employee-profile-workbook.d.ts +33 -0
- package/dist-client/pages/employee-profile/employee-profile-workbook.js +87 -0
- package/dist-client/pages/employee-profile/employee-profile-workbook.js.map +1 -0
- package/dist-client/pages/employee-profile/my-employee-profile-page.d.ts +38 -0
- package/dist-client/pages/employee-profile/my-employee-profile-page.js +282 -0
- package/dist-client/pages/employee-profile/my-employee-profile-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +7 -0
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/role-tasks.js +4 -1
- package/dist-server/role-tasks.js.map +1 -1
- package/dist-server/service/employee-profile/employee-profile-mutation.d.ts +5 -0
- package/dist-server/service/employee-profile/employee-profile-mutation.js +45 -0
- package/dist-server/service/employee-profile/employee-profile-mutation.js.map +1 -0
- package/dist-server/service/employee-profile/employee-profile-query.d.ts +6 -0
- package/dist-server/service/employee-profile/employee-profile-query.js +58 -0
- package/dist-server/service/employee-profile/employee-profile-query.js.map +1 -0
- package/dist-server/service/employee-profile/employee-profile-rows.d.ts +29 -0
- package/dist-server/service/employee-profile/employee-profile-rows.js +122 -0
- package/dist-server/service/employee-profile/employee-profile-rows.js.map +1 -0
- package/dist-server/service/employee-profile/employee-profile-rules.d.ts +55 -0
- package/dist-server/service/employee-profile/employee-profile-rules.js +80 -0
- package/dist-server/service/employee-profile/employee-profile-rules.js.map +1 -0
- package/dist-server/service/employee-profile/employee-profile-type.d.ts +24 -0
- package/dist-server/service/employee-profile/employee-profile-type.js +81 -0
- package/dist-server/service/employee-profile/employee-profile-type.js.map +1 -0
- package/dist-server/service/employee-profile/employee-profile.d.ts +24 -0
- package/dist-server/service/employee-profile/employee-profile.js +112 -0
- package/dist-server/service/employee-profile/employee-profile.js.map +1 -0
- package/dist-server/service/employee-profile/index.d.ts +5 -0
- package/dist-server/service/employee-profile/index.js +9 -0
- package/dist-server/service/employee-profile/index.js.map +1 -0
- package/dist-server/service/index.d.ts +3 -2
- package/dist-server/service/index.js +6 -2
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/tests/employee-profile-db.test.ts +186 -0
- package/tests/employee-profile-rules.test.ts +76 -0
- package/tests/employee-profile-workbook.test.ts +89 -0
- package/things-factory.config.js +3 -0
- package/translations/en.json +38 -1
- package/translations/es.json +38 -1
- package/translations/ja.json +38 -1
- package/translations/ko.json +38 -1
- package/translations/ms.json +38 -1
- package/translations/zh.json +38 -1
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@material/web/icon/icon.js';
|
|
3
|
+
import '@operato/list-state/ox-empty-note.js';
|
|
4
|
+
import '@operato/list-state/ox-page-state.js';
|
|
5
|
+
import gql from 'graphql-tag';
|
|
6
|
+
import { css, html, nothing } from 'lit';
|
|
7
|
+
import { customElement, state } from 'lit/decorators.js';
|
|
8
|
+
import { client } from '@operato/graphql';
|
|
9
|
+
import { i18next, localize } from '@operato/i18n';
|
|
10
|
+
import { notify } from '@operato/layout';
|
|
11
|
+
import { PageView } from '@operato/shell';
|
|
12
|
+
import { ButtonStyles, DocumentPageStyles, FormFieldStyles } from '@operato/styles';
|
|
13
|
+
import { PROFILE_SECTIONS, profileFormOf, profileInputOf } from './employee-profile-form';
|
|
14
|
+
const PROFILE_FIELDS = `
|
|
15
|
+
id
|
|
16
|
+
summary
|
|
17
|
+
skills
|
|
18
|
+
educations { school major degree startedOn endedOn }
|
|
19
|
+
certificates { name issuer acquiredOn }
|
|
20
|
+
careers { company department position startedOn endedOn duties }
|
|
21
|
+
projects { name client startedOn endedOn role duties technologies }
|
|
22
|
+
updatedAt
|
|
23
|
+
`;
|
|
24
|
+
/**
|
|
25
|
+
* 내 프로필 — the employee writes their own career profile: a summary, skills, and the rows of education,
|
|
26
|
+
* certificates, past jobs and projects; sales attaches it to quotations (ADR-0169 decisions 2 · 5). Saved whole.
|
|
27
|
+
* An account not linked to an employee record has no profile to write, and is told so before anything is typed.
|
|
28
|
+
*/
|
|
29
|
+
let MyEmployeeProfilePage = class MyEmployeeProfilePage extends localize(i18next)(PageView) {
|
|
30
|
+
constructor() {
|
|
31
|
+
super(...arguments);
|
|
32
|
+
this.form = profileFormOf(null);
|
|
33
|
+
/** Whether the account is linked to an employee record - unknown until read. */
|
|
34
|
+
this.linked = null;
|
|
35
|
+
this.loading = false;
|
|
36
|
+
this.error = null;
|
|
37
|
+
this.busy = false;
|
|
38
|
+
}
|
|
39
|
+
static { this.styles = [
|
|
40
|
+
FormFieldStyles,
|
|
41
|
+
ButtonStyles,
|
|
42
|
+
DocumentPageStyles,
|
|
43
|
+
css `
|
|
44
|
+
label {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
gap: var(--spacing-small, 4px);
|
|
48
|
+
font-size: var(--ui-t-micro, 11px);
|
|
49
|
+
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
50
|
+
}
|
|
51
|
+
label > :is(input, textarea) {
|
|
52
|
+
font-size: var(--ui-t-body, 13px);
|
|
53
|
+
color: var(--ui-ink, var(--md-sys-color-on-surface));
|
|
54
|
+
}
|
|
55
|
+
input,
|
|
56
|
+
textarea {
|
|
57
|
+
box-sizing: border-box;
|
|
58
|
+
width: 100%;
|
|
59
|
+
font-family: inherit;
|
|
60
|
+
}
|
|
61
|
+
input {
|
|
62
|
+
height: var(--form-element-height-medium, 32px);
|
|
63
|
+
}
|
|
64
|
+
textarea {
|
|
65
|
+
min-height: 72px;
|
|
66
|
+
resize: vertical;
|
|
67
|
+
}
|
|
68
|
+
textarea.summary {
|
|
69
|
+
min-height: 120px;
|
|
70
|
+
}
|
|
71
|
+
.hint {
|
|
72
|
+
font-size: var(--ui-t-small, 11.5px);
|
|
73
|
+
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
74
|
+
}
|
|
75
|
+
/* A row of a section: its fields in a grid, the button that takes it away at its end. */
|
|
76
|
+
.row {
|
|
77
|
+
display: grid;
|
|
78
|
+
grid-template-columns: 1fr auto;
|
|
79
|
+
gap: var(--spacing-large, 12px);
|
|
80
|
+
align-items: start;
|
|
81
|
+
padding: var(--spacing-large, 12px) 0;
|
|
82
|
+
border-top: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));
|
|
83
|
+
}
|
|
84
|
+
.row:first-of-type {
|
|
85
|
+
border-top: 0;
|
|
86
|
+
padding-top: 0;
|
|
87
|
+
}
|
|
88
|
+
.fields {
|
|
89
|
+
display: grid;
|
|
90
|
+
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
91
|
+
gap: var(--spacing-medium, 8px) var(--spacing-large, 12px);
|
|
92
|
+
}
|
|
93
|
+
.fields .wide {
|
|
94
|
+
grid-column: 1 / -1;
|
|
95
|
+
}
|
|
96
|
+
.row > button {
|
|
97
|
+
margin-top: 18px;
|
|
98
|
+
}
|
|
99
|
+
.empty {
|
|
100
|
+
margin: 0;
|
|
101
|
+
font-size: var(--ui-t-body, 13px);
|
|
102
|
+
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
103
|
+
}
|
|
104
|
+
@media (pointer: coarse) {
|
|
105
|
+
input,
|
|
106
|
+
textarea {
|
|
107
|
+
font-size: 16px;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`
|
|
111
|
+
]; }
|
|
112
|
+
get context() {
|
|
113
|
+
return {
|
|
114
|
+
title: i18next.t('title.my-employee-profile'),
|
|
115
|
+
frame: 'document',
|
|
116
|
+
actions: this.linked && !this.busy ? [{ title: i18next.t('button.save'), icon: 'save', emphasis: { raised: true }, action: () => this.save() }] : []
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
async pageUpdated(changes) {
|
|
120
|
+
if (this.active && changes.active)
|
|
121
|
+
this.load();
|
|
122
|
+
}
|
|
123
|
+
async load() {
|
|
124
|
+
this.loading = true;
|
|
125
|
+
this.error = null;
|
|
126
|
+
try {
|
|
127
|
+
const response = await client.query({
|
|
128
|
+
query: gql `
|
|
129
|
+
query {
|
|
130
|
+
myEmployees { id }
|
|
131
|
+
myEmployeeProfile { ${PROFILE_FIELDS} }
|
|
132
|
+
}
|
|
133
|
+
`,
|
|
134
|
+
fetchPolicy: 'no-cache'
|
|
135
|
+
});
|
|
136
|
+
if (response.errors?.length)
|
|
137
|
+
throw response.errors[0];
|
|
138
|
+
this.linked = (response.data?.myEmployees ?? []).length > 0;
|
|
139
|
+
this.form = profileFormOf(response.data?.myEmployeeProfile);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
this.error = error;
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
this.loading = false;
|
|
146
|
+
this.updateContext();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async save() {
|
|
150
|
+
this.busy = true;
|
|
151
|
+
this.updateContext();
|
|
152
|
+
try {
|
|
153
|
+
const response = await client.mutate({
|
|
154
|
+
mutation: gql `
|
|
155
|
+
mutation ($profile: EmployeeProfileInput!) {
|
|
156
|
+
saveMyEmployeeProfile(profile: $profile) { ${PROFILE_FIELDS} }
|
|
157
|
+
}
|
|
158
|
+
`,
|
|
159
|
+
variables: { profile: profileInputOf(this.form) }
|
|
160
|
+
});
|
|
161
|
+
if (response.errors?.length)
|
|
162
|
+
throw response.errors[0];
|
|
163
|
+
this.form = profileFormOf(response.data.saveMyEmployeeProfile);
|
|
164
|
+
notify({ message: i18next.t('text.saved') });
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
notify({ level: 'error', message: error?.message ?? String(error) });
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
this.busy = false;
|
|
171
|
+
this.updateContext();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
render() {
|
|
175
|
+
const form = this.form;
|
|
176
|
+
const set = (patch) => (this.form = { ...form, ...patch });
|
|
177
|
+
return html `<ox-page-state .loading=${this.loading && this.linked === null} .error=${this.linked === null ? this.error : null} @retry=${() => this.load()}>
|
|
178
|
+
${this.linked === false
|
|
179
|
+
? html `<ox-empty-note
|
|
180
|
+
icon="badge"
|
|
181
|
+
title=${i18next.t('text.employee-profile-not-linked-title')}
|
|
182
|
+
description=${i18next.t('text.employee-profile-not-linked-description')}
|
|
183
|
+
></ox-empty-note>`
|
|
184
|
+
: this.linked
|
|
185
|
+
? html `<div class="page">
|
|
186
|
+
<section class="page-section">
|
|
187
|
+
<header class="page-section-header"><h3>${i18next.t('label.employee-profile-summary')}</h3></header>
|
|
188
|
+
<textarea
|
|
189
|
+
class="summary"
|
|
190
|
+
aria-label=${i18next.t('label.employee-profile-summary')}
|
|
191
|
+
.value=${form.summary}
|
|
192
|
+
?disabled=${this.busy}
|
|
193
|
+
@input=${(e) => set({ summary: e.target.value })}
|
|
194
|
+
></textarea>
|
|
195
|
+
</section>
|
|
196
|
+
|
|
197
|
+
<section class="page-section">
|
|
198
|
+
<header class="page-section-header"><h3>${i18next.t('label.employee-profile-skills')}</h3></header>
|
|
199
|
+
<input
|
|
200
|
+
aria-label=${i18next.t('label.employee-profile-skills')}
|
|
201
|
+
.value=${form.skills}
|
|
202
|
+
?disabled=${this.busy}
|
|
203
|
+
@input=${(e) => set({ skills: e.target.value })}
|
|
204
|
+
/>
|
|
205
|
+
<span class="hint">${i18next.t('text.employee-profile-skills-hint')}</span>
|
|
206
|
+
</section>
|
|
207
|
+
|
|
208
|
+
${PROFILE_SECTIONS.map(section => this.renderSection(section.key, section.title, section.fields))}
|
|
209
|
+
</div>`
|
|
210
|
+
: nothing}
|
|
211
|
+
</ox-page-state>`;
|
|
212
|
+
}
|
|
213
|
+
renderSection(key, title, fields) {
|
|
214
|
+
const rows = this.form[key];
|
|
215
|
+
const setRows = (next) => (this.form = { ...this.form, [key]: next });
|
|
216
|
+
const change = (index, name, value) => setRows(rows.map((row, at) => (at === index ? { ...row, [name]: value } : row)));
|
|
217
|
+
const blank = () => Object.fromEntries(fields.map(field => [field.name, '']));
|
|
218
|
+
return html `<section class="page-section">
|
|
219
|
+
<header class="page-section-header">
|
|
220
|
+
<h3>${i18next.t(title)}</h3>
|
|
221
|
+
<div class="page-section-actions">
|
|
222
|
+
<button ?disabled=${this.busy} @click=${() => setRows([...rows, blank()])}><md-icon>add</md-icon>${i18next.t('button.add')}</button>
|
|
223
|
+
</div>
|
|
224
|
+
</header>
|
|
225
|
+
${rows.length
|
|
226
|
+
? rows.map((row, index) => html `<div class="row">
|
|
227
|
+
<div class="fields">
|
|
228
|
+
${fields.map(field => field.input === 'textarea'
|
|
229
|
+
? html `<label class="wide"
|
|
230
|
+
>${i18next.t(field.label)}
|
|
231
|
+
<textarea .value=${row[field.name] ?? ''} ?disabled=${this.busy} @input=${(e) => change(index, field.name, e.target.value)}></textarea>
|
|
232
|
+
</label>`
|
|
233
|
+
: html `<label
|
|
234
|
+
>${i18next.t(field.label)}
|
|
235
|
+
<input
|
|
236
|
+
type=${field.input ?? 'text'}
|
|
237
|
+
.value=${row[field.name] ?? ''}
|
|
238
|
+
?disabled=${this.busy}
|
|
239
|
+
@input=${(e) => change(index, field.name, e.target.value)}
|
|
240
|
+
/>
|
|
241
|
+
</label>`)}
|
|
242
|
+
</div>
|
|
243
|
+
<button
|
|
244
|
+
quiet
|
|
245
|
+
icon-only
|
|
246
|
+
title=${i18next.t('button.delete')}
|
|
247
|
+
aria-label=${i18next.t('button.delete')}
|
|
248
|
+
?disabled=${this.busy}
|
|
249
|
+
@click=${() => setRows(rows.filter((_, at) => at !== index))}
|
|
250
|
+
>
|
|
251
|
+
<md-icon>close</md-icon>
|
|
252
|
+
</button>
|
|
253
|
+
</div>`)
|
|
254
|
+
: html `<p class="empty">${i18next.t('text.employee-profile-no-rows')}</p>`}
|
|
255
|
+
</section>`;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
__decorate([
|
|
259
|
+
state(),
|
|
260
|
+
__metadata("design:type", Object)
|
|
261
|
+
], MyEmployeeProfilePage.prototype, "form", void 0);
|
|
262
|
+
__decorate([
|
|
263
|
+
state(),
|
|
264
|
+
__metadata("design:type", Object)
|
|
265
|
+
], MyEmployeeProfilePage.prototype, "linked", void 0);
|
|
266
|
+
__decorate([
|
|
267
|
+
state(),
|
|
268
|
+
__metadata("design:type", Object)
|
|
269
|
+
], MyEmployeeProfilePage.prototype, "loading", void 0);
|
|
270
|
+
__decorate([
|
|
271
|
+
state(),
|
|
272
|
+
__metadata("design:type", Object)
|
|
273
|
+
], MyEmployeeProfilePage.prototype, "error", void 0);
|
|
274
|
+
__decorate([
|
|
275
|
+
state(),
|
|
276
|
+
__metadata("design:type", Object)
|
|
277
|
+
], MyEmployeeProfilePage.prototype, "busy", void 0);
|
|
278
|
+
MyEmployeeProfilePage = __decorate([
|
|
279
|
+
customElement('my-employee-profile-page')
|
|
280
|
+
], MyEmployeeProfilePage);
|
|
281
|
+
export { MyEmployeeProfilePage };
|
|
282
|
+
//# sourceMappingURL=my-employee-profile-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"my-employee-profile-page.js","sourceRoot":"","sources":["../../../client/pages/employee-profile/my-employee-profile-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/list-state/ox-empty-note.js'\nimport '@operato/list-state/ox-page-state.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { PageView } from '@operato/shell'\nimport { ButtonStyles, DocumentPageStyles, FormFieldStyles } from '@operato/styles'\n\nimport { PROFILE_SECTIONS, profileFormOf, profileInputOf, type ProfileField, type ProfileForm, type ProfileSectionKey } from './employee-profile-form'\n\nconst PROFILE_FIELDS = `\n id\n summary\n skills\n educations { school major degree startedOn endedOn }\n certificates { name issuer acquiredOn }\n careers { company department position startedOn endedOn duties }\n projects { name client startedOn endedOn role duties technologies }\n updatedAt\n`\n\n/**\n * 내 프로필 — the employee writes their own career profile: a summary, skills, and the rows of education,\n * certificates, past jobs and projects; sales attaches it to quotations (ADR-0169 decisions 2 · 5). Saved whole.\n * An account not linked to an employee record has no profile to write, and is told so before anything is typed.\n */\n@customElement('my-employee-profile-page')\nexport class MyEmployeeProfilePage extends localize(i18next)(PageView) {\n static styles = [\n FormFieldStyles,\n ButtonStyles,\n DocumentPageStyles,\n css`\n label {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-small, 4px);\n font-size: var(--ui-t-micro, 11px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n label > :is(input, textarea) {\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n input,\n textarea {\n box-sizing: border-box;\n width: 100%;\n font-family: inherit;\n }\n input {\n height: var(--form-element-height-medium, 32px);\n }\n textarea {\n min-height: 72px;\n resize: vertical;\n }\n textarea.summary {\n min-height: 120px;\n }\n .hint {\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n /* A row of a section: its fields in a grid, the button that takes it away at its end. */\n .row {\n display: grid;\n grid-template-columns: 1fr auto;\n gap: var(--spacing-large, 12px);\n align-items: start;\n padding: var(--spacing-large, 12px) 0;\n border-top: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n }\n .row:first-of-type {\n border-top: 0;\n padding-top: 0;\n }\n .fields {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n gap: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n }\n .fields .wide {\n grid-column: 1 / -1;\n }\n .row > button {\n margin-top: 18px;\n }\n .empty {\n margin: 0;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n @media (pointer: coarse) {\n input,\n textarea {\n font-size: 16px;\n }\n }\n `\n ]\n\n @state() form: ProfileForm = profileFormOf(null)\n /** Whether the account is linked to an employee record - unknown until read. */\n @state() linked: boolean | null = null\n @state() loading = false\n @state() error: unknown = null\n @state() busy = false\n\n get context() {\n return {\n title: i18next.t('title.my-employee-profile'),\n frame: 'document',\n actions: this.linked && !this.busy ? [{ title: i18next.t('button.save'), icon: 'save', emphasis: { raised: true }, action: () => this.save() }] : []\n }\n }\n\n async pageUpdated(changes: any) {\n if ((this as any).active && changes.active) this.load()\n }\n\n private async load() {\n this.loading = true\n this.error = null\n try {\n const response = await client.query({\n query: gql`\n query {\n myEmployees { id }\n myEmployeeProfile { ${PROFILE_FIELDS} }\n }\n `,\n fetchPolicy: 'no-cache'\n })\n if (response.errors?.length) throw response.errors[0]\n this.linked = (response.data?.myEmployees ?? []).length > 0\n this.form = profileFormOf(response.data?.myEmployeeProfile)\n } catch (error) {\n this.error = error\n } finally {\n this.loading = false\n this.updateContext()\n }\n }\n\n private async save() {\n this.busy = true\n this.updateContext()\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($profile: EmployeeProfileInput!) {\n saveMyEmployeeProfile(profile: $profile) { ${PROFILE_FIELDS} }\n }\n `,\n variables: { profile: profileInputOf(this.form) }\n })\n if (response.errors?.length) throw response.errors[0]\n this.form = profileFormOf(response.data.saveMyEmployeeProfile)\n notify({ message: i18next.t('text.saved') })\n } catch (error: any) {\n notify({ level: 'error', message: error?.message ?? String(error) })\n } finally {\n this.busy = false\n this.updateContext()\n }\n }\n\n render() {\n const form = this.form\n const set = (patch: Partial<ProfileForm>) => (this.form = { ...form, ...patch })\n\n return html`<ox-page-state .loading=${this.loading && this.linked === null} .error=${this.linked === null ? this.error : null} @retry=${() => this.load()}>\n ${this.linked === false\n ? html`<ox-empty-note\n icon=\"badge\"\n title=${i18next.t('text.employee-profile-not-linked-title')}\n description=${i18next.t('text.employee-profile-not-linked-description')}\n ></ox-empty-note>`\n : this.linked\n ? html`<div class=\"page\">\n <section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${i18next.t('label.employee-profile-summary')}</h3></header>\n <textarea\n class=\"summary\"\n aria-label=${i18next.t('label.employee-profile-summary')}\n .value=${form.summary}\n ?disabled=${this.busy}\n @input=${(e: any) => set({ summary: e.target.value })}\n ></textarea>\n </section>\n\n <section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${i18next.t('label.employee-profile-skills')}</h3></header>\n <input\n aria-label=${i18next.t('label.employee-profile-skills')}\n .value=${form.skills}\n ?disabled=${this.busy}\n @input=${(e: any) => set({ skills: e.target.value })}\n />\n <span class=\"hint\">${i18next.t('text.employee-profile-skills-hint')}</span>\n </section>\n\n ${PROFILE_SECTIONS.map(section => this.renderSection(section.key, section.title, section.fields))}\n </div>`\n : nothing}\n </ox-page-state>`\n }\n\n private renderSection(key: ProfileSectionKey, title: string, fields: ProfileField[]) {\n const rows = this.form[key]\n const setRows = (next: typeof rows) => (this.form = { ...this.form, [key]: next })\n const change = (index: number, name: string, value: string) => setRows(rows.map((row, at) => (at === index ? { ...row, [name]: value } : row)))\n const blank = () => Object.fromEntries(fields.map(field => [field.name, '']))\n\n return html`<section class=\"page-section\">\n <header class=\"page-section-header\">\n <h3>${i18next.t(title)}</h3>\n <div class=\"page-section-actions\">\n <button ?disabled=${this.busy} @click=${() => setRows([...rows, blank()])}><md-icon>add</md-icon>${i18next.t('button.add')}</button>\n </div>\n </header>\n ${rows.length\n ? rows.map(\n (row, index) => html`<div class=\"row\">\n <div class=\"fields\">\n ${fields.map(field =>\n field.input === 'textarea'\n ? html`<label class=\"wide\"\n >${i18next.t(field.label)}\n <textarea .value=${row[field.name] ?? ''} ?disabled=${this.busy} @input=${(e: any) => change(index, field.name, e.target.value)}></textarea>\n </label>`\n : html`<label\n >${i18next.t(field.label)}\n <input\n type=${field.input ?? 'text'}\n .value=${row[field.name] ?? ''}\n ?disabled=${this.busy}\n @input=${(e: any) => change(index, field.name, e.target.value)}\n />\n </label>`\n )}\n </div>\n <button\n quiet\n icon-only\n title=${i18next.t('button.delete')}\n aria-label=${i18next.t('button.delete')}\n ?disabled=${this.busy}\n @click=${() => setRows(rows.filter((_, at) => at !== index))}\n >\n <md-icon>close</md-icon>\n </button>\n </div>`\n )\n : html`<p class=\"empty\">${i18next.t('text.employee-profile-no-rows')}</p>`}\n </section>`\n }\n}\n"]}
|
package/dist-client/route.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function route(page: string): "approval-line-list" | "common-approval-line-templates-page" | "my-approval-line-templates-page" | "employee-list" | "department-list" | undefined;
|
|
1
|
+
export default function route(page: string): "approval-line-list" | "common-approval-line-templates-page" | "my-approval-line-templates-page" | "employee-list" | "department-list" | "my-employee-profile" | "employee-profile-list" | undefined;
|
package/dist-client/route.js
CHANGED
|
@@ -6,6 +6,13 @@ export default function route(page) {
|
|
|
6
6
|
case 'department-list':
|
|
7
7
|
import('./pages/department/department-list-page');
|
|
8
8
|
return page;
|
|
9
|
+
/* The employee writes their own profile; sales downloads the chosen ones for a quotation (ADR-0169). */
|
|
10
|
+
case 'my-employee-profile':
|
|
11
|
+
import('./pages/employee-profile/my-employee-profile-page');
|
|
12
|
+
return page;
|
|
13
|
+
case 'employee-profile-list':
|
|
14
|
+
import('./pages/employee-profile/employee-profile-list-page');
|
|
15
|
+
return page;
|
|
9
16
|
/* 공용과 내 것을 한 목록에 — 아래 둘을 대신한다. */
|
|
10
17
|
case 'approval-line-list':
|
|
11
18
|
import('./pages/approval-line/approval-line-list-page');
|
package/dist-client/route.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAY;IACxC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,eAAe;YAClB,MAAM,CAAC,qCAAqC,CAAC,CAAA;YAC7C,OAAO,IAAI,CAAA;QAEb,KAAK,iBAAiB;YACpB,MAAM,CAAC,yCAAyC,CAAC,CAAA;YACjD,OAAO,IAAI,CAAA;QAEb,kCAAkC;QAClC,KAAK,oBAAoB;YACvB,MAAM,CAAC,+CAA+C,CAAC,CAAA;YACvD,OAAO,IAAI,CAAA;QAEb;;;;WAIG;QACH,KAAK,qCAAqC;YACxC,MAAM,CAAC,2DAA2D,CAAC,CAAA;YACnE,OAAO,IAAI,CAAA;QAEb,KAAK,iCAAiC;YACpC,MAAM,CAAC,uDAAuD,CAAC,CAAA;YAC/D,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["export default function route(page: string) {\n switch (page) {\n case 'employee-list':\n import('./pages/employee/employee-list-page')\n return page\n\n case 'department-list':\n import('./pages/department/department-list-page')\n return page\n\n /* 공용과 내 것을 한 목록에 — 아래 둘을 대신한다. */\n case 'approval-line-list':\n import('./pages/approval-line/approval-line-list-page')\n return page\n\n /*\n * 아래 두 화면은 아직 살아 있다. operato-dataset · operato-gangsters · operato-twin 의 메뉴가 이 두\n * 주소를 가리킨다. 지금 지우면 그 세 앱의 메뉴가 아무 데도 안 닿는다. operato-plant 만 새 화면으로\n * 돌렸고, 나머지가 옮겨 온 뒤에 지운다.\n */\n case 'common-approval-line-templates-page':\n import('./pages/approval-line/common-approval-line-templates-page')\n return page\n\n case 'my-approval-line-templates-page':\n import('./pages/approval-line/my-approval-line-templates-page')\n return page\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAY;IACxC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,eAAe;YAClB,MAAM,CAAC,qCAAqC,CAAC,CAAA;YAC7C,OAAO,IAAI,CAAA;QAEb,KAAK,iBAAiB;YACpB,MAAM,CAAC,yCAAyC,CAAC,CAAA;YACjD,OAAO,IAAI,CAAA;QAEb,wGAAwG;QACxG,KAAK,qBAAqB;YACxB,MAAM,CAAC,mDAAmD,CAAC,CAAA;YAC3D,OAAO,IAAI,CAAA;QAEb,KAAK,uBAAuB;YAC1B,MAAM,CAAC,qDAAqD,CAAC,CAAA;YAC7D,OAAO,IAAI,CAAA;QAEb,kCAAkC;QAClC,KAAK,oBAAoB;YACvB,MAAM,CAAC,+CAA+C,CAAC,CAAA;YACvD,OAAO,IAAI,CAAA;QAEb;;;;WAIG;QACH,KAAK,qCAAqC;YACxC,MAAM,CAAC,2DAA2D,CAAC,CAAA;YACnE,OAAO,IAAI,CAAA;QAEb,KAAK,iCAAiC;YACpC,MAAM,CAAC,uDAAuD,CAAC,CAAA;YAC/D,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["export default function route(page: string) {\n switch (page) {\n case 'employee-list':\n import('./pages/employee/employee-list-page')\n return page\n\n case 'department-list':\n import('./pages/department/department-list-page')\n return page\n\n /* The employee writes their own profile; sales downloads the chosen ones for a quotation (ADR-0169). */\n case 'my-employee-profile':\n import('./pages/employee-profile/my-employee-profile-page')\n return page\n\n case 'employee-profile-list':\n import('./pages/employee-profile/employee-profile-list-page')\n return page\n\n /* 공용과 내 것을 한 목록에 — 아래 둘을 대신한다. */\n case 'approval-line-list':\n import('./pages/approval-line/approval-line-list-page')\n return page\n\n /*\n * 아래 두 화면은 아직 살아 있다. operato-dataset · operato-gangsters · operato-twin 의 메뉴가 이 두\n * 주소를 가리킨다. 지금 지우면 그 세 앱의 메뉴가 아무 데도 안 닿는다. operato-plant 만 새 화면으로\n * 돌렸고, 나머지가 옮겨 온 뒤에 지운다.\n */\n case 'common-approval-line-templates-page':\n import('./pages/approval-line/common-approval-line-templates-page')\n return page\n\n case 'my-approval-line-templates-page':\n import('./pages/approval-line/my-approval-line-templates-page')\n return page\n }\n}\n"]}
|