retold-facto 0.0.4
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/.claude/launch.json +11 -0
- package/.dockerignore +8 -0
- package/.quackage.json +19 -0
- package/Dockerfile +26 -0
- package/bin/retold-facto.js +909 -0
- package/examples/facto-government-data.sqlite +0 -0
- package/examples/government-data-catalog.json +137 -0
- package/examples/government-data-loader.js +1432 -0
- package/package.json +91 -0
- package/scripts/facto-download.js +425 -0
- package/source/Retold-Facto.js +1042 -0
- package/source/services/Retold-Facto-BeaconProvider.js +511 -0
- package/source/services/Retold-Facto-CatalogManager.js +1252 -0
- package/source/services/Retold-Facto-DataLakeService.js +1642 -0
- package/source/services/Retold-Facto-DatasetManager.js +417 -0
- package/source/services/Retold-Facto-IngestEngine.js +1315 -0
- package/source/services/Retold-Facto-ProjectionEngine.js +3960 -0
- package/source/services/Retold-Facto-RecordManager.js +360 -0
- package/source/services/Retold-Facto-SchemaManager.js +1110 -0
- package/source/services/Retold-Facto-SourceFolderScanner.js +2243 -0
- package/source/services/Retold-Facto-SourceManager.js +730 -0
- package/source/services/Retold-Facto-StoreConnectionManager.js +441 -0
- package/source/services/Retold-Facto-ThroughputMonitor.js +478 -0
- package/source/services/web-app/codemirror-entry.js +7 -0
- package/source/services/web-app/pict-app/Pict-Application-Facto-Configuration.json +9 -0
- package/source/services/web-app/pict-app/Pict-Application-Facto.js +70 -0
- package/source/services/web-app/pict-app/Pict-Facto-Bundle.js +11 -0
- package/source/services/web-app/pict-app/providers/Pict-Provider-Facto-UI.js +66 -0
- package/source/services/web-app/pict-app/providers/Pict-Provider-Facto.js +69 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Catalog.js +93 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Connections.js +42 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Datasets.js +605 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Projections.js +188 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Scanner.js +80 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Schema.js +116 -0
- package/source/services/web-app/pict-app/providers/facto-api/Facto-API-Sources.js +104 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Catalog.js +526 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Datasets.js +173 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Ingest.js +259 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Layout.js +191 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Projections.js +231 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Records.js +326 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Scanner.js +624 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Sources.js +201 -0
- package/source/services/web-app/pict-app/views/PictView-Facto-Throughput.js +456 -0
- package/source/services/web-app/pict-app-full/Pict-Application-Facto-Full-Configuration.json +14 -0
- package/source/services/web-app/pict-app-full/Pict-Application-Facto-Full.js +391 -0
- package/source/services/web-app/pict-app-full/providers/PictRouter-Facto-Configuration.json +56 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-BottomBar.js +68 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Connections.js +340 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Dashboard.js +149 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Dashboards.js +819 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Datasets.js +178 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-IngestJobs.js +99 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Layout.js +62 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-MappingEditor.js +158 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-ProjectionDetail.js +1120 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Projections.js +172 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-QueryPanel.js +119 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-RecordViewer.js +663 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Records.js +648 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Scanner.js +1017 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaDetail.js +1404 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaDocEditor.js +1036 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaEditor.js +636 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SchemaResearch.js +357 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceDetail.js +822 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceEditor.js +1036 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-SourceResearch.js +487 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Sources.js +165 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Throughput.js +439 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-TopBar.js +335 -0
- package/source/services/web-app/pict-app-full/views/projections/Facto-Projections-Constants.js +71 -0
- package/source/services/web-app/web/chart.min.js +20 -0
- package/source/services/web-app/web/codemirror-bundle.js +30099 -0
- package/source/services/web-app/web/css/facto-themes.css +467 -0
- package/source/services/web-app/web/css/facto.css +502 -0
- package/source/services/web-app/web/index.html +28 -0
- package/source/services/web-app/web/retold-facto.js +12138 -0
- package/source/services/web-app/web/retold-facto.js.map +1 -0
- package/source/services/web-app/web/retold-facto.min.js +2 -0
- package/source/services/web-app/web/retold-facto.min.js.map +1 -0
- package/source/services/web-app/web/simple/index.html +17 -0
- package/test/Facto_Browser_Integration_tests.js +798 -0
- package/test/RetoldFacto_tests.js +4117 -0
- package/test/fixtures/weather-readings.csv +17 -0
- package/test/fixtures/weather-stations.csv +9 -0
- package/test/model/MeadowModel-Extended.json +8497 -0
- package/test/model/MeadowModel-PICT.json +1 -0
- package/test/model/MeadowModel.json +1355 -0
- package/test/model/ddl/Facto.ddl +225 -0
- package/test/model/fable-configuration.json +14 -0
|
@@ -0,0 +1,1120 @@
|
|
|
1
|
+
const libPictView = require('pict-view');
|
|
2
|
+
|
|
3
|
+
const libProjectionConstants = require('./projections/Facto-Projections-Constants.js');
|
|
4
|
+
|
|
5
|
+
const _ViewConfiguration =
|
|
6
|
+
{
|
|
7
|
+
ViewIdentifier: "Facto-Full-ProjectionDetail",
|
|
8
|
+
|
|
9
|
+
DefaultRenderable: "Facto-Full-ProjectionDetail-Content",
|
|
10
|
+
DefaultDestinationAddress: "#Facto-Full-Content-Container",
|
|
11
|
+
|
|
12
|
+
AutoRender: false,
|
|
13
|
+
|
|
14
|
+
CSS: /*css*/`
|
|
15
|
+
.facto-proj-detail-back {
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 0.35em;
|
|
19
|
+
color: var(--facto-text-secondary);
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
font-size: 0.85em;
|
|
22
|
+
margin-bottom: 0.75em;
|
|
23
|
+
transition: color 0.15s;
|
|
24
|
+
}
|
|
25
|
+
.facto-proj-detail-back:hover {
|
|
26
|
+
color: var(--facto-brand);
|
|
27
|
+
}
|
|
28
|
+
.facto-proj-detail-title-row {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: space-between;
|
|
32
|
+
margin-bottom: 1.25em;
|
|
33
|
+
}
|
|
34
|
+
.facto-proj-detail-title-row h1 {
|
|
35
|
+
margin: 0;
|
|
36
|
+
}
|
|
37
|
+
.facto-proj-detail-actions {
|
|
38
|
+
display: flex;
|
|
39
|
+
gap: 0.5em;
|
|
40
|
+
}
|
|
41
|
+
.facto-proj-meta-cards {
|
|
42
|
+
display: grid;
|
|
43
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
44
|
+
gap: 1em;
|
|
45
|
+
margin-bottom: 1.5em;
|
|
46
|
+
}
|
|
47
|
+
.facto-proj-meta-card {
|
|
48
|
+
background: var(--facto-bg-card);
|
|
49
|
+
border: 1px solid var(--facto-border-subtle);
|
|
50
|
+
border-radius: 8px;
|
|
51
|
+
padding: 1em;
|
|
52
|
+
}
|
|
53
|
+
.facto-proj-meta-card-label {
|
|
54
|
+
font-size: 0.7em;
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
text-transform: uppercase;
|
|
57
|
+
letter-spacing: 0.5px;
|
|
58
|
+
color: var(--facto-text-tertiary);
|
|
59
|
+
margin-bottom: 0.35em;
|
|
60
|
+
}
|
|
61
|
+
.facto-proj-meta-card-value {
|
|
62
|
+
font-size: 1.1em;
|
|
63
|
+
font-weight: 600;
|
|
64
|
+
color: var(--facto-text-heading);
|
|
65
|
+
}
|
|
66
|
+
.facto-proj-section {
|
|
67
|
+
margin-bottom: 1.5em;
|
|
68
|
+
}
|
|
69
|
+
.facto-proj-section-header {
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: space-between;
|
|
73
|
+
margin-bottom: 0.75em;
|
|
74
|
+
}
|
|
75
|
+
.facto-proj-section-header h2 {
|
|
76
|
+
margin: 0;
|
|
77
|
+
font-size: 1em;
|
|
78
|
+
text-transform: uppercase;
|
|
79
|
+
letter-spacing: 0.05em;
|
|
80
|
+
color: var(--facto-text-secondary);
|
|
81
|
+
}
|
|
82
|
+
.facto-proj-ddl-preview {
|
|
83
|
+
background: var(--facto-bg-code, #f0e8d8);
|
|
84
|
+
border: 1px solid var(--facto-border-subtle);
|
|
85
|
+
border-radius: 6px;
|
|
86
|
+
padding: 0.75em;
|
|
87
|
+
font-family: 'SF Mono', Consolas, monospace;
|
|
88
|
+
font-size: 0.82em;
|
|
89
|
+
white-space: pre-wrap;
|
|
90
|
+
max-height: 150px;
|
|
91
|
+
overflow: auto;
|
|
92
|
+
color: var(--facto-text-secondary);
|
|
93
|
+
margin-bottom: 0.75em;
|
|
94
|
+
}
|
|
95
|
+
.facto-proj-detail-loading {
|
|
96
|
+
text-align: center;
|
|
97
|
+
padding: 3em;
|
|
98
|
+
color: var(--facto-text-tertiary);
|
|
99
|
+
}
|
|
100
|
+
.facto-proj-deploy-form {
|
|
101
|
+
display: grid;
|
|
102
|
+
grid-template-columns: 1fr 1fr auto auto;
|
|
103
|
+
gap: 0.75em;
|
|
104
|
+
align-items: end;
|
|
105
|
+
padding: 1em;
|
|
106
|
+
background: var(--facto-bg-surface, #fcf8f0);
|
|
107
|
+
border: 1px solid var(--facto-border, #d6c8ae);
|
|
108
|
+
border-radius: 8px;
|
|
109
|
+
margin-bottom: 0.75em;
|
|
110
|
+
}
|
|
111
|
+
.facto-proj-deploy-form label {
|
|
112
|
+
display: block;
|
|
113
|
+
font-size: 0.72em;
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
text-transform: uppercase;
|
|
116
|
+
letter-spacing: 0.5px;
|
|
117
|
+
color: var(--facto-text-tertiary);
|
|
118
|
+
margin-bottom: 0.25em;
|
|
119
|
+
}
|
|
120
|
+
.facto-proj-deploy-log {
|
|
121
|
+
font-family: 'SF Mono', monospace;
|
|
122
|
+
font-size: 0.78em;
|
|
123
|
+
padding: 0.6em;
|
|
124
|
+
background: var(--facto-bg-code, #f0e8d8);
|
|
125
|
+
border: 1px solid var(--facto-border-subtle, #e8ddc8);
|
|
126
|
+
border-radius: 6px;
|
|
127
|
+
white-space: pre-wrap;
|
|
128
|
+
max-height: 200px;
|
|
129
|
+
overflow: auto;
|
|
130
|
+
margin-top: 0.5em;
|
|
131
|
+
color: var(--facto-text-secondary);
|
|
132
|
+
}
|
|
133
|
+
.facto-proj-import-form {
|
|
134
|
+
display: grid;
|
|
135
|
+
grid-template-columns: 1fr 1fr auto auto;
|
|
136
|
+
gap: 0.75em;
|
|
137
|
+
align-items: end;
|
|
138
|
+
padding: 1em;
|
|
139
|
+
background: var(--facto-bg-surface, #fcf8f0);
|
|
140
|
+
border: 1px solid var(--facto-border, #d6c8ae);
|
|
141
|
+
border-radius: 8px;
|
|
142
|
+
margin-bottom: 0.75em;
|
|
143
|
+
}
|
|
144
|
+
.facto-proj-import-form label {
|
|
145
|
+
display: block;
|
|
146
|
+
font-size: 0.72em;
|
|
147
|
+
font-weight: 600;
|
|
148
|
+
text-transform: uppercase;
|
|
149
|
+
letter-spacing: 0.5px;
|
|
150
|
+
color: var(--facto-text-tertiary);
|
|
151
|
+
margin-bottom: 0.25em;
|
|
152
|
+
}
|
|
153
|
+
.facto-proj-import-stage-row {
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
gap: 0.5em;
|
|
157
|
+
grid-column: 1 / -1;
|
|
158
|
+
}
|
|
159
|
+
.facto-proj-import-stage-row label {
|
|
160
|
+
text-transform: none;
|
|
161
|
+
font-size: 0.82em;
|
|
162
|
+
margin-bottom: 0;
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
}
|
|
165
|
+
.facto-proj-import-staging-link {
|
|
166
|
+
display: inline-block;
|
|
167
|
+
margin-top: 0.5em;
|
|
168
|
+
font-size: 0.82em;
|
|
169
|
+
color: var(--facto-brand);
|
|
170
|
+
cursor: pointer;
|
|
171
|
+
text-decoration: underline;
|
|
172
|
+
}
|
|
173
|
+
`,
|
|
174
|
+
|
|
175
|
+
Templates:
|
|
176
|
+
[
|
|
177
|
+
{
|
|
178
|
+
Hash: "Facto-Full-ProjectionDetail-Template",
|
|
179
|
+
Template: /*html*/`
|
|
180
|
+
<div class="facto-content">
|
|
181
|
+
<div class="facto-proj-detail-back" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].goBack()">
|
|
182
|
+
← Back to Projections
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div id="Facto-ProjectionDetail-Loading" class="facto-proj-detail-loading">Loading projection...</div>
|
|
186
|
+
|
|
187
|
+
<div id="Facto-ProjectionDetail-Container" style="display:none;">
|
|
188
|
+
<div class="facto-proj-detail-title-row">
|
|
189
|
+
<h1 id="Facto-ProjectionDetail-Title"></h1>
|
|
190
|
+
<div class="facto-proj-detail-actions">
|
|
191
|
+
<button class="facto-btn facto-btn-primary" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].editSchema()">Edit Schema</button>
|
|
192
|
+
<button class="facto-btn facto-btn-secondary" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].editMappings()">Mappings</button>
|
|
193
|
+
<button class="facto-btn facto-btn-secondary" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].showQuery()">Query</button>
|
|
194
|
+
<button class="facto-btn facto-btn-danger" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].deleteProjection()">Delete</button>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<div id="Facto-ProjectionDetail-Meta" class="facto-proj-meta-cards"></div>
|
|
199
|
+
|
|
200
|
+
<div class="facto-proj-section">
|
|
201
|
+
<div class="facto-proj-section-header">
|
|
202
|
+
<h2>Schema</h2>
|
|
203
|
+
<button class="facto-btn facto-btn-primary facto-btn-small" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].editSchema()">Edit Schema</button>
|
|
204
|
+
</div>
|
|
205
|
+
<div id="Facto-ProjectionDetail-Schema"></div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div class="facto-proj-section">
|
|
209
|
+
<div class="facto-proj-section-header">
|
|
210
|
+
<h2>Deployed Stores</h2>
|
|
211
|
+
<button class="facto-btn facto-btn-primary facto-btn-small" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].showDeployForm()">+ Deploy to Store</button>
|
|
212
|
+
</div>
|
|
213
|
+
<div id="Facto-ProjectionDetail-Deploy" style="display:none;">
|
|
214
|
+
<div class="facto-proj-deploy-form">
|
|
215
|
+
<div>
|
|
216
|
+
<label>Connection</label>
|
|
217
|
+
<select id="Facto-ProjectionDetail-Deploy-Connection"></select>
|
|
218
|
+
</div>
|
|
219
|
+
<div>
|
|
220
|
+
<label>Table Name</label>
|
|
221
|
+
<input type="text" id="Facto-ProjectionDetail-Deploy-TableName" placeholder="e.g. Schools">
|
|
222
|
+
</div>
|
|
223
|
+
<div>
|
|
224
|
+
<button class="facto-btn facto-btn-primary facto-btn-small" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].deployToStore()">Deploy</button>
|
|
225
|
+
</div>
|
|
226
|
+
<div>
|
|
227
|
+
<button class="facto-btn facto-btn-secondary facto-btn-small" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].hideDeployForm()">Cancel</button>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
<div id="Facto-ProjectionDetail-Deploy-Log" class="facto-proj-deploy-log" style="display:none;"></div>
|
|
231
|
+
</div>
|
|
232
|
+
<div id="Facto-ProjectionDetail-Stores"></div>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div class="facto-proj-section">
|
|
236
|
+
<div class="facto-proj-section-header">
|
|
237
|
+
<h2>Mappings</h2>
|
|
238
|
+
<button class="facto-btn facto-btn-primary facto-btn-small" onclick="{~P~}.views['Facto-Full-ProjectionDetail'].editMappings()">Manage Mappings</button>
|
|
239
|
+
</div>
|
|
240
|
+
<div id="Facto-ProjectionDetail-Mappings"></div>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
<div class="facto-proj-section">
|
|
244
|
+
<div class="facto-proj-section-header">
|
|
245
|
+
<h2>Import Data</h2>
|
|
246
|
+
</div>
|
|
247
|
+
<div id="Facto-ProjectionDetail-Import"></div>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div id="Facto-Proj-Schema-Editor-Container" style="display:none;"></div>
|
|
252
|
+
<div id="Facto-Proj-Mapping-Editor-Container" style="display:none;"></div>
|
|
253
|
+
<div id="Facto-Proj-Query-Container" style="display:none;"></div>
|
|
254
|
+
</div>
|
|
255
|
+
`
|
|
256
|
+
}
|
|
257
|
+
],
|
|
258
|
+
|
|
259
|
+
Renderables:
|
|
260
|
+
[
|
|
261
|
+
{
|
|
262
|
+
RenderableHash: "Facto-Full-ProjectionDetail-Content",
|
|
263
|
+
TemplateHash: "Facto-Full-ProjectionDetail-Template",
|
|
264
|
+
DestinationAddress: "#Facto-Full-Content-Container",
|
|
265
|
+
RenderMethod: "replace"
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
class FactoFullProjectionDetailView extends libPictView
|
|
271
|
+
{
|
|
272
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
273
|
+
{
|
|
274
|
+
super(pFable, pOptions, pServiceHash);
|
|
275
|
+
|
|
276
|
+
this._CurrentIDDataset = null;
|
|
277
|
+
this._ProjectionData = null;
|
|
278
|
+
this._Schema = null;
|
|
279
|
+
this._Stores = [];
|
|
280
|
+
this._Mappings = [];
|
|
281
|
+
this._Sources = [];
|
|
282
|
+
this._Connections = [];
|
|
283
|
+
this._Stats = null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
loadProjection(pIDDataset)
|
|
287
|
+
{
|
|
288
|
+
this._CurrentIDDataset = parseInt(pIDDataset, 10) || 0;
|
|
289
|
+
this._ProjectionData = null;
|
|
290
|
+
this._Schema = null;
|
|
291
|
+
this._Stores = [];
|
|
292
|
+
this._Mappings = [];
|
|
293
|
+
this._Sources = [];
|
|
294
|
+
this._Connections = [];
|
|
295
|
+
this._Stats = null;
|
|
296
|
+
|
|
297
|
+
this.render();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
|
|
301
|
+
{
|
|
302
|
+
if (this._CurrentIDDataset)
|
|
303
|
+
{
|
|
304
|
+
this._fetchAndDisplayProjection();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_fetchAndDisplayProjection()
|
|
311
|
+
{
|
|
312
|
+
let tmpLoading = document.getElementById('Facto-ProjectionDetail-Loading');
|
|
313
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Container');
|
|
314
|
+
if (tmpLoading) tmpLoading.style.display = 'block';
|
|
315
|
+
if (tmpContainer) tmpContainer.style.display = 'none';
|
|
316
|
+
|
|
317
|
+
Promise.all(
|
|
318
|
+
[
|
|
319
|
+
this.pict.providers.Facto.loadProjections(),
|
|
320
|
+
this.pict.providers.Facto.loadProjectionSchema(this._CurrentIDDataset),
|
|
321
|
+
this.pict.providers.Facto.loadProjectionStores(this._CurrentIDDataset),
|
|
322
|
+
this.pict.providers.Facto.loadProjectionMappings(this._CurrentIDDataset),
|
|
323
|
+
this.pict.providers.Facto.loadSources(),
|
|
324
|
+
this.pict.providers.Facto.loadDatasetStats(this._CurrentIDDataset),
|
|
325
|
+
this.pict.providers.Facto.loadStoreConnections()
|
|
326
|
+
]).then(
|
|
327
|
+
(pResults) =>
|
|
328
|
+
{
|
|
329
|
+
// Find the projection dataset from the full list
|
|
330
|
+
let tmpProjections = (pResults[0] && pResults[0].Projections) ? pResults[0].Projections : [];
|
|
331
|
+
this._ProjectionData = null;
|
|
332
|
+
for (let i = 0; i < tmpProjections.length; i++)
|
|
333
|
+
{
|
|
334
|
+
if (tmpProjections[i].IDDataset == this._CurrentIDDataset)
|
|
335
|
+
{
|
|
336
|
+
this._ProjectionData = tmpProjections[i];
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
this._Schema = pResults[1] || {};
|
|
342
|
+
this._Stores = (pResults[2] && pResults[2].Stores) ? pResults[2].Stores : [];
|
|
343
|
+
this._Mappings = (pResults[3] && pResults[3].Mappings) ? pResults[3].Mappings : [];
|
|
344
|
+
this._Sources = Array.isArray(pResults[4]) ? pResults[4] : [];
|
|
345
|
+
this._Stats = pResults[5] || {};
|
|
346
|
+
this._Connections = (pResults[6] && pResults[6].Connections) ? pResults[6].Connections : [];
|
|
347
|
+
|
|
348
|
+
this._renderProjectionDetail();
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
_renderProjectionDetail()
|
|
353
|
+
{
|
|
354
|
+
let tmpLoading = document.getElementById('Facto-ProjectionDetail-Loading');
|
|
355
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Container');
|
|
356
|
+
if (tmpLoading) tmpLoading.style.display = 'none';
|
|
357
|
+
if (tmpContainer) tmpContainer.style.display = 'block';
|
|
358
|
+
|
|
359
|
+
let tmpProj = this._ProjectionData || {};
|
|
360
|
+
|
|
361
|
+
// Title
|
|
362
|
+
let tmpTitle = document.getElementById('Facto-ProjectionDetail-Title');
|
|
363
|
+
if (tmpTitle) tmpTitle.textContent = tmpProj.Name || ('Projection #' + this._CurrentIDDataset);
|
|
364
|
+
|
|
365
|
+
// URL state
|
|
366
|
+
window.history.replaceState(null, '', '#/Projection/' + this._CurrentIDDataset);
|
|
367
|
+
|
|
368
|
+
this._renderMetaCards();
|
|
369
|
+
this._renderSchemaSection();
|
|
370
|
+
this._renderStoresSection();
|
|
371
|
+
this._renderMappingsSection();
|
|
372
|
+
this._renderImportSection();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ================================================================
|
|
376
|
+
// Meta Cards
|
|
377
|
+
// ================================================================
|
|
378
|
+
|
|
379
|
+
_renderMetaCards()
|
|
380
|
+
{
|
|
381
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Meta');
|
|
382
|
+
if (!tmpContainer) return;
|
|
383
|
+
|
|
384
|
+
let tmpProj = this._ProjectionData || {};
|
|
385
|
+
let tmpSchema = this._Schema || {};
|
|
386
|
+
let tmpStats = this._Stats || {};
|
|
387
|
+
|
|
388
|
+
// Count columns from schema
|
|
389
|
+
let tmpColumnCount = 0;
|
|
390
|
+
if (tmpSchema.SchemaDefinition)
|
|
391
|
+
{
|
|
392
|
+
let tmpColumns = libProjectionConstants.microDDLToColumns(tmpSchema.SchemaDefinition);
|
|
393
|
+
tmpColumnCount = tmpColumns.length;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
let tmpHtml = '';
|
|
397
|
+
tmpHtml += '<div class="facto-proj-meta-card"><div class="facto-proj-meta-card-label">Dataset ID</div><div class="facto-proj-meta-card-value">' + (tmpProj.IDDataset || this._CurrentIDDataset) + '</div></div>';
|
|
398
|
+
tmpHtml += '<div class="facto-proj-meta-card"><div class="facto-proj-meta-card-label">Schema Version</div><div class="facto-proj-meta-card-value">v' + (tmpSchema.SchemaVersion || tmpProj.SchemaVersion || 0) + '</div></div>';
|
|
399
|
+
tmpHtml += '<div class="facto-proj-meta-card"><div class="facto-proj-meta-card-label">Columns</div><div class="facto-proj-meta-card-value">' + tmpColumnCount + '</div></div>';
|
|
400
|
+
tmpHtml += '<div class="facto-proj-meta-card"><div class="facto-proj-meta-card-label">Stores</div><div class="facto-proj-meta-card-value">' + this._Stores.length + '</div></div>';
|
|
401
|
+
tmpHtml += '<div class="facto-proj-meta-card"><div class="facto-proj-meta-card-label">Mappings</div><div class="facto-proj-meta-card-value">' + this._Mappings.length + '</div></div>';
|
|
402
|
+
tmpHtml += '<div class="facto-proj-meta-card"><div class="facto-proj-meta-card-label">Records</div><div class="facto-proj-meta-card-value">' + (tmpStats.RecordCount || 0) + '</div></div>';
|
|
403
|
+
|
|
404
|
+
tmpContainer.innerHTML = tmpHtml;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ================================================================
|
|
408
|
+
// Schema Section
|
|
409
|
+
// ================================================================
|
|
410
|
+
|
|
411
|
+
_renderSchemaSection()
|
|
412
|
+
{
|
|
413
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Schema');
|
|
414
|
+
if (!tmpContainer) return;
|
|
415
|
+
|
|
416
|
+
let tmpSchema = this._Schema || {};
|
|
417
|
+
let tmpDDL = tmpSchema.SchemaDefinition || '';
|
|
418
|
+
|
|
419
|
+
if (!tmpDDL)
|
|
420
|
+
{
|
|
421
|
+
tmpContainer.innerHTML = '<div class="facto-card" style="text-align:center; padding:1.5em; color:var(--facto-text-tertiary);">No schema defined yet. Click "Edit Schema" to create one.</div>';
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
let tmpHtml = '';
|
|
426
|
+
tmpHtml += '<div class="facto-proj-ddl-preview">' + this._escapeHtml(tmpDDL) + '</div>';
|
|
427
|
+
tmpContainer.innerHTML = tmpHtml;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ================================================================
|
|
431
|
+
// Stores Section
|
|
432
|
+
// ================================================================
|
|
433
|
+
|
|
434
|
+
_renderStoresSection()
|
|
435
|
+
{
|
|
436
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Stores');
|
|
437
|
+
if (!tmpContainer) return;
|
|
438
|
+
|
|
439
|
+
if (this._Stores.length === 0)
|
|
440
|
+
{
|
|
441
|
+
tmpContainer.innerHTML = '<div class="facto-card" style="text-align:center; padding:1.5em; color:var(--facto-text-tertiary);">No stores deployed yet. Click "+ Deploy to Store" to create one.</div>';
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Build connection lookup
|
|
446
|
+
let tmpConnMap = {};
|
|
447
|
+
for (let i = 0; i < this._Connections.length; i++)
|
|
448
|
+
{
|
|
449
|
+
tmpConnMap[this._Connections[i].IDStoreConnection] = this._Connections[i];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
let tmpHtml = '<table class="facto-table"><thead><tr>';
|
|
453
|
+
tmpHtml += '<th>ID</th><th>Connection</th><th>Target Table</th><th>Status</th><th>Deployed At</th><th>Actions</th>';
|
|
454
|
+
tmpHtml += '</tr></thead><tbody>';
|
|
455
|
+
|
|
456
|
+
for (let i = 0; i < this._Stores.length; i++)
|
|
457
|
+
{
|
|
458
|
+
let tmpStore = this._Stores[i];
|
|
459
|
+
let tmpConn = tmpConnMap[tmpStore.IDStoreConnection] || {};
|
|
460
|
+
let tmpConnName = tmpConn.Name ? (tmpConn.Name + ' (' + (tmpConn.Type || '') + ')') : ('#' + (tmpStore.IDStoreConnection || '?'));
|
|
461
|
+
let tmpStatusClass = (tmpStore.Status === 'Deployed') ? 'deployed' : (tmpStore.Status === 'Failed') ? 'failed' : 'pending';
|
|
462
|
+
let tmpDeployedAt = tmpStore.DeployedAt ? new Date(tmpStore.DeployedAt).toLocaleString() : '\u2014';
|
|
463
|
+
|
|
464
|
+
tmpHtml += '<tr>';
|
|
465
|
+
tmpHtml += '<td>' + (tmpStore.IDProjectionStore || '') + '</td>';
|
|
466
|
+
tmpHtml += '<td>' + this._escapeHtml(tmpConnName) + '</td>';
|
|
467
|
+
tmpHtml += '<td><strong>' + (tmpStore.TargetTableName || '\u2014') + '</strong></td>';
|
|
468
|
+
tmpHtml += '<td><span class="facto-status-badge ' + tmpStatusClass + '">' + (tmpStore.Status || 'Unknown') + '</span></td>';
|
|
469
|
+
tmpHtml += '<td>' + tmpDeployedAt + '</td>';
|
|
470
|
+
tmpHtml += '<td>';
|
|
471
|
+
tmpHtml += '<button class="facto-btn facto-btn-secondary facto-btn-small" onclick="pict.views[\'Facto-Full-ProjectionDetail\'].redeployStore(' + tmpStore.IDProjectionStore + ', ' + tmpStore.IDStoreConnection + ', \'' + (tmpStore.TargetTableName || '').replace(/'/g, "\\'") + '\')">Redeploy</button> ';
|
|
472
|
+
tmpHtml += '<button class="facto-btn facto-btn-danger facto-btn-small" onclick="pict.views[\'Facto-Full-ProjectionDetail\'].confirmDeleteStore(' + tmpStore.IDProjectionStore + ', \'' + (tmpStore.TargetTableName || '').replace(/'/g, "\\'") + '\')">Delete</button>';
|
|
473
|
+
tmpHtml += '</td>';
|
|
474
|
+
tmpHtml += '</tr>';
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
tmpHtml += '</tbody></table>';
|
|
478
|
+
tmpContainer.innerHTML = tmpHtml;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async confirmDeleteStore(pIDProjectionStore, pTableName)
|
|
482
|
+
{
|
|
483
|
+
// First confirmation
|
|
484
|
+
let tmpConfirmed = await this.pict.views['Pict-Section-Modal'].confirm('Are you sure you want to delete the deployed store "' + pTableName + '"?\n\nThis will DROP the table and permanently destroy all data in it.', { title: 'Delete Store', confirmLabel: 'Delete', dangerous: true });
|
|
485
|
+
if (!tmpConfirmed)
|
|
486
|
+
{
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Second confirmation — require typing the table name
|
|
491
|
+
let tmpSecondConfirm = await this.pict.views['Pict-Section-Modal'].confirm('This action is IRREVERSIBLE. Are you absolutely sure you want to delete "' + pTableName + '"?', { title: 'Confirm Deletion', confirmLabel: 'Yes, Delete ' + pTableName, dangerous: true });
|
|
492
|
+
if (!tmpSecondConfirm)
|
|
493
|
+
{
|
|
494
|
+
this.pict.views['Pict-Section-Modal'].toast('Deletion cancelled.', {type: 'warning'});
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
this.deleteStore(pIDProjectionStore, pTableName);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
deleteStore(pIDProjectionStore, pTableName)
|
|
502
|
+
{
|
|
503
|
+
this.pict.views['Pict-Section-Modal'].toast('Deleting store "' + pTableName + '"...', {type: 'info'});
|
|
504
|
+
|
|
505
|
+
this.pict.providers.Facto.deleteProjectionStore(pIDProjectionStore).then(
|
|
506
|
+
(pResponse) =>
|
|
507
|
+
{
|
|
508
|
+
if (pResponse && pResponse.Error)
|
|
509
|
+
{
|
|
510
|
+
this.pict.views['Pict-Section-Modal'].toast('Error: ' + pResponse.Error, {type: 'error'});
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
this.pict.views['Pict-Section-Modal'].toast('Store "' + pTableName + '" deleted successfully.', {type: 'success'});
|
|
515
|
+
|
|
516
|
+
// Remove from local stores list and re-render
|
|
517
|
+
this._Stores = this._Stores.filter(
|
|
518
|
+
function(s) { return s.IDProjectionStore !== pIDProjectionStore; });
|
|
519
|
+
this._renderStoresSection();
|
|
520
|
+
this._renderImportSection();
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// ================================================================
|
|
525
|
+
// Mappings Section
|
|
526
|
+
// ================================================================
|
|
527
|
+
|
|
528
|
+
_renderMappingsSection()
|
|
529
|
+
{
|
|
530
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Mappings');
|
|
531
|
+
if (!tmpContainer) return;
|
|
532
|
+
|
|
533
|
+
if (this._Mappings.length === 0)
|
|
534
|
+
{
|
|
535
|
+
tmpContainer.innerHTML = '<div class="facto-card" style="text-align:center; padding:1.5em; color:var(--facto-text-tertiary);">No mappings yet. Create a mapping to link source data to this projection.</div>';
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Build a lookup for source names
|
|
540
|
+
let tmpSourceMap = {};
|
|
541
|
+
for (let i = 0; i < this._Sources.length; i++)
|
|
542
|
+
{
|
|
543
|
+
tmpSourceMap[this._Sources[i].IDSource] = this._Sources[i].Name || this._Sources[i].Hash || ('#' + this._Sources[i].IDSource);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Build a lookup for store names
|
|
547
|
+
let tmpStoreMap = {};
|
|
548
|
+
for (let i = 0; i < this._Stores.length; i++)
|
|
549
|
+
{
|
|
550
|
+
tmpStoreMap[this._Stores[i].IDProjectionStore] = this._Stores[i].TargetTableName || ('Store ' + this._Stores[i].IDProjectionStore);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
let tmpHtml = '<table class="facto-table"><thead><tr>';
|
|
554
|
+
tmpHtml += '<th>ID</th><th>Name</th><th>Source</th><th>Target Stores</th><th>Active</th>';
|
|
555
|
+
tmpHtml += '</tr></thead><tbody>';
|
|
556
|
+
|
|
557
|
+
for (let i = 0; i < this._Mappings.length; i++)
|
|
558
|
+
{
|
|
559
|
+
let tmpMapping = this._Mappings[i];
|
|
560
|
+
let tmpSourceName = tmpSourceMap[tmpMapping.IDSource] || ('#' + (tmpMapping.IDSource || '?'));
|
|
561
|
+
let tmpActive = tmpMapping.Active
|
|
562
|
+
? '<span class="facto-badge facto-badge-success">\u2714</span>'
|
|
563
|
+
: '<span class="facto-badge facto-badge-muted">\u2718</span>';
|
|
564
|
+
|
|
565
|
+
// Parse TargetStores from config
|
|
566
|
+
let tmpStoreNames = [];
|
|
567
|
+
try
|
|
568
|
+
{
|
|
569
|
+
let tmpConfig = JSON.parse(tmpMapping.MappingConfiguration || '{}');
|
|
570
|
+
if (Array.isArray(tmpConfig.TargetStores))
|
|
571
|
+
{
|
|
572
|
+
for (let j = 0; j < tmpConfig.TargetStores.length; j++)
|
|
573
|
+
{
|
|
574
|
+
tmpStoreNames.push(tmpStoreMap[tmpConfig.TargetStores[j]] || ('#' + tmpConfig.TargetStores[j]));
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
catch (e) { /* ignore */ }
|
|
579
|
+
|
|
580
|
+
// Legacy fallback
|
|
581
|
+
if (tmpStoreNames.length === 0 && tmpMapping.IDProjectionStore)
|
|
582
|
+
{
|
|
583
|
+
tmpStoreNames.push(tmpStoreMap[tmpMapping.IDProjectionStore] || ('#' + tmpMapping.IDProjectionStore));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
let tmpStoresDisplay = tmpStoreNames.length > 0 ? tmpStoreNames.join(', ') : '\u2014';
|
|
587
|
+
|
|
588
|
+
tmpHtml += '<tr>';
|
|
589
|
+
tmpHtml += '<td>' + (tmpMapping.IDProjectionMapping || '') + '</td>';
|
|
590
|
+
tmpHtml += '<td><strong>' + (tmpMapping.Name || '\u2014') + '</strong></td>';
|
|
591
|
+
tmpHtml += '<td>' + this._escapeHtml(tmpSourceName) + '</td>';
|
|
592
|
+
tmpHtml += '<td>' + this._escapeHtml(tmpStoresDisplay) + '</td>';
|
|
593
|
+
tmpHtml += '<td>' + tmpActive + '</td>';
|
|
594
|
+
tmpHtml += '</tr>';
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
tmpHtml += '</tbody></table>';
|
|
598
|
+
tmpContainer.innerHTML = tmpHtml;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ================================================================
|
|
602
|
+
// Deploy to Store
|
|
603
|
+
// ================================================================
|
|
604
|
+
|
|
605
|
+
showDeployForm()
|
|
606
|
+
{
|
|
607
|
+
let tmpDeployDiv = document.getElementById('Facto-ProjectionDetail-Deploy');
|
|
608
|
+
if (tmpDeployDiv) tmpDeployDiv.style.display = '';
|
|
609
|
+
|
|
610
|
+
// Populate connection dropdown
|
|
611
|
+
let tmpSelect = document.getElementById('Facto-ProjectionDetail-Deploy-Connection');
|
|
612
|
+
if (tmpSelect)
|
|
613
|
+
{
|
|
614
|
+
let tmpHtml = '<option value="">Select a connection...</option>';
|
|
615
|
+
for (let i = 0; i < this._Connections.length; i++)
|
|
616
|
+
{
|
|
617
|
+
let tmpConn = this._Connections[i];
|
|
618
|
+
tmpHtml += '<option value="' + tmpConn.IDStoreConnection + '">' + this._escapeHtml(tmpConn.Name || 'Untitled') + ' (' + (tmpConn.Type || '') + ')</option>';
|
|
619
|
+
}
|
|
620
|
+
tmpSelect.innerHTML = tmpHtml;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Default table name from projection name
|
|
624
|
+
let tmpTableInput = document.getElementById('Facto-ProjectionDetail-Deploy-TableName');
|
|
625
|
+
if (tmpTableInput && !tmpTableInput.value)
|
|
626
|
+
{
|
|
627
|
+
let tmpName = this._ProjectionData ? this._ProjectionData.Name : 'Projection';
|
|
628
|
+
tmpTableInput.value = (tmpName || 'Projection').replace(/[^a-zA-Z0-9_]/g, '_');
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Hide old log
|
|
632
|
+
let tmpLog = document.getElementById('Facto-ProjectionDetail-Deploy-Log');
|
|
633
|
+
if (tmpLog) tmpLog.style.display = 'none';
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
hideDeployForm()
|
|
637
|
+
{
|
|
638
|
+
let tmpDeployDiv = document.getElementById('Facto-ProjectionDetail-Deploy');
|
|
639
|
+
if (tmpDeployDiv) tmpDeployDiv.style.display = 'none';
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
deployToStore()
|
|
643
|
+
{
|
|
644
|
+
let tmpSelect = document.getElementById('Facto-ProjectionDetail-Deploy-Connection');
|
|
645
|
+
let tmpTableInput = document.getElementById('Facto-ProjectionDetail-Deploy-TableName');
|
|
646
|
+
let tmpLog = document.getElementById('Facto-ProjectionDetail-Deploy-Log');
|
|
647
|
+
|
|
648
|
+
let tmpIDConn = tmpSelect ? parseInt(tmpSelect.value, 10) : 0;
|
|
649
|
+
let tmpTableName = tmpTableInput ? tmpTableInput.value.trim() : '';
|
|
650
|
+
|
|
651
|
+
if (!tmpIDConn)
|
|
652
|
+
{
|
|
653
|
+
this.pict.views['Pict-Section-Modal'].toast('Select a connection first.', {type: 'warning'});
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (!tmpTableName)
|
|
657
|
+
{
|
|
658
|
+
this.pict.views['Pict-Section-Modal'].toast('Enter a table name.', {type: 'warning'});
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (tmpLog)
|
|
663
|
+
{
|
|
664
|
+
tmpLog.style.display = 'block';
|
|
665
|
+
tmpLog.textContent = 'Deploying...';
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
this.pict.providers.Facto.deployProjection(this._CurrentIDDataset, tmpIDConn, tmpTableName).then(
|
|
669
|
+
(pResponse) =>
|
|
670
|
+
{
|
|
671
|
+
if (tmpLog)
|
|
672
|
+
{
|
|
673
|
+
if (pResponse && pResponse.Log)
|
|
674
|
+
{
|
|
675
|
+
tmpLog.textContent = pResponse.Log;
|
|
676
|
+
}
|
|
677
|
+
else if (pResponse && pResponse.Error)
|
|
678
|
+
{
|
|
679
|
+
tmpLog.textContent = 'ERROR: ' + pResponse.Error;
|
|
680
|
+
this.pict.views['Pict-Section-Modal'].toast('Deploy failed: ' + pResponse.Error, {type: 'error'});
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
else
|
|
684
|
+
{
|
|
685
|
+
tmpLog.textContent = 'Deployment complete.';
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
this.pict.views['Pict-Section-Modal'].toast('Store deployed successfully.', {type: 'success'});
|
|
690
|
+
|
|
691
|
+
// Refresh stores list
|
|
692
|
+
this.pict.providers.Facto.loadProjectionStores(this._CurrentIDDataset).then(
|
|
693
|
+
(pResult) =>
|
|
694
|
+
{
|
|
695
|
+
this._Stores = (pResult && pResult.Stores) ? pResult.Stores : [];
|
|
696
|
+
this._renderStoresSection();
|
|
697
|
+
this._renderImportSection();
|
|
698
|
+
this._renderMetaCards();
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
async redeployStore(pIDProjectionStore, pIDStoreConnection, pTableName)
|
|
704
|
+
{
|
|
705
|
+
let tmpConfirmed = await this.pict.views['Pict-Section-Modal'].confirm('Redeploy schema to "' + pTableName + '"? This will update the table structure.', { title: 'Redeploy Schema', confirmLabel: 'Redeploy', dangerous: true });
|
|
706
|
+
if (!tmpConfirmed) return;
|
|
707
|
+
|
|
708
|
+
let tmpLog = document.getElementById('Facto-ProjectionDetail-Deploy-Log');
|
|
709
|
+
let tmpDeployDiv = document.getElementById('Facto-ProjectionDetail-Deploy');
|
|
710
|
+
if (tmpDeployDiv) tmpDeployDiv.style.display = '';
|
|
711
|
+
if (tmpLog)
|
|
712
|
+
{
|
|
713
|
+
tmpLog.style.display = 'block';
|
|
714
|
+
tmpLog.textContent = 'Redeploying...';
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
this.pict.providers.Facto.deployProjection(this._CurrentIDDataset, pIDStoreConnection, pTableName).then(
|
|
718
|
+
(pResponse) =>
|
|
719
|
+
{
|
|
720
|
+
if (tmpLog)
|
|
721
|
+
{
|
|
722
|
+
if (pResponse && pResponse.Log)
|
|
723
|
+
{
|
|
724
|
+
tmpLog.textContent = pResponse.Log;
|
|
725
|
+
}
|
|
726
|
+
else if (pResponse && pResponse.Error)
|
|
727
|
+
{
|
|
728
|
+
tmpLog.textContent = 'ERROR: ' + pResponse.Error;
|
|
729
|
+
this.pict.views['Pict-Section-Modal'].toast('Redeploy failed: ' + pResponse.Error, {type: 'error'});
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
else
|
|
733
|
+
{
|
|
734
|
+
tmpLog.textContent = 'Redeployment complete.';
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
this.pict.views['Pict-Section-Modal'].toast('Store redeployed successfully.', {type: 'success'});
|
|
739
|
+
|
|
740
|
+
// Refresh stores list
|
|
741
|
+
this.pict.providers.Facto.loadProjectionStores(this._CurrentIDDataset).then(
|
|
742
|
+
(pResult) =>
|
|
743
|
+
{
|
|
744
|
+
this._Stores = (pResult && pResult.Stores) ? pResult.Stores : [];
|
|
745
|
+
this._renderStoresSection();
|
|
746
|
+
this._renderImportSection();
|
|
747
|
+
this._renderMetaCards();
|
|
748
|
+
});
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// ================================================================
|
|
753
|
+
// Import Data
|
|
754
|
+
// ================================================================
|
|
755
|
+
|
|
756
|
+
_renderImportSection()
|
|
757
|
+
{
|
|
758
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Import');
|
|
759
|
+
if (!tmpContainer) return;
|
|
760
|
+
|
|
761
|
+
if (this._Mappings.length === 0 || this._Stores.length === 0)
|
|
762
|
+
{
|
|
763
|
+
tmpContainer.innerHTML = '<div class="facto-card" style="text-align:center; padding:1.5em; color:var(--facto-text-tertiary);">Configure at least one mapping and deploy a store before importing data.</div>';
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
let tmpHtml = '<div class="facto-proj-import-form">';
|
|
768
|
+
|
|
769
|
+
// Mapping dropdown
|
|
770
|
+
tmpHtml += '<div>';
|
|
771
|
+
tmpHtml += '<label>Mapping</label>';
|
|
772
|
+
tmpHtml += '<select id="Facto-ProjectionDetail-Import-Mapping" onchange="pict.views[\'Facto-Full-ProjectionDetail\']._onImportMappingChange()">';
|
|
773
|
+
for (let i = 0; i < this._Mappings.length; i++)
|
|
774
|
+
{
|
|
775
|
+
let tmpMapping = this._Mappings[i];
|
|
776
|
+
tmpHtml += '<option value="' + tmpMapping.IDProjectionMapping + '">' + this._escapeHtml(tmpMapping.Name || ('Mapping #' + tmpMapping.IDProjectionMapping)) + '</option>';
|
|
777
|
+
}
|
|
778
|
+
tmpHtml += '</select>';
|
|
779
|
+
tmpHtml += '</div>';
|
|
780
|
+
|
|
781
|
+
// Store dropdown
|
|
782
|
+
tmpHtml += '<div>';
|
|
783
|
+
tmpHtml += '<label>Target Store</label>';
|
|
784
|
+
tmpHtml += '<select id="Facto-ProjectionDetail-Import-Store">';
|
|
785
|
+
for (let i = 0; i < this._Stores.length; i++)
|
|
786
|
+
{
|
|
787
|
+
let tmpStore = this._Stores[i];
|
|
788
|
+
tmpHtml += '<option value="' + tmpStore.IDProjectionStore + '">' + this._escapeHtml(tmpStore.TargetTableName || ('Store #' + tmpStore.IDProjectionStore)) + '</option>';
|
|
789
|
+
}
|
|
790
|
+
tmpHtml += '</select>';
|
|
791
|
+
tmpHtml += '</div>';
|
|
792
|
+
|
|
793
|
+
// Run Import button
|
|
794
|
+
tmpHtml += '<div>';
|
|
795
|
+
tmpHtml += '<button class="facto-btn facto-btn-primary facto-btn-small" onclick="pict.views[\'Facto-Full-ProjectionDetail\'].runImport()">Run Import</button>';
|
|
796
|
+
tmpHtml += '</div>';
|
|
797
|
+
|
|
798
|
+
// Spacer
|
|
799
|
+
tmpHtml += '<div></div>';
|
|
800
|
+
|
|
801
|
+
// Stage checkbox — full row
|
|
802
|
+
tmpHtml += '<div class="facto-proj-import-stage-row">';
|
|
803
|
+
tmpHtml += '<input type="checkbox" id="Facto-ProjectionDetail-Import-Stage">';
|
|
804
|
+
tmpHtml += '<label for="Facto-ProjectionDetail-Import-Stage">Stage comprehension for inspection</label>';
|
|
805
|
+
tmpHtml += '</div>';
|
|
806
|
+
|
|
807
|
+
tmpHtml += '</div>';
|
|
808
|
+
|
|
809
|
+
// Log area (hidden by default)
|
|
810
|
+
tmpHtml += '<div id="Facto-ProjectionDetail-Import-Log" class="facto-proj-deploy-log" style="display:none;"></div>';
|
|
811
|
+
|
|
812
|
+
// Staging download link area
|
|
813
|
+
tmpHtml += '<div id="Facto-ProjectionDetail-Import-Staging"></div>';
|
|
814
|
+
|
|
815
|
+
tmpContainer.innerHTML = tmpHtml;
|
|
816
|
+
|
|
817
|
+
// If there's a first mapping, filter stores for it
|
|
818
|
+
if (this._Mappings.length > 0)
|
|
819
|
+
{
|
|
820
|
+
this._onImportMappingChange();
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
_onImportMappingChange()
|
|
825
|
+
{
|
|
826
|
+
let tmpMappingSelect = document.getElementById('Facto-ProjectionDetail-Import-Mapping');
|
|
827
|
+
let tmpStoreSelect = document.getElementById('Facto-ProjectionDetail-Import-Store');
|
|
828
|
+
if (!tmpMappingSelect || !tmpStoreSelect) return;
|
|
829
|
+
|
|
830
|
+
let tmpIDMapping = parseInt(tmpMappingSelect.value, 10);
|
|
831
|
+
|
|
832
|
+
// Find the mapping config to see if it has TargetStores
|
|
833
|
+
let tmpTargetStores = null;
|
|
834
|
+
for (let i = 0; i < this._Mappings.length; i++)
|
|
835
|
+
{
|
|
836
|
+
if (this._Mappings[i].IDProjectionMapping == tmpIDMapping)
|
|
837
|
+
{
|
|
838
|
+
try
|
|
839
|
+
{
|
|
840
|
+
let tmpConfig = JSON.parse(this._Mappings[i].MappingConfiguration || '{}');
|
|
841
|
+
if (Array.isArray(tmpConfig.TargetStores) && tmpConfig.TargetStores.length > 0)
|
|
842
|
+
{
|
|
843
|
+
tmpTargetStores = tmpConfig.TargetStores;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
catch (e) { /* ignore */ }
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// Rebuild store dropdown, filtering to target stores if specified
|
|
852
|
+
let tmpHtml = '';
|
|
853
|
+
let tmpFilteredHtml = '';
|
|
854
|
+
let tmpAllHtml = '';
|
|
855
|
+
for (let i = 0; i < this._Stores.length; i++)
|
|
856
|
+
{
|
|
857
|
+
let tmpStore = this._Stores[i];
|
|
858
|
+
let tmpOption = '<option value="' + tmpStore.IDProjectionStore + '">' + this._escapeHtml(tmpStore.TargetTableName || ('Store #' + tmpStore.IDProjectionStore)) + '</option>';
|
|
859
|
+
tmpAllHtml += tmpOption;
|
|
860
|
+
if (!tmpTargetStores || tmpTargetStores.indexOf(tmpStore.IDProjectionStore) !== -1)
|
|
861
|
+
{
|
|
862
|
+
tmpFilteredHtml += tmpOption;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
// If filtering left no matches, show all stores instead
|
|
866
|
+
tmpStoreSelect.innerHTML = tmpFilteredHtml || tmpAllHtml;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
runImport()
|
|
870
|
+
{
|
|
871
|
+
let tmpMappingSelect = document.getElementById('Facto-ProjectionDetail-Import-Mapping');
|
|
872
|
+
let tmpStoreSelect = document.getElementById('Facto-ProjectionDetail-Import-Store');
|
|
873
|
+
let tmpStageCheckbox = document.getElementById('Facto-ProjectionDetail-Import-Stage');
|
|
874
|
+
let tmpLog = document.getElementById('Facto-ProjectionDetail-Import-Log');
|
|
875
|
+
let tmpStagingDiv = document.getElementById('Facto-ProjectionDetail-Import-Staging');
|
|
876
|
+
|
|
877
|
+
let tmpIDMapping = tmpMappingSelect ? parseInt(tmpMappingSelect.value, 10) : 0;
|
|
878
|
+
let tmpIDStore = tmpStoreSelect ? parseInt(tmpStoreSelect.value, 10) : 0;
|
|
879
|
+
let tmpStageComprehension = tmpStageCheckbox ? tmpStageCheckbox.checked : false;
|
|
880
|
+
|
|
881
|
+
if (!tmpIDMapping)
|
|
882
|
+
{
|
|
883
|
+
this.pict.views['Pict-Section-Modal'].toast('Select a mapping first.', {type: 'warning'});
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
if (!tmpIDStore)
|
|
887
|
+
{
|
|
888
|
+
this.pict.views['Pict-Section-Modal'].toast('Select a target store first.', {type: 'warning'});
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if (tmpLog)
|
|
893
|
+
{
|
|
894
|
+
tmpLog.style.display = 'block';
|
|
895
|
+
tmpLog.textContent = 'Running import...';
|
|
896
|
+
}
|
|
897
|
+
if (tmpStagingDiv)
|
|
898
|
+
{
|
|
899
|
+
tmpStagingDiv.innerHTML = '';
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
this.pict.providers.Facto.executeImport(this._CurrentIDDataset, tmpIDMapping, tmpIDStore, 100, tmpStageComprehension).then(
|
|
903
|
+
(pResponse) =>
|
|
904
|
+
{
|
|
905
|
+
if (tmpLog)
|
|
906
|
+
{
|
|
907
|
+
if (pResponse && pResponse.Log)
|
|
908
|
+
{
|
|
909
|
+
tmpLog.textContent = pResponse.Log;
|
|
910
|
+
}
|
|
911
|
+
else if (pResponse && pResponse.Error)
|
|
912
|
+
{
|
|
913
|
+
tmpLog.textContent = 'ERROR: ' + pResponse.Error;
|
|
914
|
+
this.pict.views['Pict-Section-Modal'].toast('Import failed: ' + pResponse.Error, {type: 'error'});
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
else
|
|
918
|
+
{
|
|
919
|
+
tmpLog.textContent = 'Import complete.';
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
if (pResponse && pResponse.Success)
|
|
924
|
+
{
|
|
925
|
+
this.pict.views['Pict-Section-Modal'].toast('Import complete: ' + (pResponse.RecordsCreated || 0) + ' records created.', {type: 'success'});
|
|
926
|
+
}
|
|
927
|
+
else if (pResponse && pResponse.RecordsErrored > 0)
|
|
928
|
+
{
|
|
929
|
+
this.pict.views['Pict-Section-Modal'].toast('Import completed with ' + pResponse.RecordsErrored + ' errors.', {type: 'warning'});
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Show staging download link if a file was generated
|
|
933
|
+
if (pResponse && pResponse.StagingFile && tmpStagingDiv)
|
|
934
|
+
{
|
|
935
|
+
let tmpDownloadUrl = '/facto/projection/' + this._CurrentIDDataset + '/comprehension/' + pResponse.StagingFile;
|
|
936
|
+
tmpStagingDiv.innerHTML = '<a class="facto-proj-import-staging-link" href="' + tmpDownloadUrl + '" target="_blank">Download staged comprehension: ' + this._escapeHtml(pResponse.StagingFile) + '</a>';
|
|
937
|
+
}
|
|
938
|
+
}).catch(
|
|
939
|
+
(pError) =>
|
|
940
|
+
{
|
|
941
|
+
if (tmpLog)
|
|
942
|
+
{
|
|
943
|
+
tmpLog.style.display = 'block';
|
|
944
|
+
tmpLog.textContent = 'ERROR: ' + (pError.message || pError);
|
|
945
|
+
}
|
|
946
|
+
this.pict.views['Pict-Section-Modal'].toast('Import failed.', {type: 'error'});
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// ================================================================
|
|
951
|
+
// Schema Editor Delegation
|
|
952
|
+
// ================================================================
|
|
953
|
+
|
|
954
|
+
editSchema()
|
|
955
|
+
{
|
|
956
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Container');
|
|
957
|
+
let tmpBack = document.querySelector('.facto-proj-detail-back');
|
|
958
|
+
let tmpSchemaContainer = document.getElementById('Facto-Proj-Schema-Editor-Container');
|
|
959
|
+
|
|
960
|
+
if (tmpContainer) tmpContainer.style.display = 'none';
|
|
961
|
+
if (tmpBack) tmpBack.style.display = 'none';
|
|
962
|
+
if (tmpSchemaContainer) tmpSchemaContainer.style.display = '';
|
|
963
|
+
|
|
964
|
+
let tmpName = this._ProjectionData ? this._ProjectionData.Name : '';
|
|
965
|
+
this.pict.views['Facto-Full-SchemaEditor'].editSchema(this._CurrentIDDataset, tmpName);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
closeSchemaEditor()
|
|
969
|
+
{
|
|
970
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Container');
|
|
971
|
+
let tmpBack = document.querySelector('.facto-proj-detail-back');
|
|
972
|
+
let tmpSchemaContainer = document.getElementById('Facto-Proj-Schema-Editor-Container');
|
|
973
|
+
|
|
974
|
+
if (tmpContainer) tmpContainer.style.display = '';
|
|
975
|
+
if (tmpBack) tmpBack.style.display = '';
|
|
976
|
+
if (tmpSchemaContainer) tmpSchemaContainer.style.display = 'none';
|
|
977
|
+
|
|
978
|
+
// Refresh schema section to pick up changes
|
|
979
|
+
this.pict.providers.Facto.loadProjectionSchema(this._CurrentIDDataset).then(
|
|
980
|
+
(pSchema) =>
|
|
981
|
+
{
|
|
982
|
+
this._Schema = pSchema || {};
|
|
983
|
+
this._renderSchemaSection();
|
|
984
|
+
this._renderMetaCards();
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
onSchemaUpdated(pSchemaVersion)
|
|
989
|
+
{
|
|
990
|
+
// Called by SchemaEditor after a successful save
|
|
991
|
+
if (this._ProjectionData)
|
|
992
|
+
{
|
|
993
|
+
this._ProjectionData.SchemaVersion = pSchemaVersion;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// ================================================================
|
|
998
|
+
// Mapping Editor Delegation
|
|
999
|
+
// ================================================================
|
|
1000
|
+
|
|
1001
|
+
editMappings()
|
|
1002
|
+
{
|
|
1003
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Container');
|
|
1004
|
+
let tmpBack = document.querySelector('.facto-proj-detail-back');
|
|
1005
|
+
let tmpMappingContainer = document.getElementById('Facto-Proj-Mapping-Editor-Container');
|
|
1006
|
+
|
|
1007
|
+
if (tmpContainer) tmpContainer.style.display = 'none';
|
|
1008
|
+
if (tmpBack) tmpBack.style.display = 'none';
|
|
1009
|
+
if (tmpMappingContainer) tmpMappingContainer.style.display = '';
|
|
1010
|
+
|
|
1011
|
+
let tmpName = this._ProjectionData ? this._ProjectionData.Name : '';
|
|
1012
|
+
this.pict.views['Facto-Full-MappingEditor'].editMappings(this._CurrentIDDataset, tmpName);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
closeMappingEditor()
|
|
1016
|
+
{
|
|
1017
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Container');
|
|
1018
|
+
let tmpBack = document.querySelector('.facto-proj-detail-back');
|
|
1019
|
+
let tmpMappingContainer = document.getElementById('Facto-Proj-Mapping-Editor-Container');
|
|
1020
|
+
|
|
1021
|
+
if (tmpContainer) tmpContainer.style.display = '';
|
|
1022
|
+
if (tmpBack) tmpBack.style.display = '';
|
|
1023
|
+
if (tmpMappingContainer) tmpMappingContainer.style.display = 'none';
|
|
1024
|
+
|
|
1025
|
+
// Refresh mappings section to pick up changes
|
|
1026
|
+
Promise.all(
|
|
1027
|
+
[
|
|
1028
|
+
this.pict.providers.Facto.loadProjectionMappings(this._CurrentIDDataset),
|
|
1029
|
+
this.pict.providers.Facto.loadProjectionStores(this._CurrentIDDataset)
|
|
1030
|
+
]).then(
|
|
1031
|
+
(pResults) =>
|
|
1032
|
+
{
|
|
1033
|
+
this._Mappings = (pResults[0] && pResults[0].Mappings) ? pResults[0].Mappings : [];
|
|
1034
|
+
this._Stores = (pResults[1] && pResults[1].Stores) ? pResults[1].Stores : [];
|
|
1035
|
+
this._renderMappingsSection();
|
|
1036
|
+
this._renderStoresSection();
|
|
1037
|
+
this._renderMetaCards();
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// ================================================================
|
|
1042
|
+
// Query Panel Delegation
|
|
1043
|
+
// ================================================================
|
|
1044
|
+
|
|
1045
|
+
showQuery()
|
|
1046
|
+
{
|
|
1047
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Container');
|
|
1048
|
+
let tmpBack = document.querySelector('.facto-proj-detail-back');
|
|
1049
|
+
let tmpQueryContainer = document.getElementById('Facto-Proj-Query-Container');
|
|
1050
|
+
|
|
1051
|
+
if (tmpContainer) tmpContainer.style.display = 'none';
|
|
1052
|
+
if (tmpBack) tmpBack.style.display = 'none';
|
|
1053
|
+
if (tmpQueryContainer) tmpQueryContainer.style.display = '';
|
|
1054
|
+
|
|
1055
|
+
let tmpQueryPanel = this.pict.views['Facto-Full-QueryPanel'];
|
|
1056
|
+
if (tmpQueryPanel)
|
|
1057
|
+
{
|
|
1058
|
+
tmpQueryPanel.render();
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
closeQuery()
|
|
1063
|
+
{
|
|
1064
|
+
let tmpContainer = document.getElementById('Facto-ProjectionDetail-Container');
|
|
1065
|
+
let tmpBack = document.querySelector('.facto-proj-detail-back');
|
|
1066
|
+
let tmpQueryContainer = document.getElementById('Facto-Proj-Query-Container');
|
|
1067
|
+
|
|
1068
|
+
if (tmpContainer) tmpContainer.style.display = '';
|
|
1069
|
+
if (tmpBack) tmpBack.style.display = '';
|
|
1070
|
+
if (tmpQueryContainer) tmpQueryContainer.style.display = 'none';
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// ================================================================
|
|
1074
|
+
// Actions
|
|
1075
|
+
// ================================================================
|
|
1076
|
+
|
|
1077
|
+
async deleteProjection()
|
|
1078
|
+
{
|
|
1079
|
+
let tmpName = this._ProjectionData ? this._ProjectionData.Name : this._CurrentIDDataset;
|
|
1080
|
+
let tmpConfirmed = await this.pict.views['Pict-Section-Modal'].confirm('Delete projection "' + tmpName + '"? This will remove the dataset and all associated mappings and stores.', { title: 'Delete Projection', confirmLabel: 'Delete', dangerous: true });
|
|
1081
|
+
if (!tmpConfirmed) return;
|
|
1082
|
+
|
|
1083
|
+
this.pict.providers.Facto.deleteProjection(this._CurrentIDDataset).then(
|
|
1084
|
+
(pResponse) =>
|
|
1085
|
+
{
|
|
1086
|
+
if (pResponse && pResponse.Error)
|
|
1087
|
+
{
|
|
1088
|
+
this.pict.views['Pict-Section-Modal'].toast('Error deleting projection: ' + pResponse.Error, {type: 'error'});
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
this.pict.views['Pict-Section-Modal'].toast('Projection deleted.', {type: 'success'});
|
|
1093
|
+
this.pict.PictApplication.navigateTo('/Projections');
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
refresh()
|
|
1098
|
+
{
|
|
1099
|
+
if (this._CurrentIDDataset)
|
|
1100
|
+
{
|
|
1101
|
+
this._fetchAndDisplayProjection();
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
goBack()
|
|
1106
|
+
{
|
|
1107
|
+
this.pict.PictApplication.navigateTo('/Projections');
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
_escapeHtml(pText)
|
|
1111
|
+
{
|
|
1112
|
+
let tmpDiv = document.createElement('div');
|
|
1113
|
+
tmpDiv.textContent = pText;
|
|
1114
|
+
return tmpDiv.innerHTML;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
module.exports = FactoFullProjectionDetailView;
|
|
1119
|
+
|
|
1120
|
+
module.exports.default_configuration = _ViewConfiguration;
|