@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,10 @@
1
+ import type { EntryField, EntryIssue, EntryRow } from './entry-target-shape';
2
+ export declare const PROFILE_ENTRY_SECTIONS: readonly ["summary", "skill", "education", "certificate", "career", "project"];
3
+ export type ProfileEntrySection = (typeof PROFILE_ENTRY_SECTIONS)[number];
4
+ export declare const PROFILE_ENTRY_FIELDS: EntryField[];
5
+ /** The rows of one section as what `addToMyEmployeeProfile` takes. */
6
+ export declare function profileAdditionOf(rows: EntryRow[]): Record<string, unknown>;
7
+ /** A row without what its section cannot go without is kept out; a second summary is too - the profile has one. */
8
+ export declare function profileEntryIssues(rows: EntryRow[]): EntryIssue[][];
9
+ /** The card's line for a section entered - 「프로젝트 3」. */
10
+ export declare function profileAppliedLabel(rows: EntryRow[]): string;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROFILE_ENTRY_FIELDS = exports.PROFILE_ENTRY_SECTIONS = void 0;
4
+ exports.profileAdditionOf = profileAdditionOf;
5
+ exports.profileEntryIssues = profileEntryIssues;
6
+ exports.profileAppliedLabel = profileAppliedLabel;
7
+ exports.PROFILE_ENTRY_SECTIONS = ['summary', 'skill', 'education', 'certificate', 'career', 'project'];
8
+ /** The field a row of each section cannot go without. */
9
+ const NEEDED = {
10
+ summary: 'text',
11
+ skill: 'text',
12
+ education: 'school',
13
+ certificate: 'name',
14
+ career: 'company',
15
+ project: 'name'
16
+ };
17
+ /** What each section takes into the profile's document, and under which list. */
18
+ const TAKEN = {
19
+ education: { list: 'educations', fields: ['school', 'major', 'degree', 'startedOn', 'endedOn'] },
20
+ certificate: { list: 'certificates', fields: ['name', 'issuer', 'acquiredOn'] },
21
+ career: { list: 'careers', fields: ['company', 'department', 'position', 'startedOn', 'endedOn', 'duties'] },
22
+ project: { list: 'projects', fields: ['name', 'client', 'startedOn', 'endedOn', 'role', 'duties', 'technologies'] }
23
+ };
24
+ const label = (name) => `label.employee-profile-${name}`;
25
+ const month = 'YYYY-MM as the résumé gives it (2023-03); leave it out when the résumé gives no month';
26
+ exports.PROFILE_ENTRY_FIELDS = [
27
+ {
28
+ name: 'section',
29
+ type: 'enum',
30
+ required: true,
31
+ values: [...exports.PROFILE_ENTRY_SECTIONS],
32
+ valueKeyPrefix: 'text.organization-entry-profile-section-',
33
+ labelKey: 'label.organization-entry-profile-section',
34
+ description: 'which part of the profile the row is: summary (one row, a few sentences), skill (one row a skill), education, certificate, career (a job before this company), project'
35
+ },
36
+ { name: 'text', type: 'text', labelKey: 'label.organization-entry-profile-text', description: 'summary: the few sentences about the person, in their words; skill: one skill, a word or two (TypeScript, PLC, PMP)' },
37
+ { name: 'school', type: 'string', labelKey: label('school'), description: 'education: the school' },
38
+ { name: 'major', type: 'string', labelKey: label('major'), description: 'education: the major' },
39
+ { name: 'degree', type: 'string', labelKey: label('degree'), description: 'education: the degree - 학사, 석사, 박사, 고졸' },
40
+ { name: 'name', type: 'string', labelKey: 'label.organization-entry-profile-name', description: 'certificate: its name; project: the project name' },
41
+ { name: 'issuer', type: 'string', labelKey: label('issuer'), description: 'certificate: who issued it' },
42
+ { name: 'acquiredOn', type: 'string', labelKey: label('acquired-on'), description: 'certificate: the day it was acquired, YYYY-MM-DD, or YYYY-MM when only the month is given' },
43
+ { name: 'company', type: 'string', labelKey: label('company'), description: 'career: the company' },
44
+ { name: 'department', type: 'string', labelKey: 'label.department', description: 'career: the department' },
45
+ { name: 'position', type: 'string', labelKey: label('position'), description: 'career: the position held' },
46
+ { name: 'client', type: 'string', labelKey: label('client'), description: 'project: the client it was done for' },
47
+ { name: 'role', type: 'string', labelKey: label('role'), description: 'project: the role - PM, PL, 설계, 개발' },
48
+ { name: 'technologies', type: 'string', labelKey: label('technologies'), description: 'project: the technologies used, comma separated' },
49
+ { name: 'duties', type: 'text', labelKey: label('duties'), description: 'career or project: what the person did' },
50
+ { name: 'startedOn', type: 'string', labelKey: label('started-on'), description: `education, career or project: when it began - ${month}` },
51
+ { name: 'endedOn', type: 'string', labelKey: label('ended-on'), description: `education, career or project: when it ended - ${month}; leave it out when it is still going on` }
52
+ ];
53
+ const text = (value) => (typeof value === 'string' ? value.trim() : value === undefined || value === null ? '' : String(value).trim());
54
+ /** The rows of one section as what `addToMyEmployeeProfile` takes. */
55
+ function profileAdditionOf(rows) {
56
+ const addition = {};
57
+ for (const row of rows) {
58
+ const section = row.values.section;
59
+ if (section === 'summary')
60
+ addition.summary = text(row.values.text);
61
+ else if (section === 'skill')
62
+ addition.skills = [...(addition.skills ?? []), text(row.values.text)];
63
+ else if (TAKEN[section]) {
64
+ const { list, fields } = TAKEN[section];
65
+ addition[list] = [...(addition[list] ?? []), Object.fromEntries(fields.map(field => [field, text(row.values[field])]))];
66
+ }
67
+ }
68
+ return addition;
69
+ }
70
+ /** A row without what its section cannot go without is kept out; a second summary is too - the profile has one. */
71
+ function profileEntryIssues(rows) {
72
+ const firstSummary = rows.find(row => row.values.section === 'summary');
73
+ return rows.map(row => {
74
+ const section = row.values.section;
75
+ const needed = NEEDED[section];
76
+ const issues = [];
77
+ if (needed && !text(row.values[needed]))
78
+ issues.push({ field: needed, code: 'required', blocking: true });
79
+ if (section === 'summary' && row !== firstSummary)
80
+ issues.push({ field: 'section', code: 'one-summary', messageKey: 'text.organization-entry-profile-one-summary', blocking: true });
81
+ return issues;
82
+ });
83
+ }
84
+ /** The card's line for a section entered - 「프로젝트 3」. */
85
+ function profileAppliedLabel(rows) {
86
+ return `${String(rows[0]?.values.section ?? '')} ${rows.length}`;
87
+ }
88
+ //# sourceMappingURL=employee-profile-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"employee-profile-entry.js","sourceRoot":"","sources":["../../server/ai-entry/employee-profile-entry.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The person's own career profile as rows the assistant proposes from a résumé they attached or what they said\n * (ADR-0169 decision 10). A profile is one document of sections whose rows differ in shape, so a row names its\n * section and carries that section's fields; the rows of a section go in by one call of `addToMyEmployeeProfile`.\n * Pure - the declaration (organization-entry-targets.ts) only wires these.\n */\nimport type { EntryField, EntryIssue, EntryRow } from './entry-target-shape'\n\nexport const PROFILE_ENTRY_SECTIONS = ['summary', 'skill', 'education', 'certificate', 'career', 'project'] as const\n\nexport type ProfileEntrySection = (typeof PROFILE_ENTRY_SECTIONS)[number]\n\n/** The field a row of each section cannot go without. */\nconst NEEDED: Record<ProfileEntrySection, string> = {\n summary: 'text',\n skill: 'text',\n education: 'school',\n certificate: 'name',\n career: 'company',\n project: 'name'\n}\n\n/** What each section takes into the profile's document, and under which list. */\nconst TAKEN: Record<Exclude<ProfileEntrySection, 'summary' | 'skill'>, { list: string; fields: string[] }> = {\n education: { list: 'educations', fields: ['school', 'major', 'degree', 'startedOn', 'endedOn'] },\n certificate: { list: 'certificates', fields: ['name', 'issuer', 'acquiredOn'] },\n career: { list: 'careers', fields: ['company', 'department', 'position', 'startedOn', 'endedOn', 'duties'] },\n project: { list: 'projects', fields: ['name', 'client', 'startedOn', 'endedOn', 'role', 'duties', 'technologies'] }\n}\n\nconst label = (name: string) => `label.employee-profile-${name}`\nconst month = 'YYYY-MM as the résumé gives it (2023-03); leave it out when the résumé gives no month'\n\nexport const PROFILE_ENTRY_FIELDS: EntryField[] = [\n {\n name: 'section',\n type: 'enum',\n required: true,\n values: [...PROFILE_ENTRY_SECTIONS],\n valueKeyPrefix: 'text.organization-entry-profile-section-',\n labelKey: 'label.organization-entry-profile-section',\n description: 'which part of the profile the row is: summary (one row, a few sentences), skill (one row a skill), education, certificate, career (a job before this company), project'\n },\n { name: 'text', type: 'text', labelKey: 'label.organization-entry-profile-text', description: 'summary: the few sentences about the person, in their words; skill: one skill, a word or two (TypeScript, PLC, PMP)' },\n { name: 'school', type: 'string', labelKey: label('school'), description: 'education: the school' },\n { name: 'major', type: 'string', labelKey: label('major'), description: 'education: the major' },\n { name: 'degree', type: 'string', labelKey: label('degree'), description: 'education: the degree - 학사, 석사, 박사, 고졸' },\n { name: 'name', type: 'string', labelKey: 'label.organization-entry-profile-name', description: 'certificate: its name; project: the project name' },\n { name: 'issuer', type: 'string', labelKey: label('issuer'), description: 'certificate: who issued it' },\n { name: 'acquiredOn', type: 'string', labelKey: label('acquired-on'), description: 'certificate: the day it was acquired, YYYY-MM-DD, or YYYY-MM when only the month is given' },\n { name: 'company', type: 'string', labelKey: label('company'), description: 'career: the company' },\n { name: 'department', type: 'string', labelKey: 'label.department', description: 'career: the department' },\n { name: 'position', type: 'string', labelKey: label('position'), description: 'career: the position held' },\n { name: 'client', type: 'string', labelKey: label('client'), description: 'project: the client it was done for' },\n { name: 'role', type: 'string', labelKey: label('role'), description: 'project: the role - PM, PL, 설계, 개발' },\n { name: 'technologies', type: 'string', labelKey: label('technologies'), description: 'project: the technologies used, comma separated' },\n { name: 'duties', type: 'text', labelKey: label('duties'), description: 'career or project: what the person did' },\n { name: 'startedOn', type: 'string', labelKey: label('started-on'), description: `education, career or project: when it began - ${month}` },\n { name: 'endedOn', type: 'string', labelKey: label('ended-on'), description: `education, career or project: when it ended - ${month}; leave it out when it is still going on` }\n]\n\nconst text = (value: unknown) => (typeof value === 'string' ? value.trim() : value === undefined || value === null ? '' : String(value).trim())\n\n/** The rows of one section as what `addToMyEmployeeProfile` takes. */\nexport function profileAdditionOf(rows: EntryRow[]): Record<string, unknown> {\n const addition: Record<string, unknown> = {}\n for (const row of rows) {\n const section = row.values.section as ProfileEntrySection\n if (section === 'summary') addition.summary = text(row.values.text)\n else if (section === 'skill') addition.skills = [...((addition.skills as string[]) ?? []), text(row.values.text)]\n else if (TAKEN[section]) {\n const { list, fields } = TAKEN[section]\n addition[list] = [...((addition[list] as unknown[]) ?? []), Object.fromEntries(fields.map(field => [field, text(row.values[field])]))]\n }\n }\n return addition\n}\n\n/** A row without what its section cannot go without is kept out; a second summary is too - the profile has one. */\nexport function profileEntryIssues(rows: EntryRow[]): EntryIssue[][] {\n const firstSummary = rows.find(row => row.values.section === 'summary')\n return rows.map(row => {\n const section = row.values.section as ProfileEntrySection\n const needed = NEEDED[section]\n const issues: EntryIssue[] = []\n if (needed && !text(row.values[needed])) issues.push({ field: needed, code: 'required', blocking: true })\n if (section === 'summary' && row !== firstSummary) issues.push({ field: 'section', code: 'one-summary', messageKey: 'text.organization-entry-profile-one-summary', blocking: true })\n return issues\n })\n}\n\n/** The card's line for a section entered - 「프로젝트 3」. */\nexport function profileAppliedLabel(rows: EntryRow[]): string {\n return `${String(rows[0]?.values.section ?? '')} ${rows.length}`\n}\n"]}
@@ -69,11 +69,21 @@ export interface EntryTargetDeclaration {
69
69
  description: string;
70
70
  fields: EntryField[];
71
71
  mutation: string;
72
- variables(row: EntryRow): Record<string, unknown>;
72
+ /** The mutation's variables for one row. Absent when the target enters rows by group. */
73
+ variables?(row: EntryRow): Record<string, unknown>;
73
74
  appliedOf?(data: any, row: EntryRow): {
74
75
  id?: string;
75
76
  label?: string;
76
77
  } | undefined;
78
+ /** Rows sharing the value of `by` are one call of the mutation with `variables(rows)`. */
79
+ group?: {
80
+ by: string;
81
+ variables(rows: EntryRow[]): Record<string, unknown>;
82
+ appliedOf?(data: any, rows: EntryRow[]): {
83
+ id?: string;
84
+ label?: string;
85
+ } | undefined;
86
+ };
77
87
  maxRows?: number;
78
88
  check?(rows: EntryRow[], context: {
79
89
  state: any;
@@ -1 +1 @@
1
- {"version":3,"file":"entry-target-shape.js","sourceRoot":"","sources":["../../server/ai-entry/entry-target-shape.ts"],"names":[],"mappings":"","sourcesContent":["/*\n * The shape of an AI 입력 (data entry) target - the same shape as `EntryTarget` in\n * @things-factory/ai-assistant (server/service/data-entry/entry-target.ts).\n *\n * Kept here as a structural copy so that this package declares its targets as plain data and does not\n * depend on the assistant at run time. An application that runs the assistant passes these declarations\n * to `registerEntryTarget`; the assistant validates them at boot.\n */\nexport type EntryFieldType = 'string' | 'text' | 'number' | 'integer' | 'date' | 'datetime' | 'boolean' | 'enum' | 'reference'\n\nexport interface EntryReference {\n query: string\n variables(text: string): Record<string, unknown>\n recordsOf(data: any, text: string): { id: string; label?: string }[]\n}\n\nexport interface EntryField {\n name: string\n type: EntryFieldType\n required?: boolean\n values?: string[]\n description?: string\n labelKey?: string\n total?: boolean\n valueKeyPrefix?: string\n reference?: EntryReference\n}\n\nexport interface EntryIssue {\n field?: string\n code: string\n messageKey?: string\n params?: Record<string, unknown>\n blocking: boolean\n}\n\nexport interface EntryRow {\n index: number\n values: Record<string, unknown>\n sources: unknown[]\n issues: EntryIssue[]\n}\n\nexport type EntryRecord = { id: string } & Record<string, unknown>\n\nexport interface EntryUpdate {\n read: { query: string; variables(ids: string[]): Record<string, unknown>; recordsOf(data: any, ids: string[]): EntryRecord[] }\n find?: {\n query: string\n variables(criteria: Record<string, unknown>): Record<string, unknown>\n recordsOf(data: any, criteria: Record<string, unknown>): EntryRecord[]\n criteria: EntryField[]\n }\n mutation: string\n variables(change: { id: string; record: EntryRecord; values: Record<string, unknown> }): Record<string, unknown>\n labelOf?(record: EntryRecord): string\n fields?: string[]\n /** Read-only checks of the changes, asked when the card is made and again when it is pressed - one issue list per row. */\n check?(rows: { id: string; record?: EntryRecord; values: Record<string, unknown> }[], context: { state: any }): EntryIssue[][] | Promise<EntryIssue[][]>\n}\n\nexport interface EntryTargetDeclaration {\n key: string\n labelKey: string\n description: string\n fields: EntryField[]\n mutation: string\n variables(row: EntryRow): Record<string, unknown>\n appliedOf?(data: any, row: EntryRow): { id?: string; label?: string } | undefined\n maxRows?: number\n check?(rows: EntryRow[], context: { state: any }): EntryIssue[][] | Promise<EntryIssue[][]>\n update?: EntryUpdate\n}\n"]}
1
+ {"version":3,"file":"entry-target-shape.js","sourceRoot":"","sources":["../../server/ai-entry/entry-target-shape.ts"],"names":[],"mappings":"","sourcesContent":["/*\n * The shape of an AI 입력 (data entry) target - the same shape as `EntryTarget` in\n * @things-factory/ai-assistant (server/service/data-entry/entry-target.ts).\n *\n * Kept here as a structural copy so that this package declares its targets as plain data and does not\n * depend on the assistant at run time. An application that runs the assistant passes these declarations\n * to `registerEntryTarget`; the assistant validates them at boot.\n */\nexport type EntryFieldType = 'string' | 'text' | 'number' | 'integer' | 'date' | 'datetime' | 'boolean' | 'enum' | 'reference'\n\nexport interface EntryReference {\n query: string\n variables(text: string): Record<string, unknown>\n recordsOf(data: any, text: string): { id: string; label?: string }[]\n}\n\nexport interface EntryField {\n name: string\n type: EntryFieldType\n required?: boolean\n values?: string[]\n description?: string\n labelKey?: string\n total?: boolean\n valueKeyPrefix?: string\n reference?: EntryReference\n}\n\nexport interface EntryIssue {\n field?: string\n code: string\n messageKey?: string\n params?: Record<string, unknown>\n blocking: boolean\n}\n\nexport interface EntryRow {\n index: number\n values: Record<string, unknown>\n sources: unknown[]\n issues: EntryIssue[]\n}\n\nexport type EntryRecord = { id: string } & Record<string, unknown>\n\nexport interface EntryUpdate {\n read: { query: string; variables(ids: string[]): Record<string, unknown>; recordsOf(data: any, ids: string[]): EntryRecord[] }\n find?: {\n query: string\n variables(criteria: Record<string, unknown>): Record<string, unknown>\n recordsOf(data: any, criteria: Record<string, unknown>): EntryRecord[]\n criteria: EntryField[]\n }\n mutation: string\n variables(change: { id: string; record: EntryRecord; values: Record<string, unknown> }): Record<string, unknown>\n labelOf?(record: EntryRecord): string\n fields?: string[]\n /** Read-only checks of the changes, asked when the card is made and again when it is pressed - one issue list per row. */\n check?(rows: { id: string; record?: EntryRecord; values: Record<string, unknown> }[], context: { state: any }): EntryIssue[][] | Promise<EntryIssue[][]>\n}\n\nexport interface EntryTargetDeclaration {\n key: string\n labelKey: string\n description: string\n fields: EntryField[]\n mutation: string\n /** The mutation's variables for one row. Absent when the target enters rows by group. */\n variables?(row: EntryRow): Record<string, unknown>\n appliedOf?(data: any, row: EntryRow): { id?: string; label?: string } | undefined\n /** Rows sharing the value of `by` are one call of the mutation with `variables(rows)`. */\n group?: {\n by: string\n variables(rows: EntryRow[]): Record<string, unknown>\n appliedOf?(data: any, rows: EntryRow[]): { id?: string; label?: string } | undefined\n }\n maxRows?: number\n check?(rows: EntryRow[], context: { state: any }): EntryIssue[][] | Promise<EntryIssue[][]>\n update?: EntryUpdate\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import type { EntryTargetDeclaration } from './entry-target-shape';
2
2
  export declare const ORGANIZATION_DEPARTMENT = "organization.department";
3
3
  export declare const ORGANIZATION_EMPLOYEE = "organization.employee";
4
+ export declare const ORGANIZATION_EMPLOYEE_PROFILE = "organization.employee-profile";
4
5
  export declare function departmentEntryTarget(): EntryTargetDeclaration;
5
6
  export declare const EMPLOYEE_FORBIDDEN_FIELDS: string[];
6
7
  export declare function employeeEntryTarget(): EntryTargetDeclaration;
@@ -10,3 +11,10 @@ export declare function employeeEntryTarget(): EntryTargetDeclaration;
10
11
  * above `EMPLOYEE_FORBIDDEN_FIELDS`), so registering this list never brings it along.
11
12
  */
12
13
  export declare function organizationEntryTargets(): EntryTargetDeclaration[];
14
+ /**
15
+ * The person's own career profile, from a résumé or a profile document they attach (ADR-0169 decision 10): the
16
+ * assistant proposes its parts as rows, the card shows them against the file, and pressing adds them to the person's
17
+ * own profile through `addToMyEmployeeProfile` - one call a section, nothing kept taken away. Only one's own: the door
18
+ * finds the profile from the signed-in account and takes no person.
19
+ */
20
+ export declare function employeeProfileEntryTarget(): EntryTargetDeclaration;
@@ -33,13 +33,16 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.EMPLOYEE_FORBIDDEN_FIELDS = exports.ORGANIZATION_EMPLOYEE = exports.ORGANIZATION_DEPARTMENT = void 0;
36
+ exports.EMPLOYEE_FORBIDDEN_FIELDS = exports.ORGANIZATION_EMPLOYEE_PROFILE = exports.ORGANIZATION_EMPLOYEE = exports.ORGANIZATION_DEPARTMENT = void 0;
37
37
  exports.departmentEntryTarget = departmentEntryTarget;
38
38
  exports.employeeEntryTarget = employeeEntryTarget;
39
39
  exports.organizationEntryTargets = organizationEntryTargets;
40
+ exports.employeeProfileEntryTarget = employeeProfileEntryTarget;
41
+ const employee_profile_entry_1 = require("./employee-profile-entry");
40
42
  const organization_entry_rules_1 = require("./organization-entry-rules");
41
43
  exports.ORGANIZATION_DEPARTMENT = 'organization.department';
42
44
  exports.ORGANIZATION_EMPLOYEE = 'organization.employee';
45
+ exports.ORGANIZATION_EMPLOYEE_PROFILE = 'organization.employee-profile';
43
46
  /** Departments of the person's domain whose name or 관리번호 holds the text - `search` filters are OR'd by the list builder. */
44
47
  const DEPARTMENTS_BY_TEXT = 'query ($filters: [Filter!], $pagination: Pagination) { departments(filters: $filters, pagination: $pagination) { items { id controlNo name } } }';
45
48
  const DEPARTMENTS = 'query ($filters: [Filter!], $pagination: Pagination) { departments(filters: $filters, pagination: $pagination) { items { id controlNo name description active extension parent { id controlNo name } } } }';
@@ -224,4 +227,26 @@ function employeeEntryTarget() {
224
227
  function organizationEntryTargets() {
225
228
  return [departmentEntryTarget()];
226
229
  }
230
+ /**
231
+ * The person's own career profile, from a résumé or a profile document they attach (ADR-0169 decision 10): the
232
+ * assistant proposes its parts as rows, the card shows them against the file, and pressing adds them to the person's
233
+ * own profile through `addToMyEmployeeProfile` - one call a section, nothing kept taken away. Only one's own: the door
234
+ * finds the profile from the signed-in account and takes no person.
235
+ */
236
+ function employeeProfileEntryTarget() {
237
+ return {
238
+ key: exports.ORGANIZATION_EMPLOYEE_PROFILE,
239
+ labelKey: 'label.organization-entry-employee-profile',
240
+ description: "one part of the person's OWN career profile (내 프로필), read from a résumé or profile they attached or from what they said: the summary, one skill, one school, one certificate, one job before this company, or one project. Never another person's.",
241
+ mutation: 'mutation ($profile: EmployeeProfileInput!) { addToMyEmployeeProfile(profile: $profile) { id } }',
242
+ fields: employee_profile_entry_1.PROFILE_ENTRY_FIELDS,
243
+ maxRows: 150,
244
+ group: {
245
+ by: 'section',
246
+ variables: rows => ({ profile: (0, employee_profile_entry_1.profileAdditionOf)(rows) }),
247
+ appliedOf: (data, rows) => ({ id: data?.addToMyEmployeeProfile?.id, label: (0, employee_profile_entry_1.profileAppliedLabel)(rows) })
248
+ },
249
+ check: rows => (0, employee_profile_entry_1.profileEntryIssues)(rows)
250
+ };
251
+ }
227
252
  //# sourceMappingURL=organization-entry-targets.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"organization-entry-targets.js","sourceRoot":"","sources":["../../server/ai-entry/organization-entry-targets.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * AI 입력 targets of the organization - what an application may hand its assistant so that departments\n * (and, only by an explicit product decision, employees) are entered from material\n * (operato-application/design/ai/data-entry.md).\n *\n * Plain declarations: this package does not depend on @things-factory/ai-assistant. An application that runs\n * the assistant passes them to `registerEntryTarget` and names their keys in its modes. Every row is entered\n * through the organization's own mutation, run as the person - the same `@privilege` and checks as the screens.\n *\n * The checks read through the repository of the request's domain only, and import it when they run, so that\n * reading these declarations stays light.\n */\nimport type { EntryField, EntryRow, EntryTargetDeclaration } from './entry-target-shape'\nimport {\n changedParentId,\n codeAndName,\n departmentChangeIssues,\n departmentIssues,\n departmentsNamed,\n employeeIssues,\n inputOf,\n matching,\n parentVerdictOf,\n type ParentVerdict\n} from './organization-entry-rules'\n\nexport const ORGANIZATION_DEPARTMENT = 'organization.department'\nexport const ORGANIZATION_EMPLOYEE = 'organization.employee'\n\n/** Departments of the person's domain whose name or 관리번호 holds the text - `search` filters are OR'd by the list builder. */\nconst DEPARTMENTS_BY_TEXT =\n 'query ($filters: [Filter!], $pagination: Pagination) { departments(filters: $filters, pagination: $pagination) { items { id controlNo name } } }'\n\nconst DEPARTMENTS =\n 'query ($filters: [Filter!], $pagination: Pagination) { departments(filters: $filters, pagination: $pagination) { items { id controlNo name description active extension parent { id controlNo name } } } }'\n\n/** A department the model names in words, found as the person through `departments` - the id is what the mutation takes. */\nconst departmentReference = {\n query: DEPARTMENTS_BY_TEXT,\n variables: (said: string) => ({\n filters: [\n { name: 'name', operator: 'search', value: said },\n { name: 'controlNo', operator: 'search', value: said }\n ],\n pagination: { page: 1, limit: 200 }\n }),\n recordsOf: (data: any, said: string) => departmentsNamed(data?.departments?.items ?? [], said)\n}\n\nconst DEPARTMENT_FIELDS: EntryField[] = [\n { name: 'controlNo', type: 'string', required: true, labelKey: 'field.control-no', description: \"the department's code (관리번호) as the material writes it\" },\n { name: 'name', type: 'string', required: true, labelKey: 'field.name', description: 'the department name' },\n { name: 'description', type: 'string', labelKey: 'field.description' },\n { name: 'active', type: 'boolean', labelKey: 'field.active', description: 'false only when the material says the department is closed or not in use' },\n { name: 'extension', type: 'string', labelKey: 'field.extension', description: \"the department's phone extension (내선 번호)\" },\n {\n name: 'parent',\n type: 'reference',\n labelKey: 'field.parent-department',\n description: 'the department it belongs under, by its name or 관리번호. Leave it out for a top-level department.',\n reference: departmentReference\n }\n]\n\n/* DepartmentPatch has no `extension` - the update changes only what the patch can carry. */\nconst DEPARTMENT_UPDATE_FIELDS = ['controlNo', 'name', 'description', 'active', 'parent']\n\n/*\n * Never taken: `manager` (a person, picked on the screen) and `picture` (a file upload).\n */\nconst DEPARTMENT_CREATE_FIELDS = DEPARTMENT_FIELDS.map(field => field.name)\n\nexport function departmentEntryTarget(): EntryTargetDeclaration {\n return {\n key: ORGANIZATION_DEPARTMENT,\n labelKey: 'label.organization-entry-department',\n description:\n 'one department of the organization: its code (관리번호), its name, a description, its phone extension, whether it is active, and the department it belongs under.',\n mutation: 'mutation ($department: NewDepartment!) { createDepartment(department: $department) { id controlNo name } }',\n fields: DEPARTMENT_FIELDS,\n maxRows: 200,\n variables: (row: EntryRow) => ({ department: inputOf(row.values, DEPARTMENT_CREATE_FIELDS, ['parent']) }),\n appliedOf: (data, row) => ({ id: data?.createDepartment?.id, label: codeAndName(data?.createDepartment ?? row.values) }),\n\n check: async (rows, { state }) => {\n const domainId = state?.domain?.id\n const names = [...new Set(rows.map(row => row.values.name).filter((v): v is string => typeof v === 'string' && !!v.trim()).map(v => v.trim()))]\n const codes = [...new Set(rows.map(row => row.values.controlNo).filter((v): v is string => typeof v === 'string' && !!v.trim()).map(v => v.trim()))]\n if (!domainId || (!names.length && !codes.length)) return departmentIssues(rows, [])\n const [{ getRepository }, { Department }, { In }] = await Promise.all([import('@things-factory/shell'), import('../service/department/department'), import('typeorm')])\n const existing = await getRepository(Department).find({\n where: [\n ...(names.length ? [{ domain: { id: domainId }, name: In(names) }] : []),\n ...(codes.length ? [{ domain: { id: domainId }, controlNo: In(codes) }] : [])\n ] as any,\n select: ['id', 'name', 'controlNo'] as any\n })\n return departmentIssues(rows, existing)\n },\n\n update: {\n read: {\n query: DEPARTMENTS,\n variables: ids => ({ filters: [{ name: 'id', operator: 'in', value: ids }], pagination: { page: 1, limit: ids.length } }),\n recordsOf: data => data?.departments?.items ?? []\n },\n find: {\n query: DEPARTMENTS,\n variables: criteria => ({\n filters: ['name', 'controlNo'].filter(name => typeof criteria?.[name] === 'string' && criteria[name]).map(name => ({ name, operator: 'search', value: criteria[name] })),\n pagination: { page: 1, limit: 200 }\n }),\n recordsOf: (data, criteria) => matching(data?.departments?.items ?? [], criteria),\n criteria: [\n { name: 'name', type: 'string', description: 'a piece of the department name' },\n { name: 'controlNo', type: 'string', description: \"a piece of the department's 관리번호\" }\n ]\n },\n mutation: 'mutation ($id: String!, $patch: DepartmentPatch!) { updateDepartment(id: $id, patch: $patch) { id } }',\n variables: ({ id, values }) => ({ id, patch: inputOf(values, DEPARTMENT_UPDATE_FIELDS, ['parent']) }),\n labelOf: record => codeAndName(record),\n fields: DEPARTMENT_UPDATE_FIELDS,\n\n /*\n * A new name or 관리번호 another department has, and a parent the department's own rule refuses - asked through\n * `checkedParent` itself, which only reads: it walks up from the parent within the domain and throws.\n */\n check: async (rows, { state }) => {\n const domain = state?.domain\n if (!domain?.id) return departmentChangeIssues(rows, [])\n const given = (name: string) => [...new Set(rows.filter(row => name in row.values).map(row => row.values[name]).filter((v): v is string => typeof v === 'string' && !!v.trim()).map(v => v.trim()))]\n const [names, codes] = [given('name'), given('controlNo')]\n const [{ getRepository }, { Department }, { checkedParent }, { In }] = await Promise.all([\n import('@things-factory/shell'),\n import('../service/department/department'),\n import('../service/department/department-parent'),\n import('typeorm')\n ])\n const repository = getRepository(Department)\n const existing =\n names.length || codes.length\n ? await repository.find({\n where: [\n ...(names.length ? [{ domain: { id: domain.id }, name: In(names) }] : []),\n ...(codes.length ? [{ domain: { id: domain.id }, controlNo: In(codes) }] : [])\n ] as any,\n select: ['id', 'name', 'controlNo'] as any\n })\n : []\n const parents: ParentVerdict[] = []\n for (const row of rows) {\n const parentId = changedParentId(row.values)\n if (!parentId) {\n parents.push(undefined)\n continue\n }\n try {\n await checkedParent(row.id, { parent: { id: parentId } }, repository, domain)\n parents.push(undefined)\n } catch (error: any) {\n const verdict = parentVerdictOf(error)\n if (!verdict) throw error\n parents.push(verdict)\n }\n }\n return departmentChangeIssues(rows, existing as any, parents)\n }\n }\n }\n}\n\n/*\n * ── 직원 (employee) ──────────────────────────────────────────────────────────────────────────────────\n *\n * PERSONAL DATA. A row of this target is a person's name, position and hire day, and proposing it sends that\n * material to an external model. It is declared here so the shape is settled, and **no application opens it**:\n * a mode may name `organization.employee` only by an explicit product decision.\n *\n * Kept to the fields a roster needs. Never taken: user, bankAccount, bankName, emergencyContact,\n * emergencyContactPhone, contact, photo, supervisor. No update this round.\n *\n * The department is passed as an id the assistant resolved through the domain-scoped `departments` query, and\n * createEmployee looks it up again inside the caller's domain.\n */\nexport const EMPLOYEE_FORBIDDEN_FIELDS = ['user', 'bankAccount', 'bankName', 'emergencyContact', 'emergencyContactPhone', 'contact', 'photo', 'supervisor']\n\nconst EMPLOYEE_FIELDS: EntryField[] = [\n { name: 'controlNo', type: 'string', required: true, labelKey: 'field.control-no', description: 'the employee number (사번) as the material writes it' },\n { name: 'name', type: 'string', required: true, labelKey: 'field.name', description: \"the person's name\" },\n { name: 'jobPosition', type: 'string', labelKey: 'label.job-position', description: 'the rank (직급) - 사원, 대리, 과장...' },\n { name: 'jobResponsibility', type: 'string', labelKey: 'label.job-responsibility', description: 'the role (직책) - 팀장, 파트장...' },\n { name: 'type', type: 'enum', values: ['FULLTIME', 'PARTTIME', 'TEMPORARY'], labelKey: 'field.type', valueKeyPrefix: 'text.employee-type-' },\n { name: 'hiredOn', type: 'date', labelKey: 'field.hired-on', description: 'the day the person was hired' },\n { name: 'department', type: 'reference', labelKey: 'field.department', description: 'the department, by its name or 관리번호', reference: departmentReference }\n]\n\nexport function employeeEntryTarget(): EntryTargetDeclaration {\n return {\n key: ORGANIZATION_EMPLOYEE,\n labelKey: 'label.organization-entry-employee',\n description: 'one employee: the employee number (사번), the name, the rank and the role, the kind of employment, the day hired, and the department.',\n mutation: 'mutation ($employee: NewEmployee!) { createEmployee(employee: $employee) { id controlNo name } }',\n fields: EMPLOYEE_FIELDS,\n maxRows: 200,\n variables: (row: EntryRow) => ({ employee: inputOf(row.values, EMPLOYEE_FIELDS.map(field => field.name), ['department']) }),\n appliedOf: (data, row) => ({ id: data?.createEmployee?.id, label: codeAndName(data?.createEmployee ?? row.values) }),\n check: async (rows, { state }) => {\n const domainId = state?.domain?.id\n const codes = [...new Set(rows.map(row => row.values.controlNo).filter((v): v is string => typeof v === 'string' && !!v.trim()).map(v => v.trim()))]\n if (!domainId || !codes.length) return employeeIssues(rows, [])\n const [{ getRepository }, { Employee }, { In }] = await Promise.all([import('@things-factory/shell'), import('../service/employee/employee'), import('typeorm')])\n const existing = await getRepository(Employee).find({ where: { domain: { id: domainId }, controlNo: In(codes) } as any, select: ['id', 'controlNo'] as any })\n return employeeIssues(rows, existing)\n }\n }\n}\n\n/**\n * The organization's AI 입력 targets an application may register as a whole - the department. The employee is\n * left out on purpose: it is taken only by name, `employeeEntryTarget()`, after a product decision (see the note\n * above `EMPLOYEE_FORBIDDEN_FIELDS`), so registering this list never brings it along.\n */\nexport function organizationEntryTargets(): EntryTargetDeclaration[] {\n return [departmentEntryTarget()]\n}\n"]}
1
+ {"version":3,"file":"organization-entry-targets.js","sourceRoot":"","sources":["../../server/ai-entry/organization-entry-targets.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * AI 입력 targets of the organization - what an application may hand its assistant so that departments\n * (and, only by an explicit product decision, employees) are entered from material\n * (operato-application/design/ai/data-entry.md).\n *\n * Plain declarations: this package does not depend on @things-factory/ai-assistant. An application that runs\n * the assistant passes them to `registerEntryTarget` and names their keys in its modes. Every row is entered\n * through the organization's own mutation, run as the person - the same `@privilege` and checks as the screens.\n *\n * The checks read through the repository of the request's domain only, and import it when they run, so that\n * reading these declarations stays light.\n */\nimport type { EntryField, EntryRow, EntryTargetDeclaration } from './entry-target-shape'\nimport { profileAdditionOf, profileAppliedLabel, profileEntryIssues, PROFILE_ENTRY_FIELDS } from './employee-profile-entry'\nimport {\n changedParentId,\n codeAndName,\n departmentChangeIssues,\n departmentIssues,\n departmentsNamed,\n employeeIssues,\n inputOf,\n matching,\n parentVerdictOf,\n type ParentVerdict\n} from './organization-entry-rules'\n\nexport const ORGANIZATION_DEPARTMENT = 'organization.department'\nexport const ORGANIZATION_EMPLOYEE = 'organization.employee'\nexport const ORGANIZATION_EMPLOYEE_PROFILE = 'organization.employee-profile'\n\n/** Departments of the person's domain whose name or 관리번호 holds the text - `search` filters are OR'd by the list builder. */\nconst DEPARTMENTS_BY_TEXT =\n 'query ($filters: [Filter!], $pagination: Pagination) { departments(filters: $filters, pagination: $pagination) { items { id controlNo name } } }'\n\nconst DEPARTMENTS =\n 'query ($filters: [Filter!], $pagination: Pagination) { departments(filters: $filters, pagination: $pagination) { items { id controlNo name description active extension parent { id controlNo name } } } }'\n\n/** A department the model names in words, found as the person through `departments` - the id is what the mutation takes. */\nconst departmentReference = {\n query: DEPARTMENTS_BY_TEXT,\n variables: (said: string) => ({\n filters: [\n { name: 'name', operator: 'search', value: said },\n { name: 'controlNo', operator: 'search', value: said }\n ],\n pagination: { page: 1, limit: 200 }\n }),\n recordsOf: (data: any, said: string) => departmentsNamed(data?.departments?.items ?? [], said)\n}\n\nconst DEPARTMENT_FIELDS: EntryField[] = [\n { name: 'controlNo', type: 'string', required: true, labelKey: 'field.control-no', description: \"the department's code (관리번호) as the material writes it\" },\n { name: 'name', type: 'string', required: true, labelKey: 'field.name', description: 'the department name' },\n { name: 'description', type: 'string', labelKey: 'field.description' },\n { name: 'active', type: 'boolean', labelKey: 'field.active', description: 'false only when the material says the department is closed or not in use' },\n { name: 'extension', type: 'string', labelKey: 'field.extension', description: \"the department's phone extension (내선 번호)\" },\n {\n name: 'parent',\n type: 'reference',\n labelKey: 'field.parent-department',\n description: 'the department it belongs under, by its name or 관리번호. Leave it out for a top-level department.',\n reference: departmentReference\n }\n]\n\n/* DepartmentPatch has no `extension` - the update changes only what the patch can carry. */\nconst DEPARTMENT_UPDATE_FIELDS = ['controlNo', 'name', 'description', 'active', 'parent']\n\n/*\n * Never taken: `manager` (a person, picked on the screen) and `picture` (a file upload).\n */\nconst DEPARTMENT_CREATE_FIELDS = DEPARTMENT_FIELDS.map(field => field.name)\n\nexport function departmentEntryTarget(): EntryTargetDeclaration {\n return {\n key: ORGANIZATION_DEPARTMENT,\n labelKey: 'label.organization-entry-department',\n description:\n 'one department of the organization: its code (관리번호), its name, a description, its phone extension, whether it is active, and the department it belongs under.',\n mutation: 'mutation ($department: NewDepartment!) { createDepartment(department: $department) { id controlNo name } }',\n fields: DEPARTMENT_FIELDS,\n maxRows: 200,\n variables: (row: EntryRow) => ({ department: inputOf(row.values, DEPARTMENT_CREATE_FIELDS, ['parent']) }),\n appliedOf: (data, row) => ({ id: data?.createDepartment?.id, label: codeAndName(data?.createDepartment ?? row.values) }),\n\n check: async (rows, { state }) => {\n const domainId = state?.domain?.id\n const names = [...new Set(rows.map(row => row.values.name).filter((v): v is string => typeof v === 'string' && !!v.trim()).map(v => v.trim()))]\n const codes = [...new Set(rows.map(row => row.values.controlNo).filter((v): v is string => typeof v === 'string' && !!v.trim()).map(v => v.trim()))]\n if (!domainId || (!names.length && !codes.length)) return departmentIssues(rows, [])\n const [{ getRepository }, { Department }, { In }] = await Promise.all([import('@things-factory/shell'), import('../service/department/department'), import('typeorm')])\n const existing = await getRepository(Department).find({\n where: [\n ...(names.length ? [{ domain: { id: domainId }, name: In(names) }] : []),\n ...(codes.length ? [{ domain: { id: domainId }, controlNo: In(codes) }] : [])\n ] as any,\n select: ['id', 'name', 'controlNo'] as any\n })\n return departmentIssues(rows, existing)\n },\n\n update: {\n read: {\n query: DEPARTMENTS,\n variables: ids => ({ filters: [{ name: 'id', operator: 'in', value: ids }], pagination: { page: 1, limit: ids.length } }),\n recordsOf: data => data?.departments?.items ?? []\n },\n find: {\n query: DEPARTMENTS,\n variables: criteria => ({\n filters: ['name', 'controlNo'].filter(name => typeof criteria?.[name] === 'string' && criteria[name]).map(name => ({ name, operator: 'search', value: criteria[name] })),\n pagination: { page: 1, limit: 200 }\n }),\n recordsOf: (data, criteria) => matching(data?.departments?.items ?? [], criteria),\n criteria: [\n { name: 'name', type: 'string', description: 'a piece of the department name' },\n { name: 'controlNo', type: 'string', description: \"a piece of the department's 관리번호\" }\n ]\n },\n mutation: 'mutation ($id: String!, $patch: DepartmentPatch!) { updateDepartment(id: $id, patch: $patch) { id } }',\n variables: ({ id, values }) => ({ id, patch: inputOf(values, DEPARTMENT_UPDATE_FIELDS, ['parent']) }),\n labelOf: record => codeAndName(record),\n fields: DEPARTMENT_UPDATE_FIELDS,\n\n /*\n * A new name or 관리번호 another department has, and a parent the department's own rule refuses - asked through\n * `checkedParent` itself, which only reads: it walks up from the parent within the domain and throws.\n */\n check: async (rows, { state }) => {\n const domain = state?.domain\n if (!domain?.id) return departmentChangeIssues(rows, [])\n const given = (name: string) => [...new Set(rows.filter(row => name in row.values).map(row => row.values[name]).filter((v): v is string => typeof v === 'string' && !!v.trim()).map(v => v.trim()))]\n const [names, codes] = [given('name'), given('controlNo')]\n const [{ getRepository }, { Department }, { checkedParent }, { In }] = await Promise.all([\n import('@things-factory/shell'),\n import('../service/department/department'),\n import('../service/department/department-parent'),\n import('typeorm')\n ])\n const repository = getRepository(Department)\n const existing =\n names.length || codes.length\n ? await repository.find({\n where: [\n ...(names.length ? [{ domain: { id: domain.id }, name: In(names) }] : []),\n ...(codes.length ? [{ domain: { id: domain.id }, controlNo: In(codes) }] : [])\n ] as any,\n select: ['id', 'name', 'controlNo'] as any\n })\n : []\n const parents: ParentVerdict[] = []\n for (const row of rows) {\n const parentId = changedParentId(row.values)\n if (!parentId) {\n parents.push(undefined)\n continue\n }\n try {\n await checkedParent(row.id, { parent: { id: parentId } }, repository, domain)\n parents.push(undefined)\n } catch (error: any) {\n const verdict = parentVerdictOf(error)\n if (!verdict) throw error\n parents.push(verdict)\n }\n }\n return departmentChangeIssues(rows, existing as any, parents)\n }\n }\n }\n}\n\n/*\n * ── 직원 (employee) ──────────────────────────────────────────────────────────────────────────────────\n *\n * PERSONAL DATA. A row of this target is a person's name, position and hire day, and proposing it sends that\n * material to an external model. It is declared here so the shape is settled, and **no application opens it**:\n * a mode may name `organization.employee` only by an explicit product decision.\n *\n * Kept to the fields a roster needs. Never taken: user, bankAccount, bankName, emergencyContact,\n * emergencyContactPhone, contact, photo, supervisor. No update this round.\n *\n * The department is passed as an id the assistant resolved through the domain-scoped `departments` query, and\n * createEmployee looks it up again inside the caller's domain.\n */\nexport const EMPLOYEE_FORBIDDEN_FIELDS = ['user', 'bankAccount', 'bankName', 'emergencyContact', 'emergencyContactPhone', 'contact', 'photo', 'supervisor']\n\nconst EMPLOYEE_FIELDS: EntryField[] = [\n { name: 'controlNo', type: 'string', required: true, labelKey: 'field.control-no', description: 'the employee number (사번) as the material writes it' },\n { name: 'name', type: 'string', required: true, labelKey: 'field.name', description: \"the person's name\" },\n { name: 'jobPosition', type: 'string', labelKey: 'label.job-position', description: 'the rank (직급) - 사원, 대리, 과장...' },\n { name: 'jobResponsibility', type: 'string', labelKey: 'label.job-responsibility', description: 'the role (직책) - 팀장, 파트장...' },\n { name: 'type', type: 'enum', values: ['FULLTIME', 'PARTTIME', 'TEMPORARY'], labelKey: 'field.type', valueKeyPrefix: 'text.employee-type-' },\n { name: 'hiredOn', type: 'date', labelKey: 'field.hired-on', description: 'the day the person was hired' },\n { name: 'department', type: 'reference', labelKey: 'field.department', description: 'the department, by its name or 관리번호', reference: departmentReference }\n]\n\nexport function employeeEntryTarget(): EntryTargetDeclaration {\n return {\n key: ORGANIZATION_EMPLOYEE,\n labelKey: 'label.organization-entry-employee',\n description: 'one employee: the employee number (사번), the name, the rank and the role, the kind of employment, the day hired, and the department.',\n mutation: 'mutation ($employee: NewEmployee!) { createEmployee(employee: $employee) { id controlNo name } }',\n fields: EMPLOYEE_FIELDS,\n maxRows: 200,\n variables: (row: EntryRow) => ({ employee: inputOf(row.values, EMPLOYEE_FIELDS.map(field => field.name), ['department']) }),\n appliedOf: (data, row) => ({ id: data?.createEmployee?.id, label: codeAndName(data?.createEmployee ?? row.values) }),\n check: async (rows, { state }) => {\n const domainId = state?.domain?.id\n const codes = [...new Set(rows.map(row => row.values.controlNo).filter((v): v is string => typeof v === 'string' && !!v.trim()).map(v => v.trim()))]\n if (!domainId || !codes.length) return employeeIssues(rows, [])\n const [{ getRepository }, { Employee }, { In }] = await Promise.all([import('@things-factory/shell'), import('../service/employee/employee'), import('typeorm')])\n const existing = await getRepository(Employee).find({ where: { domain: { id: domainId }, controlNo: In(codes) } as any, select: ['id', 'controlNo'] as any })\n return employeeIssues(rows, existing)\n }\n }\n}\n\n/**\n * The organization's AI 입력 targets an application may register as a whole - the department. The employee is\n * left out on purpose: it is taken only by name, `employeeEntryTarget()`, after a product decision (see the note\n * above `EMPLOYEE_FORBIDDEN_FIELDS`), so registering this list never brings it along.\n */\nexport function organizationEntryTargets(): EntryTargetDeclaration[] {\n return [departmentEntryTarget()]\n}\n\n/**\n * The person's own career profile, from a résumé or a profile document they attach (ADR-0169 decision 10): the\n * assistant proposes its parts as rows, the card shows them against the file, and pressing adds them to the person's\n * own profile through `addToMyEmployeeProfile` - one call a section, nothing kept taken away. Only one's own: the door\n * finds the profile from the signed-in account and takes no person.\n */\nexport function employeeProfileEntryTarget(): EntryTargetDeclaration {\n return {\n key: ORGANIZATION_EMPLOYEE_PROFILE,\n labelKey: 'label.organization-entry-employee-profile',\n description:\n \"one part of the person's OWN career profile (내 프로필), read from a résumé or profile they attached or from what they said: the summary, one skill, one school, one certificate, one job before this company, or one project. Never another person's.\",\n mutation: 'mutation ($profile: EmployeeProfileInput!) { addToMyEmployeeProfile(profile: $profile) { id } }',\n fields: PROFILE_ENTRY_FIELDS,\n maxRows: 150,\n group: {\n by: 'section',\n variables: rows => ({ profile: profileAdditionOf(rows) }),\n appliedOf: (data, rows) => ({ id: data?.addToMyEmployeeProfile?.id, label: profileAppliedLabel(rows) })\n },\n check: rows => profileEntryIssues(rows)\n }\n}\n"]}
@@ -12,6 +12,9 @@ exports.ROLE_TASKS = void 0;
12
12
  exports.ROLE_TASKS = [
13
13
  { id: 'organization.people-reading', grants: [{ category: 'employee', axes: ['query'] }, { category: 'department', axes: ['query'] }] },
14
14
  { id: 'organization.people-keeping', grants: [{ category: 'employee', axes: ['query', 'mutation'] }, { category: 'department', axes: ['query', 'mutation'] }] },
15
- { id: 'organization.pay-accounts', grants: [{ category: 'employee-private', axes: ['query', 'mutation'] }] }
15
+ { id: 'organization.pay-accounts', grants: [{ category: 'employee-private', axes: ['query', 'mutation'] }] },
16
+ /* One's own profile, written by the employee; every active employee's, read to attach to a quotation (ADR-0169 decision 3). */
17
+ { id: 'organization.own-profile', grants: [{ category: 'employee-self', axes: ['query', 'mutation'] }] },
18
+ { id: 'organization.profiles-reading', grants: [{ category: 'employee-profile', axes: ['query'] }] }
16
19
  ];
17
20
  //# sourceMappingURL=role-tasks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"role-tasks.js","sourceRoot":"","sources":["../server/role-tasks.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAIU,QAAA,UAAU,GAAe;IACpC,EAAE,EAAE,EAAE,6BAA6B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;IACvI,EAAE,EAAE,EAAE,6BAA6B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE;IAC/J,EAAE,EAAE,EAAE,2BAA2B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE;CAC7G,CAAA","sourcesContent":["/*\n * The work this module names, for the seats applications build (ADR-0139 decision 11).\n *\n * A task holds whole privileges: each opens everything its doors open, and the words under `task.<id>` in this\n * module's translations say what that is. Seats are put together by applications (`RoleTemplate.tasks`).\n *\n * Data only - `server/index.ts` registers it - so an application's tests read it without starting the framework.\n */\n\nimport type { RoleTask } from '@things-factory/auth-base'\n\nexport const ROLE_TASKS: RoleTask[] = [\n { id: 'organization.people-reading', grants: [{ category: 'employee', axes: ['query'] }, { category: 'department', axes: ['query'] }] },\n { id: 'organization.people-keeping', grants: [{ category: 'employee', axes: ['query', 'mutation'] }, { category: 'department', axes: ['query', 'mutation'] }] },\n { id: 'organization.pay-accounts', grants: [{ category: 'employee-private', axes: ['query', 'mutation'] }] }\n]\n"]}
1
+ {"version":3,"file":"role-tasks.js","sourceRoot":"","sources":["../server/role-tasks.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAIU,QAAA,UAAU,GAAe;IACpC,EAAE,EAAE,EAAE,6BAA6B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;IACvI,EAAE,EAAE,EAAE,6BAA6B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE;IAC/J,EAAE,EAAE,EAAE,2BAA2B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE;IAC5G,+HAA+H;IAC/H,EAAE,EAAE,EAAE,0BAA0B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE;IACxG,EAAE,EAAE,EAAE,+BAA+B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;CACrG,CAAA","sourcesContent":["/*\n * The work this module names, for the seats applications build (ADR-0139 decision 11).\n *\n * A task holds whole privileges: each opens everything its doors open, and the words under `task.<id>` in this\n * module's translations say what that is. Seats are put together by applications (`RoleTemplate.tasks`).\n *\n * Data only - `server/index.ts` registers it - so an application's tests read it without starting the framework.\n */\n\nimport type { RoleTask } from '@things-factory/auth-base'\n\nexport const ROLE_TASKS: RoleTask[] = [\n { id: 'organization.people-reading', grants: [{ category: 'employee', axes: ['query'] }, { category: 'department', axes: ['query'] }] },\n { id: 'organization.people-keeping', grants: [{ category: 'employee', axes: ['query', 'mutation'] }, { category: 'department', axes: ['query', 'mutation'] }] },\n { id: 'organization.pay-accounts', grants: [{ category: 'employee-private', axes: ['query', 'mutation'] }] },\n /* One's own profile, written by the employee; every active employee's, read to attach to a quotation (ADR-0169 decision 3). */\n { id: 'organization.own-profile', grants: [{ category: 'employee-self', axes: ['query', 'mutation'] }] },\n { id: 'organization.profiles-reading', grants: [{ category: 'employee-profile', axes: ['query'] }] }\n]\n"]}
@@ -0,0 +1,122 @@
1
+ import { Workbook, Worksheet } from 'exceljs';
2
+ /** A row of any of the four tables - each has some of these. */
3
+ type Row = {
4
+ school?: string;
5
+ major?: string;
6
+ degree?: string;
7
+ name?: string;
8
+ issuer?: string;
9
+ acquiredOn?: string;
10
+ company?: string;
11
+ department?: string;
12
+ position?: string;
13
+ duties?: string;
14
+ client?: string;
15
+ role?: string;
16
+ technologies?: string;
17
+ startedOn?: string;
18
+ endedOn?: string;
19
+ };
20
+ export type ProfilePerson = {
21
+ name?: string | null;
22
+ departmentName?: string | null;
23
+ jobPosition?: string | null;
24
+ jobResponsibility?: string | null;
25
+ hiredOn?: string | null;
26
+ profile?: {
27
+ summary?: string | null;
28
+ skills?: string[] | null;
29
+ educations?: Row[] | null;
30
+ certificates?: Row[] | null;
31
+ careers?: Row[] | null;
32
+ projects?: Row[] | null;
33
+ } | null;
34
+ };
35
+ /** The words of the document, in the reader's language - handed in so this stays free of i18next. */
36
+ export type ProfileSheetWords = Record<'title' | 'rosterTitle' | 'expertise' | 'name' | 'department' | 'jobPosition' | 'hiredOn' | 'careerLength' | 'projectsDone' | 'certificatesHeld' | 'summary' | 'skills' | 'educations' | 'certificates' | 'careers' | 'projects' | 'school' | 'major' | 'degree' | 'period' | 'certificate' | 'issuer' | 'acquiredOn' | 'company' | 'position' | 'duties' | 'project' | 'client' | 'role' | 'technologies' | 'ongoing' | 'none' | 'writtenOn' | 'averageCareer' | 'number', string> & {
37
+ /** 「12년 3개월」 */
38
+ years: (years: number, months: number) => string;
39
+ /** 「8건」 */
40
+ projectCount: (count: number) => string;
41
+ /** 「3개」 */
42
+ certificateCount: (count: number) => string;
43
+ /** 「5명」 */
44
+ peopleCount: (count: number) => string;
45
+ };
46
+ /** What a cell is - the painter styles by it. */
47
+ export type CellRole = 'company' | 'kicker' | 'name' | 'tagline' | 'rule' | 'statLabel' | 'statValue' | 'section' | 'summary' | 'label' | 'skills' | 'head' | 'value' | 'none';
48
+ /** A cell of the six-column grid: `col` from 1, spanning `span` columns. */
49
+ export type LayoutCell = {
50
+ col: number;
51
+ span?: number;
52
+ value: string;
53
+ role: CellRole;
54
+ };
55
+ /** A line of the grid; `zebra` shades every other row of a table. */
56
+ export type LayoutLine = {
57
+ cells: LayoutCell[];
58
+ height?: number;
59
+ zebra?: boolean;
60
+ };
61
+ /** The grid's six columns, in characters - together about the width of A4 in portrait. */
62
+ export declare const COLUMN_WIDTHS: number[];
63
+ /** 「2023.03」 from 「2023-03」 or 「2023-03-02」 - the dotted form a staffing profile is read in. */
64
+ export declare function dotted(day?: string | null): string;
65
+ /** A period as one cell - an open end is still going on. */
66
+ export declare function periodOf(startedOn: string | undefined, endedOn: string | undefined, ongoing: string): string;
67
+ /**
68
+ * The months a person has worked: the careers before this company and the time here since the hire day, counted
69
+ * once where they overlap. A career with no end runs to `today`; one with no start is left out.
70
+ */
71
+ export declare function careerMonths(person: ProfilePerson, today?: Date): number;
72
+ export declare function careerLengthOf(person: ProfilePerson, words: ProfileSheetWords, today?: Date): string;
73
+ /** Where the person stands, in one line: department · position / responsibility · hired on. */
74
+ export declare function taglineOf(person: ProfilePerson, words: ProfileSheetWords): string;
75
+ /** One person's page, line by line. `company` is the employer's name; `today` the day it is written. */
76
+ export declare function profileSheetLayout(person: ProfilePerson, words: ProfileSheetWords, company: string, today?: Date): LayoutLine[];
77
+ /** How many people, and their experience on average - the team's weight in one line. */
78
+ export declare function rosterTaglineOf(people: ProfilePerson[], words: ProfileSheetWords, today?: Date): string;
79
+ /** The roster that opens a document of several people - one line a person, in the order picked. */
80
+ export declare function rosterLayout(people: ProfilePerson[], words: ProfileSheetWords, company: string, today?: Date): LayoutLine[];
81
+ /**
82
+ * A sheet name for each person. Excel takes at most 31 characters, none of \ / ? * [ ] :, and no two alike ignoring
83
+ * case - two people of one name become 「홍길동」 and 「홍길동 (2)」. `reserved` are names already in the book.
84
+ */
85
+ export declare function sheetNamesOf(names: (string | null | undefined)[], reserved?: string[]): string[];
86
+ /** The document's colours - a deep navy for what leads the eye, a pale blue-grey ground, quiet lines. */
87
+ export declare const PALETTE: {
88
+ navy: string;
89
+ ground: string;
90
+ zebra: string;
91
+ line: string;
92
+ ink: string;
93
+ muted: string;
94
+ };
95
+ /**
96
+ * A line's height in points, for as many text lines as its longest boxed cell wraps into - Excel does not grow a row
97
+ * for wrapped text written by a program. A full-width character is about two of the column's character units.
98
+ */
99
+ export declare function heightOf(line: LayoutLine): number | undefined;
100
+ /** Paint a layout on a sheet: values, spans merged, each cell of a span painted so a boxed cell is boxed all round. */
101
+ export declare function paintSheet(sheet: Worksheet, lines: LayoutLine[], footer?: string): void;
102
+ /** The workbook of the people given, in their order - opened by a roster when there are several. */
103
+ export declare function profileWorkbook(people: ProfilePerson[], words: ProfileSheetWords, company: string, today?: Date): Workbook;
104
+ /** The same layout as HTML: a table of the six columns a page, the roles as classes. */
105
+ export declare function layoutHtml(lines: LayoutLine[]): string;
106
+ /** How the roles look on paper - the workbook's paint in CSS. */
107
+ export declare const PROFILE_CSS: string;
108
+ /** On a screen, the pages as sheets of A4 on a grey desk - the preview reads as the PDF will (ADR-0169 decision 9). */
109
+ export declare 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";
110
+ /** The preview: the same pages as one HTML document, to be shown in a frame. */
111
+ export declare function profilePreviewHtml(people: ProfilePerson[], words: ProfileSheetWords, company: string, today?: Date): string;
112
+ /** The document as one HTML page set: the roster first when there are several, then a page a person. */
113
+ export declare function profileHtml(people: ProfilePerson[], words: ProfileSheetWords, company: string, today?: Date): string;
114
+ /** The foot of every printed PDF page: the employer and the document at the left, the page of how many at the right. */
115
+ export declare function profileFooterHtml(company: string, words: ProfileSheetWords): string;
116
+ /** 「인력프로필-20261027.xlsx」 - the word in the reader's language, the day the profiles were taken. */
117
+ export declare function profileFileName(word: string, extension: 'xlsx' | 'pdf' | 'html', day?: Date): string;
118
+ /** A word as a document heads with it - its first letter capital where the language has capitals (「proyectos」 → 「Proyectos」). */
119
+ export declare function lead(word: string): string;
120
+ /** The document's words from the server's translator - the reader's language, each as a heading begins. */
121
+ export declare function profileWordsOf(translate: (key: string, options?: any) => string): ProfileSheetWords;
122
+ export {};