datastake-daf 0.6.183 → 0.6.185

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.
@@ -0,0 +1,371 @@
1
+ import { showHideInput } from "../ViewForm/helper";
2
+ import { renderDateFormatted } from "../../../../helpers/Forms";
3
+
4
+
5
+ // Helper function to format references - show only one line to distinguish each
6
+ const formatReferences = (description) => {
7
+ if (!description) return "";
8
+ return description.split("<br>")
9
+ .filter(line => line.trim())
10
+ .map(line => line.trim().replace(/^-+\s*/, '')) // Remove existing dashes
11
+ .filter(line => line) // Remove empty lines
12
+ .join("\n"); // Single line separation
13
+ };
14
+
15
+ // Helper function to format planning comments
16
+ const formatPlanningComments = (comments, t = (key) => key) => {
17
+ if (!comments || !Array.isArray(comments)) return t("not answered");
18
+ const validComments = comments.map(comment => comment.text || "").filter(text => text);
19
+ return validComments.length > 0 ? validComments.join(", ") : t("not answered");
20
+ };
21
+
22
+ // Helper function to format responsible persons - map IDs to names using linking data
23
+ const formatResponsiblePerson = (accountablePersonnel, linkingData = {}, t = (key) => key) => {
24
+ if (!accountablePersonnel || !Array.isArray(accountablePersonnel) || accountablePersonnel.length === 0) return t("not answered");
25
+
26
+ const names = accountablePersonnel.map(id => {
27
+ // Check if linking data has this ID
28
+ const continuousImprovementData = linkingData?.continuousImprovement || {};
29
+ const person = continuousImprovementData[id];
30
+
31
+ if (person) {
32
+ return person.firstName || person.label || id;
33
+ }
34
+
35
+ // Fallback to ID if no name found
36
+ return id;
37
+ });
38
+
39
+ return names.length > 0 ? names.join(", ") : t("not answered");
40
+ };
41
+
42
+ // Helper function to format target completion date
43
+ const formatTargetCompletionDate = (targetCompletionDate, t = (key) => key) => {
44
+ if (!targetCompletionDate) return t("not answered");
45
+ return renderDateFormatted(targetCompletionDate, 'DD MMM YYYY', 'en');
46
+ };
47
+
48
+ // Helper function to format evaluation documents from previousStage criteria - show document names with dashes
49
+ const formatEvaluationDocuments = (previousStageCriteria, subCategory, criteriaKey, t = (key) => key) => {
50
+ if (!previousStageCriteria || !Array.isArray(previousStageCriteria)) return t("not answered");
51
+
52
+ // Find the matching criteria by subCategory
53
+ const matchingCriteria = previousStageCriteria.find(criteria => criteria.subCategory === subCategory);
54
+
55
+ if (!matchingCriteria) return t("not answered");
56
+
57
+ const allDocuments = [];
58
+
59
+ // Get document names from the specific field's documentation (same as documentation field)
60
+ const fieldData = matchingCriteria[criteriaKey];
61
+ if (fieldData && fieldData.documentation && Array.isArray(fieldData.documentation)) {
62
+ fieldData.documentation.forEach(doc => {
63
+ if (doc && doc.documents && Array.isArray(doc.documents)) {
64
+ doc.documents.forEach(document => {
65
+ if (document && document.name) {
66
+ allDocuments.push(`- ${document.name}`);
67
+ }
68
+ });
69
+ }
70
+ });
71
+ }
72
+
73
+ return allDocuments.length > 0 ? allDocuments.join("\n") : t("not answered");
74
+ };
75
+
76
+ // Helper function to format valuation from previousStage criteria
77
+ const formatValuation = (previousStageCriteria, subCategory, criteriaKey, t = (key) => key) => {
78
+ if (!previousStageCriteria || !Array.isArray(previousStageCriteria)) return t("not answered");
79
+
80
+ // Find the matching criteria by subCategory
81
+ const matchingCriteria = previousStageCriteria.find(criteria => criteria.subCategory === subCategory);
82
+
83
+ if (!matchingCriteria) return t("not answered");
84
+
85
+ // Get valuation from the specific field in the criteria
86
+ const fieldData = matchingCriteria[criteriaKey];
87
+ if (fieldData && fieldData.valuation !== undefined && fieldData.valuation !== null) {
88
+ return fieldData.valuation === 'na' ? 'N/A' : `${fieldData.valuation}%`;
89
+ }
90
+
91
+ return t("not answered");
92
+ };
93
+
94
+ // Helper function to format documentation from previousStage criteria
95
+ const formatDocumentation = (previousStageCriteria, subCategory, criteriaKey, t = (key) => key) => {
96
+ if (!previousStageCriteria || !Array.isArray(previousStageCriteria)) return t("not answered");
97
+
98
+ // Find the matching criteria by subCategory
99
+ const matchingCriteria = previousStageCriteria.find(criteria => criteria.subCategory === subCategory);
100
+
101
+ if (!matchingCriteria) return t("not answered");
102
+
103
+ const allDocuments = [];
104
+
105
+ // Check if the criteria has the specific field and its documentation
106
+ if (matchingCriteria[criteriaKey] && matchingCriteria[criteriaKey].documentation) {
107
+ const docs = matchingCriteria[criteriaKey].documentation;
108
+
109
+ if (Array.isArray(docs)) {
110
+ docs.forEach(doc => {
111
+ if (doc && doc.documents && Array.isArray(doc.documents)) {
112
+ doc.documents.forEach(document => {
113
+ if (document.name) {
114
+ allDocuments.push(document.name);
115
+ }
116
+ });
117
+ }
118
+ });
119
+ }
120
+ }
121
+
122
+ return allDocuments.length > 0 ? allDocuments.join(", ") : t("not answered");
123
+ };
124
+
125
+ // Helper function to format verification method - remove <br> and add appropriate separators
126
+ const formatVerificationMethod = (description, forCSV = false, t = (key) => key) => {
127
+ if (!description) return t("not answered");
128
+ const lines = description.split("<br>")
129
+ .filter(line => line.trim())
130
+ .map(line => line.trim());
131
+
132
+ if (lines.length === 0) return t("not answered");
133
+
134
+ // For CSV export, use semicolon separator to avoid newline issues
135
+ if (forCSV) {
136
+ return lines.join("; ");
137
+ }
138
+
139
+ // For display, use newlines
140
+ return lines.join("\n");
141
+ };
142
+
143
+ // Helper function to get description by language
144
+ const getDescriptionByLanguage = (descriptions, language = "en") => {
145
+ if (!descriptions) return "";
146
+ return descriptions[language] || descriptions.en || descriptions.sp || descriptions.fr || "";
147
+ };
148
+
149
+ const getAspectToVerify = (form, sectionKey, kidPosition, country) => {
150
+ const titlesSection = form?.titles?.[sectionKey];
151
+ if (!titlesSection) return "";
152
+
153
+ const matchingTitles = Object.keys(titlesSection)
154
+ .filter(key => key.startsWith('title'))
155
+ .map(key => titlesSection[key])
156
+ .filter(title => {
157
+ if (!title.showIf) return false;
158
+ const condition = title.showIf.toLowerCase();
159
+ if (condition.includes('country is')) {
160
+ const requiredCountry = condition.replace('country is', '').trim();
161
+ return country && country.toLowerCase() === requiredCountry.toLowerCase();
162
+ }
163
+ return true;
164
+ })
165
+ .sort((a, b) => (a.position || 0) - (b.position || 0));
166
+
167
+ if (matchingTitles.length === 0) return "";
168
+
169
+ let selectedTitle = matchingTitles[0];
170
+
171
+ for (let i = 0; i < matchingTitles.length; i++) {
172
+ const titlePosition = matchingTitles[i].position || 0;
173
+ if (titlePosition <= kidPosition) {
174
+ selectedTitle = matchingTitles[i];
175
+ } else {
176
+ break;
177
+ }
178
+ }
179
+
180
+
181
+ const label = selectedTitle?.label || "";
182
+ return label.replace(/\\"/g, '"').replace(/\n/g, " ").replace(/\r/g, " ").replace(/\t/g, " ").trim();
183
+ };
184
+
185
+ export const fixDataForExcel = (originalData, stepParam = 1, module = "", t = (key) => key) => {
186
+ const {form, data} = originalData;
187
+ const language = data?.user?.language || "en";
188
+ const step = Number(stepParam);
189
+
190
+ // Table 1 data - Basic Information (vertical key-value format)
191
+ const table1Data = [
192
+ { key: t('mine'), value: data?.location?.name || "Adimental S.A.C" }, // Use placeholder if location data is missing
193
+ { key: t('module'), value: module },
194
+ { key: t('sources'), value: "" }, // Leave blank as requested
195
+ { key: t('version'), value: data?.version || "1" },
196
+ { key: t('date'), value: renderDateFormatted(new Date().toISOString(), 'DD MMM YYYY', (language === 'sp' ? 'es' : language) || 'en') }
197
+ ];
198
+
199
+ console.log("Planning Table 1 Data:", table1Data);
200
+
201
+ // Table 2 data - Planning Data
202
+ const table2Data = [];
203
+
204
+ const sections = (form?.identification?.category?.options || [])?.map((option) => ({
205
+ group: option.label,
206
+ key: option.value,
207
+ criterias: (form?.identification?.subCategory?.options || [])
208
+ .filter((_option) => {
209
+ const categoryFilter = _option?.filters?.find((f) => f.name === "category");
210
+ const stepFilter = _option?.filters?.find((f) => f.name === "step");
211
+
212
+ return (
213
+ (stepFilter?.value || 1) === step &&
214
+ (categoryFilter?.value || []).includes(option.value) &&
215
+ !!(data?.criteria || []).find(
216
+ (c) => c.subCategory === _option.value,
217
+ )
218
+ );
219
+ })
220
+ .map((_option) => {
221
+ const _criteria = (data?.criteria || []).find(
222
+ (c) => c.subCategory === _option.value,
223
+ );
224
+ const subCategoryForm = form[_criteria.subCategory];
225
+
226
+ const kids = Object.keys(subCategoryForm)
227
+ .filter((k) => {
228
+ const val = subCategoryForm[k];
229
+ if (val.showIf) {
230
+ const show = showHideInput(val, data);
231
+ return show;
232
+ }
233
+ return val?.type === "group";
234
+ })
235
+ .sort((a, b) => {
236
+ const valA = subCategoryForm[a];
237
+ const valB = subCategoryForm[b];
238
+ return valA?.position - valB?.position;
239
+ })
240
+ .map((k) => {
241
+ const formVal = subCategoryForm[k];
242
+ const evalData = _criteria[k];
243
+ const meta = (_criteria?.meta?.inputs || {})[k];
244
+
245
+ // Get reference description from form object description field
246
+ const referenceDescription = formVal.description || "";
247
+
248
+ // Get verification method from form meta
249
+ const verificationMethod = getDescriptionByLanguage(
250
+ formVal?.meta?.groupInfo?.description,
251
+ language
252
+ );
253
+
254
+ return {
255
+ key: k,
256
+ group: option.value,
257
+ criteria: formVal.label,
258
+ position: formVal.position || 0,
259
+ lastUpdate: _criteria.updatedAt,
260
+ meta,
261
+ referenceDescription,
262
+ verificationMethod,
263
+ // Planning specific data from individual field in criteria
264
+ targetCompletionDate: evalData?.targetCompletionDate,
265
+ accountablePersonnel: evalData?.accountablePersonnel,
266
+ planningComment: evalData?.planningComment,
267
+ ...evalData,
268
+ };
269
+ });
270
+
271
+ return {
272
+ key: _option.value,
273
+ group: option.value,
274
+ criteria: _option.label,
275
+ completion: _criteria.completion,
276
+ evaluationScore: _criteria.evaluationScore,
277
+ description: (form?.identification?.dscription?.options || []).find(
278
+ (d) => d.value === _option.value,
279
+ )?.label || "-",
280
+ lastUpdate: _criteria.updatedAt,
281
+ linking: _criteria.linking || {}, // Pass linking data for responsible person mapping
282
+ kids,
283
+ };
284
+ })
285
+ }));
286
+
287
+ // Process sections to create table 2 data
288
+ sections.forEach((section) => {
289
+ section.criterias.forEach((criteria) => {
290
+ criteria.kids.forEach((kid) => {
291
+ // Get previous stage criteria for evaluation documents and documentation
292
+ const previousStageCriteria = data?.previousStage?.criteria || [];
293
+
294
+ // Get linking data for responsible person mapping
295
+ const linkingData = criteria.linking || {};
296
+
297
+ const row = {
298
+ stage: `0${step}`,
299
+ aspect: section.group,
300
+ criteria: criteria.criteria,
301
+ subcriteria: kid.criteria,
302
+ verificationMethod: formatVerificationMethod(kid.verificationMethod || "", false, t),
303
+ targetCompletionDate: formatTargetCompletionDate(kid.targetCompletionDate, t),
304
+ evaluationDocuments: formatEvaluationDocuments(previousStageCriteria, criteria.key, kid.key, t),
305
+ responsiblePerson: formatResponsiblePerson(kid.accountablePersonnel, linkingData, t),
306
+ comments: formatPlanningComments(kid.planningComment, t),
307
+ valuation: formatValuation(previousStageCriteria, criteria.key, kid.key, t)
308
+ };
309
+ table2Data.push(row);
310
+ });
311
+ });
312
+ });
313
+
314
+ console.log("Planning Table 2 Data:", table2Data);
315
+ console.log("Planning Sections:", sections);
316
+
317
+ return {
318
+ table1: table1Data,
319
+ table2: table2Data,
320
+ sections: sections
321
+ };
322
+ };
323
+
324
+ export const exportToCSV = (data, filename = "planning_data.csv", t = (key) => key) => {
325
+ const { table1, table2 } = data;
326
+
327
+ let csvContent = "";
328
+
329
+ // Add Table 1 data (vertical key-value format, no title)
330
+ table1.forEach(item => {
331
+ csvContent += `"${item.key}","${item.value}"\n`;
332
+ });
333
+ csvContent += "\n"; // Empty line separator
334
+
335
+ // Add Table 2 header and data with planning-specific columns (correct order)
336
+ csvContent += `"${t('stage')}","${t('aspect')}","${t('criteria')}","${t('subcriteria')}","${t('verificationMethod')}","${t('targetCompletionDate')}","${t('evaluationDocuments')}","${t('responsiblePerson')}","${t('comments')}","${t('valuation')}"\n`;
337
+
338
+ table2.forEach(row => {
339
+ const csvRow = [
340
+ row.stage,
341
+ row.aspect,
342
+ row.criteria,
343
+ row.subcriteria,
344
+ formatVerificationMethod(row.verificationMethod || "", true, t).replace(/"/g, '""'),
345
+ row.targetCompletionDate,
346
+ (row.evaluationDocuments || "").replace(/"/g, '""'),
347
+ (row.responsiblePerson || "").replace(/"/g, '""'),
348
+ (row.comments || "").replace(/"/g, '""'),
349
+ row.valuation
350
+ ].map(field => `"${field}"`).join(",");
351
+
352
+ csvContent += csvRow + "\n";
353
+ });
354
+
355
+ return csvContent;
356
+ };
357
+
358
+ // Utility function to download CSV file
359
+ export const downloadCSV = (data, filename = "planning_data.csv", t = (key) => key) => {
360
+ const csvContent = exportToCSV(data, filename, t);
361
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
362
+ const link = document.createElement("a");
363
+ const url = URL.createObjectURL(blob);
364
+
365
+ link.setAttribute("href", url);
366
+ link.setAttribute("download", filename);
367
+ link.style.visibility = 'hidden';
368
+ document.body.appendChild(link);
369
+ link.click();
370
+ document.body.removeChild(link);
371
+ };
@@ -0,0 +1,164 @@
1
+ import React from 'react';
2
+ import { fixDataForExcel, exportToCSV, downloadCSV } from './planningConfig';
3
+ import { storyData } from './storyConfig2';
4
+
5
+ // Translation function for demo purposes
6
+ const t = (key) => {
7
+ const translations = {
8
+ // Table 1 translations
9
+ 'mine': 'Mine',
10
+ 'module': 'Module',
11
+ 'sources': 'Source',
12
+ 'version': 'Version',
13
+ 'date': 'Date',
14
+
15
+ // Table 2 translations
16
+ 'stage': 'Step',
17
+ 'aspect': 'Aspect',
18
+ 'criteria': 'Criteria',
19
+ 'subcriteria': 'Subcriteria',
20
+ 'verificationMethod': 'Means of verification',
21
+ 'targetCompletionDate': 'Target completion date',
22
+ 'responsiblePerson': 'Responsible person',
23
+ 'comments': 'Comments',
24
+ 'evaluationDocuments': 'Evaluation Documents',
25
+ 'valuation': 'Valuation',
26
+ 'not answered': 'not answered'
27
+ };
28
+
29
+ return translations[key] || key;
30
+ };
31
+
32
+ const PlanningDemo = () => {
33
+ // Process the planning story data using our configuration
34
+ const processedData = fixDataForExcel(storyData, 1, "Planning", t);
35
+
36
+ const handleDownloadCSV = () => {
37
+ downloadCSV(processedData, "planning_data.csv", t);
38
+ };
39
+
40
+ const handleViewCSV = () => {
41
+ const csvContent = exportToCSV(processedData, "planning_data.csv", t);
42
+ console.log("CSV Content:", csvContent);
43
+
44
+ // Create a downloadable blob URL for viewing
45
+ const blob = new Blob([csvContent], { type: 'text/csv' });
46
+ const url = URL.createObjectURL(blob);
47
+ window.open(url, '_blank');
48
+ };
49
+
50
+ // Helper function to render cell with gray styling for "not answered"
51
+ const renderCellContent = (content) => {
52
+ if (content === t("not answered")) {
53
+ return <span style={{ color: '#6c757d', opacity: 0.7, fontStyle: 'italic' }}>{content}</span>;
54
+ }
55
+ return content;
56
+ };
57
+
58
+ return (
59
+ <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
60
+ <h1>Planning Data Configuration Demo</h1>
61
+
62
+ <div style={{ marginBottom: '20px' }}>
63
+ <button
64
+ onClick={handleDownloadCSV}
65
+ style={{
66
+ padding: '10px 20px',
67
+ margin: '5px',
68
+ backgroundColor: '#007bff',
69
+ color: 'white',
70
+ border: 'none',
71
+ borderRadius: '4px',
72
+ cursor: 'pointer'
73
+ }}
74
+ >
75
+ Download Planning CSV
76
+ </button>
77
+ <button
78
+ onClick={handleViewCSV}
79
+ style={{
80
+ padding: '10px 20px',
81
+ margin: '5px',
82
+ backgroundColor: '#28a745',
83
+ color: 'white',
84
+ border: 'none',
85
+ borderRadius: '4px',
86
+ cursor: 'pointer'
87
+ }}
88
+ >
89
+ View CSV Content
90
+ </button>
91
+ </div>
92
+
93
+ <div style={{ marginBottom: '30px' }}>
94
+ <h2>Table 1 - Basic Information</h2>
95
+ <table style={{ borderCollapse: 'collapse', width: '100%', border: '1px solid #ddd' }}>
96
+ <tbody>
97
+ {processedData.table1.map((item, index) => (
98
+ <tr key={index}>
99
+ <td style={{ border: '1px solid #ddd', padding: '8px', fontWeight: 'bold', backgroundColor: '#f8f9fa' }}>
100
+ {item.key}
101
+ </td>
102
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>
103
+ {item.value}
104
+ </td>
105
+ </tr>
106
+ ))}
107
+ </tbody>
108
+ </table>
109
+ </div>
110
+
111
+ <div>
112
+ <h2>Table 2 - Planning Data</h2>
113
+ <div style={{ overflowX: 'auto' }}>
114
+ <table style={{ borderCollapse: 'collapse', width: '100%', border: '1px solid #ddd' }}>
115
+ <thead>
116
+ <tr style={{ backgroundColor: '#f8f9fa' }}>
117
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '60px' }}>Step</th>
118
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '120px' }}>Aspect</th>
119
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '150px' }}>Criteria</th>
120
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '150px' }}>Subcriteria</th>
121
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '200px' }}>Means of verification</th>
122
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '120px' }}>Target completion date</th>
123
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '150px' }}>Evaluation Documents</th>
124
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '150px' }}>Responsible person</th>
125
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '200px' }}>Comments</th>
126
+ <th style={{ border: '1px solid #ddd', padding: '8px', minWidth: '80px' }}>Valoración</th>
127
+ </tr>
128
+ </thead>
129
+ <tbody>
130
+ {processedData.table2.map((row, index) => (
131
+ <tr key={index} style={{ backgroundColor: index % 2 === 0 ? '#ffffff' : '#f8f9fa' }}>
132
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>{renderCellContent(row.stage)}</td>
133
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>{renderCellContent(row.aspect)}</td>
134
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>{renderCellContent(row.criteria)}</td>
135
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>{renderCellContent(row.subcriteria)}</td>
136
+ <td style={{ border: '1px solid #ddd', padding: '8px', whiteSpace: 'pre-wrap' }}>{renderCellContent(row.verificationMethod)}</td>
137
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>{renderCellContent(row.targetCompletionDate)}</td>
138
+ <td style={{ border: '1px solid #ddd', padding: '8px', whiteSpace: 'pre-wrap' }}>{renderCellContent(row.evaluationDocuments)}</td>
139
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>{renderCellContent(row.responsiblePerson)}</td>
140
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>{renderCellContent(row.comments)}</td>
141
+ <td style={{ border: '1px solid #ddd', padding: '8px' }}>{renderCellContent(row.valuation)}</td>
142
+ </tr>
143
+ ))}
144
+ </tbody>
145
+ </table>
146
+ </div>
147
+ </div>
148
+
149
+ <div style={{ marginTop: '30px', padding: '15px', backgroundColor: '#e9ecef', borderRadius: '4px' }}>
150
+ <h3>Data Structure Overview</h3>
151
+ <p><strong>Planning-specific fields extracted:</strong></p>
152
+ <ul>
153
+ <li><strong>Target Completion Date:</strong> From <code>data?.criteria</code> → <code>targetCompletionDate</code></li>
154
+ <li><strong>Responsible Person:</strong> From <code>data?.criteria</code> → <code>accountablePersonnel</code> (array joined with commas)</li>
155
+ <li><strong>Comments:</strong> From <code>data?.criteria</code> → <code>planningComment</code> (array of objects with text key)</li>
156
+ <li><strong>Evaluation Documents:</strong> From <code>previousStage?.criteria</code> → <code>documentation</code> → <code>documents</code> → <code>name</code> (with dashes on new lines)</li>
157
+ <li><strong>Valuation:</strong> From individual field <code>valuation</code> property</li>
158
+ </ul>
159
+ </div>
160
+ </div>
161
+ );
162
+ };
163
+
164
+ export default PlanningDemo;