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.
- package/dist/components/index.js +493 -421
- package/dist/utils/index.js +441 -404
- package/package.json +2 -2
- package/src/@daf/core/components/Dashboard/Globe/index.jsx +34 -0
- package/src/@daf/core/components/Dashboard/Globe/style.js +4 -1
- package/src/@daf/core/components/ExcelTest/ExcelTest.stories.js +38 -0
- package/src/@daf/core/components/ExcelTest/config.js +254 -0
- package/src/@daf/core/components/ExcelTest/index.jsx +139 -0
- package/src/@daf/core/components/ExcelTest/planningConfig.js +371 -0
- package/src/@daf/core/components/ExcelTest/planningDemo.js +164 -0
- package/src/@daf/core/components/ExcelTest/storyConfig2.js +10413 -0
- package/src/@daf/core/components/ExcelTest/storyconfig.js +8439 -0
- package/src/@daf/core/components/Icon/configs/Briefcase.js +14 -0
- package/src/@daf/core/components/Icon/configs/Certificate.js +15 -0
- package/src/@daf/core/components/Icon/configs/SchoolHat.js +14 -0
- package/src/@daf/core/components/Icon/configs/index.js +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "datastake-daf",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.185",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@ant-design/icons": "^5.2.5",
|
|
6
6
|
"@antv/g2": "^5.1.1",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"buffer": "^6.0.3",
|
|
16
16
|
"countries-list": "^2.6.1",
|
|
17
17
|
"country-city-location": "^1.0.13",
|
|
18
|
-
"datastake-daf": "
|
|
18
|
+
"datastake-daf": "0.6.184",
|
|
19
19
|
"dayjs": "^1.11.12",
|
|
20
20
|
"deepmerge": "^4.3.1",
|
|
21
21
|
"dot-object": "^2.1.5",
|
|
@@ -8,6 +8,7 @@ import CustomIcon from "../../Icon/CustomIcon.jsx";
|
|
|
8
8
|
import ComponentWithFocus from "../ComponentWithFocus/index.jsx";
|
|
9
9
|
import { formatClassname } from "../../../../../helpers/ClassesHelper";
|
|
10
10
|
import Filters from "../../Filters/FloatingFilters/index.js";
|
|
11
|
+
import { useResizeContext } from "../../../context/Resize/index.js";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Globe Component
|
|
@@ -129,6 +130,9 @@ function Globe({
|
|
|
129
130
|
[data],
|
|
130
131
|
);
|
|
131
132
|
|
|
133
|
+
// Get resize context for sidebar state changes
|
|
134
|
+
const { isCollapsed } = useResizeContext();
|
|
135
|
+
|
|
132
136
|
// Use custom hook to configure globe functionality
|
|
133
137
|
const { container, activeMarker, mapOptionsButtonsConfig, forceResize } = useGlobe({
|
|
134
138
|
data: mappedData,
|
|
@@ -171,6 +175,36 @@ function Globe({
|
|
|
171
175
|
}
|
|
172
176
|
}, [forceResize]);
|
|
173
177
|
|
|
178
|
+
// Force resize when sidebar state changes (handles width changes)
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
if (forceResize) {
|
|
181
|
+
// Trigger resize when sidebar collapses/expands
|
|
182
|
+
const timer = setTimeout(() => {
|
|
183
|
+
forceResize();
|
|
184
|
+
}, 100);
|
|
185
|
+
|
|
186
|
+
return () => clearTimeout(timer);
|
|
187
|
+
}
|
|
188
|
+
}, [isCollapsed, forceResize]);
|
|
189
|
+
|
|
190
|
+
// Add window resize listener as backup
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const handleWindowResize = () => {
|
|
193
|
+
if (forceResize) {
|
|
194
|
+
// Small delay to ensure DOM has updated
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
forceResize();
|
|
197
|
+
}, 100);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
window.addEventListener('resize', handleWindowResize);
|
|
202
|
+
|
|
203
|
+
return () => {
|
|
204
|
+
window.removeEventListener('resize', handleWindowResize);
|
|
205
|
+
};
|
|
206
|
+
}, [forceResize]);
|
|
207
|
+
|
|
174
208
|
return (
|
|
175
209
|
<ComponentWithFocus>
|
|
176
210
|
<Style className={formatClassname([showSider && activeMarker && "with-sider"])}>
|
|
@@ -5,7 +5,8 @@ const Style = styled.div`
|
|
|
5
5
|
flex-direction: row;
|
|
6
6
|
position: relative;
|
|
7
7
|
width: 100%;
|
|
8
|
-
height:
|
|
8
|
+
height: 100%;
|
|
9
|
+
min-height: 400px;
|
|
9
10
|
|
|
10
11
|
.filter-cont {
|
|
11
12
|
position: absolute;
|
|
@@ -96,6 +97,8 @@ const Style = styled.div`
|
|
|
96
97
|
flex: 1;
|
|
97
98
|
background: rgb(0, 0, 0);
|
|
98
99
|
z-index: 1;
|
|
100
|
+
width: 100%;
|
|
101
|
+
height: 100%;
|
|
99
102
|
|
|
100
103
|
/* Mapbox GL JS specific styles */
|
|
101
104
|
.mapboxgl-ctrl-top-right {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import ExcelTest from "./index";
|
|
2
|
+
import ThemeLayout from "../ThemeLayout";
|
|
3
|
+
import { storyData } from "./storyconfig";
|
|
4
|
+
import PlanningDemo from "./planningDemo";
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: "Core/ExcelTest",
|
|
8
|
+
component: ExcelTest,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
decorators: [
|
|
11
|
+
(Story) => (
|
|
12
|
+
<div style={{ margin: "2em" }}>
|
|
13
|
+
<ThemeLayout>
|
|
14
|
+
<Story />
|
|
15
|
+
</ThemeLayout>
|
|
16
|
+
</div>
|
|
17
|
+
),
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Primary = {
|
|
22
|
+
name: "Form Test Default",
|
|
23
|
+
args: {
|
|
24
|
+
// Add any props your component needs here
|
|
25
|
+
// Currently your component doesn't accept props but you might want to pass data
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const Planning = {
|
|
30
|
+
name: "Planning Configuration",
|
|
31
|
+
render: () => (
|
|
32
|
+
<div style={{ margin: "2em" }}>
|
|
33
|
+
<ThemeLayout>
|
|
34
|
+
<PlanningDemo />
|
|
35
|
+
</ThemeLayout>
|
|
36
|
+
</div>
|
|
37
|
+
),
|
|
38
|
+
};
|
|
@@ -0,0 +1,254 @@
|
|
|
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 comments
|
|
16
|
+
const formatComments = (comments) => {
|
|
17
|
+
if (!comments || !Array.isArray(comments)) return "";
|
|
18
|
+
return comments.map(comment => `- ${comment.text || comment.description || ""}`).join("\n");
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Helper function to format verification method - remove <br> and add appropriate separators
|
|
22
|
+
const formatVerificationMethod = (description, forCSV = false) => {
|
|
23
|
+
if (!description) return "";
|
|
24
|
+
const lines = description.split("<br>")
|
|
25
|
+
.filter(line => line.trim())
|
|
26
|
+
.map(line => line.trim());
|
|
27
|
+
|
|
28
|
+
// For CSV export, use semicolon separator to avoid newline issues
|
|
29
|
+
if (forCSV) {
|
|
30
|
+
return lines.join("; ");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// For display, use newlines
|
|
34
|
+
return lines.join("\n");
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Helper function to get description by language
|
|
38
|
+
const getDescriptionByLanguage = (descriptions, language = "en") => {
|
|
39
|
+
if (!descriptions) return "";
|
|
40
|
+
return descriptions[language] || descriptions.en || descriptions.sp || descriptions.fr || "";
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const getAspectToVerify = (form, sectionKey, kidPosition, country) => {
|
|
44
|
+
const titlesSection = form?.titles?.[sectionKey];
|
|
45
|
+
if (!titlesSection) return "";
|
|
46
|
+
|
|
47
|
+
const matchingTitles = Object.keys(titlesSection)
|
|
48
|
+
.filter(key => key.startsWith('title'))
|
|
49
|
+
.map(key => titlesSection[key])
|
|
50
|
+
.filter(title => {
|
|
51
|
+
if (!title.showIf) return false;
|
|
52
|
+
const condition = title.showIf.toLowerCase();
|
|
53
|
+
if (condition.includes('country is')) {
|
|
54
|
+
const requiredCountry = condition.replace('country is', '').trim();
|
|
55
|
+
return country && country.toLowerCase() === requiredCountry.toLowerCase();
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
})
|
|
59
|
+
.sort((a, b) => (a.position || 0) - (b.position || 0));
|
|
60
|
+
|
|
61
|
+
if (matchingTitles.length === 0) return "";
|
|
62
|
+
|
|
63
|
+
let selectedTitle = matchingTitles[0];
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < matchingTitles.length; i++) {
|
|
66
|
+
const titlePosition = matchingTitles[i].position || 0;
|
|
67
|
+
if (titlePosition <= kidPosition) {
|
|
68
|
+
selectedTitle = matchingTitles[i];
|
|
69
|
+
} else {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
const label = selectedTitle?.label || "";
|
|
76
|
+
return label.replace(/\\"/g, '"').replace(/\n/g, " ").replace(/\r/g, " ").replace(/\t/g, " ").trim();
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const fixDataForExcel = (originalData, stepParam = 1, module = "", t = (key) => key) => {
|
|
80
|
+
const {form, data} = originalData;
|
|
81
|
+
const language = data?.user?.language || "en";
|
|
82
|
+
const step = Number(stepParam);
|
|
83
|
+
// Table 1 data - Basic Information (vertical key-value format)
|
|
84
|
+
const table1Data = [
|
|
85
|
+
{ key: t('mine'), value: data?.location?.name || "" },
|
|
86
|
+
{ key: t('module'), value: module },
|
|
87
|
+
{ key: t('sources'), value: "" }, // Leave blank as requested
|
|
88
|
+
{ key: t('version'), value: data?.version || data?.location?.version || "" },
|
|
89
|
+
{ key: t('date'), value: renderDateFormatted(new Date().toISOString(), 'DD MMM YYYY', (language === 'sp' ? 'es' : language) || 'en') } // Current date in YYYY-MM-DD format
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
console.log("Table 1 Data:", table1Data);
|
|
93
|
+
|
|
94
|
+
// Table 2 data - Detailed Evaluation Data
|
|
95
|
+
const table2Data = [];
|
|
96
|
+
|
|
97
|
+
const sections = (form?.identification?.category?.options || [])?.map((option) => ({
|
|
98
|
+
group: option.label,
|
|
99
|
+
key: option.value,
|
|
100
|
+
criterias: (form?.identification?.subCategory?.options || [])
|
|
101
|
+
.filter((_option) => {
|
|
102
|
+
const categoryFilter = _option?.filters?.find((f) => f.name === "category");
|
|
103
|
+
const stepFilter = _option?.filters?.find((f) => f.name === "step");
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
(stepFilter?.value || 1) === step &&
|
|
107
|
+
(categoryFilter?.value || []).includes(option.value) &&
|
|
108
|
+
!!(data?.criteria || []).find(
|
|
109
|
+
(c) => c.subCategory === _option.value,
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
})
|
|
113
|
+
.map((_option) => {
|
|
114
|
+
const _criteria = (data?.criteria || []).find(
|
|
115
|
+
(c) => c.subCategory === _option.value,
|
|
116
|
+
);
|
|
117
|
+
const subCategoryForm = form[_criteria.subCategory];
|
|
118
|
+
|
|
119
|
+
const kids = Object.keys(subCategoryForm)
|
|
120
|
+
.filter((k) => {
|
|
121
|
+
const val = subCategoryForm[k];
|
|
122
|
+
if (val.showIf) {
|
|
123
|
+
const show = showHideInput(val, data);
|
|
124
|
+
return show;
|
|
125
|
+
}
|
|
126
|
+
return val?.type === "group";
|
|
127
|
+
})
|
|
128
|
+
.sort((a, b) => {
|
|
129
|
+
const valA = subCategoryForm[a];
|
|
130
|
+
const valB = subCategoryForm[b];
|
|
131
|
+
return valA?.position - valB?.position;
|
|
132
|
+
})
|
|
133
|
+
.map((k) => {
|
|
134
|
+
const formVal = subCategoryForm[k];
|
|
135
|
+
const evalData = _criteria[k];
|
|
136
|
+
const meta = (_criteria?.meta?.inputs || {})[k];
|
|
137
|
+
|
|
138
|
+
// Get reference description from form object description field
|
|
139
|
+
const referenceDescription = formVal.description || "";
|
|
140
|
+
|
|
141
|
+
// Get verification method from form meta
|
|
142
|
+
const verificationMethod = getDescriptionByLanguage(
|
|
143
|
+
formVal?.meta?.groupInfo?.description,
|
|
144
|
+
language
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
key: k,
|
|
149
|
+
group: option.value,
|
|
150
|
+
criteria: formVal.label,
|
|
151
|
+
position: formVal.position || 0, // Include position for title mapping
|
|
152
|
+
lastUpdate: _criteria.updatedAt,
|
|
153
|
+
meta,
|
|
154
|
+
referenceDescription,
|
|
155
|
+
verificationMethod,
|
|
156
|
+
...evalData,
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
key: _option.value,
|
|
162
|
+
group: option.value,
|
|
163
|
+
criteria: _option.label,
|
|
164
|
+
completion: _criteria.completion,
|
|
165
|
+
evaluationScore: _criteria.evaluationScore,
|
|
166
|
+
description: (form?.identification?.dscription?.options || []).find(
|
|
167
|
+
(d) => d.value === _option.value,
|
|
168
|
+
)?.label || "-",
|
|
169
|
+
lastUpdate: _criteria.updatedAt,
|
|
170
|
+
kids,
|
|
171
|
+
};
|
|
172
|
+
})
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
// Process sections to create table 2 data
|
|
176
|
+
sections.forEach((section) => {
|
|
177
|
+
section.criterias.forEach((criteria) => {
|
|
178
|
+
criteria.kids.forEach((kid) => {
|
|
179
|
+
const row = {
|
|
180
|
+
stage: `0${step}`,
|
|
181
|
+
aspect: section.group, // From sections group
|
|
182
|
+
criteria: criteria.criteria, // From criterias -> criteria key
|
|
183
|
+
aspectToVerify: getAspectToVerify(form, criteria.key, kid.position, data?.location?.country), // Get from titles based on showIf condition and actual kid position
|
|
184
|
+
reference: formatReferences(kid.referenceDescription), // Format references properly
|
|
185
|
+
subcriteria: kid.criteria, // From kids -> criteria key
|
|
186
|
+
verificationMethod: formatVerificationMethod(kid.verificationMethod || ""), // Remove <br> and add newlines
|
|
187
|
+
valuation: (kid?.valuation === 'na' ? 'N/A' : `${kid.valuation}%`) ?? "", // Add % formatting
|
|
188
|
+
comments: formatComments(kid.comment), // Format comments with - on each line
|
|
189
|
+
totalValuation: (criteria?.evaluationScore === 'na' ? 'N/A' : `${criteria.evaluationScore}%`) ?? "" // Add % formatting
|
|
190
|
+
};
|
|
191
|
+
table2Data.push(row);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
console.log("Table 2 Data:", table2Data);
|
|
197
|
+
console.log("Sections:", sections);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
table1: table1Data,
|
|
201
|
+
table2: table2Data,
|
|
202
|
+
sections: sections
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
export const exportToCSV = (data, filename = "evaluation_data.csv", t = (key) => key) => {
|
|
208
|
+
const { table1, table2 } = data;
|
|
209
|
+
|
|
210
|
+
let csvContent = "";
|
|
211
|
+
|
|
212
|
+
// Add Table 1 data (vertical key-value format, no title)
|
|
213
|
+
table1.forEach(item => {
|
|
214
|
+
csvContent += `"${item.key}","${item.value}"\n`;
|
|
215
|
+
});
|
|
216
|
+
csvContent += "\n"; // Empty line separator
|
|
217
|
+
|
|
218
|
+
// Add Table 2 header and data (no title, use translation function for headers)
|
|
219
|
+
csvContent += `"${t('stage')}","${t('aspect')}","${t('criteria')}","${t('aspectToVerify')}","${t('reference')}","${t('subcriteria')}","${t('verificationMethod')}","${t('valuation')}","${t('comments')}","${t('totalValuation')}"\n`;
|
|
220
|
+
|
|
221
|
+
table2.forEach(row => {
|
|
222
|
+
const csvRow = [
|
|
223
|
+
row.stage,
|
|
224
|
+
row.aspect,
|
|
225
|
+
row.criteria,
|
|
226
|
+
(row.aspectToVerify || "").replace(/"/g, '""'), // Escape quotes properly
|
|
227
|
+
(row.reference || "").replace(/"/g, '""'), // Escape quotes
|
|
228
|
+
row.subcriteria,
|
|
229
|
+
formatVerificationMethod(row.verificationMethod || "", true).replace(/"/g, '""'), // Format for CSV and escape quotes
|
|
230
|
+
row.valuation,
|
|
231
|
+
(row.comments || "").replace(/"/g, '""'), // Escape quotes
|
|
232
|
+
row.totalValuation
|
|
233
|
+
].map(field => `"${field}"`).join(",");
|
|
234
|
+
|
|
235
|
+
csvContent += csvRow + "\n";
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
return csvContent;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Utility function to download CSV file
|
|
242
|
+
export const downloadCSV = (data, filename = "evaluation_data.csv", t = (key) => key) => {
|
|
243
|
+
const csvContent = exportToCSV(data, filename, t);
|
|
244
|
+
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
245
|
+
const link = document.createElement("a");
|
|
246
|
+
const url = URL.createObjectURL(blob);
|
|
247
|
+
|
|
248
|
+
link.setAttribute("href", url);
|
|
249
|
+
link.setAttribute("download", filename);
|
|
250
|
+
link.style.visibility = 'hidden';
|
|
251
|
+
document.body.appendChild(link);
|
|
252
|
+
link.click();
|
|
253
|
+
document.body.removeChild(link);
|
|
254
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import { storyData } from './storyconfig'
|
|
3
|
+
import { fixDataForExcel, downloadCSV } from './config'
|
|
4
|
+
|
|
5
|
+
function ExcelTest() {
|
|
6
|
+
const [excelData, setExcelData] = useState(null);
|
|
7
|
+
|
|
8
|
+
// Simple translation function for testing
|
|
9
|
+
const t = (key) => {
|
|
10
|
+
const translations = {
|
|
11
|
+
mine: "Mine",
|
|
12
|
+
module: "Module",
|
|
13
|
+
sources: "Sources",
|
|
14
|
+
version: "Version",
|
|
15
|
+
date: "Date",
|
|
16
|
+
stage: "Stage",
|
|
17
|
+
aspect: "Aspect",
|
|
18
|
+
criteria: "Criteria",
|
|
19
|
+
aspectToVerify: "Aspect to Verify",
|
|
20
|
+
reference: "Reference",
|
|
21
|
+
subcriteria: "Subcriteria",
|
|
22
|
+
verificationMethod: "Verification Method",
|
|
23
|
+
valuation: "Valuation",
|
|
24
|
+
comments: "Comments",
|
|
25
|
+
totalValuation: "Total Valuation"
|
|
26
|
+
};
|
|
27
|
+
return translations[key] || key;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
// Test the function with sample parameters including translation function
|
|
32
|
+
const response = fixDataForExcel(storyData, 1, "Sample Module", t);
|
|
33
|
+
setExcelData(response);
|
|
34
|
+
console.log("Excel Data Generated:", response);
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
|
|
39
|
+
<h1>Form Test - Excel Data Mapping</h1>
|
|
40
|
+
|
|
41
|
+
{excelData && (
|
|
42
|
+
<>
|
|
43
|
+
<div style={{ marginBottom: '20px' }}>
|
|
44
|
+
<button
|
|
45
|
+
onClick={() => downloadCSV(excelData, 'evaluation_data.csv', t)}
|
|
46
|
+
style={{
|
|
47
|
+
backgroundColor: '#4CAF50',
|
|
48
|
+
color: 'white',
|
|
49
|
+
padding: '10px 20px',
|
|
50
|
+
border: 'none',
|
|
51
|
+
borderRadius: '4px',
|
|
52
|
+
cursor: 'pointer',
|
|
53
|
+
fontSize: '16px'
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
📥 Download as CSV
|
|
57
|
+
</button>
|
|
58
|
+
<p style={{ marginTop: '5px', color: '#666', fontSize: '14px' }}>
|
|
59
|
+
Click to download the data as CSV file for Excel import
|
|
60
|
+
<br />
|
|
61
|
+
<strong>Note:</strong> For duplicate values (like same Stage), manually merge cells in Excel after import.
|
|
62
|
+
</p>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
</>
|
|
66
|
+
)}
|
|
67
|
+
|
|
68
|
+
{excelData && (
|
|
69
|
+
<>
|
|
70
|
+
<h2>Basic Information</h2>
|
|
71
|
+
<div style={{ marginBottom: '30px' }}>
|
|
72
|
+
<table border="1" style={{ borderCollapse: 'collapse', width: '50%' }}>
|
|
73
|
+
<thead>
|
|
74
|
+
<tr>
|
|
75
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>Field</th>
|
|
76
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>Value</th>
|
|
77
|
+
</tr>
|
|
78
|
+
</thead>
|
|
79
|
+
<tbody>
|
|
80
|
+
{excelData.table1.map((item, index) => (
|
|
81
|
+
<tr key={index}>
|
|
82
|
+
<td style={{ padding: '8px', fontWeight: 'bold' }}>{item.key}</td>
|
|
83
|
+
<td style={{ padding: '8px' }}>{item.value}</td>
|
|
84
|
+
</tr>
|
|
85
|
+
))}
|
|
86
|
+
</tbody>
|
|
87
|
+
</table>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<h2>Detailed Evaluation Data</h2>
|
|
91
|
+
<div style={{ overflowX: 'auto' }}>
|
|
92
|
+
<table border="1" style={{ borderCollapse: 'collapse', width: '100%', minWidth: '1200px' }}>
|
|
93
|
+
<thead>
|
|
94
|
+
<tr>
|
|
95
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('stage')}</th>
|
|
96
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('aspect')}</th>
|
|
97
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('criteria')}</th>
|
|
98
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('aspectToVerify')}</th>
|
|
99
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('reference')}</th>
|
|
100
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('subcriteria')}</th>
|
|
101
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('verificationMethod')}</th>
|
|
102
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('valuation')}</th>
|
|
103
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('comments')}</th>
|
|
104
|
+
<th style={{ padding: '8px', backgroundColor: '#f5f5f5' }}>{t('totalValuation')}</th>
|
|
105
|
+
</tr>
|
|
106
|
+
</thead>
|
|
107
|
+
<tbody>
|
|
108
|
+
{excelData.table2.map((row, index) => (
|
|
109
|
+
<tr key={index}>
|
|
110
|
+
<td style={{ padding: '8px' }}>{row.stage}</td>
|
|
111
|
+
<td style={{ padding: '8px' }}>{row.aspect}</td>
|
|
112
|
+
<td style={{ padding: '8px' }}>{row.criteria}</td>
|
|
113
|
+
<td style={{ padding: '8px' }}>{row.aspectToVerify}</td>
|
|
114
|
+
<td style={{ padding: '8px', whiteSpace: 'pre-line' }}>{row.reference}</td>
|
|
115
|
+
<td style={{ padding: '8px' }}>{row.subcriteria}</td>
|
|
116
|
+
<td style={{ padding: '8px', whiteSpace: 'pre-line' }}>{row.verificationMethod}</td>
|
|
117
|
+
<td style={{ padding: '8px' }}>{row.valuation}</td>
|
|
118
|
+
<td style={{ padding: '8px', whiteSpace: 'pre-line' }}>{row.comments}</td>
|
|
119
|
+
<td style={{ padding: '8px' }}>{row.totalValuation}</td>
|
|
120
|
+
</tr>
|
|
121
|
+
))}
|
|
122
|
+
</tbody>
|
|
123
|
+
</table>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<h3>Debug Information</h3>
|
|
127
|
+
<details style={{ marginTop: '20px' }}>
|
|
128
|
+
<summary>Raw Data Structure</summary>
|
|
129
|
+
<pre style={{ background: '#f5f5f5', padding: '10px', overflow: 'auto', maxHeight: '400px' }}>
|
|
130
|
+
{JSON.stringify(excelData, null, 2)}
|
|
131
|
+
</pre>
|
|
132
|
+
</details>
|
|
133
|
+
</>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export default ExcelTest
|