@things-factory/compliance 10.1.256 → 10.1.257

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 (64) hide show
  1. package/dist-client/components/compliance-case-form.d.ts +11 -6
  2. package/dist-client/components/compliance-case-form.js +67 -88
  3. package/dist-client/components/compliance-case-form.js.map +1 -1
  4. package/dist-client/components/compliance-case-sheet.d.ts +14 -12
  5. package/dist-client/components/compliance-case-sheet.js +92 -133
  6. package/dist-client/components/compliance-case-sheet.js.map +1 -1
  7. package/dist-client/components/compliance-my-inventions.d.ts +3 -4
  8. package/dist-client/components/compliance-my-inventions.js +10 -30
  9. package/dist-client/components/compliance-my-inventions.js.map +1 -1
  10. package/dist-client/components/compliance-my-privacy-requests.d.ts +6 -6
  11. package/dist-client/components/compliance-my-privacy-requests.js +30 -66
  12. package/dist-client/components/compliance-my-privacy-requests.js.map +1 -1
  13. package/dist-client/components/compliance-my-reports.d.ts +3 -4
  14. package/dist-client/components/compliance-my-reports.js +19 -40
  15. package/dist-client/components/compliance-my-reports.js.map +1 -1
  16. package/dist-client/components/invention-report-form.d.ts +5 -4
  17. package/dist-client/components/invention-report-form.js +24 -45
  18. package/dist-client/components/invention-report-form.js.map +1 -1
  19. package/dist-client/components/my-trainings.d.ts +3 -5
  20. package/dist-client/components/my-trainings.js +20 -49
  21. package/dist-client/components/my-trainings.js.map +1 -1
  22. package/dist-client/components/seal-use-form.d.ts +9 -6
  23. package/dist-client/components/seal-use-form.js +60 -81
  24. package/dist-client/components/seal-use-form.js.map +1 -1
  25. package/dist-client/components/training-attendance-form.d.ts +6 -4
  26. package/dist-client/components/training-attendance-form.js +30 -50
  27. package/dist-client/components/training-attendance-form.js.map +1 -1
  28. package/dist-client/pages/regulation-detail.d.ts +15 -6
  29. package/dist-client/pages/regulation-detail.js +169 -142
  30. package/dist-client/pages/regulation-detail.js.map +1 -1
  31. package/dist-client/pages/regulations.d.ts +7 -5
  32. package/dist-client/pages/regulations.js +38 -56
  33. package/dist-client/pages/regulations.js.map +1 -1
  34. package/dist-client/regulation/regulation-guide.js +2 -1
  35. package/dist-client/regulation/regulation-guide.js.map +1 -1
  36. package/dist-client/tsconfig.tsbuildinfo +1 -1
  37. package/dist-client/words.js +2 -2
  38. package/dist-client/words.js.map +1 -1
  39. package/dist-server/service/compliance-case/compliance-case-query.js +1 -1
  40. package/dist-server/service/compliance-case/compliance-case-query.js.map +1 -1
  41. package/dist-server/service/compliance-case/compliance-case-rules.d.ts +1 -3
  42. package/dist-server/service/compliance-case/compliance-case-rules.js +2 -4
  43. package/dist-server/service/compliance-case/compliance-case-rules.js.map +1 -1
  44. package/dist-server/service/compliance-case/compliance-case-service.d.ts +3 -3
  45. package/dist-server/service/compliance-case/compliance-case-service.js +4 -6
  46. package/dist-server/service/compliance-case/compliance-case-service.js.map +1 -1
  47. package/dist-server/service/compliance-case/compliance-case-type.js +1 -1
  48. package/dist-server/service/compliance-case/compliance-case-type.js.map +1 -1
  49. package/dist-server/service/compliance-case/compliance-case.js +1 -1
  50. package/dist-server/service/compliance-case/compliance-case.js.map +1 -1
  51. package/dist-server/service/regulation/regulation-rules.d.ts +3 -3
  52. package/dist-server/service/regulation/regulation-rules.js +17 -17
  53. package/dist-server/service/regulation/regulation-rules.js.map +1 -1
  54. package/dist-server/service/regulation/regulation.d.ts +1 -1
  55. package/dist-server/service/regulation/regulation.js +1 -1
  56. package/dist-server/service/regulation/regulation.js.map +1 -1
  57. package/dist-server/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +17 -17
  59. package/translations/en.json +27 -14
  60. package/translations/es.json +27 -14
  61. package/translations/ja.json +27 -14
  62. package/translations/ko.json +27 -14
  63. package/translations/ms.json +27 -14
  64. package/translations/zh.json +27 -14
@@ -1 +1 @@
1
- {"version":3,"file":"training-attendance-form.js","sourceRoot":"","sources":["../../client/components/training-attendance-form.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/icon/icon.js'\nimport '@operato/input/ox-select.js'\nimport '@operato/popup/ox-popup-actions.js'\nimport '@operato/popup/ox-popup-list.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify, openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\n\nimport { failureOf } from '../words.js'\n\ntype Choice = { value: string; display: string }\n\n/** Opens the form for a session; resolves true when the attendance was recorded. */\nexport function openTrainingAttendanceForm(session: { id: string; name: string; startsOn: string; endsOn?: string | null }): Promise<boolean> {\n return new Promise(resolve => {\n let done = false\n const handle: any = openPopup(\n html`<training-attendance-form .session=${session} @close=${() => handle.close()} @recorded=${() => ((done = true), handle.close())}></training-attendance-form>`,\n { backdrop: true, size: 'medium', title: i18next.t('title.training-attendance', { name: session.name }) }\n )\n handle.onclosed = () => resolve(done)\n })\n}\n\n/**\n * 출석 기록 (ADR-0187 decision 17) - the people who attended a session, recorded at once as completed. Someone done\n * already for the kind and year is left as they are.\n */\n@customElement('training-attendance-form')\nexport class TrainingAttendanceForm extends localize(i18next)(LitElement) {\n static styles = [\n CommonPopupStyles,\n PopupBodyStyles,\n FormFieldStyles,\n PopupFieldStyles,\n css`\n [invalid] {\n color: var(--ui-crit, var(--md-sys-color-error));\n font-size: var(--ui-t-small, 11.5px);\n }\n [invalid]:empty {\n display: none;\n }\n [guide] p {\n margin: 0;\n color: var(--ui-muted);\n line-height: var(--popup-line-height);\n }\n ox-select {\n width: 100%;\n }\n .chosen {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-small, 4px);\n }\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 4px 2px 8px;\n border-radius: 999px;\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n .chip button {\n border: 0;\n background: none;\n cursor: pointer;\n color: inherit;\n display: inline-flex;\n padding: 0;\n }\n .chip md-icon {\n --md-icon-size: 16px;\n }\n `\n ]\n\n @property({ type: Object }) session!: { id: string; name: string; startsOn: string; endsOn?: string | null }\n\n @state() private people: Choice[] = []\n @state() private chosen: string[] = []\n @state() private completedOn = ''\n @state() private working = false\n @state() private message = ''\n\n async connectedCallback() {\n super.connectedCallback()\n this.completedOn = this.session.endsOn || this.session.startsOn\n const response = await client.query({\n query: gql`\n query {\n employees(filters: [{ name: \"retiredOn\", operator: \"is_null\" }], pagination: { page: 1, limit: 500 }, sortings: [{ name: \"name\" }]) {\n items { id name controlNo }\n }\n }\n `,\n fetchPolicy: 'no-cache'\n })\n this.people = (response.data?.employees?.items ?? []).map((one: any) => ({ value: one.id, display: one.controlNo ? `${one.name} · ${one.controlNo}` : one.name }))\n }\n\n render() {\n const left = this.people.filter(one => !this.chosen.includes(one.value))\n return html`\n <div class=\"body\">\n <div guide><p>${i18next.t('text.training-attendance-guide')}</p></div>\n\n <label class=\"field\">\n <span>${i18next.t('field.training-completed-on')}</span>\n <input type=\"date\" .value=${this.completedOn} min=${this.session.startsOn} @input=${(e: any) => (this.completedOn = e.target.value)} />\n </label>\n\n <div class=\"field\">\n <span>${i18next.t('field.training-attendees')} (${this.chosen.length})</span>\n <div class=\"chosen\">\n ${this.chosen.map(\n id => html`<span class=\"chip\"\n >${this.people.find(one => one.value === id)?.display ?? id}\n <button aria-label=${i18next.t('button.delete')} @click=${() => (this.chosen = this.chosen.filter(one => one !== id))}><md-icon>close</md-icon></button></span\n >`\n )}\n </div>\n <ox-select value=\"\" @change=${(e: CustomEvent) => e.detail && (this.chosen = [...this.chosen, String(e.detail)])}>\n <span slot=\"label\">${i18next.t('text.training-attendee-add')}</span>\n <ox-popup-list align-left nowrap with-search>${left.map(one => html`<div option value=${one.value}>${one.display}</div>`)}</ox-popup-list>\n </ox-select>\n <span invalid>${this.message}</span>\n </div>\n </div>\n\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${i18next.t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.working} @click=${() => this.record()}>${i18next.t('button.training-attendance-record')}</md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private async record() {\n this.message = ''\n this.working = true\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($sessionId: String!, $employeeIds: [String!]!, $completedOn: String) {\n recordTrainingAttendance(sessionId: $sessionId, employeeIds: $employeeIds, completedOn: $completedOn) { id }\n }\n `,\n variables: { sessionId: this.session.id, employeeIds: this.chosen, completedOn: this.completedOn || null }\n })\n if ((response as any).errors?.length) throw response\n notify({ message: i18next.t('text.training-attendance-recorded', { count: response.data.recordTrainingAttendance.length }) })\n this.dispatchEvent(new CustomEvent('recorded'))\n } catch (error) {\n this.message = failureOf(error)\n } finally {\n this.working = false\n }\n }\n}\n"]}
1
+ {"version":3,"file":"training-attendance-form.js","sourceRoot":"","sources":["../../client/components/training-attendance-form.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/icon/icon.js'\nimport '@operato/input/ox-select.js'\nimport '@operato/popup/ox-popup-actions.js'\nimport '@operato/popup/ox-popup-list.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { MutationController, QueryController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify, openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\n\nimport { failureOf } from '../words.js'\n\ntype Choice = { value: string; display: string }\n\n/** Opens the form for a session; resolves true when the attendance was recorded. */\nexport function openTrainingAttendanceForm(session: { id: string; name: string; startsOn: string; endsOn?: string | null }): Promise<boolean> {\n return new Promise(resolve => {\n let done = false\n const handle: any = openPopup(\n html`<training-attendance-form .session=${session} @close=${() => handle.close()} @recorded=${() => ((done = true), handle.close())}></training-attendance-form>`,\n { backdrop: true, size: 'medium', title: i18next.t('title.training-attendance', { name: session.name }) }\n )\n handle.onclosed = () => resolve(done)\n })\n}\n\n/**\n * 출석 기록 (ADR-0187 decision 17) - the people who attended a session, recorded at once as completed. Someone done\n * already for the kind and year is left as they are.\n */\n@customElement('training-attendance-form')\nexport class TrainingAttendanceForm extends localize(i18next)(LitElement) {\n static styles = [\n CommonPopupStyles,\n PopupBodyStyles,\n FormFieldStyles,\n PopupFieldStyles,\n css`\n [invalid] {\n color: var(--ui-crit, var(--md-sys-color-error));\n font-size: var(--ui-t-small, 11.5px);\n }\n [invalid]:empty {\n display: none;\n }\n [guide] p {\n margin: 0;\n color: var(--ui-muted);\n line-height: var(--popup-line-height);\n }\n ox-select {\n width: 100%;\n }\n .chosen {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-small, 4px);\n }\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 4px 2px 8px;\n border-radius: 999px;\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n .chip button {\n border: 0;\n background: none;\n cursor: pointer;\n color: inherit;\n display: inline-flex;\n padding: 0;\n }\n .chip md-icon {\n --md-icon-size: 16px;\n }\n `\n ]\n\n @property({ type: Object }) session!: { id: string; name: string; startsOn: string; endsOn?: string | null }\n\n @state() private chosen: string[] = []\n @state() private completedOn = ''\n\n /* The employees who may have attended (ADR-0190). */\n private employees = new QueryController(\n this,\n gql`\n query {\n employees(filters: [{ name: \"retiredOn\", operator: \"is_null\" }], pagination: { page: 1, limit: 500 }, sortings: [{ name: \"name\" }]) {\n items { id name controlNo }\n }\n }\n `\n )\n\n /* Its failure is drawn under the attendees, beside the form. */\n private recording = new MutationController(\n this,\n gql`\n mutation ($sessionId: String!, $employeeIds: [String!]!, $completedOn: String) {\n recordTrainingAttendance(sessionId: $sessionId, employeeIds: $employeeIds, completedOn: $completedOn) { id }\n }\n `,\n { showsError: true }\n )\n\n connectedCallback() {\n super.connectedCallback()\n this.completedOn = this.session.endsOn || this.session.startsOn\n }\n\n private get people(): Choice[] {\n return (this.employees.data?.employees?.items ?? []).map((one: any) => ({ value: one.id, display: one.controlNo ? `${one.name} · ${one.controlNo}` : one.name }))\n }\n\n private get working() {\n return this.recording.pending\n }\n\n private get message() {\n return this.recording.error ? failureOf(this.recording.error) : ''\n }\n\n render() {\n const left = this.people.filter(one => !this.chosen.includes(one.value))\n return html`\n <div class=\"body\">\n <div guide><p>${i18next.t('text.training-attendance-guide')}</p></div>\n\n <label class=\"field\">\n <span>${i18next.t('field.training-completed-on')}</span>\n <input type=\"date\" .value=${this.completedOn} min=${this.session.startsOn} @input=${(e: any) => (this.completedOn = e.target.value)} />\n </label>\n\n <div class=\"field\">\n <span>${i18next.t('field.training-attendees')} (${this.chosen.length})</span>\n <div class=\"chosen\">\n ${this.chosen.map(\n id => html`<span class=\"chip\"\n >${this.people.find(one => one.value === id)?.display ?? id}\n <button aria-label=${i18next.t('button.delete')} @click=${() => (this.chosen = this.chosen.filter(one => one !== id))}><md-icon>close</md-icon></button></span\n >`\n )}\n </div>\n <ox-select value=\"\" @change=${(e: CustomEvent) => e.detail && (this.chosen = [...this.chosen, String(e.detail)])}>\n <span slot=\"label\">${i18next.t('text.training-attendee-add')}</span>\n <ox-popup-list align-left nowrap with-search>${left.map(one => html`<div option value=${one.value}>${one.display}</div>`)}</ox-popup-list>\n </ox-select>\n <span invalid>${this.message}</span>\n </div>\n </div>\n\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${i18next.t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.working} @click=${() => this.record()}>${i18next.t('button.training-attendance-record')}</md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private async record() {\n const done = await this.recording.run({ sessionId: this.session.id, employeeIds: this.chosen, completedOn: this.completedOn || null })\n if (!done) return\n notify({ message: i18next.t('text.training-attendance-recorded', { count: done.recordTrainingAttendance.length }) })\n this.dispatchEvent(new CustomEvent('recorded'))\n }\n}\n"]}
@@ -11,14 +11,17 @@ declare const RegulationDetail_base: (new (...args: any[]) => import("lit").LitE
11
11
  */
12
12
  export declare class RegulationDetail extends RegulationDetail_base {
13
13
  static styles: import("lit").CSSResult[];
14
- private view;
15
- private failure;
14
+ private ruleId;
16
15
  private chosen;
17
- private draft;
18
- private employees;
19
- private procedure;
20
16
  private files;
21
17
  private originals;
18
+ private rule;
19
+ private draft;
20
+ private procedure;
21
+ private saves;
22
+ private change;
23
+ private get view();
24
+ private get busy();
22
25
  get context(): {
23
26
  title: any;
24
27
  frame: string;
@@ -30,15 +33,21 @@ export declare class RegulationDetail extends RegulationDetail_base {
30
33
  private renderInForce;
31
34
  private renderProcedureForm;
32
35
  pageUpdated(changes: any, lifecycle: any): Promise<void>;
33
- private fetch;
34
36
  private choose;
37
+ get addressDirty(): boolean;
38
+ get addressDiscardText(): string;
39
+ discardAddressEdits(change?: any): void;
40
+ private mayLeave;
41
+ private switchTo;
35
42
  private call;
36
43
  private patchOf;
44
+ private saveDraft;
37
45
  private saveVersion;
38
46
  private submit;
39
47
  private registerInForce;
40
48
  private withdraw;
41
49
  private newVersion;
50
+ /** The revision asked about is the one deleted: if the screen shows another once confirmed, nothing is deleted. */
42
51
  private deleteVersion;
43
52
  private recordProcedure;
44
53
  private deleteProcedure;
@@ -5,14 +5,14 @@ import '@operato/list-state/ox-page-state.js';
5
5
  import gql from 'graphql-tag';
6
6
  import { css, html, nothing } from 'lit';
7
7
  import { customElement, state } from 'lit/decorators.js';
8
- import { client, uploadContext } from '@operato/graphql';
8
+ import { MutationController, QueryController, uploadContext } from '@operato/graphql';
9
9
  import { i18next, localize } from '@operato/i18n';
10
10
  import { notify } from '@operato/layout';
11
11
  import { OxPrompt } from '@operato/popup/ox-prompt.js';
12
- import { navigate, PageView } from '@operato/shell';
12
+ import { navigate, PageView, DraftController } from '@operato/shell';
13
13
  import { CommonButtonStyles, DocumentPageStyles, FormFieldStyles, ScrollbarStyles } from '@operato/styles';
14
14
  import { VERSION_VIEW } from '../regulation/regulation-fragments.js';
15
- import { failureOf, PROCEDURE_KINDS, PROCEDURE_METHODS, wordOf } from '../words.js';
15
+ import { failureOf, PROCEDURE_METHODS, wordOf } from '../words.js';
16
16
  /**
17
17
  * 규정 한 건 (ADR-0187 decisions 2-6) - its versions, and of the one chosen: its text, the day it takes effect, what
18
18
  * changed, whether it is against the workers, the procedure with its papers, its approval and whether the employees were
@@ -22,14 +22,47 @@ import { failureOf, PROCEDURE_KINDS, PROCEDURE_METHODS, wordOf } from '../words.
22
22
  let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView) {
23
23
  constructor() {
24
24
  super(...arguments);
25
- this.view = null;
26
- this.failure = '';
25
+ this.ruleId = '';
27
26
  this.chosen = '';
28
- this.draft = {};
29
- this.employees = null;
30
- this.procedure = { kind: 'OPINION', day: '', method: '', workersCount: '', consentCount: '', notes: '' };
31
27
  this.files = [];
32
28
  this.originals = [];
29
+ /* The rule and its versions, read for the rule the address names (ADR-0190). */
30
+ this.rule = new QueryController(this, gql `
31
+ query ($id: String) {
32
+ companyRegulation(id: $id) {
33
+ regulation { id code name kind topics }
34
+ versions {
35
+ ...RegulationVersionViewFields
36
+ procedures { id kind day method workersCount consentCount notes files }
37
+ originals
38
+ }
39
+ }
40
+ }
41
+ ${VERSION_VIEW}
42
+ `, { variables: () => (this.ruleId ? { id: this.ruleId } : undefined) });
43
+ /* What is typed belongs to the version it was typed on: the text edits over the version read, and the procedure being
44
+ recorded. Another version drops both, so none of it lands there. */
45
+ this.draft = new DraftController(this, { read: () => this.current?.version, key: () => this.current?.version.id });
46
+ this.procedure = new DraftController(this, { read: () => ({ day: '', notes: '' }), key: () => this.current?.version.id });
47
+ /* Every change reads the rule again once it is done. */
48
+ this.saves = {
49
+ draft: this.change(gql `mutation ($id: String!, $patch: RegulationVersionPatch!) { updateRegulationVersion(id: $id, patch: $patch) { id } }`),
50
+ submit: this.change(gql `mutation ($id: String!) { submitRegulationVersion(id: $id) { id status } }`),
51
+ inForce: this.change(gql `
52
+ mutation ($id: String!, $files: [Upload!]!) {
53
+ registerRegulationInForce(id: $id, files: $files) { id status }
54
+ }
55
+ `),
56
+ withdraw: this.change(gql `mutation ($id: String!) { withdrawRegulationVersion(id: $id) { id } }`),
57
+ newVersion: this.change(gql `mutation ($id: String!) { newRegulationVersion(regulationId: $id) { id } }`),
58
+ deleteVersion: this.change(gql `mutation ($id: String!) { deleteRegulationVersion(id: $id) }`),
59
+ procedure: this.change(gql `
60
+ mutation ($procedure: NewRegulationProcedure!, $files: [Upload!]) {
61
+ recordRegulationProcedure(procedure: $procedure, files: $files) { id }
62
+ }
63
+ `),
64
+ deleteProcedure: this.change(gql `mutation ($id: String!) { deleteRegulationProcedure(id: $id) }`)
65
+ };
33
66
  }
34
67
  static { this.styles = [
35
68
  DocumentPageStyles,
@@ -84,6 +117,17 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
84
117
  }
85
118
  `
86
119
  ]; }
120
+ change(mutation) {
121
+ return new MutationController(this, mutation, { refetch: () => [this.rule] });
122
+ }
123
+ get view() {
124
+ return this.rule.data?.companyRegulation ?? null;
125
+ }
126
+ /* A save, a submission or a procedure on its way: nothing is typed, switched or sent again until it is back, so what
127
+ is sent is what the screen shows and no answer lands on input typed after it. */
128
+ get busy() {
129
+ return Object.values(this.saves).some(save => save.pending);
130
+ }
87
131
  get context() {
88
132
  const version = this.current?.version;
89
133
  const actions = [];
@@ -103,8 +147,8 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
103
147
  return versions.find((one) => one.version.id === this.chosen) ?? versions[0];
104
148
  }
105
149
  render() {
106
- if (this.failure)
107
- return html `<ox-page-state state="error" .message=${this.failure}></ox-page-state>`;
150
+ if (this.rule.error)
151
+ return html `<ox-page-state state="error" .message=${failureOf(this.rule.error)}></ox-page-state>`;
108
152
  if (!this.view)
109
153
  return html `<ox-page-state state="loading"></ox-page-state>`;
110
154
  const current = this.current;
@@ -116,9 +160,10 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
116
160
  <div class="page-section-actions"><button @click=${() => navigate('regulation-list')}>${i18next.t('button.regulation-all')}</button></div>
117
161
  </header>
118
162
  <ox-input-select-buttons
163
+ ?disabled=${this.busy}
119
164
  .value=${current.version.id}
120
165
  .options=${this.view.versions.map((one) => ({ value: one.version.id, display: `${i18next.t('text.regulation-version-of', { version: one.version.version })} · ${wordOf('regulation-period', one.period)}` }))}
121
- @change=${(e) => e.detail && this.choose(e.detail)}
166
+ @change=${(e) => e.detail && this.switchTo(e.detail)}
122
167
  ></ox-input-select-buttons>
123
168
  </section>
124
169
  ${current ? this.renderVersion(current) : nothing}
@@ -127,11 +172,12 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
127
172
  renderVersion(view) {
128
173
  const { version } = view;
129
174
  const writing = version.status === 'DRAFT';
130
- const value = (name) => this.draft[name] ?? version[name] ?? '';
131
- const set = (name, given) => (this.draft = { ...this.draft, [name]: given });
175
+ const locked = !writing || this.busy;
176
+ const value = (name) => this.draft.value(name) ?? '';
177
+ const set = (name, given) => !this.busy && this.draft.set(name, given);
132
178
  const words = { ADDED: i18next.t('text.regulation-change-added'), CHANGED: i18next.t('text.regulation-change-changed'), REMOVED: i18next.t('text.regulation-change-removed') };
133
179
  const filing = this.view.regulation.kind === 'WORK_RULES' || this.view.regulation.kind === 'WORK_RULES_ANNEX';
134
- const kinds = version.status === 'ADOPTED' ? ['FILING'] : writing ? PROCEDURE_KINDS : [];
180
+ const kinds = !filing ? [] : version.status === 'ADOPTED' ? ['FILING'] : writing ? [value('unfavourable') === true ? 'CONSENT' : 'OPINION'] : [];
135
181
  return html `<section class="page-section">
136
182
  <header class="page-section-header"><h3>${i18next.t('text.regulation-version-of', { version: version.version })} · ${wordOf('regulation-period', view.period)}</h3></header>
137
183
  ${version.adoptionNote ? html `<p class="page-section-note note">${version.adoptionNote}</p>` : nothing}
@@ -140,29 +186,29 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
140
186
  <div class="form">
141
187
  <label class="field">
142
188
  <span>${i18next.t('field.regulation-effective-on')}</span>
143
- <input type="date" ?disabled=${!writing} .value=${value('effectiveOn')} @input=${(e) => set('effectiveOn', e.target.value)} />
189
+ <input type="date" ?disabled=${locked} .value=${value('effectiveOn')} @input=${(e) => set('effectiveOn', e.target.value)} />
144
190
  <span>${i18next.t('text.regulation-effective-hint')}</span>
145
191
  </label>
146
- <div class="field">
192
+ ${filing ? html `<div class="field">
147
193
  <span>${i18next.t('field.regulation-unfavourable')}</span>
148
194
  <ox-input-select-buttons
149
- ?disabled=${!writing}
195
+ ?disabled=${locked}
150
196
  .value=${value('unfavourable') === '' ? '' : String(value('unfavourable'))}
151
197
  .options=${[{ value: 'true', display: i18next.t('text.regulation-unfavourable-yes') }, { value: 'false', display: i18next.t('text.regulation-unfavourable-no') }]}
152
198
  @change=${(e) => writing && e.detail && set('unfavourable', e.detail === 'true')}
153
199
  ></ox-input-select-buttons>
154
200
  <span>${i18next.t('text.regulation-unfavourable-hint')}</span>
155
- </div>
201
+ </div>` : nothing}
156
202
  <label class="field">
157
203
  <span>${i18next.t('field.regulation-change-summary')}</span>
158
- <input ?disabled=${!writing} .value=${value('changeSummary')} @input=${(e) => set('changeSummary', e.target.value)} />
204
+ <input ?disabled=${locked} .value=${value('changeSummary')} @input=${(e) => set('changeSummary', e.target.value)} />
159
205
  </label>
160
206
  </div>
161
207
  </section>
162
208
  <section class="page-section">
163
209
  <header class="page-section-header"><h3>${i18next.t('field.regulation-body')}</h3></header>
164
210
  <p class="page-section-note muted">${i18next.t('text.regulation-body-hint')}</p>
165
- <textarea class="body" ?disabled=${!writing} .value=${value('body')} @input=${(e) => set('body', e.target.value)}></textarea>
211
+ <textarea class="body" ?disabled=${locked} .value=${value('body')} @input=${(e) => set('body', e.target.value)}></textarea>
166
212
  </section>
167
213
  ${view.changes.length
168
214
  ? html `<section class="page-section">
@@ -172,21 +218,21 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
172
218
  </table>
173
219
  </section>`
174
220
  : nothing}
175
- <section class="page-section">
221
+ ${filing || view.procedures?.length ? html `<section class="page-section">
176
222
  <header class="page-section-header"><h3>${i18next.t('text.regulation-procedures')}</h3></header>
177
223
  <p class="page-section-note muted">${i18next.t(filing ? 'text.regulation-procedures-hint' : 'text.regulation-procedures-none-asked')}</p>
178
224
  <table>
179
225
  ${(view.procedures ?? []).map((one) => html `<tr>
180
226
  <td>${wordOf('regulation-procedure-kind', one.kind)}</td>
181
227
  <td>${one.day}</td>
182
- <td>${one.method ? wordOf('regulation-procedure-method', one.method) : ''}</td>
183
- <td>${one.kind === 'CONSENT' ? i18next.t('text.regulation-consent-count', { consented: one.consentCount, workers: one.workersCount }) : ''}</td>
228
+ <td>${one.method && Object.values(PROCEDURE_METHODS).some(methods => methods.includes(one.method)) ? wordOf('regulation-procedure-method', one.method) : ''}</td>
229
+ <td>${one.kind === 'CONSENT' && one.workersCount != null && one.consentCount != null ? i18next.t('text.regulation-consent-count', { consented: one.consentCount, workers: one.workersCount }) : ''}</td>
184
230
  <td>${i18next.t('text.regulation-files', { count: one.files })}</td>
185
- <td>${writing ? html `<button class="danger" @click=${() => this.deleteProcedure(one.id)}>${i18next.t('button.delete')}</button>` : nothing}</td>
231
+ <td>${writing ? html `<button class="danger" ?disabled=${this.busy} @click=${() => this.deleteProcedure(one.id)}>${i18next.t('button.delete')}</button>` : nothing}</td>
186
232
  </tr>`)}
187
233
  </table>
188
234
  ${kinds.length ? this.renderProcedureForm(kinds) : nothing}
189
- </section>
235
+ </section>` : nothing}
190
236
  ${writing && !this.view.versions.some((one) => one.version.status !== 'DRAFT') ? this.renderInForce() : nothing}`;
191
237
  }
192
238
  /* A rule in force before the system - its first version registered from its own day with its original (ADR-0187 amendment 2). */
@@ -197,115 +243,112 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
197
243
  <div class="procedure-form">
198
244
  <div class="field">
199
245
  <span>${i18next.t('field.regulation-original')}</span>
200
- <ox-input-file multiple .value=${this.originals} @change=${(e) => (this.originals = e.detail ?? [])}></ox-input-file>
246
+ <ox-input-file multiple ?disabled=${this.busy} .value=${this.originals} @change=${(e) => !this.busy && (this.originals = e.detail ?? [])}></ox-input-file>
201
247
  </div>
202
- <button @click=${() => this.registerInForce()}>${i18next.t('button.regulation-register-in-force')}</button>
248
+ <button ?disabled=${this.busy} @click=${() => this.registerInForce()}>${i18next.t('button.regulation-register-in-force')}</button>
203
249
  </div>
204
250
  </section>`;
205
251
  }
206
252
  renderProcedureForm(kinds) {
207
- const kind = kinds.includes(this.procedure.kind) ? this.procedure.kind : kinds[0];
208
- const set = (name, given) => (this.procedure = { ...this.procedure, kind, [name]: given });
253
+ const kind = kinds[0];
254
+ const set = (name, given) => !this.busy && this.procedure.set(name, given);
209
255
  return html `<div class="procedure-form">
210
256
  <div class="field">
211
257
  <span>${i18next.t('field.regulation-procedure-kind')}</span>
212
- <ox-input-select-buttons .value=${kind} .options=${kinds.map(code => ({ value: code, display: wordOf('regulation-procedure-kind', code) }))} @change=${(e) => e.detail && set('kind', e.detail)}></ox-input-select-buttons>
258
+ <span>${wordOf('regulation-procedure-kind', kind)}</span>
213
259
  </div>
214
- <label class="field"><span>${i18next.t('field.regulation-procedure-day')}</span><input type="date" .value=${this.procedure.day} @input=${(e) => set('day', e.target.value)} /></label>
215
- <div class="field">
216
- <span>${i18next.t('field.regulation-procedure-method')}</span>
217
- <ox-input-select-buttons .value=${this.procedure.method} .options=${PROCEDURE_METHODS[kind].map(code => ({ value: code, display: wordOf('regulation-procedure-method', code) }))} @change=${(e) => set('method', e.detail ?? '')}></ox-input-select-buttons>
218
- </div>
219
- ${kind === 'CONSENT'
220
- ? html `<label class="field">
221
- <span>${i18next.t('field.regulation-workers-count')}</span>
222
- <input type="number" min="1" .value=${this.procedure.workersCount} @input=${(e) => set('workersCount', e.target.value)} />
223
- ${this.employees !== null ? html `<span>${i18next.t('text.regulation-active-employees', { count: this.employees })}</span>` : nothing}
224
- </label>
225
- <label class="field"><span>${i18next.t('field.regulation-consent-count')}</span><input type="number" min="0" .value=${this.procedure.consentCount} @input=${(e) => set('consentCount', e.target.value)} /></label>`
226
- : nothing}
227
- <label class="field"><span>${i18next.t('field.regulation-procedure-notes')}</span><input .value=${this.procedure.notes} @input=${(e) => set('notes', e.target.value)} /></label>
260
+ <label class="field"><span>${i18next.t('field.regulation-procedure-day')}</span><input type="date" ?disabled=${this.busy} .value=${this.procedure.value('day')} @input=${(e) => set('day', e.target.value)} /></label>
261
+ <label class="field"><span>${i18next.t('field.regulation-procedure-notes')}</span><input ?disabled=${this.busy} .value=${this.procedure.value('notes')} @input=${(e) => set('notes', e.target.value)} /></label>
228
262
  <div class="field">
229
263
  <span>${i18next.t('field.regulation-procedure-files')}</span>
230
- <ox-input-file multiple .value=${this.files} @change=${(e) => (this.files = e.detail ?? [])}></ox-input-file>
264
+ <ox-input-file multiple ?disabled=${this.busy} .value=${this.files} @change=${(e) => !this.busy && (this.files = e.detail ?? [])}></ox-input-file>
231
265
  </div>
232
- <button @click=${() => this.recordProcedure(kind)}>${i18next.t('button.regulation-procedure-record')}</button>
266
+ <button ?disabled=${this.busy} @click=${() => this.recordProcedure(kind)}>${i18next.t('button.regulation-procedure-record')}</button>
233
267
  </div>`;
234
268
  }
235
269
  async pageUpdated(changes, lifecycle) {
236
270
  if (!this.active)
237
271
  return;
238
272
  const params = new URLSearchParams(location.search);
239
- this.chosen = params.get('version') ?? this.chosen;
240
- await this.fetch(params.get('id') ?? lifecycle?.params?.id);
241
- }
242
- async fetch(id = this.view?.regulation?.id) {
243
- if (!id)
244
- return;
245
- try {
246
- const response = await client.query({
247
- query: gql `
248
- query ($id: String) {
249
- companyRegulation(id: $id) {
250
- regulation { id code name kind topics }
251
- versions {
252
- ...RegulationVersionViewFields
253
- procedures { id kind day method workersCount consentCount notes files }
254
- originals
255
- }
256
- }
257
- activeEmployeeCount
258
- }
259
- ${VERSION_VIEW}
260
- `,
261
- variables: { id },
262
- fetchPolicy: 'no-cache'
263
- });
264
- if (response.errors?.length)
265
- throw response;
266
- this.view = response.data.companyRegulation;
267
- this.employees = response.data.activeEmployeeCount;
268
- this.failure = '';
269
- this.updateContext();
270
- }
271
- catch (error) {
272
- this.failure = failureOf(error);
273
- }
273
+ const id = params.get('id') ?? lifecycle?.params?.id;
274
+ const version = params.get('version');
275
+ /* Another rule, or another version asked by the address: the shell asked about what was typed (addressDirty) and
276
+ dropped it before the address changed; this page only moves to it. */
277
+ const another = !!this.view && !!id && id !== this.view.regulation.id;
278
+ if (another || (version && version !== this.chosen))
279
+ this.choose(version ?? '');
280
+ if (id)
281
+ this.ruleId = id;
274
282
  }
283
+ /* The files picked belong to the version they were picked on, as the drafts do: switching drops them. */
275
284
  choose(id) {
276
285
  this.chosen = id;
277
- this.draft = {};
286
+ this.files = [];
287
+ this.originals = [];
278
288
  this.updateContext();
279
289
  }
280
- async call(mutation, variables, done = 'text.regulation-saved', context) {
281
- try {
282
- const response = await client.mutate({ mutation, variables, ...(context ? { context } : {}) });
283
- if (response.errors?.length)
284
- throw response;
285
- notify({ message: i18next.t(done) });
286
- this.draft = {};
287
- await this.fetch();
288
- return response.data;
289
- }
290
- catch (error) {
291
- notify({ level: 'error', message: failureOf(error) });
292
- return null;
290
+ /*
291
+ * PageView reads the drafts; the files picked are the page's own unsaved state too. The shell asks about them before
292
+ * any move - 「규정 목록」, the menu, another rule's address, closing the tab - and drops them once the move is agreed.
293
+ */
294
+ get addressDirty() {
295
+ return super.addressDirty || this.files.length > 0 || this.originals.length > 0;
296
+ }
297
+ get addressDiscardText() {
298
+ return i18next.t('text.regulation-leave-unsaved');
299
+ }
300
+ discardAddressEdits(change) {
301
+ super.discardAddressEdits(change);
302
+ this.choose(this.chosen);
303
+ }
304
+ /* A version switch stays on the page, so the page asks the shell's question itself. */
305
+ async mayLeave() {
306
+ return await this.canDeactivate();
307
+ }
308
+ async switchTo(id) {
309
+ if (this.busy || id === this.current?.version.id) {
310
+ const buttons = this.renderRoot.querySelector('ox-input-select-buttons');
311
+ if (buttons)
312
+ buttons.value = this.current?.version.id;
313
+ return;
293
314
  }
315
+ if (await this.mayLeave())
316
+ return this.choose(id);
317
+ /* Staying: the buttons already show the version clicked - put them back on the one still open. */
318
+ const buttons = this.renderRoot.querySelector('ox-input-select-buttons');
319
+ if (buttons)
320
+ buttons.value = this.current?.version.id;
294
321
  }
295
- patchOf() {
296
- const patch = { ...this.draft };
322
+ async call(save, variables, done = 'text.regulation-saved', context) {
323
+ if (this.busy)
324
+ return undefined;
325
+ const data = await save.run(variables, { context });
326
+ if (data)
327
+ notify({ message: i18next.t(done) });
328
+ return data;
329
+ }
330
+ patchOf(sent) {
331
+ const patch = { ...sent };
297
332
  if (patch.effectiveOn === '')
298
333
  patch.effectiveOn = null;
299
334
  return patch;
300
335
  }
336
+ /* Saves the text edits of the version chosen - the one door that clears them. */
337
+ async saveDraft(done = 'text.regulation-saved') {
338
+ const on = this.current.version.id;
339
+ return await this.draft.submit(sent => this.call(this.saves.draft, { id: on, patch: this.patchOf(sent) }, done));
340
+ }
301
341
  async saveVersion() {
302
- if (!Object.keys(this.draft).length)
342
+ if (!this.draft.dirty)
303
343
  return;
304
- await this.call(gql `mutation ($id: String!, $patch: RegulationVersionPatch!) { updateRegulationVersion(id: $id, patch: $patch) { id } }`, { id: this.current.version.id, patch: this.patchOf() });
344
+ await this.saveDraft();
305
345
  }
306
346
  async submit() {
307
347
  const id = this.current.version.id;
308
- if (Object.keys(this.draft).length && !(await this.call(gql `mutation ($id: String!, $patch: RegulationVersionPatch!) { updateRegulationVersion(id: $id, patch: $patch) { id } }`, { id, patch: this.patchOf() })))
348
+ if (this.draft.dirty && !(await this.saveDraft()))
349
+ return;
350
+ /* Only what the screen shows is put up: anything not saved yet stops the submission. */
351
+ if (this.draft.dirty || this.current?.version.id !== id)
309
352
  return;
310
353
  const confirmed = await OxPrompt.open({
311
354
  title: i18next.t('button.regulation-submit'),
@@ -314,12 +357,15 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
314
357
  cancelButton: { text: i18next.t('button.cancel') }
315
358
  });
316
359
  if (confirmed)
317
- await this.call(gql `mutation ($id: String!) { submitRegulationVersion(id: $id) { id status } }`, { id }, 'text.regulation-submitted');
360
+ await this.call(this.saves.submit, { id }, 'text.regulation-submitted');
318
361
  }
319
362
  async registerInForce() {
320
363
  const id = this.current.version.id;
321
- if (Object.keys(this.draft).length && !(await this.call(gql `mutation ($id: String!, $patch: RegulationVersionPatch!) { updateRegulationVersion(id: $id, patch: $patch) { id } }`, { id, patch: this.patchOf() })))
364
+ if (this.draft.dirty && !(await this.saveDraft()))
322
365
  return;
366
+ if (this.draft.dirty || this.current?.version.id !== id)
367
+ return;
368
+ const originals = this.originals;
323
369
  const confirmed = await OxPrompt.open({
324
370
  title: i18next.t('button.regulation-register-in-force'),
325
371
  text: i18next.t('text.regulation-in-force-confirm', { version: this.current.version.version, day: this.current.version.effectiveOn ?? '' }),
@@ -328,75 +374,56 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
328
374
  });
329
375
  if (!confirmed)
330
376
  return;
331
- const done = await this.call(gql `
332
- mutation ($id: String!, $files: [Upload!]!) {
333
- registerRegulationInForce(id: $id, files: $files) { id status }
334
- }
335
- `, { id, files: this.originals }, 'text.regulation-registered-in-force', this.originals.length ? uploadContext(() => { }) : undefined);
336
- if (done)
377
+ const done = await this.call(this.saves.inForce, { id, files: originals }, 'text.regulation-registered-in-force', originals.length ? uploadContext(() => { }) : undefined);
378
+ if (done && this.originals === originals)
337
379
  this.originals = [];
338
380
  }
339
381
  async withdraw() {
340
- await this.call(gql `mutation ($id: String!) { withdrawRegulationVersion(id: $id) { id } }`, { id: this.current.version.id });
382
+ await this.call(this.saves.withdraw, { id: this.current.version.id });
341
383
  }
342
384
  async newVersion() {
343
- const data = await this.call(gql `mutation ($id: String!) { newRegulationVersion(regulationId: $id) { id } }`, { id: this.view.regulation.id });
385
+ const data = await this.call(this.saves.newVersion, { id: this.view.regulation.id });
344
386
  if (data)
345
387
  this.choose(data.newRegulationVersion.id);
346
388
  }
389
+ /** The revision asked about is the one deleted: if the screen shows another once confirmed, nothing is deleted. */
347
390
  async deleteVersion() {
391
+ const id = this.current?.version.id;
392
+ if (!id)
393
+ return;
348
394
  const confirmed = await OxPrompt.open({
349
395
  title: i18next.t('button.regulation-version-delete'),
350
396
  text: i18next.t('text.regulation-version-delete-confirm'),
351
397
  confirmButton: { text: i18next.t('button.delete'), destructive: true },
352
398
  cancelButton: { text: i18next.t('button.cancel') }
353
399
  });
354
- if (confirmed && (await this.call(gql `mutation ($id: String!) { deleteRegulationVersion(id: $id) }`, { id: this.current.version.id })))
355
- this.chosen = '';
400
+ if (!confirmed || this.current?.version.id !== id)
401
+ return;
402
+ if (await this.call(this.saves.deleteVersion, { id }))
403
+ this.choose('');
356
404
  }
357
405
  async recordProcedure(kind) {
358
- const { day, method, workersCount, consentCount, notes } = this.procedure;
359
- const done = await this.call(gql `
360
- mutation ($procedure: NewRegulationProcedure!, $files: [Upload!]) {
361
- recordRegulationProcedure(procedure: $procedure, files: $files) { id }
362
- }
363
- `, {
364
- procedure: { versionId: this.current.version.id, kind, day, method: method || null, workersCount: workersCount === '' ? null : Number(workersCount), consentCount: consentCount === '' ? null : Number(consentCount), notes: notes || null },
365
- files: this.files
366
- }, 'text.regulation-saved', this.files.length ? uploadContext(() => { }) : undefined);
367
- if (done) {
368
- this.procedure = { kind: 'OPINION', day: '', method: '', workersCount: '', consentCount: '', notes: '' };
406
+ const files = this.files;
407
+ const done = await this.procedure.submit(sent => this.call(this.saves.procedure, {
408
+ procedure: { versionId: this.current.version.id, kind, day: sent.day ?? '', notes: sent.notes || null },
409
+ files
410
+ }, 'text.regulation-saved', files.length ? uploadContext(() => { }) : undefined));
411
+ /* Only what was sent is done - a file picked since stays. */
412
+ if (done && this.files === files)
369
413
  this.files = [];
370
- }
371
414
  }
372
415
  async deleteProcedure(id) {
373
- await this.call(gql `mutation ($id: String!) { deleteRegulationProcedure(id: $id) }`, { id });
416
+ await this.call(this.saves.deleteProcedure, { id });
374
417
  }
375
418
  };
376
419
  __decorate([
377
420
  state(),
378
421
  __metadata("design:type", Object)
379
- ], RegulationDetail.prototype, "view", void 0);
380
- __decorate([
381
- state(),
382
- __metadata("design:type", Object)
383
- ], RegulationDetail.prototype, "failure", void 0);
422
+ ], RegulationDetail.prototype, "ruleId", void 0);
384
423
  __decorate([
385
424
  state(),
386
425
  __metadata("design:type", Object)
387
426
  ], RegulationDetail.prototype, "chosen", void 0);
388
- __decorate([
389
- state(),
390
- __metadata("design:type", Object)
391
- ], RegulationDetail.prototype, "draft", void 0);
392
- __decorate([
393
- state(),
394
- __metadata("design:type", Object)
395
- ], RegulationDetail.prototype, "employees", void 0);
396
- __decorate([
397
- state(),
398
- __metadata("design:type", Object)
399
- ], RegulationDetail.prototype, "procedure", void 0);
400
427
  __decorate([
401
428
  state(),
402
429
  __metadata("design:type", Array)