@things-factory/organization 10.1.228 → 10.1.230

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 (33) hide show
  1. package/dist-client/component/department-selector.js +0 -7
  2. package/dist-client/component/department-selector.js.map +1 -1
  3. package/dist-client/pages/department/department-list-page.js +1 -1
  4. package/dist-client/pages/department/department-list-page.js.map +1 -1
  5. package/dist-client/tsconfig.tsbuildinfo +1 -1
  6. package/dist-server/role-tasks.js +2 -0
  7. package/dist-server/role-tasks.js.map +1 -1
  8. package/dist-server/service/department/department-query.d.ts +1 -0
  9. package/dist-server/service/department/department-query.js +26 -3
  10. package/dist-server/service/department/department-query.js.map +1 -1
  11. package/dist-server/service/department/department.js +3 -1
  12. package/dist-server/service/department/department.js.map +1 -1
  13. package/dist-server/service/employee-profile/employee-profile-document.d.ts +10 -4
  14. package/dist-server/service/employee-profile/employee-profile-document.js +84 -39
  15. package/dist-server/service/employee-profile/employee-profile-document.js.map +1 -1
  16. package/dist-server/service/employee-profile/employee-profile-mutation.js.map +1 -1
  17. package/dist-server/service/employee-profile/employee-profile-rules.d.ts +12 -0
  18. package/dist-server/service/employee-profile/employee-profile-rules.js +25 -0
  19. package/dist-server/service/employee-profile/employee-profile-rules.js.map +1 -1
  20. package/dist-server/service/employee-profile/employee-profile.d.ts +1 -0
  21. package/dist-server/service/employee-profile/employee-profile.js +14 -1
  22. package/dist-server/service/employee-profile/employee-profile.js.map +1 -1
  23. package/dist-server/tsconfig.tsbuildinfo +1 -1
  24. package/package.json +2 -2
  25. package/tests/department-open-fields.test.ts +72 -0
  26. package/tests/employee-profile-db.test.ts +14 -0
  27. package/tests/employee-profile-document.test.ts +66 -3
  28. package/translations/en.json +2 -0
  29. package/translations/es.json +2 -0
  30. package/translations/ja.json +2 -0
  31. package/translations/ko.json +2 -0
  32. package/translations/ms.json +2 -0
  33. package/translations/zh.json +2 -0
@@ -11,6 +11,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
11
11
  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
+ /* The departments alone, for every seat that picks or shows one; `manager` and `members` still ask `employee` (ADR-0139). */
15
+ { id: 'organization.departments-reading', grants: [{ category: 'department', axes: ['query'] }] },
14
16
  { id: 'organization.people-keeping', grants: [{ category: 'employee', axes: ['query', 'mutation'] }, { category: 'department', axes: ['query', 'mutation'] }] },
15
17
  { id: 'organization.pay-accounts', grants: [{ category: 'employee-private', axes: ['query', 'mutation'] }] },
16
18
  /* One's own profile, written by the employee; every active employee's, read to attach to a quotation (ADR-0169 decision 3). */
@@ -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;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"]}
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,6HAA6H;IAC7H,EAAE,EAAE,EAAE,kCAAkC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;IACjG,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 /* The departments alone, for every seat that picks or shows one; `manager` and `members` still ask `employee` (ADR-0139). */\n { id: 'organization.departments-reading', grants: [{ 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"]}
@@ -8,6 +8,7 @@ export declare class DepartmentQuery {
8
8
  department(id: string, context: ResolverContext): Promise<Department>;
9
9
  departments(params: ListParam, context: ResolverContext): Promise<DepartmentList>;
10
10
  manager(department: Department): Promise<Employee>;
11
+ members(department: Department): Promise<Employee[]>;
11
12
  parent(department: Department): Promise<Department>;
12
13
  children(department: Department, params: ListParam, context: ResolverContext): Promise<Department[]>;
13
14
  picture(department: Department): Promise<string | undefined>;
@@ -10,6 +10,16 @@ const auth_base_1 = require("@things-factory/auth-base");
10
10
  const department_1 = require("./department");
11
11
  const department_type_1 = require("./department-type");
12
12
  const employee_1 = require("../employee/employee");
13
+ /*
14
+ * Every seat reads departments, while `manager` and `members` ask `employee:query` (department.ts). A list is searched,
15
+ * filtered and sorted only by the fields it shows to everybody: filtering by a closed field would tell, by whether a
16
+ * department stays in the list, what that field holds (ADR-0139 §10).
17
+ */
18
+ const DEPARTMENT_LIST_RULES = {
19
+ searchables: ['name', 'description', 'controlNo'],
20
+ filterables: ['id', 'name', 'description', 'controlNo', 'active', 'extension', 'parent', 'createdAt', 'updatedAt'],
21
+ sortables: ['name', 'description', 'controlNo', 'active', 'extension', 'createdAt', 'updatedAt']
22
+ };
13
23
  let DepartmentQuery = class DepartmentQuery {
14
24
  async departmentRoots(params, context) {
15
25
  const { domain } = context.state;
@@ -17,7 +27,7 @@ let DepartmentQuery = class DepartmentQuery {
17
27
  domain,
18
28
  params,
19
29
  repository: await (0, shell_1.getRepository)(department_1.Department),
20
- searchables: ['name', 'description', 'controlNo']
30
+ ...DEPARTMENT_LIST_RULES
21
31
  }).andWhere({ domain: { id: domain.id }, parent: { id: (0, typeorm_1.IsNull)() } });
22
32
  const [items, total] = await queryBuilder.getManyAndCount();
23
33
  return { items, total };
@@ -34,7 +44,7 @@ let DepartmentQuery = class DepartmentQuery {
34
44
  domain,
35
45
  params,
36
46
  repository: await (0, shell_1.getRepository)(department_1.Department),
37
- searchables: ['name', 'description', 'controlNo']
47
+ ...DEPARTMENT_LIST_RULES
38
48
  });
39
49
  const [items, total] = await queryBuilder.getManyAndCount();
40
50
  return { items, total };
@@ -45,6 +55,12 @@ let DepartmentQuery = class DepartmentQuery {
45
55
  id: department.managerId
46
56
  })));
47
57
  }
58
+ async members(department) {
59
+ return await (0, shell_1.getRepository)(employee_1.Employee).find({
60
+ where: { domain: { id: department.domainId }, department: { id: department.id } },
61
+ order: { controlNo: 'ASC' }
62
+ });
63
+ }
48
64
  async parent(department) {
49
65
  return (department.parentId &&
50
66
  (await (0, shell_1.getRepository)(department_1.Department).findOneBy({
@@ -57,7 +73,7 @@ let DepartmentQuery = class DepartmentQuery {
57
73
  domain,
58
74
  params,
59
75
  repository: await (0, shell_1.getRepository)(department_1.Department),
60
- searchables: ['name', 'description', 'controlNo']
76
+ ...DEPARTMENT_LIST_RULES
61
77
  }).andWhere({
62
78
  parent: { id: department.id }
63
79
  });
@@ -119,6 +135,13 @@ tslib_1.__decorate([
119
135
  tslib_1.__metadata("design:paramtypes", [department_1.Department]),
120
136
  tslib_1.__metadata("design:returntype", Promise)
121
137
  ], DepartmentQuery.prototype, "manager", null);
138
+ tslib_1.__decorate([
139
+ (0, type_graphql_1.FieldResolver)(type => [employee_1.Employee], { nullable: true }),
140
+ tslib_1.__param(0, (0, type_graphql_1.Root)()),
141
+ tslib_1.__metadata("design:type", Function),
142
+ tslib_1.__metadata("design:paramtypes", [department_1.Department]),
143
+ tslib_1.__metadata("design:returntype", Promise)
144
+ ], DepartmentQuery.prototype, "members", null);
122
145
  tslib_1.__decorate([
123
146
  (0, type_graphql_1.FieldResolver)(type => department_1.Department),
124
147
  tslib_1.__param(0, (0, type_graphql_1.Root)()),
@@ -1 +1 @@
1
- {"version":3,"file":"department-query.js","sourceRoot":"","sources":["../../../server/service/department/department-query.ts"],"names":[],"mappings":";;;;AAAA,+CAA8F;AAC9F,qCAAgC;AAChC,qEAA4D;AAC5D,iDAAuG;AACvG,yDAAgD;AAChD,6CAAyC;AACzC,uDAAkD;AAClD,mDAA+C;AAGxC,IAAM,eAAe,GAArB,MAAM,eAAe;IAGpB,AAAN,KAAK,CAAC,eAAe,CACM,MAAiB,EACnC,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,YAAY,GAAG,IAAA,qCAA6B,EAAC;YACjD,MAAM;YACN,MAAM;YACN,UAAU,EAAE,MAAM,IAAA,qBAAa,EAAC,uBAAU,CAAC;YAC3C,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,WAAW,CAAC;SAClD,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,gBAAM,GAAE,EAAE,EAAE,CAAC,CAAA;QAEpE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,YAAY,CAAC,eAAe,EAAE,CAAA;QAE3D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACzB,CAAC;IAIK,AAAN,KAAK,CAAC,UAAU,CAAY,EAAU,EAAS,OAAwB;QACrE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,OAAO,MAAM,IAAA,qBAAa,EAAC,uBAAU,CAAC,CAAC,OAAO,CAAC;YAC7C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;SACzC,CAAC,CAAA;IACJ,CAAC;IAIK,AAAN,KAAK,CAAC,WAAW,CACU,MAAiB,EACnC,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,YAAY,GAAG,IAAA,qCAA6B,EAAC;YACjD,MAAM;YACN,MAAM;YACN,UAAU,EAAE,MAAM,IAAA,qBAAa,EAAC,uBAAU,CAAC;YAC3C,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,WAAW,CAAC;SAClD,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,YAAY,CAAC,eAAe,EAAE,CAAA;QAE3D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACzB,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAS,UAAsB;QAC1C,OAAO,CACL,UAAU,CAAC,SAAS;YACpB,CAAC,MAAM,IAAA,qBAAa,EAAC,mBAAQ,CAAC,CAAC,SAAS,CAAC;gBACvC,EAAE,EAAE,UAAU,CAAC,SAAS;aACzB,CAAC,CAAC,CACJ,CAAA;IACH,CAAC;IAGK,AAAN,KAAK,CAAC,MAAM,CAAS,UAAsB;QACzC,OAAO,CACL,UAAU,CAAC,QAAQ;YACnB,CAAC,MAAM,IAAA,qBAAa,EAAC,uBAAU,CAAC,CAAC,SAAS,CAAC;gBACzC,EAAE,EAAE,UAAU,CAAC,QAAQ;aACxB,CAAC,CAAC,CACJ,CAAA;IACH,CAAC;IAGK,AAAN,KAAK,CAAC,QAAQ,CACJ,UAAsB,EACL,MAAiB,EACnC,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,YAAY,GAAG,IAAA,qCAA6B,EAAC;YACjD,MAAM;YACN,MAAM;YACN,UAAU,EAAE,MAAM,IAAA,qBAAa,EAAC,uBAAU,CAAC;YAC3C,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,WAAW,CAAC;SAClD,CAAC,CAAC,QAAQ,CAAC;YACV,MAAM,EAAE,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE;SAC9B,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,YAAY,CAAC,eAAe,EAAE,CAAA;QAE3D,OAAO,KAAK,CAAA;IACd,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAS,UAAsB;QAC1C,MAAM,UAAU,GAAe,MAAM,IAAA,qBAAa,EAAC,4BAAU,CAAC,CAAC,OAAO,CAAC;YACrE,KAAK,EAAE;gBACL,MAAM,EAAE,EAAE,EAAE,EAAE,UAAU,CAAC,QAAQ,EAAE;gBACnC,OAAO,EAAE,uBAAU,CAAC,IAAI;gBACxB,KAAK,EAAE,UAAU,CAAC,EAAE;aACrB;SACF,CAAC,CAAA;QAEF,OAAO,UAAU,EAAE,QAAQ,CAAA;IAC7B,CAAC;IAGK,AAAN,KAAK,CAAC,MAAM,CAAS,UAAsB;QACzC,OAAO,MAAM,IAAA,qBAAa,EAAC,cAAM,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC3E,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAS,UAAsB;QAC1C,OAAO,MAAM,IAAA,qBAAa,EAAC,gBAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,SAAS,EAAE,CAAC,CAAA;IAC1E,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAS,UAAsB;QAC1C,OAAO,MAAM,IAAA,qBAAa,EAAC,gBAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,SAAS,EAAE,CAAC,CAAA;IAC1E,CAAC;CACF,CAAA;AAxHY,0CAAe;AAGpB;IAFL,IAAA,wBAAS,EAAC,kFAAkF,CAAC;IAC7F,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,gCAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;IAE9F,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IACvB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAD2B,iBAAS;;sDAe3C;AAIK;IAFL,IAAA,wBAAS,EAAC,kFAAkF,CAAC;IAC7F,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,uBAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;IACtE,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iDAM7C;AAIK;IAFL,IAAA,wBAAS,EAAC,kFAAkF,CAAC;IAC7F,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,gCAAc,EAAE,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;IAEhF,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IACvB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAD2B,iBAAS;;kDAe3C;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,mBAAQ,CAAC;IACjB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAa,uBAAU;;8CAO3C;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,uBAAU,CAAC;IACpB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAa,uBAAU;;6CAO1C;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,uBAAU,CAAC,CAAC;IAEjC,mBAAA,IAAA,mBAAI,GAAE,CAAA;IACN,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IACvB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAFc,uBAAU;QACG,iBAAS;;+CAiB3C;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC;IACf,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAa,uBAAU;;8CAU3C;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IAChB,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAa,uBAAU;;6CAE1C;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,CAAC;IACb,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAa,uBAAU;;8CAE3C;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,CAAC;IACb,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAa,uBAAU;;8CAE3C;0BAvHU,eAAe;IAD3B,IAAA,uBAAQ,EAAC,uBAAU,CAAC;GACR,eAAe,CAwH3B","sourcesContent":["import { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive } from 'type-graphql'\nimport { IsNull } from 'typeorm'\nimport { Attachment } from '@things-factory/attachment-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { Department } from './department'\nimport { DepartmentList } from './department-type'\nimport { Employee } from '../employee/employee'\n\n@Resolver(Department)\nexport class DepartmentQuery {\n @Directive('@privilege(category: \"department\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => DepartmentList, { nullable: true, description: 'To fetch a Root Departments' })\n async departmentRoots(\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<DepartmentList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params,\n repository: await getRepository(Department),\n searchables: ['name', 'description', 'controlNo']\n }).andWhere({ domain: { id: domain.id }, parent: { id: IsNull() } })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n @Directive('@privilege(category: \"department\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Department!, { nullable: true, description: 'To fetch a Department' })\n async department(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Department> {\n const { domain } = context.state\n\n return await getRepository(Department).findOne({\n where: { domain: { id: domain.id }, id }\n })\n }\n\n @Directive('@privilege(category: \"department\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => DepartmentList, { description: 'To fetch multiple Departments' })\n async departments(\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<DepartmentList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params,\n repository: await getRepository(Department),\n searchables: ['name', 'description', 'controlNo']\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n @FieldResolver(type => Employee)\n async manager(@Root() department: Department): Promise<Employee> {\n return (\n department.managerId &&\n (await getRepository(Employee).findOneBy({\n id: department.managerId\n }))\n )\n }\n\n @FieldResolver(type => Department)\n async parent(@Root() department: Department): Promise<Department> {\n return (\n department.parentId &&\n (await getRepository(Department).findOneBy({\n id: department.parentId\n }))\n )\n }\n\n @FieldResolver(type => [Department])\n async children(\n @Root() department: Department,\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<Department[]> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params,\n repository: await getRepository(Department),\n searchables: ['name', 'description', 'controlNo']\n }).andWhere({\n parent: { id: department.id }\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return items\n }\n\n @FieldResolver(type => String)\n async picture(@Root() department: Department): Promise<string | undefined> {\n const attachment: Attachment = await getRepository(Attachment).findOne({\n where: {\n domain: { id: department.domainId },\n refType: Department.name,\n refBy: department.id\n }\n })\n\n return attachment?.fullpath\n }\n\n @FieldResolver(type => Domain)\n async domain(@Root() department: Department): Promise<Domain> {\n return await getRepository(Domain).findOneBy({ id: department.domainId })\n }\n\n @FieldResolver(type => User)\n async updater(@Root() department: Department): Promise<User> {\n return await getRepository(User).findOneBy({ id: department.updaterId })\n }\n\n @FieldResolver(type => User)\n async creator(@Root() department: Department): Promise<User> {\n return await getRepository(User).findOneBy({ id: department.creatorId })\n }\n}\n"]}
1
+ {"version":3,"file":"department-query.js","sourceRoot":"","sources":["../../../server/service/department/department-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive } from 'type-graphql'\nimport { IsNull } from 'typeorm'\nimport { Attachment } from '@things-factory/attachment-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { Department } from './department'\nimport { DepartmentList } from './department-type'\nimport { Employee } from '../employee/employee'\n\n/*\n * Every seat reads departments, while `manager` and `members` ask `employee:query` (department.ts). A list is searched,\n * filtered and sorted only by the fields it shows to everybody: filtering by a closed field would tell, by whether a\n * department stays in the list, what that field holds (ADR-0139 §10).\n */\nconst DEPARTMENT_LIST_RULES = {\n searchables: ['name', 'description', 'controlNo'],\n filterables: ['id', 'name', 'description', 'controlNo', 'active', 'extension', 'parent', 'createdAt', 'updatedAt'],\n sortables: ['name', 'description', 'controlNo', 'active', 'extension', 'createdAt', 'updatedAt']\n}\n\n@Resolver(Department)\nexport class DepartmentQuery {\n @Directive('@privilege(category: \"department\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => DepartmentList, { nullable: true, description: 'To fetch a Root Departments' })\n async departmentRoots(\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<DepartmentList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params,\n repository: await getRepository(Department),\n ...DEPARTMENT_LIST_RULES\n }).andWhere({ domain: { id: domain.id }, parent: { id: IsNull() } })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n @Directive('@privilege(category: \"department\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Department!, { nullable: true, description: 'To fetch a Department' })\n async department(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Department> {\n const { domain } = context.state\n\n return await getRepository(Department).findOne({\n where: { domain: { id: domain.id }, id }\n })\n }\n\n @Directive('@privilege(category: \"department\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => DepartmentList, { description: 'To fetch multiple Departments' })\n async departments(\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<DepartmentList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params,\n repository: await getRepository(Department),\n ...DEPARTMENT_LIST_RULES\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n @FieldResolver(type => Employee)\n async manager(@Root() department: Department): Promise<Employee> {\n return (\n department.managerId &&\n (await getRepository(Employee).findOneBy({\n id: department.managerId\n }))\n )\n }\n\n @FieldResolver(type => [Employee], { nullable: true })\n async members(@Root() department: Department): Promise<Employee[]> {\n return await getRepository(Employee).find({\n where: { domain: { id: department.domainId }, department: { id: department.id } },\n order: { controlNo: 'ASC' }\n })\n }\n\n @FieldResolver(type => Department)\n async parent(@Root() department: Department): Promise<Department> {\n return (\n department.parentId &&\n (await getRepository(Department).findOneBy({\n id: department.parentId\n }))\n )\n }\n\n @FieldResolver(type => [Department])\n async children(\n @Root() department: Department,\n @Args(type => ListParam) params: ListParam,\n @Ctx() context: ResolverContext\n ): Promise<Department[]> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params,\n repository: await getRepository(Department),\n ...DEPARTMENT_LIST_RULES\n }).andWhere({\n parent: { id: department.id }\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return items\n }\n\n @FieldResolver(type => String)\n async picture(@Root() department: Department): Promise<string | undefined> {\n const attachment: Attachment = await getRepository(Attachment).findOne({\n where: {\n domain: { id: department.domainId },\n refType: Department.name,\n refBy: department.id\n }\n })\n\n return attachment?.fullpath\n }\n\n @FieldResolver(type => Domain)\n async domain(@Root() department: Department): Promise<Domain> {\n return await getRepository(Domain).findOneBy({ id: department.domainId })\n }\n\n @FieldResolver(type => User)\n async updater(@Root() department: Department): Promise<User> {\n return await getRepository(User).findOneBy({ id: department.updaterId })\n }\n\n @FieldResolver(type => User)\n async creator(@Root() department: Department): Promise<User> {\n return await getRepository(User).findOneBy({ id: department.creatorId })\n }\n}\n"]}
@@ -63,11 +63,13 @@ tslib_1.__decorate([
63
63
  ], Department.prototype, "children", void 0);
64
64
  tslib_1.__decorate([
65
65
  (0, typeorm_1.OneToMany)(type => employee_1.Employee, employee => employee.department),
66
- (0, type_graphql_1.Field)(type => [employee_1.Employee]),
66
+ (0, type_graphql_1.Directive)('@privilege(category: "employee", privilege: "query", domainOwnerGranted: true)'),
67
+ (0, type_graphql_1.Field)(type => [employee_1.Employee], { nullable: true }),
67
68
  tslib_1.__metadata("design:type", Array)
68
69
  ], Department.prototype, "members", void 0);
69
70
  tslib_1.__decorate([
70
71
  (0, typeorm_1.ManyToOne)(type => employee_1.Employee, manager => manager.manages, { nullable: true }),
72
+ (0, type_graphql_1.Directive)('@privilege(category: "employee", privilege: "query", domainOwnerGranted: true)'),
71
73
  (0, type_graphql_1.Field)(type => employee_1.Employee, { nullable: true }),
72
74
  tslib_1.__metadata("design:type", employee_1.Employee)
73
75
  ], Department.prototype, "manager", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"department.js","sourceRoot":"","sources":["../../../server/service/department/department.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CreateDateColumn,\n UpdateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n Column,\n RelationId,\n ManyToOne,\n OneToMany,\n PrimaryGeneratedColumn,\n VersionColumn\n} from 'typeorm'\nimport { ObjectType, Field, Int, ID, registerEnumType } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { Employee } from '../employee/employee'\n\n@Entity()\n@Index('ix_department_0', (department: Department) => [department.domain, department.name], {\n unique: true,\n where: '\"deleted_at\" IS NULL'\n})\n@Index('ix_department_1', (department: Department) => [department.domain, department.controlNo], {\n unique: true,\n where: '\"deleted_at\" IS NULL'\n})\n@ObjectType({ description: 'Entity for Department' })\nexport class Department {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @VersionColumn()\n @Field({ nullable: true })\n version?: number = 1\n\n @ManyToOne(type => Domain)\n @Field(type => Domain)\n domain?: Domain\n\n @RelationId((department: Department) => department.domain)\n domainId?: string\n\n @Column()\n @Field()\n controlNo: string\n\n @Column()\n @Field({ nullable: true })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n @ManyToOne(type => Department, department => department.children, { nullable: true })\n @Field({ nullable: true })\n parent?: Department\n\n @RelationId((department: Department) => department.parent)\n parentId?: string\n\n @OneToMany(type => Department, department => department.parent)\n @Field(type => [Department])\n children?: Department[]\n\n @OneToMany(type => Employee, employee => employee.department)\n @Field(type => [Employee])\n members?: Employee[]\n\n @ManyToOne(type => Employee, manager => manager.manages, { nullable: true })\n @Field(type => Employee, { nullable: true })\n manager?: Employee\n\n @RelationId((department: Department) => department.manager)\n managerId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n active?: boolean\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n extension?: string\n\n @Field(type => String, { nullable: true })\n picture?: string\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((department: Department) => department.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((department: Department) => department.updater)\n updaterId?: string\n}\n"]}
1
+ {"version":3,"file":"department.js","sourceRoot":"","sources":["../../../server/service/department/department.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CreateDateColumn,\n UpdateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n Column,\n RelationId,\n ManyToOne,\n OneToMany,\n PrimaryGeneratedColumn,\n VersionColumn\n} from 'typeorm'\nimport { ObjectType, Field, Int, ID, registerEnumType, Directive } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { Employee } from '../employee/employee'\n\n@Entity()\n@Index('ix_department_0', (department: Department) => [department.domain, department.name], {\n unique: true,\n where: '\"deleted_at\" IS NULL'\n})\n@Index('ix_department_1', (department: Department) => [department.domain, department.controlNo], {\n unique: true,\n where: '\"deleted_at\" IS NULL'\n})\n@ObjectType({ description: 'Entity for Department' })\nexport class Department {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @VersionColumn()\n @Field({ nullable: true })\n version?: number = 1\n\n @ManyToOne(type => Domain)\n @Field(type => Domain)\n domain?: Domain\n\n @RelationId((department: Department) => department.domain)\n domainId?: string\n\n @Column()\n @Field()\n controlNo: string\n\n @Column()\n @Field({ nullable: true })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n @ManyToOne(type => Department, department => department.children, { nullable: true })\n @Field({ nullable: true })\n parent?: Department\n\n @RelationId((department: Department) => department.parent)\n parentId?: string\n\n @OneToMany(type => Department, department => department.parent)\n @Field(type => [Department])\n children?: Department[]\n\n /*\n * Every seat reads the departments; the people in them stay the roster's (ADR-0139). These two lead to employee\n * records, so they ask what the employee list asks - refused alone, the rest of the department still comes back.\n * A field declared here takes these directives even where `DepartmentQuery` resolves it. Both are nullable: a\n * refused non-null field would null the whole list above it.\n */\n @OneToMany(type => Employee, employee => employee.department)\n @Directive('@privilege(category: \"employee\", privilege: \"query\", domainOwnerGranted: true)')\n @Field(type => [Employee], { nullable: true })\n members?: Employee[]\n\n @ManyToOne(type => Employee, manager => manager.manages, { nullable: true })\n @Directive('@privilege(category: \"employee\", privilege: \"query\", domainOwnerGranted: true)')\n @Field(type => Employee, { nullable: true })\n manager?: Employee\n\n @RelationId((department: Department) => department.manager)\n managerId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n active?: boolean\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n extension?: string\n\n @Field(type => String, { nullable: true })\n picture?: string\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((department: Department) => department.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((department: Department) => department.updater)\n updaterId?: string\n}\n"]}
@@ -45,10 +45,14 @@ export type ProfileSheetWords = Record<'title' | 'rosterTitle' | 'expertise' | '
45
45
  };
46
46
  /** What a cell is - the painter styles by it. */
47
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. */
48
+ /**
49
+ * A cell of the six-column grid: `col` from 1, spanning `span` columns and `rows` lines - the lines below leave its
50
+ * columns out. A cell over several lines keeps its text where the first line has it, at the top.
51
+ */
49
52
  export type LayoutCell = {
50
53
  col: number;
51
54
  span?: number;
55
+ rows?: number;
52
56
  value: string;
53
57
  role: CellRole;
54
58
  };
@@ -60,6 +64,8 @@ export type LayoutLine = {
60
64
  };
61
65
  /** The grid's six columns, in characters - together about the width of A4 in portrait. */
62
66
  export declare const COLUMN_WIDTHS: number[];
67
+ /** The roster's six columns: a narrow number, then who - its career spans the last two, its skills the whole line. */
68
+ export declare const ROSTER_WIDTHS: number[];
63
69
  /** 「2023.03」 from 「2023-03」 or 「2023-03-02」 - the dotted form a staffing profile is read in. */
64
70
  export declare function dotted(day?: string | null): string;
65
71
  /** A period as one cell - an open end is still going on. */
@@ -96,13 +102,13 @@ export declare const PALETTE: {
96
102
  * 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
103
  * for wrapped text written by a program. A full-width character is about two of the column's character units.
98
104
  */
99
- export declare function heightOf(line: LayoutLine): number | undefined;
105
+ export declare function heightOf(line: LayoutLine, widths?: number[]): number | undefined;
100
106
  /** 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;
107
+ export declare function paintSheet(sheet: Worksheet, lines: LayoutLine[], footer?: string, widths?: number[]): void;
102
108
  /** The workbook of the people given, in their order - opened by a roster when there are several. */
103
109
  export declare function profileWorkbook(people: ProfilePerson[], words: ProfileSheetWords, company: string, today?: Date): Workbook;
104
110
  /** 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;
111
+ export declare function layoutHtml(lines: LayoutLine[], widths?: number[]): string;
106
112
  /** How the roles look on paper - the workbook's paint in CSS. */
107
113
  export declare const PROFILE_CSS: string;
108
114
  /** On a screen, the pages as sheets of A4 on a grey desk - the preview reads as the PDF will (ADR-0169 decision 9). */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PROFILE_SCREEN_CSS = exports.PROFILE_CSS = exports.PALETTE = exports.COLUMN_WIDTHS = void 0;
3
+ exports.PROFILE_SCREEN_CSS = exports.PROFILE_CSS = exports.PALETTE = exports.ROSTER_WIDTHS = exports.COLUMN_WIDTHS = void 0;
4
4
  exports.dotted = dotted;
5
5
  exports.periodOf = periodOf;
6
6
  exports.careerMonths = careerMonths;
@@ -24,6 +24,8 @@ const exceljs_1 = require("exceljs");
24
24
  const employee_profile_rules_1 = require("./employee-profile-rules");
25
25
  /** The grid's six columns, in characters - together about the width of A4 in portrait. */
26
26
  exports.COLUMN_WIDTHS = [18, 20, 15, 12, 21, 18];
27
+ /** The roster's six columns: a narrow number, then who - its career spans the last two, its skills the whole line. */
28
+ exports.ROSTER_WIDTHS = [6, 24, 24, 18, 16, 16];
27
29
  /** 「2023.03」 from 「2023-03」 or 「2023-03-02」 - the dotted form a staffing profile is read in. */
28
30
  function dotted(day) {
29
31
  return (day ?? '').trim().replace(/-/g, '.');
@@ -130,14 +132,14 @@ function profileSheetLayout(person, words, company, today = new Date()) {
130
132
  { col: 2, span: 5, value: (profile.skills ?? []).join(', '), role: 'skills' }
131
133
  ]
132
134
  });
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 => [
135
+ 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]], (0, employee_profile_rules_1.newestFirst)(profile.projects), row => [periodOf(row.startedOn, row.endedOn, words.ongoing), row.name, row.client, row.role, row.duties, row.technologies]);
136
+ table(words.careers, [[1, 1, words.period], [2, 1, words.company], [3, 1, words.department], [4, 1, words.position], [5, 2, words.duties]], (0, employee_profile_rules_1.newestFirst)(profile.careers), row => [periodOf(row.startedOn, row.endedOn, words.ongoing), row.company, row.department, row.position, row.duties]);
137
+ table(words.certificates, [[1, 1, words.acquiredOn], [2, 3, words.certificate], [5, 2, words.issuer]], (0, employee_profile_rules_1.newestFirst)(profile.certificates), row => [
136
138
  dotted(row.acquiredOn),
137
139
  row.name,
138
140
  row.issuer
139
141
  ]);
140
- table(words.educations, [[1, 1, words.period], [2, 2, words.school], [4, 2, words.major], [6, 1, words.degree]], profile.educations, row => [
142
+ table(words.educations, [[1, 1, words.period], [2, 2, words.school], [4, 2, words.major], [6, 1, words.degree]], (0, employee_profile_rules_1.newestFirst)(profile.educations), row => [
141
143
  periodOf(row.startedOn, row.endedOn, words.ongoing),
142
144
  row.school,
143
145
  row.major,
@@ -157,19 +159,38 @@ function rosterLayout(people, words, company, today = new Date()) {
157
159
  ...headOf(company, `${words.writtenOn} ${dotted((0, employee_profile_rules_1.todayOf)(today))}`, words.rosterTitle, rosterTaglineOf(people, words, today)),
158
160
  { cells: [], height: 16 },
159
161
  {
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
162
  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
- }))
163
+ ...[words.number, words.name, words.department, words.jobPosition].map((value, index) => ({ col: index + 1, value, role: 'head' })),
164
+ { col: 5, span: 2, value: words.careerLength, role: 'head' }
165
+ ]
166
+ },
167
+ /*
168
+ * Two lines a person: who, then the skills under the name across the rest of the page - a list too long for one
169
+ * column of six. The number runs down both lines, so it marks where a person starts; a person with no skills
170
+ * written keeps one line.
171
+ */
172
+ ...people.flatMap((person, index) => {
173
+ const shade = index % 2 ? { zebra: true } : {};
174
+ const skills = (person.profile?.skills ?? []).join(', ');
175
+ return [
176
+ {
177
+ cells: [
178
+ { col: 1, rows: skills ? 2 : 1, value: String(index + 1), role: 'value' },
179
+ ...[person.name ?? '', person.departmentName ?? '', person.jobPosition ?? ''].map((value, column) => ({ col: column + 2, value, role: 'value' })),
180
+ { col: 5, span: 2, value: careerLengthOf(person, words, today), role: 'value' }
181
+ ],
182
+ ...shade
183
+ },
184
+ ...(skills
185
+ ? [
186
+ {
187
+ cells: [{ col: 2, span: 5, value: skills, role: 'value' }],
188
+ ...shade
189
+ }
190
+ ]
191
+ : [])
192
+ ];
193
+ })
173
194
  ];
174
195
  }
175
196
  /**
@@ -233,12 +254,12 @@ const PAINT = {
233
254
  * 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
255
  * for wrapped text written by a program. A full-width character is about two of the column's character units.
235
256
  */
236
- function heightOf(line) {
257
+ function heightOf(line, widths = exports.COLUMN_WIDTHS) {
237
258
  const boxed = line.cells.filter(cell => ['label', 'skills', 'summary', 'head', 'value', 'none'].includes(cell.role));
238
259
  if (!boxed.length)
239
260
  return undefined;
240
261
  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);
262
+ const width = widths.slice(cell.col - 1, cell.col - 1 + (cell.span ?? 1)).reduce((sum, one) => sum + one, 0) - (cell.role === 'summary' ? 4 : 1);
242
263
  return cell.value.split('\n').reduce((lines, text) => {
243
264
  const units = [...text].reduce((sum, char) => sum + (char.charCodeAt(0) > 0x2e7f ? 1.9 : 0.9), 0);
244
265
  return lines + Math.max(1, Math.ceil(units / Math.max(width, 1)));
@@ -248,21 +269,24 @@ function heightOf(line) {
248
269
  return boxed.some(cell => cell.role === 'summary') ? lines * 16 + 14 : lines * 15 + 7;
249
270
  }
250
271
  /** 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));
272
+ function paintSheet(sheet, lines, footer = '', widths = exports.COLUMN_WIDTHS) {
273
+ widths.forEach((width, index) => (sheet.getColumn(index + 1).width = width));
253
274
  lines.forEach((line, index) => {
254
275
  const row = sheet.getRow(index + 1);
255
- const height = Math.max(line.height ?? 0, heightOf(line) ?? 0);
276
+ const height = Math.max(line.height ?? 0, heightOf(line, widths) ?? 0);
256
277
  if (height)
257
278
  row.height = height;
258
279
  for (const cell of line.cells) {
259
280
  const span = cell.span ?? 1;
260
281
  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 });
282
+ const rows = cell.rows ?? 1;
283
+ const painted = rows > 1 ? { ...style, alignment: { ...style.alignment, vertical: 'top' } } : style;
284
+ for (let at = 0; at < rows; at++)
285
+ for (let col = cell.col; col < cell.col + span; col++)
286
+ Object.assign(sheet.getRow(index + 1 + at).getCell(col), { style: painted });
263
287
  row.getCell(cell.col).value = cell.value;
264
- if (span > 1)
265
- sheet.mergeCells(index + 1, cell.col, index + 1, cell.col + span - 1);
288
+ if (span > 1 || rows > 1)
289
+ sheet.mergeCells(index + 1, cell.col, index + rows, cell.col + span - 1);
266
290
  }
267
291
  });
268
292
  sheet.views = [{ showGridLines: false }];
@@ -285,7 +309,7 @@ function profileWorkbook(people, words, company, today = new Date()) {
285
309
  /* The roster's name follows Excel's rules as the people's do. */
286
310
  const roster = people.length > 1 ? sheetNamesOf([words.rosterTitle]) : [];
287
311
  if (roster.length)
288
- paintSheet(book.addWorksheet(roster[0]), rosterLayout(people, words, company, today), footer);
312
+ paintSheet(book.addWorksheet(roster[0]), rosterLayout(people, words, company, today), footer, exports.ROSTER_WIDTHS);
289
313
  const names = sheetNamesOf(people.map(person => person.name), roster);
290
314
  people.forEach((person, index) => paintSheet(book.addWorksheet(names[index]), profileSheetLayout(person, words, company, today), footer));
291
315
  return book;
@@ -303,24 +327,45 @@ function cellHtml(cell) {
303
327
  return escapeHtml(cell.value).replace(/\n/g, '<br>');
304
328
  }
305
329
  /** 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('');
330
+ function layoutHtml(lines, widths = exports.COLUMN_WIDTHS) {
331
+ const total = widths.reduce((sum, width) => sum + width, 0);
332
+ const columns = widths.map(width => `<col style="width:${((width / total) * 100).toFixed(2)}%">`).join('');
333
+ /* How many more lines each column is still held by a cell from a line above. */
334
+ const held = [0, 0, 0, 0, 0, 0, 0];
309
335
  const rows = lines.map(line => {
310
336
  const style = line.height ? ` style="height:${Math.round(line.height * 1.33)}px"` : '';
337
+ const covered = held.map(lines => lines > 0);
338
+ for (let col = 1; col <= 6; col++)
339
+ held[col] = Math.max(0, held[col] - 1);
311
340
  if (!line.cells.length)
312
341
  return `<tr class="gap"${style}><td colspan="6"></td></tr>`;
313
- let next = 1;
314
342
  const cells = [];
343
+ /* Empty columns between cells, as runs that leave out the columns a cell above still holds. */
344
+ const fill = (from, to) => {
345
+ for (let col = from; col < to;) {
346
+ if (covered[col]) {
347
+ col++;
348
+ continue;
349
+ }
350
+ let end = col;
351
+ while (end < to && !covered[end])
352
+ end++;
353
+ cells.push(`<td colspan="${end - col}"></td>`);
354
+ col = end;
355
+ }
356
+ };
357
+ let next = 1;
315
358
  for (const cell of line.cells) {
316
- if (cell.col > next)
317
- cells.push(`<td colspan="${cell.col - next}"></td>`);
359
+ fill(next, cell.col);
318
360
  const span = cell.span ?? 1;
319
- cells.push(`<td class="${cell.role}"${span > 1 ? ` colspan="${span}"` : ''}>${cellHtml(cell)}</td>`);
361
+ const rows = cell.rows ?? 1;
362
+ for (let col = cell.col; col < cell.col + span; col++)
363
+ held[col] = rows - 1;
364
+ const attributes = `${span > 1 ? ` colspan="${span}"` : ''}${rows > 1 ? ` rowspan="${rows}" style="vertical-align:top"` : ''}`;
365
+ cells.push(`<td class="${cell.role}"${attributes}>${cellHtml(cell)}</td>`);
320
366
  next = cell.col + span;
321
367
  }
322
- if (next <= 6)
323
- cells.push(`<td colspan="${7 - next}"></td>`);
368
+ fill(next, 7);
324
369
  return `<tr${line.zebra ? ' class="zebra"' : ''}${style}>${cells.join('')}</tr>`;
325
370
  });
326
371
  return `<table><colgroup>${columns}</colgroup>${rows.join('')}</table>`;
@@ -369,10 +414,10 @@ function profilePreviewHtml(people, words, company, today = new Date()) {
369
414
  /** The document as one HTML page set: the roster first when there are several, then a page a person. */
370
415
  function profileHtml(people, words, company, today = new Date()) {
371
416
  const sheets = [
372
- ...(people.length > 1 ? [rosterLayout(people, words, company, today)] : []),
373
- ...people.map(person => profileSheetLayout(person, words, company, today))
417
+ ...(people.length > 1 ? [[rosterLayout(people, words, company, today), exports.ROSTER_WIDTHS]] : []),
418
+ ...people.map(person => [profileSheetLayout(person, words, company, today), exports.COLUMN_WIDTHS])
374
419
  ];
375
- return sheets.map(lines => `<section class="sheet">${layoutHtml(lines)}</section>`).join('');
420
+ return sheets.map(([lines, widths]) => `<section class="sheet">${layoutHtml(lines, widths)}</section>`).join('');
376
421
  }
377
422
  /** The foot of every printed PDF page: the employer and the document at the left, the page of how many at the right. */
378
423
  function profileFooterHtml(company, words) {