@things-factory/organization 10.1.223 → 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,31 @@
|
|
|
1
|
+
export type ProfileRow = Record<string, string>;
|
|
2
|
+
export type ProfileSectionKey = 'educations' | 'certificates' | 'careers' | 'projects';
|
|
3
|
+
export type ProfileField = {
|
|
4
|
+
name: string;
|
|
5
|
+
/** The translation key of its label. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** `month` for a period's ends (YYYY-MM), `date` for a day, `textarea` for what was done, text otherwise. */
|
|
8
|
+
input?: 'month' | 'date' | 'textarea';
|
|
9
|
+
};
|
|
10
|
+
export declare const PROFILE_SECTIONS: {
|
|
11
|
+
key: ProfileSectionKey;
|
|
12
|
+
title: string;
|
|
13
|
+
fields: ProfileField[];
|
|
14
|
+
}[];
|
|
15
|
+
/** The skills as written in one line - split at commas and line ends, blanks dropped. */
|
|
16
|
+
export declare function skillsOf(text: string): string[];
|
|
17
|
+
export type ProfileForm = {
|
|
18
|
+
summary: string;
|
|
19
|
+
skills: string;
|
|
20
|
+
} & Record<ProfileSectionKey, ProfileRow[]>;
|
|
21
|
+
/** The held profile as the form edits it - a profile not yet written is an empty form. */
|
|
22
|
+
export declare function profileFormOf(profile: any): ProfileForm;
|
|
23
|
+
/** The form as `saveMyEmployeeProfile` takes it - the whole profile. */
|
|
24
|
+
export declare function profileInputOf(form: ProfileForm): {
|
|
25
|
+
summary: string;
|
|
26
|
+
skills: string[];
|
|
27
|
+
educations: ProfileRow[];
|
|
28
|
+
certificates: ProfileRow[];
|
|
29
|
+
careers: ProfileRow[];
|
|
30
|
+
projects: ProfileRow[];
|
|
31
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* What the profile screen edits (ADR-0169 decision 2): the four sections of rows, each with its fields and the input a
|
|
3
|
+
* field takes. Pure, so the shape the screen sends is read by the tests against what the server keeps.
|
|
4
|
+
*/
|
|
5
|
+
const label = (name) => `label.employee-profile-${name}`;
|
|
6
|
+
export const PROFILE_SECTIONS = [
|
|
7
|
+
{
|
|
8
|
+
key: 'educations',
|
|
9
|
+
title: label('educations'),
|
|
10
|
+
fields: [
|
|
11
|
+
{ name: 'school', label: label('school') },
|
|
12
|
+
{ name: 'major', label: label('major') },
|
|
13
|
+
{ name: 'degree', label: label('degree') },
|
|
14
|
+
{ name: 'startedOn', label: label('started-on'), input: 'month' },
|
|
15
|
+
{ name: 'endedOn', label: label('ended-on'), input: 'month' }
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: 'certificates',
|
|
20
|
+
title: label('certificates'),
|
|
21
|
+
fields: [
|
|
22
|
+
{ name: 'name', label: label('certificate') },
|
|
23
|
+
{ name: 'issuer', label: label('issuer') },
|
|
24
|
+
{ name: 'acquiredOn', label: label('acquired-on'), input: 'date' }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key: 'careers',
|
|
29
|
+
title: label('careers'),
|
|
30
|
+
fields: [
|
|
31
|
+
{ name: 'company', label: label('company') },
|
|
32
|
+
{ name: 'department', label: 'label.department' },
|
|
33
|
+
{ name: 'position', label: label('position') },
|
|
34
|
+
{ name: 'startedOn', label: label('started-on'), input: 'month' },
|
|
35
|
+
{ name: 'endedOn', label: label('ended-on'), input: 'month' },
|
|
36
|
+
{ name: 'duties', label: label('duties'), input: 'textarea' }
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: 'projects',
|
|
41
|
+
title: label('projects'),
|
|
42
|
+
fields: [
|
|
43
|
+
{ name: 'name', label: label('project') },
|
|
44
|
+
{ name: 'client', label: label('client') },
|
|
45
|
+
{ name: 'role', label: label('role') },
|
|
46
|
+
{ name: 'startedOn', label: label('started-on'), input: 'month' },
|
|
47
|
+
{ name: 'endedOn', label: label('ended-on'), input: 'month' },
|
|
48
|
+
{ name: 'technologies', label: label('technologies') },
|
|
49
|
+
{ name: 'duties', label: label('duties'), input: 'textarea' }
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
/** The skills as written in one line - split at commas and line ends, blanks dropped. */
|
|
54
|
+
export function skillsOf(text) {
|
|
55
|
+
return text
|
|
56
|
+
.split(/[,\n]/)
|
|
57
|
+
.map(one => one.trim())
|
|
58
|
+
.filter(Boolean);
|
|
59
|
+
}
|
|
60
|
+
/** The held profile as the form edits it - a profile not yet written is an empty form. */
|
|
61
|
+
export function profileFormOf(profile) {
|
|
62
|
+
const rows = (key) => {
|
|
63
|
+
const fields = PROFILE_SECTIONS.find(section => section.key === key).fields;
|
|
64
|
+
return (profile?.[key] ?? []).map(row => Object.fromEntries(fields.map(field => [field.name, row?.[field.name] ?? ''])));
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
summary: profile?.summary ?? '',
|
|
68
|
+
skills: (profile?.skills ?? []).join(', '),
|
|
69
|
+
educations: rows('educations'),
|
|
70
|
+
certificates: rows('certificates'),
|
|
71
|
+
careers: rows('careers'),
|
|
72
|
+
projects: rows('projects')
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** The form as `saveMyEmployeeProfile` takes it - the whole profile. */
|
|
76
|
+
export function profileInputOf(form) {
|
|
77
|
+
return {
|
|
78
|
+
summary: form.summary,
|
|
79
|
+
skills: skillsOf(form.skills),
|
|
80
|
+
educations: form.educations,
|
|
81
|
+
certificates: form.certificates,
|
|
82
|
+
careers: form.careers,
|
|
83
|
+
projects: form.projects
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=employee-profile-form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"employee-profile-form.js","sourceRoot":"","sources":["../../../client/pages/employee-profile/employee-profile-form.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * What the profile screen edits (ADR-0169 decision 2): the four sections of rows, each with its fields and the input a\n * field takes. Pure, so the shape the screen sends is read by the tests against what the server keeps.\n */\n\nexport type ProfileRow = Record<string, string>\n\nexport type ProfileSectionKey = 'educations' | 'certificates' | 'careers' | 'projects'\n\nexport type ProfileField = {\n name: string\n /** The translation key of its label. */\n label: string\n /** `month` for a period's ends (YYYY-MM), `date` for a day, `textarea` for what was done, text otherwise. */\n input?: 'month' | 'date' | 'textarea'\n}\n\nconst label = (name: string) => `label.employee-profile-${name}`\n\nexport const PROFILE_SECTIONS: { key: ProfileSectionKey; title: string; fields: ProfileField[] }[] = [\n {\n key: 'educations',\n title: label('educations'),\n fields: [\n { name: 'school', label: label('school') },\n { name: 'major', label: label('major') },\n { name: 'degree', label: label('degree') },\n { name: 'startedOn', label: label('started-on'), input: 'month' },\n { name: 'endedOn', label: label('ended-on'), input: 'month' }\n ]\n },\n {\n key: 'certificates',\n title: label('certificates'),\n fields: [\n { name: 'name', label: label('certificate') },\n { name: 'issuer', label: label('issuer') },\n { name: 'acquiredOn', label: label('acquired-on'), input: 'date' }\n ]\n },\n {\n key: 'careers',\n title: label('careers'),\n fields: [\n { name: 'company', label: label('company') },\n { name: 'department', label: 'label.department' },\n { name: 'position', label: label('position') },\n { name: 'startedOn', label: label('started-on'), input: 'month' },\n { name: 'endedOn', label: label('ended-on'), input: 'month' },\n { name: 'duties', label: label('duties'), input: 'textarea' }\n ]\n },\n {\n key: 'projects',\n title: label('projects'),\n fields: [\n { name: 'name', label: label('project') },\n { name: 'client', label: label('client') },\n { name: 'role', label: label('role') },\n { name: 'startedOn', label: label('started-on'), input: 'month' },\n { name: 'endedOn', label: label('ended-on'), input: 'month' },\n { name: 'technologies', label: label('technologies') },\n { name: 'duties', label: label('duties'), input: 'textarea' }\n ]\n }\n]\n\n/** The skills as written in one line - split at commas and line ends, blanks dropped. */\nexport function skillsOf(text: string): string[] {\n return text\n .split(/[,\\n]/)\n .map(one => one.trim())\n .filter(Boolean)\n}\n\nexport type ProfileForm = { summary: string; skills: string } & Record<ProfileSectionKey, ProfileRow[]>\n\n/** The held profile as the form edits it - a profile not yet written is an empty form. */\nexport function profileFormOf(profile: any): ProfileForm {\n const rows = (key: ProfileSectionKey) => {\n const fields = PROFILE_SECTIONS.find(section => section.key === key)!.fields\n return ((profile?.[key] ?? []) as any[]).map(row => Object.fromEntries(fields.map(field => [field.name, row?.[field.name] ?? ''])))\n }\n return {\n summary: profile?.summary ?? '',\n skills: (profile?.skills ?? []).join(', '),\n educations: rows('educations'),\n certificates: rows('certificates'),\n careers: rows('careers'),\n projects: rows('projects')\n }\n}\n\n/** The form as `saveMyEmployeeProfile` takes it - the whole profile. */\nexport function profileInputOf(form: ProfileForm) {\n return {\n summary: form.summary,\n skills: skillsOf(form.skills),\n educations: form.educations,\n certificates: form.certificates,\n careers: form.careers,\n projects: form.projects\n }\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import '@operato/data-grist/ox-grist.js';
|
|
2
|
+
import { FetchOption } from '@operato/data-grist';
|
|
3
|
+
import { PageView } from '@operato/shell';
|
|
4
|
+
declare const EmployeeProfileListPage_base: (new (...args: any[]) => import("lit").LitElement) & typeof PageView;
|
|
5
|
+
/**
|
|
6
|
+
* 인력 프로필 — every active employee and when their profile was last written; the rows picked are downloaded as one
|
|
7
|
+
* workbook, a sheet a person, to attach to a quotation (ADR-0169 decision 5). Read under `employee-profile`: the
|
|
8
|
+
* basics and the profile, nothing else of the employee record.
|
|
9
|
+
*/
|
|
10
|
+
export declare class EmployeeProfileListPage extends EmployeeProfileListPage_base {
|
|
11
|
+
static styles: import("lit").CSSResult[];
|
|
12
|
+
gristConfig: any;
|
|
13
|
+
private grist;
|
|
14
|
+
get context(): {
|
|
15
|
+
title: string;
|
|
16
|
+
search: {
|
|
17
|
+
handler: (search: string) => void;
|
|
18
|
+
value: string;
|
|
19
|
+
};
|
|
20
|
+
actions: {
|
|
21
|
+
title: string;
|
|
22
|
+
icon: string;
|
|
23
|
+
emphasis: {
|
|
24
|
+
raised: boolean;
|
|
25
|
+
};
|
|
26
|
+
action: () => void;
|
|
27
|
+
}[];
|
|
28
|
+
};
|
|
29
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
30
|
+
pageInitialized(): Promise<void>;
|
|
31
|
+
/** Every active employee at once - a company's people, not a ledger - narrowed here by the search words. */
|
|
32
|
+
fetchHandler({ search }: FetchOption): Promise<{
|
|
33
|
+
total: number;
|
|
34
|
+
records: any[];
|
|
35
|
+
}>;
|
|
36
|
+
private download;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@operato/data-grist/ox-grist.js';
|
|
3
|
+
import gql from 'graphql-tag';
|
|
4
|
+
import { html } from 'lit';
|
|
5
|
+
import { customElement, query, state } from 'lit/decorators.js';
|
|
6
|
+
import * as XLSX from 'xlsx';
|
|
7
|
+
import { DataGrist } from '@operato/data-grist';
|
|
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 { CommonGristStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
|
|
13
|
+
import { isMobileDevice } from '@operato/utils';
|
|
14
|
+
import { profileFileName, profileWorkbook } from './employee-profile-workbook';
|
|
15
|
+
/** The sheet's words in the reader's language. */
|
|
16
|
+
function sheetWords() {
|
|
17
|
+
const t = (key) => i18next.t(key);
|
|
18
|
+
const p = (name) => t(`label.employee-profile-${name}`);
|
|
19
|
+
return {
|
|
20
|
+
name: t('field.name'),
|
|
21
|
+
department: t('label.department'),
|
|
22
|
+
jobPosition: t('label.job-position'),
|
|
23
|
+
jobResponsibility: t('label.job-responsibility'),
|
|
24
|
+
hiredOn: t('label.hired-on'),
|
|
25
|
+
summary: p('summary'),
|
|
26
|
+
skills: p('skills'),
|
|
27
|
+
educations: p('educations'),
|
|
28
|
+
certificates: p('certificates'),
|
|
29
|
+
careers: p('careers'),
|
|
30
|
+
projects: p('projects'),
|
|
31
|
+
school: p('school'),
|
|
32
|
+
major: p('major'),
|
|
33
|
+
degree: p('degree'),
|
|
34
|
+
period: p('period'),
|
|
35
|
+
certificate: p('certificate'),
|
|
36
|
+
issuer: p('issuer'),
|
|
37
|
+
acquiredOn: p('acquired-on'),
|
|
38
|
+
company: p('company'),
|
|
39
|
+
position: p('position'),
|
|
40
|
+
duties: p('duties'),
|
|
41
|
+
project: p('project'),
|
|
42
|
+
client: p('client'),
|
|
43
|
+
role: p('role'),
|
|
44
|
+
technologies: p('technologies')
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** A profile's last change as a day of the reader's calendar. */
|
|
48
|
+
const dayOf = (value) => {
|
|
49
|
+
if (!value)
|
|
50
|
+
return '';
|
|
51
|
+
const day = new Date(value);
|
|
52
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
53
|
+
return `${day.getFullYear()}-${pad(day.getMonth() + 1)}-${pad(day.getDate())}`;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* 인력 프로필 — every active employee and when their profile was last written; the rows picked are downloaded as one
|
|
57
|
+
* workbook, a sheet a person, to attach to a quotation (ADR-0169 decision 5). Read under `employee-profile`: the
|
|
58
|
+
* basics and the profile, nothing else of the employee record.
|
|
59
|
+
*/
|
|
60
|
+
let EmployeeProfileListPage = class EmployeeProfileListPage extends localize(i18next)(PageView) {
|
|
61
|
+
static { this.styles = [WorkSurfacePageStyles, CommonGristStyles, ScrollbarStyles]; }
|
|
62
|
+
get context() {
|
|
63
|
+
return {
|
|
64
|
+
title: i18next.t('title.employee-profile-list'),
|
|
65
|
+
search: {
|
|
66
|
+
handler: (search) => {
|
|
67
|
+
this.grist.searchText = search;
|
|
68
|
+
},
|
|
69
|
+
value: this.grist?.searchText || ''
|
|
70
|
+
},
|
|
71
|
+
actions: [{ title: i18next.t('button.employee-profile-download'), icon: 'download', emphasis: { raised: true }, action: () => this.download() }]
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
render() {
|
|
75
|
+
return html `<ox-grist
|
|
76
|
+
class="page-fill"
|
|
77
|
+
.mode=${isMobileDevice() ? 'CARD' : 'GRID'}
|
|
78
|
+
.config=${this.gristConfig}
|
|
79
|
+
.fetchHandler=${this.fetchHandler.bind(this)}
|
|
80
|
+
></ox-grist>`;
|
|
81
|
+
}
|
|
82
|
+
async pageInitialized() {
|
|
83
|
+
const text = (name, header, width, extra = {}) => ({
|
|
84
|
+
type: 'string',
|
|
85
|
+
name,
|
|
86
|
+
header,
|
|
87
|
+
width,
|
|
88
|
+
sortable: true,
|
|
89
|
+
...extra,
|
|
90
|
+
record: { editable: false, ...extra.record }
|
|
91
|
+
});
|
|
92
|
+
this.gristConfig = {
|
|
93
|
+
rows: { appendable: false, selectable: { multiple: true } },
|
|
94
|
+
pagination: { infinite: true },
|
|
95
|
+
emptyNote: { title: i18next.t('text.employee-profile-empty') },
|
|
96
|
+
list: { fields: ['name', 'departmentName'], details: ['jobPosition', 'profileUpdatedAt'] },
|
|
97
|
+
columns: [
|
|
98
|
+
{ type: 'gutter', gutterName: 'sequence' },
|
|
99
|
+
{ type: 'gutter', gutterName: 'row-selector', multiple: true },
|
|
100
|
+
text('name', i18next.t('field.name'), 120),
|
|
101
|
+
text('departmentName', i18next.t('label.department'), 160),
|
|
102
|
+
text('jobPosition', i18next.t('label.job-position'), 100),
|
|
103
|
+
text('jobResponsibility', i18next.t('label.job-responsibility'), 100),
|
|
104
|
+
text('profileUpdatedAt', i18next.t('label.employee-profile-updated-at'), 120)
|
|
105
|
+
]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** Every active employee at once - a company's people, not a ledger - narrowed here by the search words. */
|
|
109
|
+
async fetchHandler({ search }) {
|
|
110
|
+
const response = await client.query({
|
|
111
|
+
query: gql `
|
|
112
|
+
query {
|
|
113
|
+
employeeProfiles {
|
|
114
|
+
employeeId
|
|
115
|
+
controlNo
|
|
116
|
+
name
|
|
117
|
+
departmentName
|
|
118
|
+
jobPosition
|
|
119
|
+
jobResponsibility
|
|
120
|
+
hiredOn
|
|
121
|
+
profile {
|
|
122
|
+
summary
|
|
123
|
+
skills
|
|
124
|
+
educations { school major degree startedOn endedOn }
|
|
125
|
+
certificates { name issuer acquiredOn }
|
|
126
|
+
careers { company department position startedOn endedOn duties }
|
|
127
|
+
projects { name client startedOn endedOn role duties technologies }
|
|
128
|
+
updatedAt
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
`,
|
|
133
|
+
fetchPolicy: 'network-only'
|
|
134
|
+
});
|
|
135
|
+
if (response.errors?.length)
|
|
136
|
+
throw response;
|
|
137
|
+
const words = (search ?? '').trim().toLowerCase();
|
|
138
|
+
const records = response.data.employeeProfiles
|
|
139
|
+
.map(one => ({ ...one, id: one.employeeId, profileUpdatedAt: dayOf(one.profile?.updatedAt) }))
|
|
140
|
+
.filter(one => !words || [one.name, one.departmentName, one.jobPosition, one.jobResponsibility].some(value => value?.toLowerCase().includes(words)));
|
|
141
|
+
return { total: records.length, records };
|
|
142
|
+
}
|
|
143
|
+
download() {
|
|
144
|
+
const picked = this.grist?.selected ?? [];
|
|
145
|
+
if (!picked.length) {
|
|
146
|
+
notify({ level: 'warn', message: i18next.t('text.employee-profile-select-rows-first') });
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
XLSX.writeFile(profileWorkbook(picked, sheetWords()), profileFileName(i18next.t('text.employee-profile-file')));
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
__decorate([
|
|
153
|
+
state(),
|
|
154
|
+
__metadata("design:type", Object)
|
|
155
|
+
], EmployeeProfileListPage.prototype, "gristConfig", void 0);
|
|
156
|
+
__decorate([
|
|
157
|
+
query('ox-grist'),
|
|
158
|
+
__metadata("design:type", DataGrist)
|
|
159
|
+
], EmployeeProfileListPage.prototype, "grist", void 0);
|
|
160
|
+
EmployeeProfileListPage = __decorate([
|
|
161
|
+
customElement('employee-profile-list-page')
|
|
162
|
+
], EmployeeProfileListPage);
|
|
163
|
+
export { EmployeeProfileListPage };
|
|
164
|
+
//# sourceMappingURL=employee-profile-list-page.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as XLSX from 'xlsx';
|
|
2
|
+
type Row = Record<string, string | undefined>;
|
|
3
|
+
export type ProfilePerson = {
|
|
4
|
+
name?: string | null;
|
|
5
|
+
departmentName?: string | null;
|
|
6
|
+
jobPosition?: string | null;
|
|
7
|
+
jobResponsibility?: string | null;
|
|
8
|
+
hiredOn?: string | null;
|
|
9
|
+
profile?: {
|
|
10
|
+
summary?: string | null;
|
|
11
|
+
skills?: string[] | null;
|
|
12
|
+
educations?: Row[] | null;
|
|
13
|
+
certificates?: Row[] | null;
|
|
14
|
+
careers?: Row[] | null;
|
|
15
|
+
projects?: Row[] | null;
|
|
16
|
+
} | null;
|
|
17
|
+
};
|
|
18
|
+
/** The words of the sheet, in the reader's language - handed in so this stays free of i18next. */
|
|
19
|
+
export type ProfileSheetWords = Record<'name' | 'department' | 'jobPosition' | 'jobResponsibility' | 'hiredOn' | 'summary' | 'skills' | 'educations' | 'certificates' | 'careers' | 'projects' | 'school' | 'major' | 'degree' | 'period' | 'certificate' | 'issuer' | 'acquiredOn' | 'company' | 'position' | 'duties' | 'project' | 'client' | 'role' | 'technologies', string>;
|
|
20
|
+
/** A period as one cell - an open end is still going on. */
|
|
21
|
+
export declare function periodOf(startedOn?: string, endedOn?: string): string;
|
|
22
|
+
/** One person's sheet as rows of cells. */
|
|
23
|
+
export declare function profileSheetRows(person: ProfilePerson, words: ProfileSheetWords): string[][];
|
|
24
|
+
/**
|
|
25
|
+
* A sheet name for each person. Excel takes at most 31 characters, none of \ / ? * [ ] :, and no two alike ignoring
|
|
26
|
+
* case - two people of one name become 「홍길동」 and 「홍길동 (2)」.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sheetNamesOf(names: (string | null | undefined)[]): string[];
|
|
29
|
+
/** The workbook of the people given, in their order. */
|
|
30
|
+
export declare function profileWorkbook(people: ProfilePerson[], words: ProfileSheetWords): XLSX.WorkBook;
|
|
31
|
+
/** 「인력프로필-20261027.xlsx」 - the word in the reader's language, the day the profiles were taken. */
|
|
32
|
+
export declare function profileFileName(word: string, day?: Date): string;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as XLSX from 'xlsx';
|
|
2
|
+
/** A period as one cell - an open end is still going on. */
|
|
3
|
+
export function periodOf(startedOn, endedOn) {
|
|
4
|
+
if (!startedOn && !endedOn)
|
|
5
|
+
return '';
|
|
6
|
+
return [startedOn ?? '', endedOn ?? ''].join(' ~ ').trim();
|
|
7
|
+
}
|
|
8
|
+
/** One person's sheet as rows of cells. */
|
|
9
|
+
export function profileSheetRows(person, words) {
|
|
10
|
+
const profile = person.profile ?? {};
|
|
11
|
+
const value = (text) => text ?? '';
|
|
12
|
+
const table = (title, header, rows, cells) => [
|
|
13
|
+
[],
|
|
14
|
+
[title],
|
|
15
|
+
header,
|
|
16
|
+
...(rows ?? []).map(row => cells(row).map(cell => cell ?? ''))
|
|
17
|
+
];
|
|
18
|
+
return [
|
|
19
|
+
[value(person.name)],
|
|
20
|
+
[],
|
|
21
|
+
[words.name, value(person.name)],
|
|
22
|
+
[words.department, value(person.departmentName)],
|
|
23
|
+
[words.jobPosition, value(person.jobPosition)],
|
|
24
|
+
[words.jobResponsibility, value(person.jobResponsibility)],
|
|
25
|
+
[words.hiredOn, value(person.hiredOn)],
|
|
26
|
+
[words.summary, value(profile.summary)],
|
|
27
|
+
[],
|
|
28
|
+
[words.skills],
|
|
29
|
+
[(profile.skills ?? []).join(', ')],
|
|
30
|
+
...table(words.educations, [words.school, words.major, words.degree, words.period], profile.educations, row => [
|
|
31
|
+
row.school,
|
|
32
|
+
row.major,
|
|
33
|
+
row.degree,
|
|
34
|
+
periodOf(row.startedOn, row.endedOn)
|
|
35
|
+
]),
|
|
36
|
+
...table(words.certificates, [words.certificate, words.issuer, words.acquiredOn], profile.certificates, row => [row.name, row.issuer, row.acquiredOn]),
|
|
37
|
+
...table(words.careers, [words.company, words.department, words.position, words.period, words.duties], profile.careers, row => [
|
|
38
|
+
row.company,
|
|
39
|
+
row.department,
|
|
40
|
+
row.position,
|
|
41
|
+
periodOf(row.startedOn, row.endedOn),
|
|
42
|
+
row.duties
|
|
43
|
+
]),
|
|
44
|
+
...table(words.projects, [words.project, words.client, words.period, words.role, words.duties, words.technologies], profile.projects, row => [
|
|
45
|
+
row.name,
|
|
46
|
+
row.client,
|
|
47
|
+
periodOf(row.startedOn, row.endedOn),
|
|
48
|
+
row.role,
|
|
49
|
+
row.duties,
|
|
50
|
+
row.technologies
|
|
51
|
+
])
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A sheet name for each person. Excel takes at most 31 characters, none of \ / ? * [ ] :, and no two alike ignoring
|
|
56
|
+
* case - two people of one name become 「홍길동」 and 「홍길동 (2)」.
|
|
57
|
+
*/
|
|
58
|
+
export function sheetNamesOf(names) {
|
|
59
|
+
const taken = new Set();
|
|
60
|
+
return names.map((name, index) => {
|
|
61
|
+
const base = (name ?? '').replace(/[\\/?*[\]:]/g, ' ').replace(/^'+|'+$/g, '').trim() || `#${index + 1}`;
|
|
62
|
+
let candidate = base.slice(0, 31);
|
|
63
|
+
for (let n = 2; taken.has(candidate.toLowerCase()); n++) {
|
|
64
|
+
const tail = ` (${n})`;
|
|
65
|
+
candidate = base.slice(0, 31 - tail.length) + tail;
|
|
66
|
+
}
|
|
67
|
+
taken.add(candidate.toLowerCase());
|
|
68
|
+
return candidate;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/** The workbook of the people given, in their order. */
|
|
72
|
+
export function profileWorkbook(people, words) {
|
|
73
|
+
const book = XLSX.utils.book_new();
|
|
74
|
+
const names = sheetNamesOf(people.map(person => person.name));
|
|
75
|
+
people.forEach((person, index) => {
|
|
76
|
+
const sheet = XLSX.utils.aoa_to_sheet(profileSheetRows(person, words));
|
|
77
|
+
sheet['!cols'] = [{ wch: 20 }, { wch: 24 }, { wch: 18 }, { wch: 20 }, { wch: 36 }, { wch: 24 }];
|
|
78
|
+
XLSX.utils.book_append_sheet(book, sheet, names[index]);
|
|
79
|
+
});
|
|
80
|
+
return book;
|
|
81
|
+
}
|
|
82
|
+
/** 「인력프로필-20261027.xlsx」 - the word in the reader's language, the day the profiles were taken. */
|
|
83
|
+
export function profileFileName(word, day = new Date()) {
|
|
84
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
85
|
+
return `${word}-${day.getFullYear()}${pad(day.getMonth() + 1)}${pad(day.getDate())}.xlsx`;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=employee-profile-workbook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"employee-profile-workbook.js","sourceRoot":"","sources":["../../../client/pages/employee-profile/employee-profile-workbook.ts"],"names":[],"mappings":"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","sourcesContent":["import * as XLSX from 'xlsx'\n\n/*\n * The employee profiles a quotation attaches, as one workbook - one sheet a person, named by the person: the basics,\n * then each section as a titled table (ADR-0169 decision 5). Pure, apart from SheetJS building the book; the screen\n * only writes it out.\n */\n\ntype Row = Record<string, string | undefined>\n\nexport type ProfilePerson = {\n name?: string | null\n departmentName?: string | null\n jobPosition?: string | null\n jobResponsibility?: string | null\n hiredOn?: string | null\n profile?: {\n summary?: string | null\n skills?: string[] | null\n educations?: Row[] | null\n certificates?: Row[] | null\n careers?: Row[] | null\n projects?: Row[] | null\n } | null\n}\n\n/** The words of the sheet, in the reader's language - handed in so this stays free of i18next. */\nexport type ProfileSheetWords = Record<\n | 'name'\n | 'department'\n | 'jobPosition'\n | 'jobResponsibility'\n | 'hiredOn'\n | 'summary'\n | 'skills'\n | 'educations'\n | 'certificates'\n | 'careers'\n | 'projects'\n | 'school'\n | 'major'\n | 'degree'\n | 'period'\n | 'certificate'\n | 'issuer'\n | 'acquiredOn'\n | 'company'\n | 'position'\n | 'duties'\n | 'project'\n | 'client'\n | 'role'\n | 'technologies',\n string\n>\n\n/** A period as one cell - an open end is still going on. */\nexport function periodOf(startedOn?: string, endedOn?: string): string {\n if (!startedOn && !endedOn) return ''\n return [startedOn ?? '', endedOn ?? ''].join(' ~ ').trim()\n}\n\n/** One person's sheet as rows of cells. */\nexport function profileSheetRows(person: ProfilePerson, words: ProfileSheetWords): string[][] {\n const profile = person.profile ?? {}\n const value = (text?: string | null) => text ?? ''\n const table = (title: string, header: string[], rows: Row[] | null | undefined, cells: (row: Row) => (string | undefined)[]) => [\n [],\n [title],\n header,\n ...(rows ?? []).map(row => cells(row).map(cell => cell ?? ''))\n ]\n\n return [\n [value(person.name)],\n [],\n [words.name, value(person.name)],\n [words.department, value(person.departmentName)],\n [words.jobPosition, value(person.jobPosition)],\n [words.jobResponsibility, value(person.jobResponsibility)],\n [words.hiredOn, value(person.hiredOn)],\n [words.summary, value(profile.summary)],\n [],\n [words.skills],\n [(profile.skills ?? []).join(', ')],\n ...table(words.educations, [words.school, words.major, words.degree, words.period], profile.educations, row => [\n row.school,\n row.major,\n row.degree,\n periodOf(row.startedOn, row.endedOn)\n ]),\n ...table(words.certificates, [words.certificate, words.issuer, words.acquiredOn], profile.certificates, row => [row.name, row.issuer, row.acquiredOn]),\n ...table(words.careers, [words.company, words.department, words.position, words.period, words.duties], profile.careers, row => [\n row.company,\n row.department,\n row.position,\n periodOf(row.startedOn, row.endedOn),\n row.duties\n ]),\n ...table(words.projects, [words.project, words.client, words.period, words.role, words.duties, words.technologies], profile.projects, row => [\n row.name,\n row.client,\n periodOf(row.startedOn, row.endedOn),\n row.role,\n row.duties,\n row.technologies\n ])\n ]\n}\n\n/**\n * A sheet name for each person. Excel takes at most 31 characters, none of \\ / ? * [ ] :, and no two alike ignoring\n * case - two people of one name become 「홍길동」 and 「홍길동 (2)」.\n */\nexport function sheetNamesOf(names: (string | null | undefined)[]): string[] {\n const taken = new Set<string>()\n return names.map((name, index) => {\n const base = (name ?? '').replace(/[\\\\/?*[\\]:]/g, ' ').replace(/^'+|'+$/g, '').trim() || `#${index + 1}`\n let candidate = base.slice(0, 31)\n for (let n = 2; taken.has(candidate.toLowerCase()); n++) {\n const tail = ` (${n})`\n candidate = base.slice(0, 31 - tail.length) + tail\n }\n taken.add(candidate.toLowerCase())\n return candidate\n })\n}\n\n/** The workbook of the people given, in their order. */\nexport function profileWorkbook(people: ProfilePerson[], words: ProfileSheetWords): XLSX.WorkBook {\n const book = XLSX.utils.book_new()\n const names = sheetNamesOf(people.map(person => person.name))\n people.forEach((person, index) => {\n const sheet = XLSX.utils.aoa_to_sheet(profileSheetRows(person, words))\n sheet['!cols'] = [{ wch: 20 }, { wch: 24 }, { wch: 18 }, { wch: 20 }, { wch: 36 }, { wch: 24 }]\n XLSX.utils.book_append_sheet(book, sheet, names[index])\n })\n return book\n}\n\n/** 「인력프로필-20261027.xlsx」 - the word in the reader's language, the day the profiles were taken. */\nexport function profileFileName(word: string, day = new Date()): string {\n const pad = (n: number) => String(n).padStart(2, '0')\n return `${word}-${day.getFullYear()}${pad(day.getMonth() + 1)}${pad(day.getDate())}.xlsx`\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import '@material/web/icon/icon.js';
|
|
2
|
+
import '@operato/list-state/ox-empty-note.js';
|
|
3
|
+
import '@operato/list-state/ox-page-state.js';
|
|
4
|
+
import { PageView } from '@operato/shell';
|
|
5
|
+
import { type ProfileForm } from './employee-profile-form';
|
|
6
|
+
declare const MyEmployeeProfilePage_base: (new (...args: any[]) => import("lit").LitElement) & typeof PageView;
|
|
7
|
+
/**
|
|
8
|
+
* 내 프로필 — the employee writes their own career profile: a summary, skills, and the rows of education,
|
|
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.
|
|
11
|
+
*/
|
|
12
|
+
export declare class MyEmployeeProfilePage extends MyEmployeeProfilePage_base {
|
|
13
|
+
static styles: import("lit").CSSResult[];
|
|
14
|
+
form: ProfileForm;
|
|
15
|
+
/** Whether the account is linked to an employee record - unknown until read. */
|
|
16
|
+
linked: boolean | null;
|
|
17
|
+
loading: boolean;
|
|
18
|
+
error: unknown;
|
|
19
|
+
busy: boolean;
|
|
20
|
+
get context(): {
|
|
21
|
+
title: string;
|
|
22
|
+
frame: string;
|
|
23
|
+
actions: {
|
|
24
|
+
title: string;
|
|
25
|
+
icon: string;
|
|
26
|
+
emphasis: {
|
|
27
|
+
raised: boolean;
|
|
28
|
+
};
|
|
29
|
+
action: () => Promise<void>;
|
|
30
|
+
}[];
|
|
31
|
+
};
|
|
32
|
+
pageUpdated(changes: any): Promise<void>;
|
|
33
|
+
private load;
|
|
34
|
+
private save;
|
|
35
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
36
|
+
private renderSection;
|
|
37
|
+
}
|
|
38
|
+
export {};
|