@things-factory/organization 10.1.226 → 10.1.228

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist-client/pages/employee-profile/employee-profile-files.d.ts +23 -0
  2. package/dist-client/pages/employee-profile/employee-profile-files.js +50 -0
  3. package/dist-client/pages/employee-profile/employee-profile-files.js.map +1 -0
  4. package/dist-client/pages/employee-profile/employee-profile-form.d.ts +42 -10
  5. package/dist-client/pages/employee-profile/employee-profile-form.js +68 -22
  6. package/dist-client/pages/employee-profile/employee-profile-form.js.map +1 -1
  7. package/dist-client/pages/employee-profile/employee-profile-list-page.d.ts +19 -5
  8. package/dist-client/pages/employee-profile/employee-profile-list-page.js +89 -46
  9. package/dist-client/pages/employee-profile/employee-profile-list-page.js.map +1 -1
  10. package/dist-client/pages/employee-profile/employee-profile-preview.d.ts +28 -0
  11. package/dist-client/pages/employee-profile/employee-profile-preview.js +179 -0
  12. package/dist-client/pages/employee-profile/employee-profile-preview.js.map +1 -0
  13. package/dist-client/pages/employee-profile/my-employee-profile-page.d.ts +20 -2
  14. package/dist-client/pages/employee-profile/my-employee-profile-page.js +467 -93
  15. package/dist-client/pages/employee-profile/my-employee-profile-page.js.map +1 -1
  16. package/dist-client/tsconfig.tsbuildinfo +1 -1
  17. package/dist-server/ai-entry/employee-profile-entry.d.ts +10 -0
  18. package/dist-server/ai-entry/employee-profile-entry.js +88 -0
  19. package/dist-server/ai-entry/employee-profile-entry.js.map +1 -0
  20. package/dist-server/ai-entry/entry-target-shape.d.ts +11 -1
  21. package/dist-server/ai-entry/entry-target-shape.js.map +1 -1
  22. package/dist-server/ai-entry/organization-entry-targets.d.ts +8 -0
  23. package/dist-server/ai-entry/organization-entry-targets.js +26 -1
  24. package/dist-server/ai-entry/organization-entry-targets.js.map +1 -1
  25. package/dist-server/service/employee-profile/employee-profile-document.d.ts +122 -0
  26. package/dist-server/service/employee-profile/employee-profile-document.js +439 -0
  27. package/dist-server/service/employee-profile/employee-profile-document.js.map +1 -0
  28. package/dist-server/service/employee-profile/employee-profile-mutation.d.ts +1 -0
  29. package/dist-server/service/employee-profile/employee-profile-mutation.js +29 -0
  30. package/dist-server/service/employee-profile/employee-profile-mutation.js.map +1 -1
  31. package/dist-server/service/employee-profile/employee-profile-query.d.ts +9 -1
  32. package/dist-server/service/employee-profile/employee-profile-query.js +83 -4
  33. package/dist-server/service/employee-profile/employee-profile-query.js.map +1 -1
  34. package/dist-server/service/employee-profile/employee-profile-rules.d.ts +6 -0
  35. package/dist-server/service/employee-profile/employee-profile-rules.js +24 -0
  36. package/dist-server/service/employee-profile/employee-profile-rules.js.map +1 -1
  37. package/dist-server/service/employee-profile/employee-profile-type.d.ts +11 -0
  38. package/dist-server/service/employee-profile/employee-profile-type.js +30 -1
  39. package/dist-server/service/employee-profile/employee-profile-type.js.map +1 -1
  40. package/dist-server/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +8 -7
  42. package/tests/employee-profile-db.test.ts +65 -4
  43. package/tests/employee-profile-document.test.ts +219 -0
  44. package/tests/employee-profile-entry.test.ts +72 -0
  45. package/tests/employee-profile-rules.test.ts +33 -6
  46. package/translations/en.json +43 -3
  47. package/translations/es.json +43 -3
  48. package/translations/ja.json +43 -3
  49. package/translations/ko.json +43 -3
  50. package/translations/ms.json +43 -3
  51. package/translations/zh.json +43 -3
  52. package/dist-client/pages/employee-profile/employee-profile-workbook.d.ts +0 -33
  53. package/dist-client/pages/employee-profile/employee-profile-workbook.js +0 -87
  54. package/dist-client/pages/employee-profile/employee-profile-workbook.js.map +0 -1
  55. package/tests/employee-profile-workbook.test.ts +0 -89
@@ -1 +1 @@
1
- {"version":3,"file":"employee-profile-list-page.js","sourceRoot":"","sources":["../../../client/pages/employee-profile/employee-profile-list-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport { html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\nimport * as XLSX from 'xlsx'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n\nimport { profileFileName, profileWorkbook, type ProfileSheetWords } from './employee-profile-workbook'\n\n/** The sheet's words in the reader's language. */\nfunction sheetWords(): ProfileSheetWords {\n const t = (key: string) => i18next.t(key)\n const p = (name: string) => t(`label.employee-profile-${name}`)\n return {\n name: t('field.name'),\n department: t('label.department'),\n jobPosition: t('label.job-position'),\n jobResponsibility: t('label.job-responsibility'),\n hiredOn: t('label.hired-on'),\n summary: p('summary'),\n skills: p('skills'),\n educations: p('educations'),\n certificates: p('certificates'),\n careers: p('careers'),\n projects: p('projects'),\n school: p('school'),\n major: p('major'),\n degree: p('degree'),\n period: p('period'),\n certificate: p('certificate'),\n issuer: p('issuer'),\n acquiredOn: p('acquired-on'),\n company: p('company'),\n position: p('position'),\n duties: p('duties'),\n project: p('project'),\n client: p('client'),\n role: p('role'),\n technologies: p('technologies')\n }\n}\n\n/** A profile's last change as a day of the reader's calendar. */\nconst dayOf = (value?: string | null) => {\n if (!value) return ''\n const day = new Date(value)\n const pad = (n: number) => String(n).padStart(2, '0')\n return `${day.getFullYear()}-${pad(day.getMonth() + 1)}-${pad(day.getDate())}`\n}\n\n/**\n * 인력 프로필 — every active employee and when their profile was last written; the rows picked are downloaded as one\n * workbook, a sheet a person, to attach to a quotation (ADR-0169 decision 5). Read under `employee-profile`: the\n * basics and the profile, nothing else of the employee record.\n */\n@customElement('employee-profile-list-page')\nexport class EmployeeProfileListPage extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, ScrollbarStyles]\n\n @state() gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('title.employee-profile-list'),\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n },\n actions: [{ title: i18next.t('button.employee-profile-download'), icon: 'download', emphasis: { raised: true }, action: () => this.download() }]\n }\n }\n\n render() {\n return html`<ox-grist\n class=\"page-fill\"\n .mode=${isMobileDevice() ? 'CARD' : 'GRID'}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n ></ox-grist>`\n }\n\n async pageInitialized() {\n const text = (name: string, header: string, width: number, extra: any = {}) => ({\n type: 'string',\n name,\n header,\n width,\n sortable: true,\n ...extra,\n record: { editable: false, ...extra.record }\n })\n this.gristConfig = {\n rows: { appendable: false, selectable: { multiple: true } },\n pagination: { infinite: true },\n emptyNote: { title: i18next.t('text.employee-profile-empty') },\n list: { fields: ['name', 'departmentName'], details: ['jobPosition', 'profileUpdatedAt'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n { type: 'gutter', gutterName: 'row-selector', multiple: true },\n text('name', i18next.t('field.name'), 120),\n text('departmentName', i18next.t('label.department'), 160),\n text('jobPosition', i18next.t('label.job-position'), 100),\n text('jobResponsibility', i18next.t('label.job-responsibility'), 100),\n text('profileUpdatedAt', i18next.t('label.employee-profile-updated-at'), 120)\n ]\n }\n }\n\n /** Every active employee at once - a company's people, not a ledger - narrowed here by the search words. */\n async fetchHandler({ search }: FetchOption) {\n const response = await client.query({\n query: gql`\n query {\n employeeProfiles {\n employeeId\n controlNo\n name\n departmentName\n jobPosition\n jobResponsibility\n hiredOn\n profile {\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 `,\n fetchPolicy: 'network-only'\n })\n if (response.errors?.length) throw response\n\n const words = (search ?? '').trim().toLowerCase()\n const records = (response.data.employeeProfiles as any[])\n .map(one => ({ ...one, id: one.employeeId, profileUpdatedAt: dayOf(one.profile?.updatedAt) }))\n .filter(one => !words || [one.name, one.departmentName, one.jobPosition, one.jobResponsibility].some(value => value?.toLowerCase().includes(words)))\n return { total: records.length, records }\n }\n\n private download() {\n const picked = this.grist?.selected ?? []\n if (!picked.length) {\n notify({ level: 'warn', message: i18next.t('text.employee-profile-select-rows-first') })\n return\n }\n XLSX.writeFile(profileWorkbook(picked, sheetWords()), profileFileName(i18next.t('text.employee-profile-file')))\n }\n}\n"]}
1
+ {"version":3,"file":"employee-profile-list-page.js","sourceRoot":"","sources":["../../../client/pages/employee-profile/employee-profile-list-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\nimport '@operato/input/ox-select.js'\nimport '@operato/popup/ox-popup-list.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\nimport { getLanguages } from '@things-factory/auth-base/dist-client'\n\nimport { profileFile, readerLanguageOf, saveProfileFile, type Language, type ProfileFormat } from './employee-profile-files'\nimport { openProfilePreview } from './employee-profile-preview'\n\nconst dayOf = (value?: string | null) => {\n if (!value) return ''\n const day = new Date(value)\n const pad = (n: number) => String(n).padStart(2, '0')\n return `${day.getFullYear()}-${pad(day.getMonth() + 1)}-${pad(day.getDate())}`\n}\n\n/**\n * 인력 프로필 — every active employee and when their profile was last written; the rows picked are downloaded as one\n * document to attach to a quotation - Excel or PDF, laid out once on the server (ADR-0169 decision 5). Read under\n * `employee-profile`: the basics and the profile, nothing else of the employee record.\n */\n@customElement('employee-profile-list-page')\nexport class EmployeeProfileListPage extends localize(i18next)(PageView) {\n static styles = [\n WorkSurfacePageStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n ScrollbarStyles,\n css`\n .filters {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n ox-select {\n min-width: 140px;\n }\n `\n ]\n\n @state() gristConfig: any\n /** The languages the application runs, and the one the document's own words are printed in - the reader's at first. */\n @state() languages: Language[] = []\n @state() language = ''\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('title.employee-profile-list'),\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n },\n actions: [\n { title: i18next.t('button.employee-profile-preview'), icon: 'preview', action: () => this.preview() },\n { title: i18next.t('button.employee-profile-download-pdf'), icon: 'picture_as_pdf', action: () => this.download('PDF') },\n { title: i18next.t('button.employee-profile-download'), icon: 'download', emphasis: { raised: true }, action: () => this.download('XLSX') }\n ]\n }\n }\n\n render() {\n const shown = this.languages.find(one => one.code === this.language)?.display ?? this.language\n return html`<ox-grist\n class=\"page-fill\"\n search-mode=\"term\"\n .mode=${isMobileDevice() ? 'CARD' : 'GRID'}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n >\n ${this.languages.length > 1\n ? html`<div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <span>${i18next.t('label.employee-profile-document-language')}</span>\n <ox-select\n .value=${this.language}\n aria-label=${i18next.t('label.employee-profile-document-language')}\n @change=${(e: CustomEvent) => (this.language = String(e.detail || this.language))}\n >\n <span slot=\"label\">${shown}</span>\n <ox-popup-list align-left nowrap with-search>${this.languages.map(one => html`<div option value=${one.code}>${one.display}</div>`)}</ox-popup-list>\n </ox-select>\n </div>\n </div>`\n : ''}\n </ox-grist>`\n }\n\n async pageInitialized() {\n /* The reader's language first - matched to a code the application runs, by its language part when the region differs. */\n this.languages = ((await getLanguages()) ?? []) as Language[]\n this.language = readerLanguageOf(this.languages)\n\n const text = (name: string, header: string, width: number, extra: any = {}) => ({\n type: 'string',\n name,\n header,\n width,\n sortable: true,\n ...extra,\n record: { editable: false, ...extra.record }\n })\n this.gristConfig = {\n rows: { appendable: false, selectable: { multiple: true } },\n pagination: { infinite: true },\n emptyNote: { title: i18next.t('text.employee-profile-empty') },\n list: { fields: ['name', 'departmentName'], details: ['jobPosition', 'profileUpdatedAt'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n { type: 'gutter', gutterName: 'row-selector', multiple: true },\n text('name', i18next.t('field.name'), 120),\n text('departmentName', i18next.t('label.department'), 160),\n text('jobPosition', i18next.t('label.job-position'), 100),\n text('jobResponsibility', i18next.t('label.job-responsibility'), 100),\n text('profileUpdatedAt', i18next.t('label.employee-profile-updated-at'), 120)\n ]\n }\n }\n\n /** Every active employee at once - a company's people, not a ledger - narrowed here by the search words. */\n async fetchHandler({ search }: FetchOption) {\n const response = await client.query({\n query: gql`\n query {\n employeeProfiles {\n employeeId\n controlNo\n name\n departmentName\n jobPosition\n jobResponsibility\n hiredOn\n profile {\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 `,\n fetchPolicy: 'network-only'\n })\n if (response.errors?.length) throw response\n\n const words = (search ?? '').trim().toLowerCase()\n const records = (response.data.employeeProfiles as any[])\n .map(one => ({ ...one, id: one.employeeId, profileUpdatedAt: dayOf(one.profile?.updatedAt) }))\n .filter(one => !words || [one.name, one.departmentName, one.jobPosition, one.jobResponsibility].some(value => value?.toLowerCase().includes(words)))\n return { total: records.length, records }\n }\n\n /** The ids of the rows picked - or nothing, having said so. */\n private picked(): string[] | null {\n const picked = this.grist?.selected ?? []\n if (picked.length) return picked.map((one: any) => one.employeeId)\n notify({ level: 'warn', message: i18next.t('text.employee-profile-select-rows-first') })\n return null\n }\n\n private preview() {\n const employeeIds = this.picked()\n if (employeeIds) openProfilePreview({ employeeIds }, this.languages, this.language)\n }\n\n private async download(format: ProfileFormat) {\n const employeeIds = this.picked()\n if (!employeeIds) return\n try {\n saveProfileFile(await profileFile({ employeeIds }, format, this.language))\n } catch (error: any) {\n notify({ level: 'error', message: error?.message ?? String(error) })\n }\n }\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import '@material/web/icon/icon.js';
2
+ import '@operato/input/ox-select.js';
3
+ import '@operato/popup/ox-popup-list.js';
4
+ import { LitElement } from 'lit';
5
+ import { type Language, type ProfileSubject } from './employee-profile-files';
6
+ declare const EmployeeProfilePreview_base: (new (...args: any[]) => LitElement) & typeof LitElement;
7
+ /**
8
+ * The profiles as they will be handed in, before they are (ADR-0169 decision 9): the server's pages of A4 in a frame,
9
+ * the language of the document's own words to change, and the two downloads in the same language. Opened from the list
10
+ * for the people chosen and from one's own page for oneself.
11
+ */
12
+ export declare class EmployeeProfilePreview extends EmployeeProfilePreview_base {
13
+ static styles: import("lit").CSSResult[];
14
+ subject: ProfileSubject;
15
+ languages: Language[];
16
+ language: string;
17
+ private page;
18
+ private busy;
19
+ firstUpdated(): void;
20
+ private show;
21
+ private download;
22
+ /** As tall as its pages - read from its document, which is ours (same origin, no script). */
23
+ private fit;
24
+ render(): import("lit-html").TemplateResult<1>;
25
+ }
26
+ /** Open the preview of `subject`'s profiles in `language`. */
27
+ export declare function openProfilePreview(subject: ProfileSubject, languages: Language[], language: string): import("@operato/layout").PopupHandle<unknown>;
28
+ export {};
@@ -0,0 +1,179 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import '@operato/input/ox-select.js';
4
+ import '@operato/popup/ox-popup-list.js';
5
+ import { css, html, LitElement, nothing } from 'lit';
6
+ import { customElement, property, state } from 'lit/decorators.js';
7
+ import { i18next, localize } from '@operato/i18n';
8
+ import { notify, openPopup } from '@operato/layout';
9
+ import { ButtonStyles, ScrollbarStyles } from '@operato/styles';
10
+ import { profileFile, saveProfileFile, textOf } from './employee-profile-files';
11
+ /**
12
+ * The profiles as they will be handed in, before they are (ADR-0169 decision 9): the server's pages of A4 in a frame,
13
+ * the language of the document's own words to change, and the two downloads in the same language. Opened from the list
14
+ * for the people chosen and from one's own page for oneself.
15
+ */
16
+ let EmployeeProfilePreview = class EmployeeProfilePreview extends localize(i18next)(LitElement) {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.languages = [];
20
+ this.language = '';
21
+ this.page = '';
22
+ this.busy = false;
23
+ }
24
+ static { this.styles = [
25
+ ButtonStyles,
26
+ ScrollbarStyles,
27
+ css `
28
+ :host {
29
+ display: flex;
30
+ flex-direction: column;
31
+ height: 100%;
32
+ min-height: 0;
33
+ background: var(--ui-bg, var(--md-sys-color-surface));
34
+ }
35
+ .bar {
36
+ display: flex;
37
+ flex-wrap: wrap;
38
+ align-items: center;
39
+ gap: var(--spacing-medium, 8px);
40
+ padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);
41
+ border-bottom: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));
42
+ font-size: var(--ui-t-small, 11.5px);
43
+ color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
44
+ }
45
+ .bar .spacer {
46
+ flex: 1;
47
+ }
48
+ ox-select {
49
+ min-width: 140px;
50
+ }
51
+ /* The frame grows to its pages and this box scrolls them - with the house scrollbar, not the frame's own. */
52
+ .frame {
53
+ position: relative;
54
+ flex: 1;
55
+ min-height: 0;
56
+ overflow: auto;
57
+ background: #e9edf1;
58
+ }
59
+ iframe {
60
+ display: block;
61
+ width: 100%;
62
+ border: 0;
63
+ overflow: hidden;
64
+ }
65
+ .loading {
66
+ position: absolute;
67
+ inset: 0;
68
+ display: grid;
69
+ place-items: center;
70
+ background: rgba(233, 237, 241, 0.6);
71
+ font-size: var(--ui-t-body, 13px);
72
+ }
73
+ `
74
+ ]; }
75
+ firstUpdated() {
76
+ this.show();
77
+ }
78
+ async show() {
79
+ this.busy = true;
80
+ try {
81
+ this.page = textOf(await profileFile(this.subject, 'HTML', this.language));
82
+ }
83
+ catch (error) {
84
+ notify({ level: 'error', message: error?.message ?? String(error) });
85
+ }
86
+ finally {
87
+ this.busy = false;
88
+ }
89
+ }
90
+ async download(format) {
91
+ this.busy = true;
92
+ try {
93
+ saveProfileFile(await profileFile(this.subject, format, this.language));
94
+ }
95
+ catch (error) {
96
+ notify({ level: 'error', message: error?.message ?? String(error) });
97
+ }
98
+ finally {
99
+ this.busy = false;
100
+ }
101
+ }
102
+ /** As tall as its pages - read from its document, which is ours (same origin, no script). */
103
+ fit(frame) {
104
+ const height = frame.contentDocument?.documentElement?.scrollHeight;
105
+ if (height)
106
+ frame.style.height = `${height}px`;
107
+ }
108
+ render() {
109
+ const shown = this.languages.find(one => one.code === this.language)?.display ?? this.language;
110
+ return html `
111
+ <div class="bar">
112
+ ${this.languages.length > 1
113
+ ? html `<span>${i18next.t('label.employee-profile-document-language')}</span>
114
+ <ox-select
115
+ .value=${this.language}
116
+ aria-label=${i18next.t('label.employee-profile-document-language')}
117
+ @change=${(e) => {
118
+ const next = String(e.detail || this.language);
119
+ if (next === this.language)
120
+ return;
121
+ this.language = next;
122
+ this.show();
123
+ }}
124
+ >
125
+ <span slot="label">${shown}</span>
126
+ <ox-popup-list align-left nowrap with-search>${this.languages.map(one => html `<div option value=${one.code}>${one.display}</div>`)}</ox-popup-list>
127
+ </ox-select>`
128
+ : nothing}
129
+ <span class="spacer"></span>
130
+ <button ?disabled=${this.busy} @click=${() => this.download('PDF')}><md-icon>picture_as_pdf</md-icon>${i18next.t('button.employee-profile-download-pdf')}</button>
131
+ <button raised ?disabled=${this.busy} @click=${() => this.download('XLSX')}><md-icon>download</md-icon>${i18next.t('button.employee-profile-download')}</button>
132
+ </div>
133
+ <div class="frame">
134
+ <!-- No script runs in the frame; an empty sandbox also kept the page from drawing in the app's browser. -->
135
+ <iframe
136
+ title=${i18next.t('title.employee-profile-preview')}
137
+ sandbox="allow-same-origin"
138
+ scrolling="no"
139
+ .srcdoc=${this.page}
140
+ @load=${(e) => this.fit(e.target)}
141
+ ></iframe>
142
+ ${this.busy ? html `<div class="loading">${i18next.t('text.loading')}</div>` : nothing}
143
+ </div>
144
+ `;
145
+ }
146
+ };
147
+ __decorate([
148
+ property({ attribute: false }),
149
+ __metadata("design:type", Object)
150
+ ], EmployeeProfilePreview.prototype, "subject", void 0);
151
+ __decorate([
152
+ property({ attribute: false }),
153
+ __metadata("design:type", Array)
154
+ ], EmployeeProfilePreview.prototype, "languages", void 0);
155
+ __decorate([
156
+ property(),
157
+ __metadata("design:type", Object)
158
+ ], EmployeeProfilePreview.prototype, "language", void 0);
159
+ __decorate([
160
+ state(),
161
+ __metadata("design:type", Object)
162
+ ], EmployeeProfilePreview.prototype, "page", void 0);
163
+ __decorate([
164
+ state(),
165
+ __metadata("design:type", Object)
166
+ ], EmployeeProfilePreview.prototype, "busy", void 0);
167
+ EmployeeProfilePreview = __decorate([
168
+ customElement('employee-profile-preview')
169
+ ], EmployeeProfilePreview);
170
+ export { EmployeeProfilePreview };
171
+ /** Open the preview of `subject`'s profiles in `language`. */
172
+ export function openProfilePreview(subject, languages, language) {
173
+ return openPopup(html `<employee-profile-preview .subject=${subject} .languages=${languages} .language=${language}></employee-profile-preview>`, {
174
+ backdrop: true,
175
+ size: 'large',
176
+ title: i18next.t('title.employee-profile-preview')
177
+ });
178
+ }
179
+ //# sourceMappingURL=employee-profile-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"employee-profile-preview.js","sourceRoot":"","sources":["../../../client/pages/employee-profile/employee-profile-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/input/ox-select.js'\nimport '@operato/popup/ox-popup-list.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { notify, openPopup } from '@operato/layout'\nimport { ButtonStyles, ScrollbarStyles } from '@operato/styles'\n\nimport { profileFile, saveProfileFile, textOf, type Language, type ProfileFormat, type ProfileSubject } from './employee-profile-files'\n\n/**\n * The profiles as they will be handed in, before they are (ADR-0169 decision 9): the server's pages of A4 in a frame,\n * the language of the document's own words to change, and the two downloads in the same language. Opened from the list\n * for the people chosen and from one's own page for oneself.\n */\n@customElement('employee-profile-preview')\nexport class EmployeeProfilePreview extends localize(i18next)(LitElement) {\n static styles = [\n ButtonStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--ui-bg, var(--md-sys-color-surface));\n }\n .bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n .bar .spacer {\n flex: 1;\n }\n ox-select {\n min-width: 140px;\n }\n /* The frame grows to its pages and this box scrolls them - with the house scrollbar, not the frame's own. */\n .frame {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: auto;\n background: #e9edf1;\n }\n iframe {\n display: block;\n width: 100%;\n border: 0;\n overflow: hidden;\n }\n .loading {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n background: rgba(233, 237, 241, 0.6);\n font-size: var(--ui-t-body, 13px);\n }\n `\n ]\n\n @property({ attribute: false }) subject!: ProfileSubject\n @property({ attribute: false }) languages: Language[] = []\n @property() language = ''\n\n @state() private page = ''\n @state() private busy = false\n\n firstUpdated() {\n this.show()\n }\n\n private async show() {\n this.busy = true\n try {\n this.page = textOf(await profileFile(this.subject, 'HTML', this.language))\n } catch (error: any) {\n notify({ level: 'error', message: error?.message ?? String(error) })\n } finally {\n this.busy = false\n }\n }\n\n private async download(format: ProfileFormat) {\n this.busy = true\n try {\n saveProfileFile(await profileFile(this.subject, format, this.language))\n } catch (error: any) {\n notify({ level: 'error', message: error?.message ?? String(error) })\n } finally {\n this.busy = false\n }\n }\n\n /** As tall as its pages - read from its document, which is ours (same origin, no script). */\n private fit(frame: HTMLIFrameElement) {\n const height = frame.contentDocument?.documentElement?.scrollHeight\n if (height) frame.style.height = `${height}px`\n }\n\n render() {\n const shown = this.languages.find(one => one.code === this.language)?.display ?? this.language\n return html`\n <div class=\"bar\">\n ${this.languages.length > 1\n ? html`<span>${i18next.t('label.employee-profile-document-language')}</span>\n <ox-select\n .value=${this.language}\n aria-label=${i18next.t('label.employee-profile-document-language')}\n @change=${(e: CustomEvent) => {\n const next = String(e.detail || this.language)\n if (next === this.language) return\n this.language = next\n this.show()\n }}\n >\n <span slot=\"label\">${shown}</span>\n <ox-popup-list align-left nowrap with-search>${this.languages.map(one => html`<div option value=${one.code}>${one.display}</div>`)}</ox-popup-list>\n </ox-select>`\n : nothing}\n <span class=\"spacer\"></span>\n <button ?disabled=${this.busy} @click=${() => this.download('PDF')}><md-icon>picture_as_pdf</md-icon>${i18next.t('button.employee-profile-download-pdf')}</button>\n <button raised ?disabled=${this.busy} @click=${() => this.download('XLSX')}><md-icon>download</md-icon>${i18next.t('button.employee-profile-download')}</button>\n </div>\n <div class=\"frame\">\n <!-- No script runs in the frame; an empty sandbox also kept the page from drawing in the app's browser. -->\n <iframe\n title=${i18next.t('title.employee-profile-preview')}\n sandbox=\"allow-same-origin\"\n scrolling=\"no\"\n .srcdoc=${this.page}\n @load=${(e: Event) => this.fit(e.target as HTMLIFrameElement)}\n ></iframe>\n ${this.busy ? html`<div class=\"loading\">${i18next.t('text.loading')}</div>` : nothing}\n </div>\n `\n }\n}\n\n/** Open the preview of `subject`'s profiles in `language`. */\nexport function openProfilePreview(subject: ProfileSubject, languages: Language[], language: string) {\n return openPopup(html`<employee-profile-preview .subject=${subject} .languages=${languages} .language=${language}></employee-profile-preview>`, {\n backdrop: true,\n size: 'large',\n title: i18next.t('title.employee-profile-preview')\n })\n}\n"]}
@@ -7,16 +7,26 @@ declare const MyEmployeeProfilePage_base: (new (...args: any[]) => import("lit")
7
7
  /**
8
8
  * 내 프로필 — the employee writes their own career profile: a summary, skills, and the rows of education,
9
9
  * certificates, past jobs and projects; sales attaches it to quotations (ADR-0169 decisions 2 · 5). Saved whole.
10
- * An account not linked to an employee record has no profile to write, and is told so before anything is typed.
10
+ *
11
+ * The page is built to be filled in: who it is about and how complete it is stand at the top, each missing essential a
12
+ * click away; each row is a card whose headline reads like the document's line; a section with nothing yet says what
13
+ * it is for; and the document itself is one click away (PDF), as sales will hand it in. An account not linked to an
14
+ * employee record has no profile to write, and is told so before anything is typed.
11
15
  */
12
16
  export declare class MyEmployeeProfilePage extends MyEmployeeProfilePage_base {
13
17
  static styles: import("lit").CSSResult[];
14
18
  form: ProfileForm;
19
+ /** Who the profile is about - the employee record's basics. */
20
+ me: any;
15
21
  /** Whether the account is linked to an employee record - unknown until read. */
16
22
  linked: boolean | null;
17
23
  loading: boolean;
18
24
  error: unknown;
19
25
  busy: boolean;
26
+ updatedAt: string | null;
27
+ /** The form as last read or saved - what is written differs from it until saved. */
28
+ private saved;
29
+ private get dirty();
20
30
  get context(): {
21
31
  title: string;
22
32
  frame: string;
@@ -26,13 +36,21 @@ export declare class MyEmployeeProfilePage extends MyEmployeeProfilePage_base {
26
36
  emphasis: {
27
37
  raised: boolean;
28
38
  };
29
- action: () => Promise<void>;
39
+ action: () => Promise<boolean>;
30
40
  }[];
31
41
  };
32
42
  pageUpdated(changes: any): Promise<void>;
33
43
  private load;
44
+ private hold;
34
45
  private save;
46
+ /** The document as sales will hand it in - what is written is saved first, so the preview shows it. */
47
+ private preview;
48
+ private goTo;
35
49
  render(): import("lit-html").TemplateResult<1>;
50
+ private renderHero;
51
+ private renderSummary;
52
+ private renderSkills;
36
53
  private renderSection;
54
+ private renderEntry;
37
55
  }
38
56
  export {};