@things-factory/organization 10.1.223 → 10.1.227

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 (69) 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 +63 -0
  5. package/dist-client/pages/employee-profile/employee-profile-form.js +132 -0
  6. package/dist-client/pages/employee-profile/employee-profile-form.js.map +1 -0
  7. package/dist-client/pages/employee-profile/employee-profile-list-page.d.ts +52 -0
  8. package/dist-client/pages/employee-profile/employee-profile-list-page.js +206 -0
  9. package/dist-client/pages/employee-profile/employee-profile-list-page.js.map +1 -0
  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 +56 -0
  14. package/dist-client/pages/employee-profile/my-employee-profile-page.js +656 -0
  15. package/dist-client/pages/employee-profile/my-employee-profile-page.js.map +1 -0
  16. package/dist-client/route.d.ts +1 -1
  17. package/dist-client/route.js +7 -0
  18. package/dist-client/route.js.map +1 -1
  19. package/dist-client/tsconfig.tsbuildinfo +1 -1
  20. package/dist-server/ai-entry/employee-profile-entry.d.ts +10 -0
  21. package/dist-server/ai-entry/employee-profile-entry.js +88 -0
  22. package/dist-server/ai-entry/employee-profile-entry.js.map +1 -0
  23. package/dist-server/ai-entry/entry-target-shape.d.ts +11 -1
  24. package/dist-server/ai-entry/entry-target-shape.js.map +1 -1
  25. package/dist-server/ai-entry/organization-entry-targets.d.ts +8 -0
  26. package/dist-server/ai-entry/organization-entry-targets.js +26 -1
  27. package/dist-server/ai-entry/organization-entry-targets.js.map +1 -1
  28. package/dist-server/role-tasks.js +4 -1
  29. package/dist-server/role-tasks.js.map +1 -1
  30. package/dist-server/service/employee-profile/employee-profile-document.d.ts +122 -0
  31. package/dist-server/service/employee-profile/employee-profile-document.js +439 -0
  32. package/dist-server/service/employee-profile/employee-profile-document.js.map +1 -0
  33. package/dist-server/service/employee-profile/employee-profile-mutation.d.ts +6 -0
  34. package/dist-server/service/employee-profile/employee-profile-mutation.js +74 -0
  35. package/dist-server/service/employee-profile/employee-profile-mutation.js.map +1 -0
  36. package/dist-server/service/employee-profile/employee-profile-query.d.ts +14 -0
  37. package/dist-server/service/employee-profile/employee-profile-query.js +137 -0
  38. package/dist-server/service/employee-profile/employee-profile-query.js.map +1 -0
  39. package/dist-server/service/employee-profile/employee-profile-rows.d.ts +29 -0
  40. package/dist-server/service/employee-profile/employee-profile-rows.js +122 -0
  41. package/dist-server/service/employee-profile/employee-profile-rows.js.map +1 -0
  42. package/dist-server/service/employee-profile/employee-profile-rules.d.ts +61 -0
  43. package/dist-server/service/employee-profile/employee-profile-rules.js +104 -0
  44. package/dist-server/service/employee-profile/employee-profile-rules.js.map +1 -0
  45. package/dist-server/service/employee-profile/employee-profile-type.d.ts +35 -0
  46. package/dist-server/service/employee-profile/employee-profile-type.js +110 -0
  47. package/dist-server/service/employee-profile/employee-profile-type.js.map +1 -0
  48. package/dist-server/service/employee-profile/employee-profile.d.ts +24 -0
  49. package/dist-server/service/employee-profile/employee-profile.js +112 -0
  50. package/dist-server/service/employee-profile/employee-profile.js.map +1 -0
  51. package/dist-server/service/employee-profile/index.d.ts +5 -0
  52. package/dist-server/service/employee-profile/index.js +9 -0
  53. package/dist-server/service/employee-profile/index.js.map +1 -0
  54. package/dist-server/service/index.d.ts +3 -2
  55. package/dist-server/service/index.js +6 -2
  56. package/dist-server/service/index.js.map +1 -1
  57. package/dist-server/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +8 -6
  59. package/tests/employee-profile-db.test.ts +247 -0
  60. package/tests/employee-profile-document.test.ts +219 -0
  61. package/tests/employee-profile-entry.test.ts +72 -0
  62. package/tests/employee-profile-rules.test.ts +103 -0
  63. package/things-factory.config.js +3 -0
  64. package/translations/en.json +78 -1
  65. package/translations/es.json +78 -1
  66. package/translations/ja.json +78 -1
  67. package/translations/ko.json +78 -1
  68. package/translations/ms.json +78 -1
  69. package/translations/zh.json +78 -1
@@ -0,0 +1,439 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROFILE_SCREEN_CSS = exports.PROFILE_CSS = exports.PALETTE = exports.COLUMN_WIDTHS = void 0;
4
+ exports.dotted = dotted;
5
+ exports.periodOf = periodOf;
6
+ exports.careerMonths = careerMonths;
7
+ exports.careerLengthOf = careerLengthOf;
8
+ exports.taglineOf = taglineOf;
9
+ exports.profileSheetLayout = profileSheetLayout;
10
+ exports.rosterTaglineOf = rosterTaglineOf;
11
+ exports.rosterLayout = rosterLayout;
12
+ exports.sheetNamesOf = sheetNamesOf;
13
+ exports.heightOf = heightOf;
14
+ exports.paintSheet = paintSheet;
15
+ exports.profileWorkbook = profileWorkbook;
16
+ exports.layoutHtml = layoutHtml;
17
+ exports.profilePreviewHtml = profilePreviewHtml;
18
+ exports.profileHtml = profileHtml;
19
+ exports.profileFooterHtml = profileFooterHtml;
20
+ exports.profileFileName = profileFileName;
21
+ exports.lead = lead;
22
+ exports.profileWordsOf = profileWordsOf;
23
+ const exceljs_1 = require("exceljs");
24
+ const employee_profile_rules_1 = require("./employee-profile-rules");
25
+ /** The grid's six columns, in characters - together about the width of A4 in portrait. */
26
+ exports.COLUMN_WIDTHS = [18, 20, 15, 12, 21, 18];
27
+ /** 「2023.03」 from 「2023-03」 or 「2023-03-02」 - the dotted form a staffing profile is read in. */
28
+ function dotted(day) {
29
+ return (day ?? '').trim().replace(/-/g, '.');
30
+ }
31
+ /** A period as one cell - an open end is still going on. */
32
+ function periodOf(startedOn, endedOn, ongoing) {
33
+ if (!startedOn && !endedOn)
34
+ return '';
35
+ return `${dotted(startedOn)} ~ ${endedOn ? dotted(endedOn) : ongoing}`.trim();
36
+ }
37
+ const monthIndex = (day) => {
38
+ const match = /^(\d{4})-(\d{2})/.exec((day ?? '').trim());
39
+ return match ? Number(match[1]) * 12 + Number(match[2]) - 1 : null;
40
+ };
41
+ /**
42
+ * The months a person has worked: the careers before this company and the time here since the hire day, counted
43
+ * once where they overlap. A career with no end runs to `today`; one with no start is left out.
44
+ */
45
+ function careerMonths(person, today = new Date()) {
46
+ const now = today.getFullYear() * 12 + today.getMonth();
47
+ const months = new Set();
48
+ const spans = [
49
+ ...(person.profile?.careers ?? []).map(row => [monthIndex(row.startedOn), row.endedOn ? monthIndex(row.endedOn) : now]),
50
+ [monthIndex(person.hiredOn), now]
51
+ ];
52
+ for (const [from, to] of spans) {
53
+ if (from === null || to === null)
54
+ continue;
55
+ for (let month = from; month <= Math.min(to, now); month++)
56
+ months.add(month);
57
+ }
58
+ return months.size;
59
+ }
60
+ function careerLengthOf(person, words, today = new Date()) {
61
+ const months = careerMonths(person, today);
62
+ return months ? words.years(Math.floor(months / 12), months % 12) : '';
63
+ }
64
+ /** Where the person stands, in one line: department · position / responsibility · hired on. */
65
+ function taglineOf(person, words) {
66
+ const role = [person.jobPosition, person.jobResponsibility].filter(Boolean).join(' / ');
67
+ return [person.departmentName, role, person.hiredOn ? `${words.hiredOn} ${dotted(person.hiredOn)}` : ''].filter(Boolean).join(' · ');
68
+ }
69
+ /** The head every page opens with: the employer and what the page is, the name large, a line under it. */
70
+ function headOf(company, kicker, name, tagline) {
71
+ return [
72
+ {
73
+ cells: [
74
+ { col: 1, span: 3, value: company, role: 'company' },
75
+ { col: 4, span: 3, value: kicker, role: 'kicker' }
76
+ ]
77
+ },
78
+ { cells: [{ col: 1, span: 6, value: name, role: 'name' }], height: 40 },
79
+ { cells: [{ col: 1, span: 6, value: tagline, role: 'tagline' }], height: 20 },
80
+ { cells: [{ col: 1, span: 6, value: '', role: 'rule' }], height: 6 }
81
+ ];
82
+ }
83
+ /** One person's page, line by line. `company` is the employer's name; `today` the day it is written. */
84
+ function profileSheetLayout(person, words, company, today = new Date()) {
85
+ const profile = person.profile ?? {};
86
+ const lines = [...headOf(company, words.title, person.name ?? '', taglineOf(person, words))];
87
+ const gap = (height = 12) => lines.push({ cells: [], height });
88
+ const section = (title) => {
89
+ gap(16);
90
+ lines.push({ cells: [{ col: 1, span: 6, value: title, role: 'section' }], height: 24 });
91
+ gap(6);
92
+ };
93
+ /* A table: its head and its rows in the columns given as [col, span], every other row shaded; none says so. */
94
+ const table = (title, columns, rows, cells) => {
95
+ section(title);
96
+ lines.push({ cells: columns.map(([col, span, head]) => ({ col, span, value: head, role: 'head' })) });
97
+ if (!rows?.length)
98
+ return lines.push({ cells: [{ col: 1, span: 6, value: words.none, role: 'none' }] });
99
+ rows.forEach((row, index) => lines.push({
100
+ cells: cells(row).map((text, at) => ({ col: columns[at][0], span: columns[at][1], value: text ?? '', role: 'value' })),
101
+ ...(index % 2 ? { zebra: true } : {})
102
+ }));
103
+ };
104
+ /* The figures a buyer weighs first. */
105
+ const projects = profile.projects?.length ?? 0;
106
+ const certificates = profile.certificates?.length ?? 0;
107
+ gap(14);
108
+ lines.push({
109
+ cells: [
110
+ { col: 1, span: 2, value: words.careerLength, role: 'statLabel' },
111
+ { col: 3, span: 2, value: words.projectsDone, role: 'statLabel' },
112
+ { col: 5, span: 2, value: words.certificatesHeld, role: 'statLabel' }
113
+ ],
114
+ height: 20
115
+ });
116
+ lines.push({
117
+ cells: [
118
+ { col: 1, span: 2, value: careerLengthOf(person, words, today) || '—', role: 'statValue' },
119
+ { col: 3, span: 2, value: projects ? words.projectCount(projects) : '—', role: 'statValue' },
120
+ { col: 5, span: 2, value: certificates ? words.certificateCount(certificates) : '—', role: 'statValue' }
121
+ ],
122
+ height: 34
123
+ });
124
+ section(words.expertise);
125
+ if (profile.summary?.trim())
126
+ lines.push({ cells: [{ col: 1, span: 6, value: profile.summary.trim(), role: 'summary' }] });
127
+ lines.push({
128
+ cells: [
129
+ { col: 1, value: words.skills, role: 'label' },
130
+ { col: 2, span: 5, value: (profile.skills ?? []).join(', '), role: 'skills' }
131
+ ]
132
+ });
133
+ table(words.projects, [[1, 1, words.period], [2, 1, words.project], [3, 1, words.client], [4, 1, words.role], [5, 1, words.duties], [6, 1, words.technologies]], profile.projects, row => [periodOf(row.startedOn, row.endedOn, words.ongoing), row.name, row.client, row.role, row.duties, row.technologies]);
134
+ table(words.careers, [[1, 1, words.period], [2, 1, words.company], [3, 1, words.department], [4, 1, words.position], [5, 2, words.duties]], profile.careers, row => [periodOf(row.startedOn, row.endedOn, words.ongoing), row.company, row.department, row.position, row.duties]);
135
+ table(words.certificates, [[1, 1, words.acquiredOn], [2, 3, words.certificate], [5, 2, words.issuer]], profile.certificates, row => [
136
+ dotted(row.acquiredOn),
137
+ row.name,
138
+ row.issuer
139
+ ]);
140
+ table(words.educations, [[1, 1, words.period], [2, 2, words.school], [4, 2, words.major], [6, 1, words.degree]], profile.educations, row => [
141
+ periodOf(row.startedOn, row.endedOn, words.ongoing),
142
+ row.school,
143
+ row.major,
144
+ row.degree
145
+ ]);
146
+ return lines;
147
+ }
148
+ /** How many people, and their experience on average - the team's weight in one line. */
149
+ function rosterTaglineOf(people, words, today = new Date()) {
150
+ const months = people.map(person => careerMonths(person, today));
151
+ const average = people.length ? Math.round(months.reduce((sum, one) => sum + one, 0) / people.length) : 0;
152
+ return [words.peopleCount(people.length), average ? `${words.averageCareer} ${words.years(Math.floor(average / 12), average % 12)}` : ''].filter(Boolean).join(' · ');
153
+ }
154
+ /** The roster that opens a document of several people - one line a person, in the order picked. */
155
+ function rosterLayout(people, words, company, today = new Date()) {
156
+ return [
157
+ ...headOf(company, `${words.writtenOn} ${dotted((0, employee_profile_rules_1.todayOf)(today))}`, words.rosterTitle, rosterTaglineOf(people, words, today)),
158
+ { cells: [], height: 16 },
159
+ {
160
+ cells: [words.number, words.name, words.department, words.jobPosition, words.careerLength, words.skills].map((value, index) => ({ col: index + 1, value, role: 'head' }))
161
+ },
162
+ ...people.map((person, index) => ({
163
+ cells: [
164
+ String(index + 1),
165
+ person.name ?? '',
166
+ person.departmentName ?? '',
167
+ person.jobPosition ?? '',
168
+ careerLengthOf(person, words, today),
169
+ (person.profile?.skills ?? []).join(', ')
170
+ ].map((value, column) => ({ col: column + 1, value, role: 'value' })),
171
+ ...(index % 2 ? { zebra: true } : {})
172
+ }))
173
+ ];
174
+ }
175
+ /**
176
+ * A sheet name for each person. Excel takes at most 31 characters, none of \ / ? * [ ] :, and no two alike ignoring
177
+ * case - two people of one name become 「홍길동」 and 「홍길동 (2)」. `reserved` are names already in the book.
178
+ */
179
+ function sheetNamesOf(names, reserved = []) {
180
+ const taken = new Set(reserved.map(name => name.toLowerCase()));
181
+ return names.map((name, index) => {
182
+ const base = (name ?? '').replace(/[\\/?*[\]:]/g, ' ').replace(/^'+|'+$/g, '').trim() || `#${index + 1}`;
183
+ let candidate = base.slice(0, 31);
184
+ for (let n = 2; taken.has(candidate.toLowerCase()); n++) {
185
+ const tail = ` (${n})`;
186
+ candidate = base.slice(0, 31 - tail.length) + tail;
187
+ }
188
+ taken.add(candidate.toLowerCase());
189
+ return candidate;
190
+ });
191
+ }
192
+ /** The document's colours - a deep navy for what leads the eye, a pale blue-grey ground, quiet lines. */
193
+ exports.PALETTE = { navy: '1F3A5F', ground: 'EEF3F8', zebra: 'F7F9FB', line: 'C9D2DC', ink: '17242C', muted: '5F6B76' };
194
+ const FONT = 'Malgun Gothic';
195
+ const argb = (hex) => ({ argb: `FF${hex}` });
196
+ const LINE = { style: 'thin', color: argb(exports.PALETTE.line) };
197
+ const RULED = { top: LINE, left: LINE, bottom: LINE, right: LINE };
198
+ const ground = (hex) => ({ type: 'pattern', pattern: 'solid', fgColor: argb(hex) });
199
+ const font = (size, extra = {}) => ({ name: FONT, size, color: argb(exports.PALETTE.ink), ...extra });
200
+ /** How each role of cell is painted in Excel. */
201
+ const PAINT = {
202
+ company: { font: font(9, { color: argb(exports.PALETTE.muted) }), alignment: { horizontal: 'left', vertical: 'bottom' } },
203
+ kicker: { font: font(9, { bold: true, color: argb(exports.PALETTE.navy) }), alignment: { horizontal: 'right', vertical: 'bottom' } },
204
+ name: { font: font(22, { bold: true, color: argb(exports.PALETTE.navy) }), alignment: { horizontal: 'left', vertical: 'bottom' } },
205
+ tagline: { font: font(10.5, { color: argb(exports.PALETTE.muted) }), alignment: { horizontal: 'left', vertical: 'top' } },
206
+ rule: { border: { bottom: { style: 'thick', color: argb(exports.PALETTE.navy) } } },
207
+ statLabel: {
208
+ font: font(9, { color: argb(exports.PALETTE.muted) }),
209
+ fill: ground(exports.PALETTE.ground),
210
+ alignment: { horizontal: 'center', vertical: 'bottom' },
211
+ border: { top: { style: 'medium', color: argb(exports.PALETTE.navy) } }
212
+ },
213
+ statValue: { font: font(16, { bold: true, color: argb(exports.PALETTE.navy) }), fill: ground(exports.PALETTE.ground), alignment: { horizontal: 'center', vertical: 'middle' } },
214
+ section: { font: font(12, { bold: true, color: argb(exports.PALETTE.navy) }), alignment: { horizontal: 'left', vertical: 'bottom' }, border: { bottom: { style: 'medium', color: argb(exports.PALETTE.navy) } } },
215
+ summary: {
216
+ font: font(10.5),
217
+ fill: ground(exports.PALETTE.ground),
218
+ alignment: { horizontal: 'left', vertical: 'middle', wrapText: true, indent: 1 },
219
+ border: { left: { style: 'thick', color: argb(exports.PALETTE.navy) } }
220
+ },
221
+ label: { font: font(9.5, { bold: true, color: argb(exports.PALETTE.navy) }), fill: ground(exports.PALETTE.ground), alignment: { horizontal: 'center', vertical: 'middle', wrapText: true }, border: RULED },
222
+ skills: { font: font(10, { bold: true }), alignment: { horizontal: 'left', vertical: 'middle', wrapText: true, indent: 1 }, border: RULED },
223
+ head: {
224
+ font: font(9.5, { bold: true, color: argb('FFFFFF') }),
225
+ fill: ground(exports.PALETTE.navy),
226
+ alignment: { horizontal: 'center', vertical: 'middle', wrapText: true },
227
+ border: { top: { style: 'thin', color: argb(exports.PALETTE.navy) }, bottom: { style: 'thin', color: argb(exports.PALETTE.navy) }, left: LINE, right: LINE }
228
+ },
229
+ value: { font: font(9.5), alignment: { horizontal: 'left', vertical: 'middle', wrapText: true, indent: 1 }, border: RULED },
230
+ none: { font: font(9.5, { color: argb(exports.PALETTE.muted) }), alignment: { horizontal: 'center', vertical: 'middle' }, border: RULED }
231
+ };
232
+ /**
233
+ * A line's height in points, for as many text lines as its longest boxed cell wraps into - Excel does not grow a row
234
+ * for wrapped text written by a program. A full-width character is about two of the column's character units.
235
+ */
236
+ function heightOf(line) {
237
+ const boxed = line.cells.filter(cell => ['label', 'skills', 'summary', 'head', 'value', 'none'].includes(cell.role));
238
+ if (!boxed.length)
239
+ return undefined;
240
+ const wraps = boxed.map(cell => {
241
+ const width = exports.COLUMN_WIDTHS.slice(cell.col - 1, cell.col - 1 + (cell.span ?? 1)).reduce((sum, one) => sum + one, 0) - (cell.role === 'summary' ? 4 : 1);
242
+ return cell.value.split('\n').reduce((lines, text) => {
243
+ const units = [...text].reduce((sum, char) => sum + (char.charCodeAt(0) > 0x2e7f ? 1.9 : 0.9), 0);
244
+ return lines + Math.max(1, Math.ceil(units / Math.max(width, 1)));
245
+ }, 0);
246
+ });
247
+ const lines = Math.max(...wraps);
248
+ return boxed.some(cell => cell.role === 'summary') ? lines * 16 + 14 : lines * 15 + 7;
249
+ }
250
+ /** Paint a layout on a sheet: values, spans merged, each cell of a span painted so a boxed cell is boxed all round. */
251
+ function paintSheet(sheet, lines, footer = '') {
252
+ exports.COLUMN_WIDTHS.forEach((width, index) => (sheet.getColumn(index + 1).width = width));
253
+ lines.forEach((line, index) => {
254
+ const row = sheet.getRow(index + 1);
255
+ const height = Math.max(line.height ?? 0, heightOf(line) ?? 0);
256
+ if (height)
257
+ row.height = height;
258
+ for (const cell of line.cells) {
259
+ const span = cell.span ?? 1;
260
+ const style = line.zebra && cell.role === 'value' ? { ...PAINT.value, fill: ground(exports.PALETTE.zebra) } : PAINT[cell.role];
261
+ for (let col = cell.col; col < cell.col + span; col++)
262
+ Object.assign(row.getCell(col), { style });
263
+ row.getCell(cell.col).value = cell.value;
264
+ if (span > 1)
265
+ sheet.mergeCells(index + 1, cell.col, index + 1, cell.col + span - 1);
266
+ }
267
+ });
268
+ sheet.views = [{ showGridLines: false }];
269
+ sheet.pageSetup = {
270
+ paperSize: 9,
271
+ orientation: 'portrait',
272
+ fitToPage: true,
273
+ fitToWidth: 1,
274
+ fitToHeight: 0,
275
+ horizontalCentered: true,
276
+ margins: { left: 0.5, right: 0.5, top: 0.6, bottom: 0.7, header: 0.3, footer: 0.3 }
277
+ };
278
+ /* The employer at the foot of every printed page, and the page of how many. `&` is Excel's escape. */
279
+ sheet.headerFooter = { oddFooter: `&L&8${footer.replace(/&/g, '&&')}&R&8&P / &N` };
280
+ }
281
+ /** The workbook of the people given, in their order - opened by a roster when there are several. */
282
+ function profileWorkbook(people, words, company, today = new Date()) {
283
+ const book = new exceljs_1.Workbook();
284
+ const footer = [company, words.title].filter(Boolean).join(' · ');
285
+ /* The roster's name follows Excel's rules as the people's do. */
286
+ const roster = people.length > 1 ? sheetNamesOf([words.rosterTitle]) : [];
287
+ if (roster.length)
288
+ paintSheet(book.addWorksheet(roster[0]), rosterLayout(people, words, company, today), footer);
289
+ const names = sheetNamesOf(people.map(person => person.name), roster);
290
+ people.forEach((person, index) => paintSheet(book.addWorksheet(names[index]), profileSheetLayout(person, words, company, today), footer));
291
+ return book;
292
+ }
293
+ const escapeHtml = (text) => text.replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c]);
294
+ /** A cell's content as HTML: skills as tags, line breaks kept, everything escaped. */
295
+ function cellHtml(cell) {
296
+ if (cell.role === 'skills') {
297
+ return cell.value
298
+ .split(', ')
299
+ .filter(Boolean)
300
+ .map(skill => `<span class="tag">${escapeHtml(skill)}</span>`)
301
+ .join('');
302
+ }
303
+ return escapeHtml(cell.value).replace(/\n/g, '<br>');
304
+ }
305
+ /** The same layout as HTML: a table of the six columns a page, the roles as classes. */
306
+ function layoutHtml(lines) {
307
+ const total = exports.COLUMN_WIDTHS.reduce((sum, width) => sum + width, 0);
308
+ const columns = exports.COLUMN_WIDTHS.map(width => `<col style="width:${((width / total) * 100).toFixed(2)}%">`).join('');
309
+ const rows = lines.map(line => {
310
+ const style = line.height ? ` style="height:${Math.round(line.height * 1.33)}px"` : '';
311
+ if (!line.cells.length)
312
+ return `<tr class="gap"${style}><td colspan="6"></td></tr>`;
313
+ let next = 1;
314
+ const cells = [];
315
+ for (const cell of line.cells) {
316
+ if (cell.col > next)
317
+ cells.push(`<td colspan="${cell.col - next}"></td>`);
318
+ const span = cell.span ?? 1;
319
+ cells.push(`<td class="${cell.role}"${span > 1 ? ` colspan="${span}"` : ''}>${cellHtml(cell)}</td>`);
320
+ next = cell.col + span;
321
+ }
322
+ if (next <= 6)
323
+ cells.push(`<td colspan="${7 - next}"></td>`);
324
+ return `<tr${line.zebra ? ' class="zebra"' : ''}${style}>${cells.join('')}</tr>`;
325
+ });
326
+ return `<table><colgroup>${columns}</colgroup>${rows.join('')}</table>`;
327
+ }
328
+ /** How the roles look on paper - the workbook's paint in CSS. */
329
+ exports.PROFILE_CSS = `
330
+ body { margin: 0; font-family: 'Pretendard', 'Malgun Gothic', 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif; font-size: 9.5pt; color: #${exports.PALETTE.ink};
331
+ -webkit-print-color-adjust: exact; print-color-adjust: exact }
332
+ .sheet { break-after: page }
333
+ .sheet:last-child { break-after: auto }
334
+ table { width: 100%; border-collapse: collapse; table-layout: fixed }
335
+ td { padding: 5px 7px; vertical-align: middle; word-break: keep-all; overflow-wrap: anywhere; line-height: 1.45 }
336
+ tr.gap td { padding: 0 }
337
+ td.company { font-size: 8.5pt; color: #${exports.PALETTE.muted}; padding: 0 0 2px }
338
+ td.kicker { font-size: 8.5pt; font-weight: 700; color: #${exports.PALETTE.navy}; text-align: right; letter-spacing: 0.08em; padding: 0 0 2px }
339
+ td.name { font-size: 24pt; font-weight: 800; color: #${exports.PALETTE.navy}; letter-spacing: 0.02em; padding: 6px 0 0; vertical-align: bottom }
340
+ td.tagline { font-size: 10.5pt; color: #${exports.PALETTE.muted}; padding: 2px 0 8px; vertical-align: top }
341
+ td.rule { border-bottom: 3px solid #${exports.PALETTE.navy}; padding: 0 }
342
+ td.statLabel { background: #${exports.PALETTE.ground}; border-top: 2px solid #${exports.PALETTE.navy}; text-align: center; font-size: 8.5pt; color: #${exports.PALETTE.muted}; padding: 8px 0 0 }
343
+ td.statValue { background: #${exports.PALETTE.ground}; text-align: center; font-size: 17pt; font-weight: 800; color: #${exports.PALETTE.navy}; padding: 0 0 8px }
344
+ td.statLabel + td.statLabel, td.statValue + td.statValue { border-left: 8px solid #fff }
345
+ td.section { font-size: 12pt; font-weight: 800; color: #${exports.PALETTE.navy}; border-bottom: 2px solid #${exports.PALETTE.navy}; padding: 0 0 4px; vertical-align: bottom }
346
+ td.summary { background: #${exports.PALETTE.ground}; border-left: 4px solid #${exports.PALETTE.navy}; font-size: 10.5pt; padding: 10px 14px }
347
+ td.label { background: #${exports.PALETTE.ground}; color: #${exports.PALETTE.navy}; font-weight: 700; text-align: center; border: 1px solid #${exports.PALETTE.line} }
348
+ td.skills { border: 1px solid #${exports.PALETTE.line}; padding: 6px 8px }
349
+ .tag { display: inline-block; margin: 2px 4px 2px 0; padding: 2px 9px; border-radius: 10px; background: #${exports.PALETTE.navy}; color: #fff; font-size: 8.5pt; font-weight: 600 }
350
+ td.head { background: #${exports.PALETTE.navy}; color: #fff; font-weight: 700; text-align: center; border: 1px solid #${exports.PALETTE.navy}; font-size: 9pt }
351
+ td.value { border: 1px solid #${exports.PALETTE.line} }
352
+ tr.zebra td.value { background: #${exports.PALETTE.zebra} }
353
+ td.none { border: 1px solid #${exports.PALETTE.line}; color: #${exports.PALETTE.muted}; text-align: center }
354
+ tr { break-inside: avoid }
355
+ `;
356
+ /** On a screen, the pages as sheets of A4 on a grey desk - the preview reads as the PDF will (ADR-0169 decision 9). */
357
+ exports.PROFILE_SCREEN_CSS = `
358
+ @media screen {
359
+ html { background: #e9edf1 }
360
+ body { padding: 24px 0 }
361
+ .sheet { box-sizing: border-box; width: 210mm; min-height: 297mm; margin: 0 auto 24px; padding: 14mm 12mm; background: #fff;
362
+ box-shadow: 0 1px 3px rgba(23, 36, 44, .12), 0 8px 24px rgba(23, 36, 44, .08) }
363
+ }
364
+ `;
365
+ /** The preview: the same pages as one HTML document, to be shown in a frame. */
366
+ function profilePreviewHtml(people, words, company, today = new Date()) {
367
+ return `<!doctype html><html><head><meta charset="utf-8"><style>${exports.PROFILE_CSS}${exports.PROFILE_SCREEN_CSS}</style></head><body>${profileHtml(people, words, company, today)}</body></html>`;
368
+ }
369
+ /** The document as one HTML page set: the roster first when there are several, then a page a person. */
370
+ function profileHtml(people, words, company, today = new Date()) {
371
+ const sheets = [
372
+ ...(people.length > 1 ? [rosterLayout(people, words, company, today)] : []),
373
+ ...people.map(person => profileSheetLayout(person, words, company, today))
374
+ ];
375
+ return sheets.map(lines => `<section class="sheet">${layoutHtml(lines)}</section>`).join('');
376
+ }
377
+ /** The foot of every printed PDF page: the employer and the document at the left, the page of how many at the right. */
378
+ function profileFooterHtml(company, words) {
379
+ /* The footer is a Handlebars template in the pdf module - a brace in the name must not open one. */
380
+ const left = escapeHtml([company, words.title].filter(Boolean).join(' · ')).replace(/[{}]/g, c => (c === '{' ? '&#123;' : '&#125;'));
381
+ return (`<div style="width:100%;padding:0 12mm;display:flex;justify-content:space-between;font-family:'Malgun Gothic','Apple SD Gothic Neo','Noto Sans KR',sans-serif;` +
382
+ `font-size:7.5pt;color:#${exports.PALETTE.muted}"><span>${left}</span><span><span class="pageNumber"></span> / <span class="totalPages"></span></span></div>`);
383
+ }
384
+ /** 「인력프로필-20261027.xlsx」 - the word in the reader's language, the day the profiles were taken. */
385
+ function profileFileName(word, extension, day = new Date()) {
386
+ const pad = (n) => String(n).padStart(2, '0');
387
+ return `${word}-${day.getFullYear()}${pad(day.getMonth() + 1)}${pad(day.getDate())}.${extension}`;
388
+ }
389
+ /** A word as a document heads with it - its first letter capital where the language has capitals (「proyectos」 → 「Proyectos」). */
390
+ function lead(word) {
391
+ return word ? word.charAt(0).toLocaleUpperCase() + word.slice(1) : word;
392
+ }
393
+ /** The document's words from the server's translator - the reader's language, each as a heading begins. */
394
+ function profileWordsOf(translate) {
395
+ const t = (key, options) => lead(translate(key, options));
396
+ const p = (name) => t(`label.employee-profile-${name}`);
397
+ return {
398
+ title: t('text.employee-profile-doc-title'),
399
+ rosterTitle: t('text.employee-profile-doc-roster'),
400
+ expertise: t('text.employee-profile-doc-expertise'),
401
+ name: t('field.name'),
402
+ department: t('label.department'),
403
+ jobPosition: t('label.job-position'),
404
+ hiredOn: t('label.hired-on'),
405
+ careerLength: p('career-length'),
406
+ projectsDone: t('text.employee-profile-doc-projects-done'),
407
+ certificatesHeld: t('text.employee-profile-doc-certificates-held'),
408
+ summary: p('summary'),
409
+ skills: p('skills'),
410
+ educations: p('educations'),
411
+ certificates: p('certificates'),
412
+ careers: p('careers'),
413
+ projects: p('projects'),
414
+ school: p('school'),
415
+ major: p('major'),
416
+ degree: p('degree'),
417
+ period: p('period'),
418
+ certificate: p('certificate'),
419
+ issuer: p('issuer'),
420
+ acquiredOn: p('acquired-on'),
421
+ company: p('company'),
422
+ position: p('position'),
423
+ duties: p('duties'),
424
+ project: p('project'),
425
+ client: p('client'),
426
+ role: p('role'),
427
+ technologies: p('technologies'),
428
+ ongoing: t('text.employee-profile-doc-ongoing'),
429
+ none: t('text.employee-profile-doc-none'),
430
+ writtenOn: t('text.employee-profile-doc-written-on'),
431
+ number: t('text.employee-profile-doc-number'),
432
+ years: (years, months) => t('text.employee-profile-doc-years', { years, months }),
433
+ projectCount: n => t('text.employee-profile-doc-project-count', { n }),
434
+ certificateCount: n => t('text.employee-profile-doc-certificate-count', { n }),
435
+ peopleCount: n => t('text.employee-profile-doc-people', { n }),
436
+ averageCareer: t('text.employee-profile-doc-average-career')
437
+ };
438
+ }
439
+ //# sourceMappingURL=employee-profile-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"employee-profile-document.js","sourceRoot":"","sources":["../../../server/service/employee-profile/employee-profile-document.ts"],"names":[],"mappings":"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","sourcesContent":["import { Borders, Style, Workbook, Worksheet } from 'exceljs'\n\nimport { todayOf } from './employee-profile-rules'\n\n/*\n * The employee profiles a quotation attaches, as a document a person can hand in as it is (ADR-0169 decisions 6 · 8):\n * a page a person, made to show what the person is worth - the name large with where they stand, the figures a buyer\n * weighs (total experience, projects delivered, certificates held), the expertise in their own words and skills, then\n * the projects first, the careers, certificates and education - laid out for A4. Several people open with a roster.\n *\n * One layout (`profileSheetLayout`, `rosterLayout`) is painted twice: as an Excel workbook, a sheet a page\n * (`profileWorkbook`), and as HTML printed to PDF, a page break a sheet (`profileHtml`). Pure - the door reads the data\n * and the pdf module prints.\n */\n\n/** A row of any of the four tables - each has some of these. */\ntype Row = {\n school?: string\n major?: string\n degree?: string\n name?: string\n issuer?: string\n acquiredOn?: string\n company?: string\n department?: string\n position?: string\n duties?: string\n client?: string\n role?: string\n technologies?: string\n startedOn?: string\n endedOn?: string\n}\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 document, in the reader's language - handed in so this stays free of i18next. */\nexport type ProfileSheetWords = Record<\n | 'title'\n | 'rosterTitle'\n | 'expertise'\n | 'name'\n | 'department'\n | 'jobPosition'\n | 'hiredOn'\n | 'careerLength'\n | 'projectsDone'\n | 'certificatesHeld'\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 | 'ongoing'\n | 'none'\n | 'writtenOn'\n | 'averageCareer'\n | 'number',\n string\n> & {\n /** 「12년 3개월」 */\n years: (years: number, months: number) => string\n /** 「8건」 */\n projectCount: (count: number) => string\n /** 「3개」 */\n certificateCount: (count: number) => string\n /** 「5명」 */\n peopleCount: (count: number) => string\n}\n\n/** What a cell is - the painter styles by it. */\nexport type CellRole =\n | 'company'\n | 'kicker'\n | 'name'\n | 'tagline'\n | 'rule'\n | 'statLabel'\n | 'statValue'\n | 'section'\n | 'summary'\n | 'label'\n | 'skills'\n | 'head'\n | 'value'\n | 'none'\n\n/** A cell of the six-column grid: `col` from 1, spanning `span` columns. */\nexport type LayoutCell = { col: number; span?: number; value: string; role: CellRole }\n\n/** A line of the grid; `zebra` shades every other row of a table. */\nexport type LayoutLine = { cells: LayoutCell[]; height?: number; zebra?: boolean }\n\n/** The grid's six columns, in characters - together about the width of A4 in portrait. */\nexport const COLUMN_WIDTHS = [18, 20, 15, 12, 21, 18]\n\n/** 「2023.03」 from 「2023-03」 or 「2023-03-02」 - the dotted form a staffing profile is read in. */\nexport function dotted(day?: string | null): string {\n return (day ?? '').trim().replace(/-/g, '.')\n}\n\n/** A period as one cell - an open end is still going on. */\nexport function periodOf(startedOn: string | undefined, endedOn: string | undefined, ongoing: string): string {\n if (!startedOn && !endedOn) return ''\n return `${dotted(startedOn)} ~ ${endedOn ? dotted(endedOn) : ongoing}`.trim()\n}\n\nconst monthIndex = (day?: string | null): number | null => {\n const match = /^(\\d{4})-(\\d{2})/.exec((day ?? '').trim())\n return match ? Number(match[1]) * 12 + Number(match[2]) - 1 : null\n}\n\n/**\n * The months a person has worked: the careers before this company and the time here since the hire day, counted\n * once where they overlap. A career with no end runs to `today`; one with no start is left out.\n */\nexport function careerMonths(person: ProfilePerson, today = new Date()): number {\n const now = today.getFullYear() * 12 + today.getMonth()\n const months = new Set<number>()\n const spans = [\n ...(person.profile?.careers ?? []).map(row => [monthIndex(row.startedOn), row.endedOn ? monthIndex(row.endedOn) : now] as const),\n [monthIndex(person.hiredOn), now] as const\n ]\n for (const [from, to] of spans) {\n if (from === null || to === null) continue\n for (let month = from; month <= Math.min(to, now); month++) months.add(month)\n }\n return months.size\n}\n\nexport function careerLengthOf(person: ProfilePerson, words: ProfileSheetWords, today = new Date()): string {\n const months = careerMonths(person, today)\n return months ? words.years(Math.floor(months / 12), months % 12) : ''\n}\n\n/** Where the person stands, in one line: department · position / responsibility · hired on. */\nexport function taglineOf(person: ProfilePerson, words: ProfileSheetWords): string {\n const role = [person.jobPosition, person.jobResponsibility].filter(Boolean).join(' / ')\n return [person.departmentName, role, person.hiredOn ? `${words.hiredOn} ${dotted(person.hiredOn)}` : ''].filter(Boolean).join(' · ')\n}\n\n/** The head every page opens with: the employer and what the page is, the name large, a line under it. */\nfunction headOf(company: string, kicker: string, name: string, tagline: string): LayoutLine[] {\n return [\n {\n cells: [\n { col: 1, span: 3, value: company, role: 'company' },\n { col: 4, span: 3, value: kicker, role: 'kicker' }\n ]\n },\n { cells: [{ col: 1, span: 6, value: name, role: 'name' }], height: 40 },\n { cells: [{ col: 1, span: 6, value: tagline, role: 'tagline' }], height: 20 },\n { cells: [{ col: 1, span: 6, value: '', role: 'rule' }], height: 6 }\n ]\n}\n\n/** One person's page, line by line. `company` is the employer's name; `today` the day it is written. */\nexport function profileSheetLayout(person: ProfilePerson, words: ProfileSheetWords, company: string, today = new Date()): LayoutLine[] {\n const profile = person.profile ?? {}\n const lines: LayoutLine[] = [...headOf(company, words.title, person.name ?? '', taglineOf(person, words))]\n const gap = (height = 12) => lines.push({ cells: [], height })\n const section = (title: string) => {\n gap(16)\n lines.push({ cells: [{ col: 1, span: 6, value: title, role: 'section' }], height: 24 })\n gap(6)\n }\n\n /* A table: its head and its rows in the columns given as [col, span], every other row shaded; none says so. */\n const table = (title: string, columns: [number, number, string][], rows: Row[] | null | undefined, cells: (row: Row) => (string | undefined)[]) => {\n section(title)\n lines.push({ cells: columns.map(([col, span, head]) => ({ col, span, value: head, role: 'head' })) })\n if (!rows?.length) return lines.push({ cells: [{ col: 1, span: 6, value: words.none, role: 'none' }] })\n rows.forEach((row, index) =>\n lines.push({\n cells: cells(row).map((text, at) => ({ col: columns[at][0], span: columns[at][1], value: text ?? '', role: 'value' })),\n ...(index % 2 ? { zebra: true } : {})\n })\n )\n }\n\n /* The figures a buyer weighs first. */\n const projects = profile.projects?.length ?? 0\n const certificates = profile.certificates?.length ?? 0\n gap(14)\n lines.push({\n cells: [\n { col: 1, span: 2, value: words.careerLength, role: 'statLabel' },\n { col: 3, span: 2, value: words.projectsDone, role: 'statLabel' },\n { col: 5, span: 2, value: words.certificatesHeld, role: 'statLabel' }\n ],\n height: 20\n })\n lines.push({\n cells: [\n { col: 1, span: 2, value: careerLengthOf(person, words, today) || '—', role: 'statValue' },\n { col: 3, span: 2, value: projects ? words.projectCount(projects) : '—', role: 'statValue' },\n { col: 5, span: 2, value: certificates ? words.certificateCount(certificates) : '—', role: 'statValue' }\n ],\n height: 34\n })\n\n section(words.expertise)\n if (profile.summary?.trim()) lines.push({ cells: [{ col: 1, span: 6, value: profile.summary.trim(), role: 'summary' }] })\n lines.push({\n cells: [\n { col: 1, value: words.skills, role: 'label' },\n { col: 2, span: 5, value: (profile.skills ?? []).join(', '), role: 'skills' }\n ]\n })\n\n table(\n words.projects,\n [[1, 1, words.period], [2, 1, words.project], [3, 1, words.client], [4, 1, words.role], [5, 1, words.duties], [6, 1, words.technologies]],\n profile.projects,\n row => [periodOf(row.startedOn, row.endedOn, words.ongoing), row.name, row.client, row.role, row.duties, row.technologies]\n )\n table(\n words.careers,\n [[1, 1, words.period], [2, 1, words.company], [3, 1, words.department], [4, 1, words.position], [5, 2, words.duties]],\n profile.careers,\n row => [periodOf(row.startedOn, row.endedOn, words.ongoing), row.company, row.department, row.position, row.duties]\n )\n table(words.certificates, [[1, 1, words.acquiredOn], [2, 3, words.certificate], [5, 2, words.issuer]], profile.certificates, row => [\n dotted(row.acquiredOn),\n row.name,\n row.issuer\n ])\n table(words.educations, [[1, 1, words.period], [2, 2, words.school], [4, 2, words.major], [6, 1, words.degree]], profile.educations, row => [\n periodOf(row.startedOn, row.endedOn, words.ongoing),\n row.school,\n row.major,\n row.degree\n ])\n return lines\n}\n\n/** How many people, and their experience on average - the team's weight in one line. */\nexport function rosterTaglineOf(people: ProfilePerson[], words: ProfileSheetWords, today = new Date()): string {\n const months = people.map(person => careerMonths(person, today))\n const average = people.length ? Math.round(months.reduce((sum, one) => sum + one, 0) / people.length) : 0\n return [words.peopleCount(people.length), average ? `${words.averageCareer} ${words.years(Math.floor(average / 12), average % 12)}` : ''].filter(Boolean).join(' · ')\n}\n\n/** The roster that opens a document of several people - one line a person, in the order picked. */\nexport function rosterLayout(people: ProfilePerson[], words: ProfileSheetWords, company: string, today = new Date()): LayoutLine[] {\n return [\n ...headOf(company, `${words.writtenOn} ${dotted(todayOf(today))}`, words.rosterTitle, rosterTaglineOf(people, words, today)),\n { cells: [], height: 16 },\n {\n cells: [words.number, words.name, words.department, words.jobPosition, words.careerLength, words.skills].map((value, index) => ({ col: index + 1, value, role: 'head' as const }))\n },\n ...people.map((person, index) => ({\n cells: [\n String(index + 1),\n person.name ?? '',\n person.departmentName ?? '',\n person.jobPosition ?? '',\n careerLengthOf(person, words, today),\n (person.profile?.skills ?? []).join(', ')\n ].map((value, column) => ({ col: column + 1, value, role: 'value' as const })),\n ...(index % 2 ? { zebra: true } : {})\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)」. `reserved` are names already in the book.\n */\nexport function sheetNamesOf(names: (string | null | undefined)[], reserved: string[] = []): string[] {\n const taken = new Set<string>(reserved.map(name => name.toLowerCase()))\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 document's colours - a deep navy for what leads the eye, a pale blue-grey ground, quiet lines. */\nexport const PALETTE = { navy: '1F3A5F', ground: 'EEF3F8', zebra: 'F7F9FB', line: 'C9D2DC', ink: '17242C', muted: '5F6B76' }\n\nconst FONT = 'Malgun Gothic'\nconst argb = (hex: string) => ({ argb: `FF${hex}` })\nconst LINE = { style: 'thin' as const, color: argb(PALETTE.line) }\nconst RULED: Partial<Borders> = { top: LINE, left: LINE, bottom: LINE, right: LINE }\nconst ground = (hex: string) => ({ type: 'pattern' as const, pattern: 'solid' as const, fgColor: argb(hex) })\nconst font = (size: number, extra: Partial<Style['font']> = {}) => ({ name: FONT, size, color: argb(PALETTE.ink), ...extra })\n\n/** How each role of cell is painted in Excel. */\nconst PAINT: Record<CellRole, Partial<Style>> = {\n company: { font: font(9, { color: argb(PALETTE.muted) }), alignment: { horizontal: 'left', vertical: 'bottom' } },\n kicker: { font: font(9, { bold: true, color: argb(PALETTE.navy) }), alignment: { horizontal: 'right', vertical: 'bottom' } },\n name: { font: font(22, { bold: true, color: argb(PALETTE.navy) }), alignment: { horizontal: 'left', vertical: 'bottom' } },\n tagline: { font: font(10.5, { color: argb(PALETTE.muted) }), alignment: { horizontal: 'left', vertical: 'top' } },\n rule: { border: { bottom: { style: 'thick', color: argb(PALETTE.navy) } } },\n statLabel: {\n font: font(9, { color: argb(PALETTE.muted) }),\n fill: ground(PALETTE.ground),\n alignment: { horizontal: 'center', vertical: 'bottom' },\n border: { top: { style: 'medium', color: argb(PALETTE.navy) } }\n },\n statValue: { font: font(16, { bold: true, color: argb(PALETTE.navy) }), fill: ground(PALETTE.ground), alignment: { horizontal: 'center', vertical: 'middle' } },\n section: { font: font(12, { bold: true, color: argb(PALETTE.navy) }), alignment: { horizontal: 'left', vertical: 'bottom' }, border: { bottom: { style: 'medium', color: argb(PALETTE.navy) } } },\n summary: {\n font: font(10.5),\n fill: ground(PALETTE.ground),\n alignment: { horizontal: 'left', vertical: 'middle', wrapText: true, indent: 1 },\n border: { left: { style: 'thick', color: argb(PALETTE.navy) } }\n },\n label: { font: font(9.5, { bold: true, color: argb(PALETTE.navy) }), fill: ground(PALETTE.ground), alignment: { horizontal: 'center', vertical: 'middle', wrapText: true }, border: RULED },\n skills: { font: font(10, { bold: true }), alignment: { horizontal: 'left', vertical: 'middle', wrapText: true, indent: 1 }, border: RULED },\n head: {\n font: font(9.5, { bold: true, color: argb('FFFFFF') }),\n fill: ground(PALETTE.navy),\n alignment: { horizontal: 'center', vertical: 'middle', wrapText: true },\n border: { top: { style: 'thin', color: argb(PALETTE.navy) }, bottom: { style: 'thin', color: argb(PALETTE.navy) }, left: LINE, right: LINE }\n },\n value: { font: font(9.5), alignment: { horizontal: 'left', vertical: 'middle', wrapText: true, indent: 1 }, border: RULED },\n none: { font: font(9.5, { color: argb(PALETTE.muted) }), alignment: { horizontal: 'center', vertical: 'middle' }, border: RULED }\n}\n\n/**\n * A line's height in points, for as many text lines as its longest boxed cell wraps into - Excel does not grow a row\n * for wrapped text written by a program. A full-width character is about two of the column's character units.\n */\nexport function heightOf(line: LayoutLine): number | undefined {\n const boxed = line.cells.filter(cell => ['label', 'skills', 'summary', 'head', 'value', 'none'].includes(cell.role))\n if (!boxed.length) return undefined\n const wraps = boxed.map(cell => {\n const width = COLUMN_WIDTHS.slice(cell.col - 1, cell.col - 1 + (cell.span ?? 1)).reduce((sum, one) => sum + one, 0) - (cell.role === 'summary' ? 4 : 1)\n return cell.value.split('\\n').reduce((lines, text) => {\n const units = [...text].reduce((sum, char) => sum + (char.charCodeAt(0) > 0x2e7f ? 1.9 : 0.9), 0)\n return lines + Math.max(1, Math.ceil(units / Math.max(width, 1)))\n }, 0)\n })\n const lines = Math.max(...wraps)\n return boxed.some(cell => cell.role === 'summary') ? lines * 16 + 14 : lines * 15 + 7\n}\n\n/** Paint a layout on a sheet: values, spans merged, each cell of a span painted so a boxed cell is boxed all round. */\nexport function paintSheet(sheet: Worksheet, lines: LayoutLine[], footer = '') {\n COLUMN_WIDTHS.forEach((width, index) => (sheet.getColumn(index + 1).width = width))\n lines.forEach((line, index) => {\n const row = sheet.getRow(index + 1)\n const height = Math.max(line.height ?? 0, heightOf(line) ?? 0)\n if (height) row.height = height\n for (const cell of line.cells) {\n const span = cell.span ?? 1\n const style = line.zebra && cell.role === 'value' ? { ...PAINT.value, fill: ground(PALETTE.zebra) } : PAINT[cell.role]\n for (let col = cell.col; col < cell.col + span; col++) Object.assign(row.getCell(col), { style })\n row.getCell(cell.col).value = cell.value\n if (span > 1) sheet.mergeCells(index + 1, cell.col, index + 1, cell.col + span - 1)\n }\n })\n sheet.views = [{ showGridLines: false }]\n sheet.pageSetup = {\n paperSize: 9,\n orientation: 'portrait',\n fitToPage: true,\n fitToWidth: 1,\n fitToHeight: 0,\n horizontalCentered: true,\n margins: { left: 0.5, right: 0.5, top: 0.6, bottom: 0.7, header: 0.3, footer: 0.3 }\n }\n /* The employer at the foot of every printed page, and the page of how many. `&` is Excel's escape. */\n sheet.headerFooter = { oddFooter: `&L&8${footer.replace(/&/g, '&&')}&R&8&P / &N` }\n}\n\n/** The workbook of the people given, in their order - opened by a roster when there are several. */\nexport function profileWorkbook(people: ProfilePerson[], words: ProfileSheetWords, company: string, today = new Date()): Workbook {\n const book = new Workbook()\n const footer = [company, words.title].filter(Boolean).join(' · ')\n /* The roster's name follows Excel's rules as the people's do. */\n const roster = people.length > 1 ? sheetNamesOf([words.rosterTitle]) : []\n if (roster.length) paintSheet(book.addWorksheet(roster[0]), rosterLayout(people, words, company, today), footer)\n const names = sheetNamesOf(people.map(person => person.name), roster)\n people.forEach((person, index) => paintSheet(book.addWorksheet(names[index]), profileSheetLayout(person, words, company, today), footer))\n return book\n}\n\nconst escapeHtml = (text: string) => text.replace(/[&<>\"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' })[c]!)\n\n/** A cell's content as HTML: skills as tags, line breaks kept, everything escaped. */\nfunction cellHtml(cell: LayoutCell): string {\n if (cell.role === 'skills') {\n return cell.value\n .split(', ')\n .filter(Boolean)\n .map(skill => `<span class=\"tag\">${escapeHtml(skill)}</span>`)\n .join('')\n }\n return escapeHtml(cell.value).replace(/\\n/g, '<br>')\n}\n\n/** The same layout as HTML: a table of the six columns a page, the roles as classes. */\nexport function layoutHtml(lines: LayoutLine[]): string {\n const total = COLUMN_WIDTHS.reduce((sum, width) => sum + width, 0)\n const columns = COLUMN_WIDTHS.map(width => `<col style=\"width:${((width / total) * 100).toFixed(2)}%\">`).join('')\n const rows = lines.map(line => {\n const style = line.height ? ` style=\"height:${Math.round(line.height * 1.33)}px\"` : ''\n if (!line.cells.length) return `<tr class=\"gap\"${style}><td colspan=\"6\"></td></tr>`\n let next = 1\n const cells: string[] = []\n for (const cell of line.cells) {\n if (cell.col > next) cells.push(`<td colspan=\"${cell.col - next}\"></td>`)\n const span = cell.span ?? 1\n cells.push(`<td class=\"${cell.role}\"${span > 1 ? ` colspan=\"${span}\"` : ''}>${cellHtml(cell)}</td>`)\n next = cell.col + span\n }\n if (next <= 6) cells.push(`<td colspan=\"${7 - next}\"></td>`)\n return `<tr${line.zebra ? ' class=\"zebra\"' : ''}${style}>${cells.join('')}</tr>`\n })\n return `<table><colgroup>${columns}</colgroup>${rows.join('')}</table>`\n}\n\n/** How the roles look on paper - the workbook's paint in CSS. */\nexport const PROFILE_CSS = `\nbody { margin: 0; font-family: 'Pretendard', 'Malgun Gothic', 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif; font-size: 9.5pt; color: #${PALETTE.ink};\n -webkit-print-color-adjust: exact; print-color-adjust: exact }\n.sheet { break-after: page }\n.sheet:last-child { break-after: auto }\ntable { width: 100%; border-collapse: collapse; table-layout: fixed }\ntd { padding: 5px 7px; vertical-align: middle; word-break: keep-all; overflow-wrap: anywhere; line-height: 1.45 }\ntr.gap td { padding: 0 }\ntd.company { font-size: 8.5pt; color: #${PALETTE.muted}; padding: 0 0 2px }\ntd.kicker { font-size: 8.5pt; font-weight: 700; color: #${PALETTE.navy}; text-align: right; letter-spacing: 0.08em; padding: 0 0 2px }\ntd.name { font-size: 24pt; font-weight: 800; color: #${PALETTE.navy}; letter-spacing: 0.02em; padding: 6px 0 0; vertical-align: bottom }\ntd.tagline { font-size: 10.5pt; color: #${PALETTE.muted}; padding: 2px 0 8px; vertical-align: top }\ntd.rule { border-bottom: 3px solid #${PALETTE.navy}; padding: 0 }\ntd.statLabel { background: #${PALETTE.ground}; border-top: 2px solid #${PALETTE.navy}; text-align: center; font-size: 8.5pt; color: #${PALETTE.muted}; padding: 8px 0 0 }\ntd.statValue { background: #${PALETTE.ground}; text-align: center; font-size: 17pt; font-weight: 800; color: #${PALETTE.navy}; padding: 0 0 8px }\ntd.statLabel + td.statLabel, td.statValue + td.statValue { border-left: 8px solid #fff }\ntd.section { font-size: 12pt; font-weight: 800; color: #${PALETTE.navy}; border-bottom: 2px solid #${PALETTE.navy}; padding: 0 0 4px; vertical-align: bottom }\ntd.summary { background: #${PALETTE.ground}; border-left: 4px solid #${PALETTE.navy}; font-size: 10.5pt; padding: 10px 14px }\ntd.label { background: #${PALETTE.ground}; color: #${PALETTE.navy}; font-weight: 700; text-align: center; border: 1px solid #${PALETTE.line} }\ntd.skills { border: 1px solid #${PALETTE.line}; padding: 6px 8px }\n.tag { display: inline-block; margin: 2px 4px 2px 0; padding: 2px 9px; border-radius: 10px; background: #${PALETTE.navy}; color: #fff; font-size: 8.5pt; font-weight: 600 }\ntd.head { background: #${PALETTE.navy}; color: #fff; font-weight: 700; text-align: center; border: 1px solid #${PALETTE.navy}; font-size: 9pt }\ntd.value { border: 1px solid #${PALETTE.line} }\ntr.zebra td.value { background: #${PALETTE.zebra} }\ntd.none { border: 1px solid #${PALETTE.line}; color: #${PALETTE.muted}; text-align: center }\ntr { break-inside: avoid }\n`\n\n/** On a screen, the pages as sheets of A4 on a grey desk - the preview reads as the PDF will (ADR-0169 decision 9). */\nexport const PROFILE_SCREEN_CSS = `\n@media screen {\n html { background: #e9edf1 }\n body { padding: 24px 0 }\n .sheet { box-sizing: border-box; width: 210mm; min-height: 297mm; margin: 0 auto 24px; padding: 14mm 12mm; background: #fff;\n box-shadow: 0 1px 3px rgba(23, 36, 44, .12), 0 8px 24px rgba(23, 36, 44, .08) }\n}\n`\n\n/** The preview: the same pages as one HTML document, to be shown in a frame. */\nexport function profilePreviewHtml(people: ProfilePerson[], words: ProfileSheetWords, company: string, today = new Date()): string {\n return `<!doctype html><html><head><meta charset=\"utf-8\"><style>${PROFILE_CSS}${PROFILE_SCREEN_CSS}</style></head><body>${profileHtml(people, words, company, today)}</body></html>`\n}\n\n/** The document as one HTML page set: the roster first when there are several, then a page a person. */\nexport function profileHtml(people: ProfilePerson[], words: ProfileSheetWords, company: string, today = new Date()): string {\n const sheets = [\n ...(people.length > 1 ? [rosterLayout(people, words, company, today)] : []),\n ...people.map(person => profileSheetLayout(person, words, company, today))\n ]\n return sheets.map(lines => `<section class=\"sheet\">${layoutHtml(lines)}</section>`).join('')\n}\n\n/** The foot of every printed PDF page: the employer and the document at the left, the page of how many at the right. */\nexport function profileFooterHtml(company: string, words: ProfileSheetWords): string {\n /* The footer is a Handlebars template in the pdf module - a brace in the name must not open one. */\n const left = escapeHtml([company, words.title].filter(Boolean).join(' · ')).replace(/[{}]/g, c => (c === '{' ? '&#123;' : '&#125;'))\n return (\n `<div style=\"width:100%;padding:0 12mm;display:flex;justify-content:space-between;font-family:'Malgun Gothic','Apple SD Gothic Neo','Noto Sans KR',sans-serif;` +\n `font-size:7.5pt;color:#${PALETTE.muted}\"><span>${left}</span><span><span class=\"pageNumber\"></span> / <span class=\"totalPages\"></span></span></div>`\n )\n}\n\n/** 「인력프로필-20261027.xlsx」 - the word in the reader's language, the day the profiles were taken. */\nexport function profileFileName(word: string, extension: 'xlsx' | 'pdf' | 'html', 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())}.${extension}`\n}\n\n/** A word as a document heads with it - its first letter capital where the language has capitals (「proyectos」 → 「Proyectos」). */\nexport function lead(word: string): string {\n return word ? word.charAt(0).toLocaleUpperCase() + word.slice(1) : word\n}\n\n/** The document's words from the server's translator - the reader's language, each as a heading begins. */\nexport function profileWordsOf(translate: (key: string, options?: any) => string): ProfileSheetWords {\n const t = (key: string, options?: any) => lead(translate(key, options))\n const p = (name: string) => t(`label.employee-profile-${name}`)\n return {\n title: t('text.employee-profile-doc-title'),\n rosterTitle: t('text.employee-profile-doc-roster'),\n expertise: t('text.employee-profile-doc-expertise'),\n name: t('field.name'),\n department: t('label.department'),\n jobPosition: t('label.job-position'),\n hiredOn: t('label.hired-on'),\n careerLength: p('career-length'),\n projectsDone: t('text.employee-profile-doc-projects-done'),\n certificatesHeld: t('text.employee-profile-doc-certificates-held'),\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 ongoing: t('text.employee-profile-doc-ongoing'),\n none: t('text.employee-profile-doc-none'),\n writtenOn: t('text.employee-profile-doc-written-on'),\n number: t('text.employee-profile-doc-number'),\n years: (years, months) => t('text.employee-profile-doc-years', { years, months }),\n projectCount: n => t('text.employee-profile-doc-project-count', { n }),\n certificateCount: n => t('text.employee-profile-doc-certificate-count', { n }),\n peopleCount: n => t('text.employee-profile-doc-people', { n }),\n averageCareer: t('text.employee-profile-doc-average-career')\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import { EmployeeProfile } from './employee-profile';
2
+ import { EmployeeProfileInput } from './employee-profile-type';
3
+ export declare class EmployeeProfileMutation {
4
+ saveMyEmployeeProfile(profile: EmployeeProfileInput, context: ResolverContext): Promise<EmployeeProfile>;
5
+ addToMyEmployeeProfile(profile: EmployeeProfileInput, context: ResolverContext): Promise<EmployeeProfile>;
6
+ }