@things-factory/compliance 10.1.255 → 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 (86) 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 +17 -5
  29. package/dist-client/pages/regulation-detail.js +202 -133
  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 +79 -68
  33. package/dist-client/pages/regulations.js.map +1 -1
  34. package/dist-client/regulation/regulation-fragments.js +5 -0
  35. package/dist-client/regulation/regulation-fragments.js.map +1 -1
  36. package/dist-client/regulation/regulation-guide.js +2 -1
  37. package/dist-client/regulation/regulation-guide.js.map +1 -1
  38. package/dist-client/tsconfig.tsbuildinfo +1 -1
  39. package/dist-client/words.js +2 -2
  40. package/dist-client/words.js.map +1 -1
  41. package/dist-server/index.d.ts +1 -0
  42. package/dist-server/index.js +1 -0
  43. package/dist-server/index.js.map +1 -1
  44. package/dist-server/service/compliance-case/compliance-case-query.js +1 -1
  45. package/dist-server/service/compliance-case/compliance-case-query.js.map +1 -1
  46. package/dist-server/service/compliance-case/compliance-case-rules.d.ts +1 -3
  47. package/dist-server/service/compliance-case/compliance-case-rules.js +2 -4
  48. package/dist-server/service/compliance-case/compliance-case-rules.js.map +1 -1
  49. package/dist-server/service/compliance-case/compliance-case-service.d.ts +3 -3
  50. package/dist-server/service/compliance-case/compliance-case-service.js +4 -6
  51. package/dist-server/service/compliance-case/compliance-case-service.js.map +1 -1
  52. package/dist-server/service/compliance-case/compliance-case-type.js +1 -1
  53. package/dist-server/service/compliance-case/compliance-case-type.js.map +1 -1
  54. package/dist-server/service/compliance-case/compliance-case.js +1 -1
  55. package/dist-server/service/compliance-case/compliance-case.js.map +1 -1
  56. package/dist-server/service/index.d.ts +1 -1
  57. package/dist-server/service/index.js +2 -1
  58. package/dist-server/service/index.js.map +1 -1
  59. package/dist-server/service/regulation/regulation-mutation.d.ts +1 -0
  60. package/dist-server/service/regulation/regulation-mutation.js +14 -0
  61. package/dist-server/service/regulation/regulation-mutation.js.map +1 -1
  62. package/dist-server/service/regulation/regulation-read.js +11 -2
  63. package/dist-server/service/regulation/regulation-read.js.map +1 -1
  64. package/dist-server/service/regulation/regulation-rules.d.ts +25 -6
  65. package/dist-server/service/regulation/regulation-rules.js +85 -29
  66. package/dist-server/service/regulation/regulation-rules.js.map +1 -1
  67. package/dist-server/service/regulation/regulation-service.d.ts +12 -0
  68. package/dist-server/service/regulation/regulation-service.js +48 -7
  69. package/dist-server/service/regulation/regulation-service.js.map +1 -1
  70. package/dist-server/service/regulation/regulation-type.d.ts +4 -0
  71. package/dist-server/service/regulation/regulation-type.js +16 -0
  72. package/dist-server/service/regulation/regulation-type.js.map +1 -1
  73. package/dist-server/service/regulation/regulation.d.ts +3 -1
  74. package/dist-server/service/regulation/regulation.js +12 -2
  75. package/dist-server/service/regulation/regulation.js.map +1 -1
  76. package/dist-server/tsconfig.tsbuildinfo +1 -1
  77. package/dist-server/work-doors.d.ts +1 -0
  78. package/dist-server/work-doors.js +15 -0
  79. package/dist-server/work-doors.js.map +1 -0
  80. package/package.json +17 -17
  81. package/translations/en.json +46 -14
  82. package/translations/es.json +46 -14
  83. package/translations/ja.json +46 -14
  84. package/translations/ko.json +46 -14
  85. package/translations/ms.json +46 -14
  86. package/translations/zh.json +46 -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,13 +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;
16
+ private files;
17
+ private originals;
18
+ private rule;
17
19
  private draft;
18
- private employees;
19
20
  private procedure;
20
- private files;
21
+ private saves;
22
+ private change;
23
+ private get view();
24
+ private get busy();
21
25
  get context(): {
22
26
  title: any;
23
27
  frame: string;
@@ -26,16 +30,24 @@ export declare class RegulationDetail extends RegulationDetail_base {
26
30
  private get current();
27
31
  render(): import("lit-html").TemplateResult<1>;
28
32
  private renderVersion;
33
+ private renderInForce;
29
34
  private renderProcedureForm;
30
35
  pageUpdated(changes: any, lifecycle: any): Promise<void>;
31
- private fetch;
32
36
  private choose;
37
+ get addressDirty(): boolean;
38
+ get addressDiscardText(): string;
39
+ discardAddressEdits(change?: any): void;
40
+ private mayLeave;
41
+ private switchTo;
33
42
  private call;
34
43
  private patchOf;
44
+ private saveDraft;
35
45
  private saveVersion;
36
46
  private submit;
47
+ private registerInForce;
37
48
  private withdraw;
38
49
  private newVersion;
50
+ /** The revision asked about is the one deleted: if the screen shows another once confirmed, nothing is deleted. */
39
51
  private deleteVersion;
40
52
  private recordProcedure;
41
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,13 +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 = [];
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
+ };
32
66
  }
33
67
  static { this.styles = [
34
68
  DocumentPageStyles,
@@ -83,6 +117,17 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
83
117
  }
84
118
  `
85
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
+ }
86
131
  get context() {
87
132
  const version = this.current?.version;
88
133
  const actions = [];
@@ -102,8 +147,8 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
102
147
  return versions.find((one) => one.version.id === this.chosen) ?? versions[0];
103
148
  }
104
149
  render() {
105
- if (this.failure)
106
- 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>`;
107
152
  if (!this.view)
108
153
  return html `<ox-page-state state="loading"></ox-page-state>`;
109
154
  const current = this.current;
@@ -115,9 +160,10 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
115
160
  <div class="page-section-actions"><button @click=${() => navigate('regulation-list')}>${i18next.t('button.regulation-all')}</button></div>
116
161
  </header>
117
162
  <ox-input-select-buttons
163
+ ?disabled=${this.busy}
118
164
  .value=${current.version.id}
119
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)}` }))}
120
- @change=${(e) => e.detail && this.choose(e.detail)}
166
+ @change=${(e) => e.detail && this.switchTo(e.detail)}
121
167
  ></ox-input-select-buttons>
122
168
  </section>
123
169
  ${current ? this.renderVersion(current) : nothing}
@@ -126,168 +172,183 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
126
172
  renderVersion(view) {
127
173
  const { version } = view;
128
174
  const writing = version.status === 'DRAFT';
129
- const value = (name) => this.draft[name] ?? version[name] ?? '';
130
- 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);
131
178
  const words = { ADDED: i18next.t('text.regulation-change-added'), CHANGED: i18next.t('text.regulation-change-changed'), REMOVED: i18next.t('text.regulation-change-removed') };
132
179
  const filing = this.view.regulation.kind === 'WORK_RULES' || this.view.regulation.kind === 'WORK_RULES_ANNEX';
133
- const kinds = version.status === 'ADOPTED' ? ['FILING'] : writing ? PROCEDURE_KINDS : [];
180
+ const kinds = !filing ? [] : version.status === 'ADOPTED' ? ['FILING'] : writing ? [value('unfavourable') === true ? 'CONSENT' : 'OPINION'] : [];
134
181
  return html `<section class="page-section">
135
182
  <header class="page-section-header"><h3>${i18next.t('text.regulation-version-of', { version: version.version })} · ${wordOf('regulation-period', view.period)}</h3></header>
136
183
  ${version.adoptionNote ? html `<p class="page-section-note note">${version.adoptionNote}</p>` : nothing}
137
- ${version.status === 'ADOPTED' && !view.told ? html `<p class="page-section-note note">${i18next.t('text.regulation-not-told-note')}</p>` : nothing}
184
+ ${version.registeredInForce ? html `<p class="page-section-note">${i18next.t('text.regulation-registered-note', { count: view.originals ?? 0 })}</p>` : nothing}
185
+ ${version.status === 'ADOPTED' && !view.told && !version.registeredInForce ? html `<p class="page-section-note note">${i18next.t('text.regulation-not-told-note')}</p>` : nothing}
138
186
  <div class="form">
139
187
  <label class="field">
140
188
  <span>${i18next.t('field.regulation-effective-on')}</span>
141
- <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)} />
142
190
  <span>${i18next.t('text.regulation-effective-hint')}</span>
143
191
  </label>
144
- <div class="field">
192
+ ${filing ? html `<div class="field">
145
193
  <span>${i18next.t('field.regulation-unfavourable')}</span>
146
194
  <ox-input-select-buttons
147
- ?disabled=${!writing}
195
+ ?disabled=${locked}
148
196
  .value=${value('unfavourable') === '' ? '' : String(value('unfavourable'))}
149
197
  .options=${[{ value: 'true', display: i18next.t('text.regulation-unfavourable-yes') }, { value: 'false', display: i18next.t('text.regulation-unfavourable-no') }]}
150
198
  @change=${(e) => writing && e.detail && set('unfavourable', e.detail === 'true')}
151
199
  ></ox-input-select-buttons>
152
200
  <span>${i18next.t('text.regulation-unfavourable-hint')}</span>
153
- </div>
201
+ </div>` : nothing}
154
202
  <label class="field">
155
203
  <span>${i18next.t('field.regulation-change-summary')}</span>
156
- <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)} />
157
205
  </label>
158
206
  </div>
159
207
  </section>
160
208
  <section class="page-section">
161
209
  <header class="page-section-header"><h3>${i18next.t('field.regulation-body')}</h3></header>
162
210
  <p class="page-section-note muted">${i18next.t('text.regulation-body-hint')}</p>
163
- <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>
164
212
  </section>
165
213
  ${view.changes.length
166
214
  ? html `<section class="page-section">
167
215
  <header class="page-section-header"><h3>${i18next.t('text.regulation-changes')}</h3></header>
168
216
  <table>
169
- ${view.changes.map((one) => html `<tr><td>제${one.number}조${one.title ? `(${one.title})` : ''}</td><td>${words[one.change]}</td></tr>`)}
217
+ ${view.changes.map((one) => html `<tr><td>${one.heading}</td><td>${words[one.change]}</td></tr>`)}
170
218
  </table>
171
219
  </section>`
172
220
  : nothing}
173
- <section class="page-section">
221
+ ${filing || view.procedures?.length ? html `<section class="page-section">
174
222
  <header class="page-section-header"><h3>${i18next.t('text.regulation-procedures')}</h3></header>
175
223
  <p class="page-section-note muted">${i18next.t(filing ? 'text.regulation-procedures-hint' : 'text.regulation-procedures-none-asked')}</p>
176
224
  <table>
177
225
  ${(view.procedures ?? []).map((one) => html `<tr>
178
226
  <td>${wordOf('regulation-procedure-kind', one.kind)}</td>
179
227
  <td>${one.day}</td>
180
- <td>${one.method ? wordOf('regulation-procedure-method', one.method) : ''}</td>
181
- <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>
182
230
  <td>${i18next.t('text.regulation-files', { count: one.files })}</td>
183
- <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>
184
232
  </tr>`)}
185
233
  </table>
186
234
  ${kinds.length ? this.renderProcedureForm(kinds) : nothing}
187
- </section>`;
235
+ </section>` : nothing}
236
+ ${writing && !this.view.versions.some((one) => one.version.status !== 'DRAFT') ? this.renderInForce() : nothing}`;
237
+ }
238
+ /* A rule in force before the system - its first version registered from its own day with its original (ADR-0187 amendment 2). */
239
+ renderInForce() {
240
+ return html `<section class="page-section">
241
+ <header class="page-section-header"><h3>${i18next.t('text.regulation-in-force-title')}</h3></header>
242
+ <p class="page-section-note muted">${i18next.t('text.regulation-in-force-hint')}</p>
243
+ <div class="procedure-form">
244
+ <div class="field">
245
+ <span>${i18next.t('field.regulation-original')}</span>
246
+ <ox-input-file multiple ?disabled=${this.busy} .value=${this.originals} @change=${(e) => !this.busy && (this.originals = e.detail ?? [])}></ox-input-file>
247
+ </div>
248
+ <button ?disabled=${this.busy} @click=${() => this.registerInForce()}>${i18next.t('button.regulation-register-in-force')}</button>
249
+ </div>
250
+ </section>`;
188
251
  }
189
252
  renderProcedureForm(kinds) {
190
- const kind = kinds.includes(this.procedure.kind) ? this.procedure.kind : kinds[0];
191
- 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);
192
255
  return html `<div class="procedure-form">
193
256
  <div class="field">
194
257
  <span>${i18next.t('field.regulation-procedure-kind')}</span>
195
- <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>
196
- </div>
197
- <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>
198
- <div class="field">
199
- <span>${i18next.t('field.regulation-procedure-method')}</span>
200
- <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>
258
+ <span>${wordOf('regulation-procedure-kind', kind)}</span>
201
259
  </div>
202
- ${kind === 'CONSENT'
203
- ? html `<label class="field">
204
- <span>${i18next.t('field.regulation-workers-count')}</span>
205
- <input type="number" min="1" .value=${this.procedure.workersCount} @input=${(e) => set('workersCount', e.target.value)} />
206
- ${this.employees !== null ? html `<span>${i18next.t('text.regulation-active-employees', { count: this.employees })}</span>` : nothing}
207
- </label>
208
- <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>`
209
- : nothing}
210
- <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>
211
262
  <div class="field">
212
263
  <span>${i18next.t('field.regulation-procedure-files')}</span>
213
- <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>
214
265
  </div>
215
- <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>
216
267
  </div>`;
217
268
  }
218
269
  async pageUpdated(changes, lifecycle) {
219
270
  if (!this.active)
220
271
  return;
221
272
  const params = new URLSearchParams(location.search);
222
- this.chosen = params.get('version') ?? this.chosen;
223
- await this.fetch(params.get('id') ?? lifecycle?.params?.id);
224
- }
225
- async fetch(id = this.view?.regulation?.id) {
226
- if (!id)
227
- return;
228
- try {
229
- const response = await client.query({
230
- query: gql `
231
- query ($id: String) {
232
- companyRegulation(id: $id) {
233
- regulation { id code name kind topics }
234
- versions {
235
- ...RegulationVersionViewFields
236
- procedures { id kind day method workersCount consentCount notes files }
237
- }
238
- }
239
- activeEmployeeCount
240
- }
241
- ${VERSION_VIEW}
242
- `,
243
- variables: { id },
244
- fetchPolicy: 'no-cache'
245
- });
246
- if (response.errors?.length)
247
- throw response;
248
- this.view = response.data.companyRegulation;
249
- this.employees = response.data.activeEmployeeCount;
250
- this.failure = '';
251
- this.updateContext();
252
- }
253
- catch (error) {
254
- this.failure = failureOf(error);
255
- }
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;
256
282
  }
283
+ /* The files picked belong to the version they were picked on, as the drafts do: switching drops them. */
257
284
  choose(id) {
258
285
  this.chosen = id;
259
- this.draft = {};
286
+ this.files = [];
287
+ this.originals = [];
260
288
  this.updateContext();
261
289
  }
262
- async call(mutation, variables, done = 'text.regulation-saved', context) {
263
- try {
264
- const response = await client.mutate({ mutation, variables, ...(context ? { context } : {}) });
265
- if (response.errors?.length)
266
- throw response;
267
- notify({ message: i18next.t(done) });
268
- this.draft = {};
269
- await this.fetch();
270
- return response.data;
271
- }
272
- catch (error) {
273
- notify({ level: 'error', message: failureOf(error) });
274
- 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;
275
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;
276
321
  }
277
- patchOf() {
278
- 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 };
279
332
  if (patch.effectiveOn === '')
280
333
  patch.effectiveOn = null;
281
334
  return patch;
282
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
+ }
283
341
  async saveVersion() {
284
- if (!Object.keys(this.draft).length)
342
+ if (!this.draft.dirty)
285
343
  return;
286
- 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();
287
345
  }
288
346
  async submit() {
289
347
  const id = this.current.version.id;
290
- 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)
291
352
  return;
292
353
  const confirmed = await OxPrompt.open({
293
354
  title: i18next.t('button.regulation-submit'),
@@ -296,73 +357,81 @@ let RegulationDetail = class RegulationDetail extends localize(i18next)(PageView
296
357
  cancelButton: { text: i18next.t('button.cancel') }
297
358
  });
298
359
  if (confirmed)
299
- 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');
361
+ }
362
+ async registerInForce() {
363
+ const id = this.current.version.id;
364
+ if (this.draft.dirty && !(await this.saveDraft()))
365
+ return;
366
+ if (this.draft.dirty || this.current?.version.id !== id)
367
+ return;
368
+ const originals = this.originals;
369
+ const confirmed = await OxPrompt.open({
370
+ title: i18next.t('button.regulation-register-in-force'),
371
+ text: i18next.t('text.regulation-in-force-confirm', { version: this.current.version.version, day: this.current.version.effectiveOn ?? '' }),
372
+ confirmButton: { text: i18next.t('button.regulation-register-in-force') },
373
+ cancelButton: { text: i18next.t('button.cancel') }
374
+ });
375
+ if (!confirmed)
376
+ return;
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)
379
+ this.originals = [];
300
380
  }
301
381
  async withdraw() {
302
- 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 });
303
383
  }
304
384
  async newVersion() {
305
- 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 });
306
386
  if (data)
307
387
  this.choose(data.newRegulationVersion.id);
308
388
  }
389
+ /** The revision asked about is the one deleted: if the screen shows another once confirmed, nothing is deleted. */
309
390
  async deleteVersion() {
391
+ const id = this.current?.version.id;
392
+ if (!id)
393
+ return;
310
394
  const confirmed = await OxPrompt.open({
311
395
  title: i18next.t('button.regulation-version-delete'),
312
396
  text: i18next.t('text.regulation-version-delete-confirm'),
313
397
  confirmButton: { text: i18next.t('button.delete'), destructive: true },
314
398
  cancelButton: { text: i18next.t('button.cancel') }
315
399
  });
316
- if (confirmed && (await this.call(gql `mutation ($id: String!) { deleteRegulationVersion(id: $id) }`, { id: this.current.version.id })))
317
- this.chosen = '';
400
+ if (!confirmed || this.current?.version.id !== id)
401
+ return;
402
+ if (await this.call(this.saves.deleteVersion, { id }))
403
+ this.choose('');
318
404
  }
319
405
  async recordProcedure(kind) {
320
- const { day, method, workersCount, consentCount, notes } = this.procedure;
321
- const done = await this.call(gql `
322
- mutation ($procedure: NewRegulationProcedure!, $files: [Upload!]) {
323
- recordRegulationProcedure(procedure: $procedure, files: $files) { id }
324
- }
325
- `, {
326
- procedure: { versionId: this.current.version.id, kind, day, method: method || null, workersCount: workersCount === '' ? null : Number(workersCount), consentCount: consentCount === '' ? null : Number(consentCount), notes: notes || null },
327
- files: this.files
328
- }, 'text.regulation-saved', this.files.length ? uploadContext(() => { }) : undefined);
329
- if (done) {
330
- 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)
331
413
  this.files = [];
332
- }
333
414
  }
334
415
  async deleteProcedure(id) {
335
- await this.call(gql `mutation ($id: String!) { deleteRegulationProcedure(id: $id) }`, { id });
416
+ await this.call(this.saves.deleteProcedure, { id });
336
417
  }
337
418
  };
338
419
  __decorate([
339
420
  state(),
340
421
  __metadata("design:type", Object)
341
- ], RegulationDetail.prototype, "view", void 0);
342
- __decorate([
343
- state(),
344
- __metadata("design:type", Object)
345
- ], RegulationDetail.prototype, "failure", void 0);
422
+ ], RegulationDetail.prototype, "ruleId", void 0);
346
423
  __decorate([
347
424
  state(),
348
425
  __metadata("design:type", Object)
349
426
  ], RegulationDetail.prototype, "chosen", void 0);
350
427
  __decorate([
351
428
  state(),
352
- __metadata("design:type", Object)
353
- ], RegulationDetail.prototype, "draft", void 0);
354
- __decorate([
355
- state(),
356
- __metadata("design:type", Object)
357
- ], RegulationDetail.prototype, "employees", void 0);
358
- __decorate([
359
- state(),
360
- __metadata("design:type", Object)
361
- ], RegulationDetail.prototype, "procedure", void 0);
429
+ __metadata("design:type", Array)
430
+ ], RegulationDetail.prototype, "files", void 0);
362
431
  __decorate([
363
432
  state(),
364
433
  __metadata("design:type", Array)
365
- ], RegulationDetail.prototype, "files", void 0);
434
+ ], RegulationDetail.prototype, "originals", void 0);
366
435
  RegulationDetail = __decorate([
367
436
  customElement('regulation-detail')
368
437
  ], RegulationDetail);