@mj-biz-apps/contracts-ng 0.1.0
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.
- package/LICENSE +183 -0
- package/dist/lib/data/entity-names.d.ts +29 -0
- package/dist/lib/data/entity-names.d.ts.map +1 -0
- package/dist/lib/data/entity-names.js +29 -0
- package/dist/lib/data/entity-names.js.map +1 -0
- package/dist/lib/data/provider.d.ts +44 -0
- package/dist/lib/data/provider.d.ts.map +1 -0
- package/dist/lib/data/provider.js +48 -0
- package/dist/lib/data/provider.js.map +1 -0
- package/dist/lib/directives/fk-navigate.directive.d.ts +63 -0
- package/dist/lib/directives/fk-navigate.directive.d.ts.map +1 -0
- package/dist/lib/directives/fk-navigate.directive.js +99 -0
- package/dist/lib/directives/fk-navigate.directive.js.map +1 -0
- package/dist/lib/form-panels/contract.panels.d.ts +124 -0
- package/dist/lib/form-panels/contract.panels.d.ts.map +1 -0
- package/dist/lib/form-panels/contract.panels.js +1167 -0
- package/dist/lib/form-panels/contract.panels.js.map +1 -0
- package/dist/lib/form-panels/organization.panels.d.ts +23 -0
- package/dist/lib/form-panels/organization.panels.d.ts.map +1 -0
- package/dist/lib/form-panels/organization.panels.js +118 -0
- package/dist/lib/form-panels/organization.panels.js.map +1 -0
- package/dist/lib/form-panels/record-files.panel.d.ts +151 -0
- package/dist/lib/form-panels/record-files.panel.d.ts.map +1 -0
- package/dist/lib/form-panels/record-files.panel.js +1046 -0
- package/dist/lib/form-panels/record-files.panel.js.map +1 -0
- package/dist/lib/form-panels/supersede.panel.d.ts +68 -0
- package/dist/lib/form-panels/supersede.panel.d.ts.map +1 -0
- package/dist/lib/form-panels/supersede.panel.js +415 -0
- package/dist/lib/form-panels/supersede.panel.js.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContract/mjbizappscontractscontract.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContract/mjbizappscontractscontract.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContract/mjbizappscontractscontract.form.component.js +196 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContract/mjbizappscontractscontract.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplate/mjbizappscontractscontracttemplate.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplate/mjbizappscontractscontracttemplate.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplate/mjbizappscontractscontracttemplate.form.component.js +120 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplate/mjbizappscontractscontracttemplate.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateModification/mjbizappscontractscontracttemplatemodification.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateModification/mjbizappscontractscontracttemplatemodification.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateModification/mjbizappscontractscontracttemplatemodification.form.component.js +79 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateModification/mjbizappscontractscontracttemplatemodification.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateProvision/mjbizappscontractscontracttemplateprovision.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateProvision/mjbizappscontractscontracttemplateprovision.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateProvision/mjbizappscontractscontracttemplateprovision.form.component.js +79 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateProvision/mjbizappscontractscontracttemplateprovision.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateType/mjbizappscontractscontracttemplatetype.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateType/mjbizappscontractscontracttemplatetype.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateType/mjbizappscontractscontracttemplatetype.form.component.js +86 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractTemplateType/mjbizappscontractscontracttemplatetype.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractType/mjbizappscontractscontracttype.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractType/mjbizappscontractscontracttype.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractType/mjbizappscontractscontracttype.form.component.js +100 -0
- package/dist/lib/generated/Entities/mjBizAppsContractsContractType/mjbizappscontractscontracttype.form.component.js.map +1 -0
- package/dist/lib/generated/generated-forms.module.d.ts +23 -0
- package/dist/lib/generated/generated-forms.module.d.ts.map +1 -0
- package/dist/lib/generated/generated-forms.module.js +78 -0
- package/dist/lib/generated/generated-forms.module.js.map +1 -0
- package/dist/lib/pages/configuration.page.d.ts +83 -0
- package/dist/lib/pages/configuration.page.d.ts.map +1 -0
- package/dist/lib/pages/configuration.page.js +240 -0
- package/dist/lib/pages/configuration.page.js.map +1 -0
- package/dist/lib/pages/contract-grid.page.d.ts +169 -0
- package/dist/lib/pages/contract-grid.page.d.ts.map +1 -0
- package/dist/lib/pages/contract-grid.page.js +487 -0
- package/dist/lib/pages/contract-grid.page.js.map +1 -0
- package/dist/lib/pages/contracts-dashboard.page.d.ts +56 -0
- package/dist/lib/pages/contracts-dashboard.page.d.ts.map +1 -0
- package/dist/lib/pages/contracts-dashboard.page.js +215 -0
- package/dist/lib/pages/contracts-dashboard.page.js.map +1 -0
- package/dist/lib/pages/modifications.page.d.ts +96 -0
- package/dist/lib/pages/modifications.page.d.ts.map +1 -0
- package/dist/lib/pages/modifications.page.js +243 -0
- package/dist/lib/pages/modifications.page.js.map +1 -0
- package/dist/lib/pages/templates.page.d.ts +120 -0
- package/dist/lib/pages/templates.page.d.ts.map +1 -0
- package/dist/lib/pages/templates.page.js +258 -0
- package/dist/lib/pages/templates.page.js.map +1 -0
- package/dist/lib/sections/contracts-sections.component.d.ts +112 -0
- package/dist/lib/sections/contracts-sections.component.d.ts.map +1 -0
- package/dist/lib/sections/contracts-sections.component.js +412 -0
- package/dist/lib/sections/contracts-sections.component.js.map +1 -0
- package/dist/lib/sections/section-nav.model.d.ts +78 -0
- package/dist/lib/sections/section-nav.model.d.ts.map +1 -0
- package/dist/lib/sections/section-nav.model.js +110 -0
- package/dist/lib/sections/section-nav.model.js.map +1 -0
- package/dist/lib/sections/section-shell.component.d.ts +73 -0
- package/dist/lib/sections/section-shell.component.d.ts.map +1 -0
- package/dist/lib/sections/section-shell.component.js +170 -0
- package/dist/lib/sections/section-shell.component.js.map +1 -0
- package/dist/public-api.d.ts +44 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +105 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,1046 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview `Documents` — the record-scoped file panel MJ does not have yet.
|
|
9
|
+
*
|
|
10
|
+
* CARRIED FORWARD FROM V1 (plan §6.5). The rebuild deleted every other hand-written component in
|
|
11
|
+
* this package; this one survived because the gap it fills is MJ's, not v1's, and it never knew
|
|
12
|
+
* anything about the billing schema. Only its registrations changed — see the bottom of the file.
|
|
13
|
+
*
|
|
14
|
+
* THE GAP THIS FILLS. MJ models record↔file linking properly: `__mj.FileEntityRecordLink` is a
|
|
15
|
+
* polymorphic `EntityID` + `RecordID` pair, so any record can carry any number of files. What it
|
|
16
|
+
* does NOT ship is a way to SEE them on a record. `@memberjunction/ng-file-storage` has
|
|
17
|
+
* `mj-file-browser`, `mj-files-grid` and `mj-files-file-upload`, but all three are **category**
|
|
18
|
+
* scoped; nothing in the MJ Angular tree queries `FileEntityRecordLink` at runtime (the only
|
|
19
|
+
* references are the generated CRUD forms for the link entity itself). So "the files on THIS
|
|
20
|
+
* record" had no UI at all.
|
|
21
|
+
*
|
|
22
|
+
* WHY IT IS A PANEL AND NOT A TAB. `BaseFormPanel` is MJ's own extension point: a panel registers
|
|
23
|
+
* for an entity + slot and the form mounts it automatically, with no edit to the form and nothing
|
|
24
|
+
* for CodeGen to overwrite. That means this panel works identically on every GENERATED form it
|
|
25
|
+
* registers for — and it keeps working when they are regenerated, which is the whole reason v2
|
|
26
|
+
* stopped replacing forms and started contributing panels to them (plan §6.4).
|
|
27
|
+
*
|
|
28
|
+
* BUILT DONATION-SHAPED, ON PURPOSE. Nothing below knows what a contract is. The logic reads
|
|
29
|
+
* `Record.EntityInfo.ID` and `Record.PrimaryKey`, so it is already entity-agnostic — the only
|
|
30
|
+
* contracts-specific thing in the file is *which entities it registers for*, isolated into the
|
|
31
|
+
* one-line subclasses at the bottom. To donate this to MJ base, move the base class and
|
|
32
|
+
* re-register it once with `entity: '*'` (PANELS.md's wildcard); the only new requirement then is
|
|
33
|
+
* self-hiding on forms that have no files, which `HasFiles` already answers.
|
|
34
|
+
*
|
|
35
|
+
* WHY NOT WILDCARD HERE AND NOW: a wildcard panel must render nothing on the ~99% of forms it does
|
|
36
|
+
* not apply to, but a panel that hides when empty can never accept the FIRST file. Registering
|
|
37
|
+
* explicitly for the two entities that carry paper keeps the attach affordance available while
|
|
38
|
+
* staying honest about clutter. That tension is the real design question to settle before donating.
|
|
39
|
+
*
|
|
40
|
+
* @module @mj-biz-apps/contracts-ng
|
|
41
|
+
*/
|
|
42
|
+
import { ChangeDetectorRef, Component, inject } from '@angular/core';
|
|
43
|
+
import { CommonModule } from '@angular/common';
|
|
44
|
+
import { FormsModule } from '@angular/forms';
|
|
45
|
+
import { RegisterClassEx } from '@memberjunction/global';
|
|
46
|
+
import { BaseFormPanel, BaseFormsModule } from '@memberjunction/ng-base-forms';
|
|
47
|
+
import { Metadata } from '@memberjunction/core';
|
|
48
|
+
import { FileOpenService } from '@memberjunction/ng-file-storage';
|
|
49
|
+
import { GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';
|
|
50
|
+
import { ScopedRunView } from '../data/provider';
|
|
51
|
+
import * as i0 from "@angular/core";
|
|
52
|
+
import * as i1 from "@angular/common";
|
|
53
|
+
import * as i2 from "@angular/forms";
|
|
54
|
+
import * as i3 from "@memberjunction/ng-base-forms";
|
|
55
|
+
function RecordFilesPanelBase_div_2_div_1_button_4_Template(rf, ctx) { if (rf & 1) {
|
|
56
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
57
|
+
i0.ɵɵelementStart(0, "button", 13);
|
|
58
|
+
i0.ɵɵlistener("click", function RecordFilesPanelBase_div_2_div_1_button_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const f_r2 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.Open(f_r2)); });
|
|
59
|
+
i0.ɵɵtext(1, "Open");
|
|
60
|
+
i0.ɵɵelementEnd();
|
|
61
|
+
} if (rf & 2) {
|
|
62
|
+
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
63
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy);
|
|
64
|
+
} }
|
|
65
|
+
function RecordFilesPanelBase_div_2_div_1_ng_container_6_Template(rf, ctx) { if (rf & 1) {
|
|
66
|
+
i0.ɵɵelementContainerStart(0);
|
|
67
|
+
i0.ɵɵtext(1);
|
|
68
|
+
i0.ɵɵelementContainerEnd();
|
|
69
|
+
} if (rf & 2) {
|
|
70
|
+
const f_r2 = i0.ɵɵnextContext().$implicit;
|
|
71
|
+
i0.ɵɵadvance();
|
|
72
|
+
i0.ɵɵtextInterpolate1("", f_r2.Status, " \u00B7 ");
|
|
73
|
+
} }
|
|
74
|
+
function RecordFilesPanelBase_div_2_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
75
|
+
i0.ɵɵelementStart(0, "div", 8);
|
|
76
|
+
i0.ɵɵelement(1, "i", 9);
|
|
77
|
+
i0.ɵɵelementStart(2, "span", 10);
|
|
78
|
+
i0.ɵɵtext(3);
|
|
79
|
+
i0.ɵɵelementEnd();
|
|
80
|
+
i0.ɵɵtemplate(4, RecordFilesPanelBase_div_2_div_1_button_4_Template, 2, 1, "button", 11);
|
|
81
|
+
i0.ɵɵelementStart(5, "span", 12);
|
|
82
|
+
i0.ɵɵtemplate(6, RecordFilesPanelBase_div_2_div_1_ng_container_6_Template, 2, 1, "ng-container", 2);
|
|
83
|
+
i0.ɵɵtext(7);
|
|
84
|
+
i0.ɵɵpipe(8, "date");
|
|
85
|
+
i0.ɵɵelementEnd()();
|
|
86
|
+
} if (rf & 2) {
|
|
87
|
+
const f_r2 = ctx.$implicit;
|
|
88
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
89
|
+
i0.ɵɵadvance(3);
|
|
90
|
+
i0.ɵɵtextInterpolate(f_r2.Name);
|
|
91
|
+
i0.ɵɵadvance();
|
|
92
|
+
i0.ɵɵproperty("ngIf", ctx_r2.CanDownload && f_r2.AccountID);
|
|
93
|
+
i0.ɵɵadvance(2);
|
|
94
|
+
i0.ɵɵproperty("ngIf", f_r2.Status);
|
|
95
|
+
i0.ɵɵadvance();
|
|
96
|
+
i0.ɵɵtextInterpolate1(" ", f_r2.CreatedAt ? i0.ɵɵpipeBind2(8, 4, f_r2.CreatedAt, "mediumDate") : "", " ");
|
|
97
|
+
} }
|
|
98
|
+
function RecordFilesPanelBase_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
99
|
+
i0.ɵɵelementStart(0, "div");
|
|
100
|
+
i0.ɵɵtemplate(1, RecordFilesPanelBase_div_2_div_1_Template, 9, 7, "div", 7);
|
|
101
|
+
i0.ɵɵelementEnd();
|
|
102
|
+
} if (rf & 2) {
|
|
103
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
104
|
+
i0.ɵɵadvance();
|
|
105
|
+
i0.ɵɵproperty("ngForOf", ctx_r2.Files);
|
|
106
|
+
} }
|
|
107
|
+
function RecordFilesPanelBase_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
108
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
109
|
+
i0.ɵɵtext(1, " No documents attached to this record yet. ");
|
|
110
|
+
i0.ɵɵelementEnd();
|
|
111
|
+
} }
|
|
112
|
+
function RecordFilesPanelBase_div_4_Template(rf, ctx) { if (rf & 1) {
|
|
113
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
114
|
+
i0.ɵɵtext(1, "Loading documents\u2026");
|
|
115
|
+
i0.ɵɵelementEnd();
|
|
116
|
+
} }
|
|
117
|
+
function RecordFilesPanelBase_div_5_p_7_Template(rf, ctx) { if (rf & 1) {
|
|
118
|
+
i0.ɵɵelementStart(0, "p", 6);
|
|
119
|
+
i0.ɵɵtext(1, " No storage account is configured on this MJ instance, so there is nowhere to register a document to. Configure a ");
|
|
120
|
+
i0.ɵɵelementStart(2, "code");
|
|
121
|
+
i0.ɵɵtext(3, "MJ: File Storage Account");
|
|
122
|
+
i0.ɵɵelementEnd();
|
|
123
|
+
i0.ɵɵtext(4, " first \u2014 for SharePoint that also needs an Azure AD app registration, which is an IT task. ");
|
|
124
|
+
i0.ɵɵelementEnd();
|
|
125
|
+
} }
|
|
126
|
+
function RecordFilesPanelBase_div_5_p_8_Template(rf, ctx) { if (rf & 1) {
|
|
127
|
+
i0.ɵɵelementStart(0, "p", 6)(1, "strong");
|
|
128
|
+
i0.ɵɵtext(2, "Register");
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
i0.ɵɵtext(3, " points at a document that is ALREADY in storage \u2014 it creates the record and the link, and moves no bytes. That is the real flow here: executed PDFs arrive in SharePoint by a route MJ knows nothing about (PandaDoc \u2192 HubSpot \u2192 SharePoint), so the job is to find the object and record where it is, not to upload it. ");
|
|
131
|
+
i0.ɵɵelementEnd();
|
|
132
|
+
} }
|
|
133
|
+
function RecordFilesPanelBase_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
134
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
135
|
+
i0.ɵɵelementStart(0, "div", 15)(1, "div", 16);
|
|
136
|
+
i0.ɵɵtext(2, "Attach the executed document");
|
|
137
|
+
i0.ɵɵelementEnd();
|
|
138
|
+
i0.ɵɵelementStart(3, "div", 17)(4, "input", 18);
|
|
139
|
+
i0.ɵɵtwoWayListener("ngModelChange", function RecordFilesPanelBase_div_5_Template_input_ngModelChange_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.RegisterPath, $event) || (ctx_r2.RegisterPath = $event); return i0.ɵɵresetView($event); });
|
|
140
|
+
i0.ɵɵelementEnd();
|
|
141
|
+
i0.ɵɵelementStart(5, "button", 13);
|
|
142
|
+
i0.ɵɵlistener("click", function RecordFilesPanelBase_div_5_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.RegisterExisting()); });
|
|
143
|
+
i0.ɵɵtext(6, " Register existing ");
|
|
144
|
+
i0.ɵɵelementEnd()();
|
|
145
|
+
i0.ɵɵtemplate(7, RecordFilesPanelBase_div_5_p_7_Template, 5, 0, "p", 5)(8, RecordFilesPanelBase_div_5_p_8_Template, 4, 0, "p", 5);
|
|
146
|
+
i0.ɵɵelementEnd();
|
|
147
|
+
} if (rf & 2) {
|
|
148
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
149
|
+
i0.ɵɵadvance(4);
|
|
150
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r2.RegisterPath);
|
|
151
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy);
|
|
152
|
+
i0.ɵɵadvance();
|
|
153
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy || !ctx_r2.RegisterPath.trim() || !ctx_r2.DefaultAccountID);
|
|
154
|
+
i0.ɵɵadvance(2);
|
|
155
|
+
i0.ɵɵproperty("ngIf", !ctx_r2.DefaultAccountID);
|
|
156
|
+
i0.ɵɵadvance();
|
|
157
|
+
i0.ɵɵproperty("ngIf", ctx_r2.DefaultAccountID);
|
|
158
|
+
} }
|
|
159
|
+
function RecordFilesPanelBase_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
160
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "strong");
|
|
161
|
+
i0.ɵɵtext(2, "Nothing was attached.");
|
|
162
|
+
i0.ɵɵelementEnd();
|
|
163
|
+
i0.ɵɵtext(3);
|
|
164
|
+
i0.ɵɵelementEnd();
|
|
165
|
+
} if (rf & 2) {
|
|
166
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
167
|
+
i0.ɵɵadvance(3);
|
|
168
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.ActionError, " ");
|
|
169
|
+
} }
|
|
170
|
+
function RecordFilesPanelBase_p_7_Template(rf, ctx) { if (rf & 1) {
|
|
171
|
+
i0.ɵɵelementStart(0, "p", 6)(1, "a", 19);
|
|
172
|
+
i0.ɵɵtext(2, " Open in the signing provider ");
|
|
173
|
+
i0.ɵɵelementEnd();
|
|
174
|
+
i0.ɵɵtext(3, " \u2014 the always-works fallback. It needs no storage configuration at all, so it is the link that still resolves when SharePoint is misconfigured or the document was never filed. ");
|
|
175
|
+
i0.ɵɵelementEnd();
|
|
176
|
+
} if (rf & 2) {
|
|
177
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
178
|
+
i0.ɵɵadvance();
|
|
179
|
+
i0.ɵɵproperty("href", ctx_r2.SigningProviderURL, i0.ɵɵsanitizeUrl);
|
|
180
|
+
} }
|
|
181
|
+
function RecordFilesPanelBase_p_8_Template(rf, ctx) { if (rf & 1) {
|
|
182
|
+
i0.ɵɵelementStart(0, "p", 6);
|
|
183
|
+
i0.ɵɵtext(1);
|
|
184
|
+
i0.ɵɵelementEnd();
|
|
185
|
+
} if (rf & 2) {
|
|
186
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
187
|
+
i0.ɵɵadvance();
|
|
188
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.Files.length, " document(s) are attached to this record. Opening them \u2014 and seeing their names \u2014 needs document permission, which is granted to finance, legal and sales leadership. ");
|
|
189
|
+
} }
|
|
190
|
+
function ContractFilesPanel_div_2_div_1_button_4_Template(rf, ctx) { if (rf & 1) {
|
|
191
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
192
|
+
i0.ɵɵelementStart(0, "button", 13);
|
|
193
|
+
i0.ɵɵlistener("click", function ContractFilesPanel_div_2_div_1_button_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const f_r2 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.Open(f_r2)); });
|
|
194
|
+
i0.ɵɵtext(1, "Open");
|
|
195
|
+
i0.ɵɵelementEnd();
|
|
196
|
+
} if (rf & 2) {
|
|
197
|
+
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
198
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy);
|
|
199
|
+
} }
|
|
200
|
+
function ContractFilesPanel_div_2_div_1_ng_container_6_Template(rf, ctx) { if (rf & 1) {
|
|
201
|
+
i0.ɵɵelementContainerStart(0);
|
|
202
|
+
i0.ɵɵtext(1);
|
|
203
|
+
i0.ɵɵelementContainerEnd();
|
|
204
|
+
} if (rf & 2) {
|
|
205
|
+
const f_r2 = i0.ɵɵnextContext().$implicit;
|
|
206
|
+
i0.ɵɵadvance();
|
|
207
|
+
i0.ɵɵtextInterpolate1("", f_r2.Status, " \u00B7 ");
|
|
208
|
+
} }
|
|
209
|
+
function ContractFilesPanel_div_2_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
210
|
+
i0.ɵɵelementStart(0, "div", 8);
|
|
211
|
+
i0.ɵɵelement(1, "i", 9);
|
|
212
|
+
i0.ɵɵelementStart(2, "span", 10);
|
|
213
|
+
i0.ɵɵtext(3);
|
|
214
|
+
i0.ɵɵelementEnd();
|
|
215
|
+
i0.ɵɵtemplate(4, ContractFilesPanel_div_2_div_1_button_4_Template, 2, 1, "button", 11);
|
|
216
|
+
i0.ɵɵelementStart(5, "span", 12);
|
|
217
|
+
i0.ɵɵtemplate(6, ContractFilesPanel_div_2_div_1_ng_container_6_Template, 2, 1, "ng-container", 2);
|
|
218
|
+
i0.ɵɵtext(7);
|
|
219
|
+
i0.ɵɵpipe(8, "date");
|
|
220
|
+
i0.ɵɵelementEnd()();
|
|
221
|
+
} if (rf & 2) {
|
|
222
|
+
const f_r2 = ctx.$implicit;
|
|
223
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
224
|
+
i0.ɵɵadvance(3);
|
|
225
|
+
i0.ɵɵtextInterpolate(f_r2.Name);
|
|
226
|
+
i0.ɵɵadvance();
|
|
227
|
+
i0.ɵɵproperty("ngIf", ctx_r2.CanDownload && f_r2.AccountID);
|
|
228
|
+
i0.ɵɵadvance(2);
|
|
229
|
+
i0.ɵɵproperty("ngIf", f_r2.Status);
|
|
230
|
+
i0.ɵɵadvance();
|
|
231
|
+
i0.ɵɵtextInterpolate1(" ", f_r2.CreatedAt ? i0.ɵɵpipeBind2(8, 4, f_r2.CreatedAt, "mediumDate") : "", " ");
|
|
232
|
+
} }
|
|
233
|
+
function ContractFilesPanel_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
234
|
+
i0.ɵɵelementStart(0, "div");
|
|
235
|
+
i0.ɵɵtemplate(1, ContractFilesPanel_div_2_div_1_Template, 9, 7, "div", 7);
|
|
236
|
+
i0.ɵɵelementEnd();
|
|
237
|
+
} if (rf & 2) {
|
|
238
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
239
|
+
i0.ɵɵadvance();
|
|
240
|
+
i0.ɵɵproperty("ngForOf", ctx_r2.Files);
|
|
241
|
+
} }
|
|
242
|
+
function ContractFilesPanel_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
243
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
244
|
+
i0.ɵɵtext(1, " No documents attached to this record yet. ");
|
|
245
|
+
i0.ɵɵelementEnd();
|
|
246
|
+
} }
|
|
247
|
+
function ContractFilesPanel_div_4_Template(rf, ctx) { if (rf & 1) {
|
|
248
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
249
|
+
i0.ɵɵtext(1, "Loading documents\u2026");
|
|
250
|
+
i0.ɵɵelementEnd();
|
|
251
|
+
} }
|
|
252
|
+
function ContractFilesPanel_div_5_p_7_Template(rf, ctx) { if (rf & 1) {
|
|
253
|
+
i0.ɵɵelementStart(0, "p", 6);
|
|
254
|
+
i0.ɵɵtext(1, " No storage account is configured on this MJ instance, so there is nowhere to register a document to. Configure a ");
|
|
255
|
+
i0.ɵɵelementStart(2, "code");
|
|
256
|
+
i0.ɵɵtext(3, "MJ: File Storage Account");
|
|
257
|
+
i0.ɵɵelementEnd();
|
|
258
|
+
i0.ɵɵtext(4, " first \u2014 for SharePoint that also needs an Azure AD app registration, which is an IT task. ");
|
|
259
|
+
i0.ɵɵelementEnd();
|
|
260
|
+
} }
|
|
261
|
+
function ContractFilesPanel_div_5_p_8_Template(rf, ctx) { if (rf & 1) {
|
|
262
|
+
i0.ɵɵelementStart(0, "p", 6)(1, "strong");
|
|
263
|
+
i0.ɵɵtext(2, "Register");
|
|
264
|
+
i0.ɵɵelementEnd();
|
|
265
|
+
i0.ɵɵtext(3, " points at a document that is ALREADY in storage \u2014 it creates the record and the link, and moves no bytes. That is the real flow here: executed PDFs arrive in SharePoint by a route MJ knows nothing about (PandaDoc \u2192 HubSpot \u2192 SharePoint), so the job is to find the object and record where it is, not to upload it. ");
|
|
266
|
+
i0.ɵɵelementEnd();
|
|
267
|
+
} }
|
|
268
|
+
function ContractFilesPanel_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
269
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
270
|
+
i0.ɵɵelementStart(0, "div", 15)(1, "div", 16);
|
|
271
|
+
i0.ɵɵtext(2, "Attach the executed document");
|
|
272
|
+
i0.ɵɵelementEnd();
|
|
273
|
+
i0.ɵɵelementStart(3, "div", 17)(4, "input", 18);
|
|
274
|
+
i0.ɵɵtwoWayListener("ngModelChange", function ContractFilesPanel_div_5_Template_input_ngModelChange_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.RegisterPath, $event) || (ctx_r2.RegisterPath = $event); return i0.ɵɵresetView($event); });
|
|
275
|
+
i0.ɵɵelementEnd();
|
|
276
|
+
i0.ɵɵelementStart(5, "button", 13);
|
|
277
|
+
i0.ɵɵlistener("click", function ContractFilesPanel_div_5_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.RegisterExisting()); });
|
|
278
|
+
i0.ɵɵtext(6, " Register existing ");
|
|
279
|
+
i0.ɵɵelementEnd()();
|
|
280
|
+
i0.ɵɵtemplate(7, ContractFilesPanel_div_5_p_7_Template, 5, 0, "p", 5)(8, ContractFilesPanel_div_5_p_8_Template, 4, 0, "p", 5);
|
|
281
|
+
i0.ɵɵelementEnd();
|
|
282
|
+
} if (rf & 2) {
|
|
283
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
284
|
+
i0.ɵɵadvance(4);
|
|
285
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r2.RegisterPath);
|
|
286
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy);
|
|
287
|
+
i0.ɵɵadvance();
|
|
288
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy || !ctx_r2.RegisterPath.trim() || !ctx_r2.DefaultAccountID);
|
|
289
|
+
i0.ɵɵadvance(2);
|
|
290
|
+
i0.ɵɵproperty("ngIf", !ctx_r2.DefaultAccountID);
|
|
291
|
+
i0.ɵɵadvance();
|
|
292
|
+
i0.ɵɵproperty("ngIf", ctx_r2.DefaultAccountID);
|
|
293
|
+
} }
|
|
294
|
+
function ContractFilesPanel_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
295
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "strong");
|
|
296
|
+
i0.ɵɵtext(2, "Nothing was attached.");
|
|
297
|
+
i0.ɵɵelementEnd();
|
|
298
|
+
i0.ɵɵtext(3);
|
|
299
|
+
i0.ɵɵelementEnd();
|
|
300
|
+
} if (rf & 2) {
|
|
301
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
302
|
+
i0.ɵɵadvance(3);
|
|
303
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.ActionError, " ");
|
|
304
|
+
} }
|
|
305
|
+
function ContractFilesPanel_p_7_Template(rf, ctx) { if (rf & 1) {
|
|
306
|
+
i0.ɵɵelementStart(0, "p", 6)(1, "a", 19);
|
|
307
|
+
i0.ɵɵtext(2, " Open in the signing provider ");
|
|
308
|
+
i0.ɵɵelementEnd();
|
|
309
|
+
i0.ɵɵtext(3, " \u2014 the always-works fallback. It needs no storage configuration at all, so it is the link that still resolves when SharePoint is misconfigured or the document was never filed. ");
|
|
310
|
+
i0.ɵɵelementEnd();
|
|
311
|
+
} if (rf & 2) {
|
|
312
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
313
|
+
i0.ɵɵadvance();
|
|
314
|
+
i0.ɵɵproperty("href", ctx_r2.SigningProviderURL, i0.ɵɵsanitizeUrl);
|
|
315
|
+
} }
|
|
316
|
+
function ContractFilesPanel_p_8_Template(rf, ctx) { if (rf & 1) {
|
|
317
|
+
i0.ɵɵelementStart(0, "p", 6);
|
|
318
|
+
i0.ɵɵtext(1);
|
|
319
|
+
i0.ɵɵelementEnd();
|
|
320
|
+
} if (rf & 2) {
|
|
321
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
322
|
+
i0.ɵɵadvance();
|
|
323
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.Files.length, " document(s) are attached to this record. Opening them \u2014 and seeing their names \u2014 needs document permission, which is granted to finance, legal and sales leadership. ");
|
|
324
|
+
} }
|
|
325
|
+
const _c0 = "\n\n\n\n\n\n\n\n .mjc-file-row[_ngcontent-%COMP%] {\n display: flex; align-items: center; gap: var(--mj-space-3);\n padding: var(--mj-space-2) 0;\n font-size: var(--mj-text-sm);\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n .mjc-file-row[_ngcontent-%COMP%]:last-child { border-bottom: none; }\n .mjc-file-row[_ngcontent-%COMP%] .mjc-file-icon[_ngcontent-%COMP%] { color: var(--mj-status-error); }\n .mjc-file-row[_ngcontent-%COMP%] .mjc-file-name[_ngcontent-%COMP%] { font-weight: 600; overflow-wrap: anywhere; }\n .mjc-file-row[_ngcontent-%COMP%] .mjc-file-meta[_ngcontent-%COMP%] {\n margin-left: auto; font-size: var(--mj-text-xs);\n color: var(--mj-text-muted); white-space: nowrap;\n }\n .mjc-reg[_ngcontent-%COMP%] { margin-top: var(--mj-space-3); }\n .mjc-reg__title[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs); font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); margin-bottom: var(--mj-space-2);\n }\n .mjc-reg__row[_ngcontent-%COMP%] { display: flex; gap: var(--mj-space-2); align-items: center; flex-wrap: wrap; }\n .mjc-reg__row[_ngcontent-%COMP%] input[_ngcontent-%COMP%] { flex: 1 1 22rem; }";
|
|
326
|
+
function ContractTemplateFilesPanel_div_2_div_1_button_4_Template(rf, ctx) { if (rf & 1) {
|
|
327
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
328
|
+
i0.ɵɵelementStart(0, "button", 13);
|
|
329
|
+
i0.ɵɵlistener("click", function ContractTemplateFilesPanel_div_2_div_1_button_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const f_r2 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.Open(f_r2)); });
|
|
330
|
+
i0.ɵɵtext(1, "Open");
|
|
331
|
+
i0.ɵɵelementEnd();
|
|
332
|
+
} if (rf & 2) {
|
|
333
|
+
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
334
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy);
|
|
335
|
+
} }
|
|
336
|
+
function ContractTemplateFilesPanel_div_2_div_1_ng_container_6_Template(rf, ctx) { if (rf & 1) {
|
|
337
|
+
i0.ɵɵelementContainerStart(0);
|
|
338
|
+
i0.ɵɵtext(1);
|
|
339
|
+
i0.ɵɵelementContainerEnd();
|
|
340
|
+
} if (rf & 2) {
|
|
341
|
+
const f_r2 = i0.ɵɵnextContext().$implicit;
|
|
342
|
+
i0.ɵɵadvance();
|
|
343
|
+
i0.ɵɵtextInterpolate1("", f_r2.Status, " \u00B7 ");
|
|
344
|
+
} }
|
|
345
|
+
function ContractTemplateFilesPanel_div_2_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
346
|
+
i0.ɵɵelementStart(0, "div", 8);
|
|
347
|
+
i0.ɵɵelement(1, "i", 9);
|
|
348
|
+
i0.ɵɵelementStart(2, "span", 10);
|
|
349
|
+
i0.ɵɵtext(3);
|
|
350
|
+
i0.ɵɵelementEnd();
|
|
351
|
+
i0.ɵɵtemplate(4, ContractTemplateFilesPanel_div_2_div_1_button_4_Template, 2, 1, "button", 11);
|
|
352
|
+
i0.ɵɵelementStart(5, "span", 12);
|
|
353
|
+
i0.ɵɵtemplate(6, ContractTemplateFilesPanel_div_2_div_1_ng_container_6_Template, 2, 1, "ng-container", 2);
|
|
354
|
+
i0.ɵɵtext(7);
|
|
355
|
+
i0.ɵɵpipe(8, "date");
|
|
356
|
+
i0.ɵɵelementEnd()();
|
|
357
|
+
} if (rf & 2) {
|
|
358
|
+
const f_r2 = ctx.$implicit;
|
|
359
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
360
|
+
i0.ɵɵadvance(3);
|
|
361
|
+
i0.ɵɵtextInterpolate(f_r2.Name);
|
|
362
|
+
i0.ɵɵadvance();
|
|
363
|
+
i0.ɵɵproperty("ngIf", ctx_r2.CanDownload && f_r2.AccountID);
|
|
364
|
+
i0.ɵɵadvance(2);
|
|
365
|
+
i0.ɵɵproperty("ngIf", f_r2.Status);
|
|
366
|
+
i0.ɵɵadvance();
|
|
367
|
+
i0.ɵɵtextInterpolate1(" ", f_r2.CreatedAt ? i0.ɵɵpipeBind2(8, 4, f_r2.CreatedAt, "mediumDate") : "", " ");
|
|
368
|
+
} }
|
|
369
|
+
function ContractTemplateFilesPanel_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
370
|
+
i0.ɵɵelementStart(0, "div");
|
|
371
|
+
i0.ɵɵtemplate(1, ContractTemplateFilesPanel_div_2_div_1_Template, 9, 7, "div", 7);
|
|
372
|
+
i0.ɵɵelementEnd();
|
|
373
|
+
} if (rf & 2) {
|
|
374
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
375
|
+
i0.ɵɵadvance();
|
|
376
|
+
i0.ɵɵproperty("ngForOf", ctx_r2.Files);
|
|
377
|
+
} }
|
|
378
|
+
function ContractTemplateFilesPanel_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
379
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
380
|
+
i0.ɵɵtext(1, " No documents attached to this record yet. ");
|
|
381
|
+
i0.ɵɵelementEnd();
|
|
382
|
+
} }
|
|
383
|
+
function ContractTemplateFilesPanel_div_4_Template(rf, ctx) { if (rf & 1) {
|
|
384
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
385
|
+
i0.ɵɵtext(1, "Loading documents\u2026");
|
|
386
|
+
i0.ɵɵelementEnd();
|
|
387
|
+
} }
|
|
388
|
+
function ContractTemplateFilesPanel_div_5_p_7_Template(rf, ctx) { if (rf & 1) {
|
|
389
|
+
i0.ɵɵelementStart(0, "p", 6);
|
|
390
|
+
i0.ɵɵtext(1, " No storage account is configured on this MJ instance, so there is nowhere to register a document to. Configure a ");
|
|
391
|
+
i0.ɵɵelementStart(2, "code");
|
|
392
|
+
i0.ɵɵtext(3, "MJ: File Storage Account");
|
|
393
|
+
i0.ɵɵelementEnd();
|
|
394
|
+
i0.ɵɵtext(4, " first \u2014 for SharePoint that also needs an Azure AD app registration, which is an IT task. ");
|
|
395
|
+
i0.ɵɵelementEnd();
|
|
396
|
+
} }
|
|
397
|
+
function ContractTemplateFilesPanel_div_5_p_8_Template(rf, ctx) { if (rf & 1) {
|
|
398
|
+
i0.ɵɵelementStart(0, "p", 6)(1, "strong");
|
|
399
|
+
i0.ɵɵtext(2, "Register");
|
|
400
|
+
i0.ɵɵelementEnd();
|
|
401
|
+
i0.ɵɵtext(3, " points at a document that is ALREADY in storage \u2014 it creates the record and the link, and moves no bytes. That is the real flow here: executed PDFs arrive in SharePoint by a route MJ knows nothing about (PandaDoc \u2192 HubSpot \u2192 SharePoint), so the job is to find the object and record where it is, not to upload it. ");
|
|
402
|
+
i0.ɵɵelementEnd();
|
|
403
|
+
} }
|
|
404
|
+
function ContractTemplateFilesPanel_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
405
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
406
|
+
i0.ɵɵelementStart(0, "div", 15)(1, "div", 16);
|
|
407
|
+
i0.ɵɵtext(2, "Attach the executed document");
|
|
408
|
+
i0.ɵɵelementEnd();
|
|
409
|
+
i0.ɵɵelementStart(3, "div", 17)(4, "input", 18);
|
|
410
|
+
i0.ɵɵtwoWayListener("ngModelChange", function ContractTemplateFilesPanel_div_5_Template_input_ngModelChange_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.RegisterPath, $event) || (ctx_r2.RegisterPath = $event); return i0.ɵɵresetView($event); });
|
|
411
|
+
i0.ɵɵelementEnd();
|
|
412
|
+
i0.ɵɵelementStart(5, "button", 13);
|
|
413
|
+
i0.ɵɵlistener("click", function ContractTemplateFilesPanel_div_5_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.RegisterExisting()); });
|
|
414
|
+
i0.ɵɵtext(6, " Register existing ");
|
|
415
|
+
i0.ɵɵelementEnd()();
|
|
416
|
+
i0.ɵɵtemplate(7, ContractTemplateFilesPanel_div_5_p_7_Template, 5, 0, "p", 5)(8, ContractTemplateFilesPanel_div_5_p_8_Template, 4, 0, "p", 5);
|
|
417
|
+
i0.ɵɵelementEnd();
|
|
418
|
+
} if (rf & 2) {
|
|
419
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
420
|
+
i0.ɵɵadvance(4);
|
|
421
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r2.RegisterPath);
|
|
422
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy);
|
|
423
|
+
i0.ɵɵadvance();
|
|
424
|
+
i0.ɵɵproperty("disabled", ctx_r2.Busy || !ctx_r2.RegisterPath.trim() || !ctx_r2.DefaultAccountID);
|
|
425
|
+
i0.ɵɵadvance(2);
|
|
426
|
+
i0.ɵɵproperty("ngIf", !ctx_r2.DefaultAccountID);
|
|
427
|
+
i0.ɵɵadvance();
|
|
428
|
+
i0.ɵɵproperty("ngIf", ctx_r2.DefaultAccountID);
|
|
429
|
+
} }
|
|
430
|
+
function ContractTemplateFilesPanel_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
431
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "strong");
|
|
432
|
+
i0.ɵɵtext(2, "Nothing was attached.");
|
|
433
|
+
i0.ɵɵelementEnd();
|
|
434
|
+
i0.ɵɵtext(3);
|
|
435
|
+
i0.ɵɵelementEnd();
|
|
436
|
+
} if (rf & 2) {
|
|
437
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
438
|
+
i0.ɵɵadvance(3);
|
|
439
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.ActionError, " ");
|
|
440
|
+
} }
|
|
441
|
+
function ContractTemplateFilesPanel_p_7_Template(rf, ctx) { if (rf & 1) {
|
|
442
|
+
i0.ɵɵelementStart(0, "p", 6)(1, "a", 19);
|
|
443
|
+
i0.ɵɵtext(2, " Open in the signing provider ");
|
|
444
|
+
i0.ɵɵelementEnd();
|
|
445
|
+
i0.ɵɵtext(3, " \u2014 the always-works fallback. It needs no storage configuration at all, so it is the link that still resolves when SharePoint is misconfigured or the document was never filed. ");
|
|
446
|
+
i0.ɵɵelementEnd();
|
|
447
|
+
} if (rf & 2) {
|
|
448
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
449
|
+
i0.ɵɵadvance();
|
|
450
|
+
i0.ɵɵproperty("href", ctx_r2.SigningProviderURL, i0.ɵɵsanitizeUrl);
|
|
451
|
+
} }
|
|
452
|
+
function ContractTemplateFilesPanel_p_8_Template(rf, ctx) { if (rf & 1) {
|
|
453
|
+
i0.ɵɵelementStart(0, "p", 6);
|
|
454
|
+
i0.ɵɵtext(1);
|
|
455
|
+
i0.ɵɵelementEnd();
|
|
456
|
+
} if (rf & 2) {
|
|
457
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
458
|
+
i0.ɵɵadvance();
|
|
459
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.Files.length, " document(s) are attached to this record. Opening them \u2014 and seeing their names \u2014 needs document permission, which is granted to finance, legal and sales leadership. ");
|
|
460
|
+
} }
|
|
461
|
+
const PANEL_STYLES = `
|
|
462
|
+
/* SEMANTIC TOKENS ONLY — no hex, no bare px for colour/type/space.
|
|
463
|
+
*
|
|
464
|
+
* This panel came forward from v1 and its styles did not: it carried seven hex FALLBACKS
|
|
465
|
+
* inside var() (\`var(--mj-text-muted, #64748b)\`) and six hardcoded px font sizes. The
|
|
466
|
+
* fallbacks look harmless and are not — they are the value that renders the moment a token
|
|
467
|
+
* name is wrong, so a typo silently pins a light-mode colour into a dark theme instead of
|
|
468
|
+
* failing visibly. Every rule below now resolves through a token or a kit class. */
|
|
469
|
+
.mjc-file-row {
|
|
470
|
+
display: flex; align-items: center; gap: var(--mj-space-3);
|
|
471
|
+
padding: var(--mj-space-2) 0;
|
|
472
|
+
font-size: var(--mj-text-sm);
|
|
473
|
+
border-bottom: 1px solid var(--mj-border-subtle);
|
|
474
|
+
}
|
|
475
|
+
.mjc-file-row:last-child { border-bottom: none; }
|
|
476
|
+
.mjc-file-row .mjc-file-icon { color: var(--mj-status-error); }
|
|
477
|
+
.mjc-file-row .mjc-file-name { font-weight: 600; overflow-wrap: anywhere; }
|
|
478
|
+
.mjc-file-row .mjc-file-meta {
|
|
479
|
+
margin-left: auto; font-size: var(--mj-text-xs);
|
|
480
|
+
color: var(--mj-text-muted); white-space: nowrap;
|
|
481
|
+
}
|
|
482
|
+
.mjc-reg { margin-top: var(--mj-space-3); }
|
|
483
|
+
.mjc-reg__title {
|
|
484
|
+
font-size: var(--mj-text-xs); font-weight: 600;
|
|
485
|
+
text-transform: uppercase; letter-spacing: .04em;
|
|
486
|
+
color: var(--mj-text-muted); margin-bottom: var(--mj-space-2);
|
|
487
|
+
}
|
|
488
|
+
.mjc-reg__row { display: flex; gap: var(--mj-space-2); align-items: center; flex-wrap: wrap; }
|
|
489
|
+
.mjc-reg__row input { flex: 1 1 22rem; }
|
|
490
|
+
`;
|
|
491
|
+
const PANEL_TEMPLATE = `
|
|
492
|
+
<mj-collapsible-panel
|
|
493
|
+
SectionKey="recordFiles"
|
|
494
|
+
SectionName="Documents"
|
|
495
|
+
Icon="fa-solid fa-folder-open"
|
|
496
|
+
[Form]="FormComponent"
|
|
497
|
+
[FormContext]="FormContext"
|
|
498
|
+
>
|
|
499
|
+
<div class="mjc-body">
|
|
500
|
+
<div *ngIf="Files.length">
|
|
501
|
+
<div class="mjc-file-row" *ngFor="let f of Files">
|
|
502
|
+
<i class="fa-solid fa-file-lines mjc-file-icon" aria-hidden="true"></i>
|
|
503
|
+
<span class="mjc-file-name">{{ f.Name }}</span>
|
|
504
|
+
<button type="button" class="mjc-btn mjc-btn--flat mjc-btn--sm" *ngIf="CanDownload && f.AccountID"
|
|
505
|
+
(click)="Open(f)" [disabled]="Busy">Open</button>
|
|
506
|
+
<span class="mjc-file-meta">
|
|
507
|
+
<ng-container *ngIf="f.Status">{{ f.Status }} · </ng-container>
|
|
508
|
+
{{ f.CreatedAt ? (f.CreatedAt | date: 'mediumDate') : '' }}
|
|
509
|
+
</span>
|
|
510
|
+
</div>
|
|
511
|
+
</div>
|
|
512
|
+
|
|
513
|
+
<div class="mjc-empty" *ngIf="!IsLoading && !Files.length">
|
|
514
|
+
No documents attached to this record yet.
|
|
515
|
+
</div>
|
|
516
|
+
<div class="mjc-empty" *ngIf="IsLoading">Loading documents…</div>
|
|
517
|
+
|
|
518
|
+
<div class="mjc-reg" *ngIf="EditMode && CanDownload">
|
|
519
|
+
<div class="mjc-reg__title">Attach the executed document</div>
|
|
520
|
+
|
|
521
|
+
<div class="mjc-reg__row">
|
|
522
|
+
<input type="text" [(ngModel)]="RegisterPath" [disabled]="Busy"
|
|
523
|
+
placeholder="Path in storage, e.g. Contracts/2026/CTR-000004.pdf"
|
|
524
|
+
aria-label="Path of an existing document in storage" />
|
|
525
|
+
<button type="button" class="mjc-btn mjc-btn--flat mjc-btn--sm" (click)="RegisterExisting()"
|
|
526
|
+
[disabled]="Busy || !RegisterPath.trim() || !DefaultAccountID">
|
|
527
|
+
Register existing
|
|
528
|
+
</button>
|
|
529
|
+
</div>
|
|
530
|
+
|
|
531
|
+
<p class="mjc-note" *ngIf="!DefaultAccountID">
|
|
532
|
+
No storage account is configured on this MJ instance, so there is nowhere to register a
|
|
533
|
+
document to. Configure a <code>MJ: File Storage Account</code> first — for SharePoint that
|
|
534
|
+
also needs an Azure AD app registration, which is an IT task.
|
|
535
|
+
</p>
|
|
536
|
+
|
|
537
|
+
<p class="mjc-note" *ngIf="DefaultAccountID">
|
|
538
|
+
<strong>Register</strong> points at a document that is ALREADY in storage — it creates the
|
|
539
|
+
record and the link, and moves no bytes. That is the real flow here: executed PDFs arrive in
|
|
540
|
+
SharePoint by a route MJ knows nothing about (PandaDoc → HubSpot → SharePoint), so
|
|
541
|
+
the job is to find the object and record where it is, not to upload it.
|
|
542
|
+
</p>
|
|
543
|
+
</div>
|
|
544
|
+
|
|
545
|
+
<div class="mjc-note" *ngIf="ActionError">
|
|
546
|
+
<strong>Nothing was attached.</strong> {{ ActionError }}
|
|
547
|
+
</div>
|
|
548
|
+
|
|
549
|
+
<p class="mjc-note" *ngIf="SigningProviderURL">
|
|
550
|
+
<a [href]="SigningProviderURL" target="_blank" rel="noopener noreferrer">
|
|
551
|
+
Open in the signing provider
|
|
552
|
+
</a>
|
|
553
|
+
— the always-works fallback. It needs no storage configuration at all, so it is the link that
|
|
554
|
+
still resolves when SharePoint is misconfigured or the document was never filed.
|
|
555
|
+
</p>
|
|
556
|
+
|
|
557
|
+
<p class="mjc-note" *ngIf="!CanDownload && Files.length">
|
|
558
|
+
{{ Files.length }} document(s) are attached to this record. Opening them — and seeing their
|
|
559
|
+
names — needs document permission, which is granted to finance, legal and sales leadership.
|
|
560
|
+
</p>
|
|
561
|
+
|
|
562
|
+
<p class="mjc-note">
|
|
563
|
+
Documents attach through MJ's polymorphic <code>__mj.FileEntityRecordLink</code>
|
|
564
|
+
(<code>EntityID</code> + <code>RecordID</code>) rather than a column on this schema — so one record
|
|
565
|
+
can carry the signed PDF, its exhibits and a countersigned amendment, and no future table needs a
|
|
566
|
+
new column to acquire paper.
|
|
567
|
+
</p>
|
|
568
|
+
</div>
|
|
569
|
+
</mj-collapsible-panel>
|
|
570
|
+
`;
|
|
571
|
+
const ENTITY_FILE_LINKS = 'MJ: File Entity Record Links';
|
|
572
|
+
const ENTITY_FILES = 'MJ: Files';
|
|
573
|
+
const ENTITY_STORAGE_ACCOUNTS = 'MJ: File Storage Accounts';
|
|
574
|
+
/**
|
|
575
|
+
* All of the behaviour. Deliberately knows nothing about contracts — see the file header.
|
|
576
|
+
*/
|
|
577
|
+
export class RecordFilesPanelBase extends BaseFormPanel {
|
|
578
|
+
cdr = inject(ChangeDetectorRef);
|
|
579
|
+
fileOpen = inject(FileOpenService);
|
|
580
|
+
Files = [];
|
|
581
|
+
IsLoading = true;
|
|
582
|
+
/** The storage account every action runs against, or null when the instance has none. */
|
|
583
|
+
DefaultAccountID = null;
|
|
584
|
+
/** Path typed into the register box. */
|
|
585
|
+
RegisterPath = '';
|
|
586
|
+
/** True while an action is in flight, so the buttons cannot be double-fired. */
|
|
587
|
+
Busy = false;
|
|
588
|
+
/** The last action failure, shown in the panel rather than a toast that scrolls away. */
|
|
589
|
+
ActionError = null;
|
|
590
|
+
/**
|
|
591
|
+
* Whether this user may DOWNLOAD, as opposed to merely see that documents exist (D-9).
|
|
592
|
+
*
|
|
593
|
+
* ⚠ THE OBVIOUS IMPLEMENTATION IS VACUOUS, and this was written that way first. Reading the HOST
|
|
594
|
+
* RECORD's `CanRead` asks "may they see this contract" — but everyone looking at the panel can
|
|
595
|
+
* already see the contract, so the gate returns true for every viewer and the restriction can
|
|
596
|
+
* never bind. D-9 exists precisely to separate two populations (leadership/finance/legal may
|
|
597
|
+
* download; everyone else sees the record), and gating on record visibility collapses them into
|
|
598
|
+
* one. Caught on review of PR #9.
|
|
599
|
+
*
|
|
600
|
+
* So the gate is the **`MJ: Files` entity's** read permission — a surface genuinely DISTINCT from
|
|
601
|
+
* record visibility. An administrator grants Files read to the three D-9 roles and to nobody else,
|
|
602
|
+
* and the check never mentions contracts, so the panel stays entity-agnostic for donation to MJ.
|
|
603
|
+
*
|
|
604
|
+
* `GetUserPermisions` is the platform's own allow/deny aggregation across the user's roles, so this
|
|
605
|
+
* cannot drift from what the instance actually grants. FALSE on any doubt: a hidden Open button is
|
|
606
|
+
* a nuisance, a leaked executed contract is not.
|
|
607
|
+
*/
|
|
608
|
+
get CanDownload() {
|
|
609
|
+
try {
|
|
610
|
+
// D-25 all the way down: the provider answers BOTH questions — who the user is and what
|
|
611
|
+
// the entity's permissions are. An earlier version had the provider in hand and then asked
|
|
612
|
+
// a bare `new Metadata()` for the entity anyway, which is the exact pattern the rule bans.
|
|
613
|
+
const provider = this.FormComponent?.ProviderToUse ?? Metadata.Provider;
|
|
614
|
+
const user = provider?.CurrentUser;
|
|
615
|
+
if (!user)
|
|
616
|
+
return false;
|
|
617
|
+
const files = provider?.EntityByName(ENTITY_FILES);
|
|
618
|
+
return files?.GetUserPermisions(user)?.CanRead === true;
|
|
619
|
+
}
|
|
620
|
+
catch {
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* The signing provider's own URL, when the host record carries one — the always-works fallback.
|
|
626
|
+
*
|
|
627
|
+
* Read generically (`SigningProviderURL`) rather than by typing this panel to ContractEntity,
|
|
628
|
+
* because the panel is deliberately entity-agnostic and intended for donation to MJ. A host entity
|
|
629
|
+
* without the field simply has no fallback link.
|
|
630
|
+
*/
|
|
631
|
+
get SigningProviderURL() {
|
|
632
|
+
try {
|
|
633
|
+
const v = this.Record?.Get?.('SigningProviderURL');
|
|
634
|
+
return v ? String(v) : null;
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
get HasFiles() {
|
|
641
|
+
return this.Files.length > 0;
|
|
642
|
+
}
|
|
643
|
+
async ngOnInit() {
|
|
644
|
+
await this.resolveAccount();
|
|
645
|
+
await this.load();
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Two reads: the links for this record, then the files they name. It is deliberately two rather
|
|
649
|
+
* than one join — `RunView` is the app's only sanctioned read path (§11.1), and the second read
|
|
650
|
+
* is skipped entirely when a record has no links, which is the common case.
|
|
651
|
+
*/
|
|
652
|
+
async load() {
|
|
653
|
+
this.IsLoading = true;
|
|
654
|
+
try {
|
|
655
|
+
const entityID = this.Record?.EntityInfo?.ID;
|
|
656
|
+
const recordID = this.Record?.PrimaryKey?.GetValueByFieldName('ID') ?? this.Record?.PrimaryKey?.Values();
|
|
657
|
+
if (!entityID || recordID == null) {
|
|
658
|
+
this.Files = [];
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
// D-25: the HOST FORM's provider, not the ambient one. A panel showing files for a record
|
|
662
|
+
// must read from wherever that record came from; the flagged bare `new RunView()` was correct
|
|
663
|
+
// only while exactly one provider existed.
|
|
664
|
+
const rv = ScopedRunView(this.FormComponent?.ProviderToUse);
|
|
665
|
+
const links = await rv.RunView({
|
|
666
|
+
EntityName: ENTITY_FILE_LINKS,
|
|
667
|
+
Fields: ['ID', 'FileID'],
|
|
668
|
+
ExtraFilter: `EntityID='${entityID}' AND RecordID='${String(recordID).replace(/'/g, "''")}'`,
|
|
669
|
+
ResultType: 'simple',
|
|
670
|
+
});
|
|
671
|
+
if (!links?.Success || !links.Results.length) {
|
|
672
|
+
this.Files = [];
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
// DECIDED CONSCIOUSLY (PR #9 review): a user without Files read sees the COUNT, not the
|
|
676
|
+
// names. Two reasons. Reading `MJ: Files` is exactly what they lack permission for, so
|
|
677
|
+
// attempting it would either fail or — worse — succeed and leak the filenames the gate is
|
|
678
|
+
// meant to withhold; and a filename is itself disclosure ("Termination Notice - Northwind
|
|
679
|
+
// v3.pdf" tells you plenty). Item 9's done-when is "sees the record but no download", and
|
|
680
|
+
// knowing that two documents exist satisfies it without disclosing what they are.
|
|
681
|
+
if (!this.CanDownload) {
|
|
682
|
+
this.Files = links.Results.map((l) => ({
|
|
683
|
+
LinkID: l.ID,
|
|
684
|
+
FileID: l.FileID,
|
|
685
|
+
Name: 'Document (name withheld)',
|
|
686
|
+
Provider: null,
|
|
687
|
+
Status: null,
|
|
688
|
+
CreatedAt: null,
|
|
689
|
+
ProviderKey: null,
|
|
690
|
+
AccountID: null,
|
|
691
|
+
}));
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
const ids = links.Results.map((l) => `'${l.FileID}'`).join(',');
|
|
695
|
+
const files = await rv.RunView({
|
|
696
|
+
EntityName: ENTITY_FILES,
|
|
697
|
+
Fields: ['ID', 'Name', 'ProviderKey', 'Status', '__mj_CreatedAt', 'ProviderID'],
|
|
698
|
+
ExtraFilter: `ID IN (${ids})`,
|
|
699
|
+
ResultType: 'simple',
|
|
700
|
+
});
|
|
701
|
+
const byID = new Map((files?.Success ? files.Results : []).map((f) => [f.ID, f]));
|
|
702
|
+
this.Files = links.Results.map((l) => {
|
|
703
|
+
const f = byID.get(l.FileID);
|
|
704
|
+
return {
|
|
705
|
+
LinkID: l.ID,
|
|
706
|
+
FileID: l.FileID,
|
|
707
|
+
Name: f?.Name ?? '(file not found)',
|
|
708
|
+
Provider: f?.ProviderKey ?? null,
|
|
709
|
+
Status: f?.Status ?? null,
|
|
710
|
+
CreatedAt: f?.__mj_CreatedAt ?? null,
|
|
711
|
+
ProviderKey: f?.ProviderKey ?? null,
|
|
712
|
+
// The download API takes a storage ACCOUNT, and a File row names a PROVIDER (the
|
|
713
|
+
// type). We resolve the instance's account once in ngOnInit and use it for every
|
|
714
|
+
// row — correct while an instance has one account, which is the case here and is
|
|
715
|
+
// asserted rather than assumed: the Open button is hidden when there is none.
|
|
716
|
+
AccountID: this.DefaultAccountID,
|
|
717
|
+
};
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
finally {
|
|
721
|
+
this.IsLoading = false;
|
|
722
|
+
this.cdr.detectChanges();
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Resolve the instance's storage account once.
|
|
727
|
+
*
|
|
728
|
+
* A `File` row names a PROVIDER (the type — "SharePoint"); the download and upload APIs take an
|
|
729
|
+
* ACCOUNT (a configured instance of one). Nothing on the link or the file gives us the account, so
|
|
730
|
+
* it is looked up here. When there is none, every affordance that needs storage is HIDDEN rather
|
|
731
|
+
* than shown-and-failing: an Open button that always errors is worse than no button.
|
|
732
|
+
*/
|
|
733
|
+
async resolveAccount() {
|
|
734
|
+
try {
|
|
735
|
+
const rv = ScopedRunView(this.FormComponent?.ProviderToUse);
|
|
736
|
+
const accounts = await rv.RunView({
|
|
737
|
+
EntityName: ENTITY_STORAGE_ACCOUNTS,
|
|
738
|
+
Fields: ['ID', 'Name'],
|
|
739
|
+
OrderBy: '__mj_CreatedAt ASC',
|
|
740
|
+
MaxRows: 1,
|
|
741
|
+
ResultType: 'simple',
|
|
742
|
+
});
|
|
743
|
+
this.DefaultAccountID = accounts?.Success && accounts.Results.length ? accounts.Results[0].ID : null;
|
|
744
|
+
}
|
|
745
|
+
catch {
|
|
746
|
+
this.DefaultAccountID = null;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* REGISTER an object that is already in storage (§6.5) — the flow this app actually needs.
|
|
751
|
+
*
|
|
752
|
+
* MJ's own `CreateFile` mutation assumes an UPLOAD: it creates the row `Pending` and hands back a
|
|
753
|
+
* pre-auth URL for the browser to push bytes to. Our executed PDFs are already in SharePoint,
|
|
754
|
+
* delivered by PandaDoc → HubSpot → SharePoint, a route MJ knows nothing about. So there is nothing
|
|
755
|
+
* to upload — the work is to create the `MJ: Files` row with the right `ProviderKey` and link it.
|
|
756
|
+
*
|
|
757
|
+
* Done entirely through the ENTITY layer, no GraphQL: two saves, and the second is skipped if the
|
|
758
|
+
* first fails, so a half-registered document (a file row nothing points at) cannot result. It
|
|
759
|
+
* verifies the object EXISTS first — registering a path that is not there produces a record that
|
|
760
|
+
* lies, and a document nobody can open is exactly what this panel exists to prevent.
|
|
761
|
+
*/
|
|
762
|
+
async RegisterExisting() {
|
|
763
|
+
const path = this.RegisterPath.trim();
|
|
764
|
+
if (!path || !this.DefaultAccountID)
|
|
765
|
+
return;
|
|
766
|
+
this.Busy = true;
|
|
767
|
+
this.ActionError = null;
|
|
768
|
+
try {
|
|
769
|
+
const provider = this.FormComponent?.ProviderToUse ?? Metadata.Provider;
|
|
770
|
+
const client = new GraphQLFileStorageClient(provider);
|
|
771
|
+
const exists = await client.ObjectExists(this.DefaultAccountID, path);
|
|
772
|
+
if (!exists) {
|
|
773
|
+
this.ActionError =
|
|
774
|
+
`Nothing is stored at "${path}". Check the path — registering a document that is not ` +
|
|
775
|
+
`there would create a record that cannot be opened.`;
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const entityID = this.Record?.EntityInfo?.ID;
|
|
779
|
+
const recordID = this.Record?.PrimaryKey?.GetValueByFieldName('ID');
|
|
780
|
+
if (!entityID || recordID == null) {
|
|
781
|
+
this.ActionError = 'This record has no primary key yet — save it before attaching a document.';
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
// Constructed through the HOST FORM's provider (D-25), so the file and link rows are
|
|
785
|
+
// written wherever the record being annotated came from.
|
|
786
|
+
const md = this.FormComponent?.ProviderToUse ?? Metadata.Provider;
|
|
787
|
+
const file = await md.GetEntityObject(ENTITY_FILES);
|
|
788
|
+
file.NewRecord();
|
|
789
|
+
file.Set('Name', path.split('/').pop() || path);
|
|
790
|
+
file.Set('ProviderID', await this.resolveProviderID());
|
|
791
|
+
file.Set('ProviderKey', path);
|
|
792
|
+
// 'Uploaded' is the status meaning "the bytes are there". The row was not created BY an
|
|
793
|
+
// upload, but the state it describes is identical, and lying about it as 'Pending' would
|
|
794
|
+
// make the file invisible to anything that filters on readiness.
|
|
795
|
+
file.Set('Status', 'Uploaded');
|
|
796
|
+
if (!(await file.Save())) {
|
|
797
|
+
this.ActionError = file.LatestResult?.Message ?? 'Could not create the file record.';
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const link = await md.GetEntityObject(ENTITY_FILE_LINKS);
|
|
801
|
+
link.NewRecord();
|
|
802
|
+
link.Set('FileID', file.Get('ID'));
|
|
803
|
+
link.Set('EntityID', entityID);
|
|
804
|
+
link.Set('RecordID', String(recordID));
|
|
805
|
+
if (!(await link.Save())) {
|
|
806
|
+
this.ActionError =
|
|
807
|
+
`The document record was created but could not be linked to this record ` +
|
|
808
|
+
`(${link.LatestResult?.Message ?? 'save failed'}). It will not appear here until it is.`;
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
this.RegisterPath = '';
|
|
812
|
+
await this.load();
|
|
813
|
+
}
|
|
814
|
+
catch (err) {
|
|
815
|
+
this.ActionError = String(err);
|
|
816
|
+
}
|
|
817
|
+
finally {
|
|
818
|
+
this.Busy = false;
|
|
819
|
+
this.cdr.detectChanges();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
/** The provider (type) behind the resolved account — a File row references the provider, not the account. */
|
|
823
|
+
async resolveProviderID() {
|
|
824
|
+
try {
|
|
825
|
+
const rv = ScopedRunView(this.FormComponent?.ProviderToUse);
|
|
826
|
+
const r = await rv.RunView({
|
|
827
|
+
EntityName: ENTITY_STORAGE_ACCOUNTS,
|
|
828
|
+
Fields: ['ProviderID'],
|
|
829
|
+
ExtraFilter: `ID = '${this.DefaultAccountID}'`,
|
|
830
|
+
ResultType: 'simple',
|
|
831
|
+
});
|
|
832
|
+
return r?.Success && r.Results.length ? r.Results[0].ProviderID : null;
|
|
833
|
+
}
|
|
834
|
+
catch {
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Open a document via a time-limited pre-auth URL.
|
|
840
|
+
*
|
|
841
|
+
* Delegated to MJ's own `FileOpenService`, which mints the URL over GraphQL and is provider-agnostic
|
|
842
|
+
* — the same call opens a SharePoint object, an S3 object or a Box object. Its `Provider` is set from
|
|
843
|
+
* the host form's (D-25), which the service's own docs prescribe for multi-provider clients.
|
|
844
|
+
*
|
|
845
|
+
* The URL is pre-authenticated and time-limited, so the bytes never proxy through MJAPI and the link
|
|
846
|
+
* cannot be shared indefinitely.
|
|
847
|
+
*/
|
|
848
|
+
async Open(f) {
|
|
849
|
+
if (!f.AccountID || !f.ProviderKey) {
|
|
850
|
+
this.ActionError = 'This document has no storage path recorded, so there is nothing to open.';
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
this.Busy = true;
|
|
854
|
+
this.ActionError = null;
|
|
855
|
+
try {
|
|
856
|
+
this.fileOpen.Provider = this.FormComponent?.ProviderToUse ?? null;
|
|
857
|
+
const ok = await this.fileOpen.OpenFile(f.AccountID, f.ProviderKey);
|
|
858
|
+
if (!ok)
|
|
859
|
+
this.ActionError = 'The storage provider would not produce a download link for that document.';
|
|
860
|
+
}
|
|
861
|
+
catch (err) {
|
|
862
|
+
this.ActionError = String(err);
|
|
863
|
+
}
|
|
864
|
+
finally {
|
|
865
|
+
this.Busy = false;
|
|
866
|
+
this.cdr.detectChanges();
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵRecordFilesPanelBase_BaseFactory; return function RecordFilesPanelBase_Factory(__ngFactoryType__) { return (ɵRecordFilesPanelBase_BaseFactory || (ɵRecordFilesPanelBase_BaseFactory = i0.ɵɵgetInheritedFactory(RecordFilesPanelBase)))(__ngFactoryType__ || RecordFilesPanelBase); }; })();
|
|
870
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RecordFilesPanelBase, selectors: [["mjc-record-files-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 20, vars: 9, consts: [["SectionKey", "recordFiles", "SectionName", "Documents", "Icon", "fa-solid fa-folder-open", 3, "Form", "FormContext"], [1, "mjc-body"], [4, "ngIf"], ["class", "mjc-empty", 4, "ngIf"], ["class", "mjc-reg", 4, "ngIf"], ["class", "mjc-note", 4, "ngIf"], [1, "mjc-note"], ["class", "mjc-file-row", 4, "ngFor", "ngForOf"], [1, "mjc-file-row"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-lines", "mjc-file-icon"], [1, "mjc-file-name"], ["type", "button", "class", "mjc-btn mjc-btn--flat mjc-btn--sm", 3, "disabled", "click", 4, "ngIf"], [1, "mjc-file-meta"], ["type", "button", 1, "mjc-btn", "mjc-btn--flat", "mjc-btn--sm", 3, "click", "disabled"], [1, "mjc-empty"], [1, "mjc-reg"], [1, "mjc-reg__title"], [1, "mjc-reg__row"], ["type", "text", "placeholder", "Path in storage, e.g. Contracts/2026/CTR-000004.pdf", "aria-label", "Path of an existing document in storage", 3, "ngModelChange", "ngModel", "disabled"], ["target", "_blank", "rel", "noopener noreferrer", 3, "href"]], template: function RecordFilesPanelBase_Template(rf, ctx) { if (rf & 1) {
|
|
871
|
+
i0.ɵɵelementStart(0, "mj-collapsible-panel", 0)(1, "div", 1);
|
|
872
|
+
i0.ɵɵtemplate(2, RecordFilesPanelBase_div_2_Template, 2, 1, "div", 2)(3, RecordFilesPanelBase_div_3_Template, 2, 0, "div", 3)(4, RecordFilesPanelBase_div_4_Template, 2, 0, "div", 3)(5, RecordFilesPanelBase_div_5_Template, 9, 5, "div", 4)(6, RecordFilesPanelBase_div_6_Template, 4, 1, "div", 5)(7, RecordFilesPanelBase_p_7_Template, 4, 1, "p", 5)(8, RecordFilesPanelBase_p_8_Template, 2, 1, "p", 5);
|
|
873
|
+
i0.ɵɵelementStart(9, "p", 6);
|
|
874
|
+
i0.ɵɵtext(10, " Documents attach through MJ's polymorphic ");
|
|
875
|
+
i0.ɵɵelementStart(11, "code");
|
|
876
|
+
i0.ɵɵtext(12, "__mj.FileEntityRecordLink");
|
|
877
|
+
i0.ɵɵelementEnd();
|
|
878
|
+
i0.ɵɵtext(13, " (");
|
|
879
|
+
i0.ɵɵelementStart(14, "code");
|
|
880
|
+
i0.ɵɵtext(15, "EntityID");
|
|
881
|
+
i0.ɵɵelementEnd();
|
|
882
|
+
i0.ɵɵtext(16, " + ");
|
|
883
|
+
i0.ɵɵelementStart(17, "code");
|
|
884
|
+
i0.ɵɵtext(18, "RecordID");
|
|
885
|
+
i0.ɵɵelementEnd();
|
|
886
|
+
i0.ɵɵtext(19, ") rather than a column on this schema \u2014 so one record can carry the signed PDF, its exhibits and a countersigned amendment, and no future table needs a new column to acquire paper. ");
|
|
887
|
+
i0.ɵɵelementEnd()()();
|
|
888
|
+
} if (rf & 2) {
|
|
889
|
+
i0.ɵɵproperty("Form", ctx.FormComponent)("FormContext", ctx.FormContext);
|
|
890
|
+
i0.ɵɵadvance(2);
|
|
891
|
+
i0.ɵɵproperty("ngIf", ctx.Files.length);
|
|
892
|
+
i0.ɵɵadvance();
|
|
893
|
+
i0.ɵɵproperty("ngIf", !ctx.IsLoading && !ctx.Files.length);
|
|
894
|
+
i0.ɵɵadvance();
|
|
895
|
+
i0.ɵɵproperty("ngIf", ctx.IsLoading);
|
|
896
|
+
i0.ɵɵadvance();
|
|
897
|
+
i0.ɵɵproperty("ngIf", ctx.EditMode && ctx.CanDownload);
|
|
898
|
+
i0.ɵɵadvance();
|
|
899
|
+
i0.ɵɵproperty("ngIf", ctx.ActionError);
|
|
900
|
+
i0.ɵɵadvance();
|
|
901
|
+
i0.ɵɵproperty("ngIf", ctx.SigningProviderURL);
|
|
902
|
+
i0.ɵɵadvance();
|
|
903
|
+
i0.ɵɵproperty("ngIf", !ctx.CanDownload && ctx.Files.length);
|
|
904
|
+
} }, dependencies: [CommonModule, i1.NgForOf, i1.NgIf, FormsModule, i2.DefaultValueAccessor, i2.NgControlStatus, i2.NgModel, BaseFormsModule, i3.MjCollapsiblePanelComponent, i1.DatePipe], styles: [_c0] });
|
|
905
|
+
}
|
|
906
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordFilesPanelBase, [{
|
|
907
|
+
type: Component,
|
|
908
|
+
args: [{ standalone: true, imports: [CommonModule, FormsModule, BaseFormsModule], selector: 'mjc-record-files-panel', template: PANEL_TEMPLATE, styles: ["\n /* SEMANTIC TOKENS ONLY \u2014 no hex, no bare px for colour/type/space.\n *\n * This panel came forward from v1 and its styles did not: it carried seven hex FALLBACKS\n * inside var() (`var(--mj-text-muted, #64748b)`) and six hardcoded px font sizes. The\n * fallbacks look harmless and are not \u2014 they are the value that renders the moment a token\n * name is wrong, so a typo silently pins a light-mode colour into a dark theme instead of\n * failing visibly. Every rule below now resolves through a token or a kit class. */\n .mjc-file-row {\n display: flex; align-items: center; gap: var(--mj-space-3);\n padding: var(--mj-space-2) 0;\n font-size: var(--mj-text-sm);\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n .mjc-file-row:last-child { border-bottom: none; }\n .mjc-file-row .mjc-file-icon { color: var(--mj-status-error); }\n .mjc-file-row .mjc-file-name { font-weight: 600; overflow-wrap: anywhere; }\n .mjc-file-row .mjc-file-meta {\n margin-left: auto; font-size: var(--mj-text-xs);\n color: var(--mj-text-muted); white-space: nowrap;\n }\n .mjc-reg { margin-top: var(--mj-space-3); }\n .mjc-reg__title {\n font-size: var(--mj-text-xs); font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); margin-bottom: var(--mj-space-2);\n }\n .mjc-reg__row { display: flex; gap: var(--mj-space-2); align-items: center; flex-wrap: wrap; }\n .mjc-reg__row input { flex: 1 1 22rem; }\n"] }]
|
|
909
|
+
}], null, null); })();
|
|
910
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RecordFilesPanelBase, { className: "RecordFilesPanelBase", filePath: "lib/form-panels/record-files.panel.ts", lineNumber: 187 }); })();
|
|
911
|
+
// =====================================================================================
|
|
912
|
+
// The only contracts-specific code in this file: WHICH entities get the panel.
|
|
913
|
+
// One thin subclass per entity, because a class can carry exactly one registration — and each
|
|
914
|
+
// needs its own @Component, so they share the hoisted template/styles rather than duplicating them.
|
|
915
|
+
// =====================================================================================
|
|
916
|
+
let ContractFilesPanel = class ContractFilesPanel extends RecordFilesPanelBase {
|
|
917
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵContractFilesPanel_BaseFactory; return function ContractFilesPanel_Factory(__ngFactoryType__) { return (ɵContractFilesPanel_BaseFactory || (ɵContractFilesPanel_BaseFactory = i0.ɵɵgetInheritedFactory(ContractFilesPanel)))(__ngFactoryType__ || ContractFilesPanel); }; })();
|
|
918
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ContractFilesPanel, selectors: [["mjc-contract-files-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 20, vars: 9, consts: [["SectionKey", "recordFiles", "SectionName", "Documents", "Icon", "fa-solid fa-folder-open", 3, "Form", "FormContext"], [1, "mjc-body"], [4, "ngIf"], ["class", "mjc-empty", 4, "ngIf"], ["class", "mjc-reg", 4, "ngIf"], ["class", "mjc-note", 4, "ngIf"], [1, "mjc-note"], ["class", "mjc-file-row", 4, "ngFor", "ngForOf"], [1, "mjc-file-row"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-lines", "mjc-file-icon"], [1, "mjc-file-name"], ["type", "button", "class", "mjc-btn mjc-btn--flat mjc-btn--sm", 3, "disabled", "click", 4, "ngIf"], [1, "mjc-file-meta"], ["type", "button", 1, "mjc-btn", "mjc-btn--flat", "mjc-btn--sm", 3, "click", "disabled"], [1, "mjc-empty"], [1, "mjc-reg"], [1, "mjc-reg__title"], [1, "mjc-reg__row"], ["type", "text", "placeholder", "Path in storage, e.g. Contracts/2026/CTR-000004.pdf", "aria-label", "Path of an existing document in storage", 3, "ngModelChange", "ngModel", "disabled"], ["target", "_blank", "rel", "noopener noreferrer", 3, "href"]], template: function ContractFilesPanel_Template(rf, ctx) { if (rf & 1) {
|
|
919
|
+
i0.ɵɵelementStart(0, "mj-collapsible-panel", 0)(1, "div", 1);
|
|
920
|
+
i0.ɵɵtemplate(2, ContractFilesPanel_div_2_Template, 2, 1, "div", 2)(3, ContractFilesPanel_div_3_Template, 2, 0, "div", 3)(4, ContractFilesPanel_div_4_Template, 2, 0, "div", 3)(5, ContractFilesPanel_div_5_Template, 9, 5, "div", 4)(6, ContractFilesPanel_div_6_Template, 4, 1, "div", 5)(7, ContractFilesPanel_p_7_Template, 4, 1, "p", 5)(8, ContractFilesPanel_p_8_Template, 2, 1, "p", 5);
|
|
921
|
+
i0.ɵɵelementStart(9, "p", 6);
|
|
922
|
+
i0.ɵɵtext(10, " Documents attach through MJ's polymorphic ");
|
|
923
|
+
i0.ɵɵelementStart(11, "code");
|
|
924
|
+
i0.ɵɵtext(12, "__mj.FileEntityRecordLink");
|
|
925
|
+
i0.ɵɵelementEnd();
|
|
926
|
+
i0.ɵɵtext(13, " (");
|
|
927
|
+
i0.ɵɵelementStart(14, "code");
|
|
928
|
+
i0.ɵɵtext(15, "EntityID");
|
|
929
|
+
i0.ɵɵelementEnd();
|
|
930
|
+
i0.ɵɵtext(16, " + ");
|
|
931
|
+
i0.ɵɵelementStart(17, "code");
|
|
932
|
+
i0.ɵɵtext(18, "RecordID");
|
|
933
|
+
i0.ɵɵelementEnd();
|
|
934
|
+
i0.ɵɵtext(19, ") rather than a column on this schema \u2014 so one record can carry the signed PDF, its exhibits and a countersigned amendment, and no future table needs a new column to acquire paper. ");
|
|
935
|
+
i0.ɵɵelementEnd()()();
|
|
936
|
+
} if (rf & 2) {
|
|
937
|
+
i0.ɵɵproperty("Form", ctx.FormComponent)("FormContext", ctx.FormContext);
|
|
938
|
+
i0.ɵɵadvance(2);
|
|
939
|
+
i0.ɵɵproperty("ngIf", ctx.Files.length);
|
|
940
|
+
i0.ɵɵadvance();
|
|
941
|
+
i0.ɵɵproperty("ngIf", !ctx.IsLoading && !ctx.Files.length);
|
|
942
|
+
i0.ɵɵadvance();
|
|
943
|
+
i0.ɵɵproperty("ngIf", ctx.IsLoading);
|
|
944
|
+
i0.ɵɵadvance();
|
|
945
|
+
i0.ɵɵproperty("ngIf", ctx.EditMode && ctx.CanDownload);
|
|
946
|
+
i0.ɵɵadvance();
|
|
947
|
+
i0.ɵɵproperty("ngIf", ctx.ActionError);
|
|
948
|
+
i0.ɵɵadvance();
|
|
949
|
+
i0.ɵɵproperty("ngIf", ctx.SigningProviderURL);
|
|
950
|
+
i0.ɵɵadvance();
|
|
951
|
+
i0.ɵɵproperty("ngIf", !ctx.CanDownload && ctx.Files.length);
|
|
952
|
+
} }, dependencies: [CommonModule, i1.NgForOf, i1.NgIf, FormsModule, i2.DefaultValueAccessor, i2.NgControlStatus, i2.NgModel, BaseFormsModule, i3.MjCollapsiblePanelComponent, i1.DatePipe], styles: [_c0] });
|
|
953
|
+
};
|
|
954
|
+
ContractFilesPanel = __decorate([
|
|
955
|
+
RegisterClassEx(BaseFormPanel, {
|
|
956
|
+
key: 'contracts:record-files:contract',
|
|
957
|
+
skipNullKeyWarning: true,
|
|
958
|
+
metadata: {
|
|
959
|
+
entity: 'MJ_BizApps_Contracts: Contracts',
|
|
960
|
+
slot: 'after-fields',
|
|
961
|
+
sortKey: 60,
|
|
962
|
+
// contributionKey MUST equal this panel's SectionKey — the container resolves chrome by the
|
|
963
|
+
// former and builds rail groups from the latter. Without a contributionKey at all,
|
|
964
|
+
// `contributionRailKey()` returns null for a panel with no `relatedEntity` and chrome skips it,
|
|
965
|
+
// so Documents could never be a rail item however it was configured.
|
|
966
|
+
contributionKey: 'recordFiles',
|
|
967
|
+
// No `inclusion: 'Primary'` — see the long note in contract.panels.ts: Primary means
|
|
968
|
+
// "promote ahead of Details", which is the opposite of what this form wants.
|
|
969
|
+
},
|
|
970
|
+
})
|
|
971
|
+
], ContractFilesPanel);
|
|
972
|
+
export { ContractFilesPanel };
|
|
973
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContractFilesPanel, [{
|
|
974
|
+
type: Component,
|
|
975
|
+
args: [{ standalone: true, imports: [CommonModule, FormsModule, BaseFormsModule], selector: 'mjc-contract-files-panel', template: PANEL_TEMPLATE, styles: ["\n /* SEMANTIC TOKENS ONLY \u2014 no hex, no bare px for colour/type/space.\n *\n * This panel came forward from v1 and its styles did not: it carried seven hex FALLBACKS\n * inside var() (`var(--mj-text-muted, #64748b)`) and six hardcoded px font sizes. The\n * fallbacks look harmless and are not \u2014 they are the value that renders the moment a token\n * name is wrong, so a typo silently pins a light-mode colour into a dark theme instead of\n * failing visibly. Every rule below now resolves through a token or a kit class. */\n .mjc-file-row {\n display: flex; align-items: center; gap: var(--mj-space-3);\n padding: var(--mj-space-2) 0;\n font-size: var(--mj-text-sm);\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n .mjc-file-row:last-child { border-bottom: none; }\n .mjc-file-row .mjc-file-icon { color: var(--mj-status-error); }\n .mjc-file-row .mjc-file-name { font-weight: 600; overflow-wrap: anywhere; }\n .mjc-file-row .mjc-file-meta {\n margin-left: auto; font-size: var(--mj-text-xs);\n color: var(--mj-text-muted); white-space: nowrap;\n }\n .mjc-reg { margin-top: var(--mj-space-3); }\n .mjc-reg__title {\n font-size: var(--mj-text-xs); font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); margin-bottom: var(--mj-space-2);\n }\n .mjc-reg__row { display: flex; gap: var(--mj-space-2); align-items: center; flex-wrap: wrap; }\n .mjc-reg__row input { flex: 1 1 22rem; }\n"] }]
|
|
976
|
+
}], null, null); })();
|
|
977
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ContractFilesPanel, { className: "ContractFilesPanel", filePath: "lib/form-panels/record-files.panel.ts", lineNumber: 526 }); })();
|
|
978
|
+
let ContractTemplateFilesPanel = class ContractTemplateFilesPanel extends RecordFilesPanelBase {
|
|
979
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵContractTemplateFilesPanel_BaseFactory; return function ContractTemplateFilesPanel_Factory(__ngFactoryType__) { return (ɵContractTemplateFilesPanel_BaseFactory || (ɵContractTemplateFilesPanel_BaseFactory = i0.ɵɵgetInheritedFactory(ContractTemplateFilesPanel)))(__ngFactoryType__ || ContractTemplateFilesPanel); }; })();
|
|
980
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ContractTemplateFilesPanel, selectors: [["mjc-template-files-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 20, vars: 9, consts: [["SectionKey", "recordFiles", "SectionName", "Documents", "Icon", "fa-solid fa-folder-open", 3, "Form", "FormContext"], [1, "mjc-body"], [4, "ngIf"], ["class", "mjc-empty", 4, "ngIf"], ["class", "mjc-reg", 4, "ngIf"], ["class", "mjc-note", 4, "ngIf"], [1, "mjc-note"], ["class", "mjc-file-row", 4, "ngFor", "ngForOf"], [1, "mjc-file-row"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-lines", "mjc-file-icon"], [1, "mjc-file-name"], ["type", "button", "class", "mjc-btn mjc-btn--flat mjc-btn--sm", 3, "disabled", "click", 4, "ngIf"], [1, "mjc-file-meta"], ["type", "button", 1, "mjc-btn", "mjc-btn--flat", "mjc-btn--sm", 3, "click", "disabled"], [1, "mjc-empty"], [1, "mjc-reg"], [1, "mjc-reg__title"], [1, "mjc-reg__row"], ["type", "text", "placeholder", "Path in storage, e.g. Contracts/2026/CTR-000004.pdf", "aria-label", "Path of an existing document in storage", 3, "ngModelChange", "ngModel", "disabled"], ["target", "_blank", "rel", "noopener noreferrer", 3, "href"]], template: function ContractTemplateFilesPanel_Template(rf, ctx) { if (rf & 1) {
|
|
981
|
+
i0.ɵɵelementStart(0, "mj-collapsible-panel", 0)(1, "div", 1);
|
|
982
|
+
i0.ɵɵtemplate(2, ContractTemplateFilesPanel_div_2_Template, 2, 1, "div", 2)(3, ContractTemplateFilesPanel_div_3_Template, 2, 0, "div", 3)(4, ContractTemplateFilesPanel_div_4_Template, 2, 0, "div", 3)(5, ContractTemplateFilesPanel_div_5_Template, 9, 5, "div", 4)(6, ContractTemplateFilesPanel_div_6_Template, 4, 1, "div", 5)(7, ContractTemplateFilesPanel_p_7_Template, 4, 1, "p", 5)(8, ContractTemplateFilesPanel_p_8_Template, 2, 1, "p", 5);
|
|
983
|
+
i0.ɵɵelementStart(9, "p", 6);
|
|
984
|
+
i0.ɵɵtext(10, " Documents attach through MJ's polymorphic ");
|
|
985
|
+
i0.ɵɵelementStart(11, "code");
|
|
986
|
+
i0.ɵɵtext(12, "__mj.FileEntityRecordLink");
|
|
987
|
+
i0.ɵɵelementEnd();
|
|
988
|
+
i0.ɵɵtext(13, " (");
|
|
989
|
+
i0.ɵɵelementStart(14, "code");
|
|
990
|
+
i0.ɵɵtext(15, "EntityID");
|
|
991
|
+
i0.ɵɵelementEnd();
|
|
992
|
+
i0.ɵɵtext(16, " + ");
|
|
993
|
+
i0.ɵɵelementStart(17, "code");
|
|
994
|
+
i0.ɵɵtext(18, "RecordID");
|
|
995
|
+
i0.ɵɵelementEnd();
|
|
996
|
+
i0.ɵɵtext(19, ") rather than a column on this schema \u2014 so one record can carry the signed PDF, its exhibits and a countersigned amendment, and no future table needs a new column to acquire paper. ");
|
|
997
|
+
i0.ɵɵelementEnd()()();
|
|
998
|
+
} if (rf & 2) {
|
|
999
|
+
i0.ɵɵproperty("Form", ctx.FormComponent)("FormContext", ctx.FormContext);
|
|
1000
|
+
i0.ɵɵadvance(2);
|
|
1001
|
+
i0.ɵɵproperty("ngIf", ctx.Files.length);
|
|
1002
|
+
i0.ɵɵadvance();
|
|
1003
|
+
i0.ɵɵproperty("ngIf", !ctx.IsLoading && !ctx.Files.length);
|
|
1004
|
+
i0.ɵɵadvance();
|
|
1005
|
+
i0.ɵɵproperty("ngIf", ctx.IsLoading);
|
|
1006
|
+
i0.ɵɵadvance();
|
|
1007
|
+
i0.ɵɵproperty("ngIf", ctx.EditMode && ctx.CanDownload);
|
|
1008
|
+
i0.ɵɵadvance();
|
|
1009
|
+
i0.ɵɵproperty("ngIf", ctx.ActionError);
|
|
1010
|
+
i0.ɵɵadvance();
|
|
1011
|
+
i0.ɵɵproperty("ngIf", ctx.SigningProviderURL);
|
|
1012
|
+
i0.ɵɵadvance();
|
|
1013
|
+
i0.ɵɵproperty("ngIf", !ctx.CanDownload && ctx.Files.length);
|
|
1014
|
+
} }, dependencies: [CommonModule, i1.NgForOf, i1.NgIf, FormsModule, i2.DefaultValueAccessor, i2.NgControlStatus, i2.NgModel, BaseFormsModule, i3.MjCollapsiblePanelComponent, i1.DatePipe], styles: [_c0] });
|
|
1015
|
+
};
|
|
1016
|
+
ContractTemplateFilesPanel = __decorate([
|
|
1017
|
+
RegisterClassEx(BaseFormPanel, {
|
|
1018
|
+
key: 'contracts:record-files:template',
|
|
1019
|
+
skipNullKeyWarning: true,
|
|
1020
|
+
metadata: {
|
|
1021
|
+
entity: 'MJ_BizApps_Contracts: Contract Templates',
|
|
1022
|
+
slot: 'after-fields',
|
|
1023
|
+
sortKey: 60,
|
|
1024
|
+
contributionKey: 'recordFiles',
|
|
1025
|
+
// NOT 'Primary' here, unlike on the Contract form — ruled 2026-08-19: the template rail must
|
|
1026
|
+
// read Details, then Provisions.
|
|
1027
|
+
//
|
|
1028
|
+
// MJ's rail band order is fixed: lead contributions → Details → Primary related → More. And a
|
|
1029
|
+
// Primary contribution is ALWAYS a lead (`isLeadContribution` requires inclusion === 'Primary'
|
|
1030
|
+
// with no chromeGroup), so there is no configuration where Documents keeps a first-class rail
|
|
1031
|
+
// item and still sits after Details — Primary here put Documents FIRST.
|
|
1032
|
+
//
|
|
1033
|
+
// 'more' is the honest resolution rather than a compromise: it stays reachable under the More
|
|
1034
|
+
// folder, and a template version's source PDF genuinely is secondary to its clause list. The
|
|
1035
|
+
// Contract form keeps Documents Primary, where paper is the point.
|
|
1036
|
+
chromeGroup: 'more',
|
|
1037
|
+
},
|
|
1038
|
+
})
|
|
1039
|
+
], ContractTemplateFilesPanel);
|
|
1040
|
+
export { ContractTemplateFilesPanel };
|
|
1041
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContractTemplateFilesPanel, [{
|
|
1042
|
+
type: Component,
|
|
1043
|
+
args: [{ standalone: true, imports: [CommonModule, FormsModule, BaseFormsModule], selector: 'mjc-template-files-panel', template: PANEL_TEMPLATE, styles: ["\n /* SEMANTIC TOKENS ONLY \u2014 no hex, no bare px for colour/type/space.\n *\n * This panel came forward from v1 and its styles did not: it carried seven hex FALLBACKS\n * inside var() (`var(--mj-text-muted, #64748b)`) and six hardcoded px font sizes. The\n * fallbacks look harmless and are not \u2014 they are the value that renders the moment a token\n * name is wrong, so a typo silently pins a light-mode colour into a dark theme instead of\n * failing visibly. Every rule below now resolves through a token or a kit class. */\n .mjc-file-row {\n display: flex; align-items: center; gap: var(--mj-space-3);\n padding: var(--mj-space-2) 0;\n font-size: var(--mj-text-sm);\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n .mjc-file-row:last-child { border-bottom: none; }\n .mjc-file-row .mjc-file-icon { color: var(--mj-status-error); }\n .mjc-file-row .mjc-file-name { font-weight: 600; overflow-wrap: anywhere; }\n .mjc-file-row .mjc-file-meta {\n margin-left: auto; font-size: var(--mj-text-xs);\n color: var(--mj-text-muted); white-space: nowrap;\n }\n .mjc-reg { margin-top: var(--mj-space-3); }\n .mjc-reg__title {\n font-size: var(--mj-text-xs); font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); margin-bottom: var(--mj-space-2);\n }\n .mjc-reg__row { display: flex; gap: var(--mj-space-2); align-items: center; flex-wrap: wrap; }\n .mjc-reg__row input { flex: 1 1 22rem; }\n"] }]
|
|
1044
|
+
}], null, null); })();
|
|
1045
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ContractTemplateFilesPanel, { className: "ContractTemplateFilesPanel", filePath: "lib/form-panels/record-files.panel.ts", lineNumber: 557 }); })();
|
|
1046
|
+
//# sourceMappingURL=record-files.panel.js.map
|