@visns-studio/visns-components 5.15.25 → 5.15.27
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/package.json +11 -11
- package/src/components/DataGrid.jsx +13 -30
- package/src/components/ImportWizardModal.jsx +0 -8
- package/src/components/controls/DataGridSearch.jsx +0 -5
- package/src/components/generic/ExcelStyleGrid.jsx +789 -0
- package/src/components/generic/GenericDashboard.jsx +9 -1
- package/src/components/generic/GenericDetail.jsx +0 -3
- package/src/components/generic/TimelineWidget.jsx +61 -0
- package/src/components/hooks/useExcelGrid.js +366 -0
- package/src/components/styles/ExcelStyleGrid.css +251 -0
- package/src/components/styles/GenericDashboard.module.scss +2 -2
|
@@ -9,10 +9,12 @@ import parse from 'html-react-parser';
|
|
|
9
9
|
import { createSwapy } from 'swapy';
|
|
10
10
|
import { Minus, Plus, Star, Trash2, Eye, RotateCcw, Clock, Database, BarChart3, AlertCircle } from 'lucide-react';
|
|
11
11
|
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
12
|
+
import moment from 'moment';
|
|
12
13
|
|
|
13
14
|
import CustomFetch from '../Fetch';
|
|
14
15
|
import MultiSelect from '../MultiSelect';
|
|
15
16
|
import StandardModal from './StandardModal';
|
|
17
|
+
import TimelineWidget from './TimelineWidget';
|
|
16
18
|
|
|
17
19
|
import styles from '../styles/GenericDashboard.module.scss';
|
|
18
20
|
|
|
@@ -1506,7 +1508,11 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
1506
1508
|
return (
|
|
1507
1509
|
<div
|
|
1508
1510
|
className={styles['table-container']}
|
|
1509
|
-
style={{
|
|
1511
|
+
style={{
|
|
1512
|
+
maxWidth: getTableMaxWidth(),
|
|
1513
|
+
maxHeight: widget.height || 'none',
|
|
1514
|
+
overflowY: widget.height ? 'auto' : 'visible'
|
|
1515
|
+
}}
|
|
1510
1516
|
>
|
|
1511
1517
|
{tableData.length > 0 ? (
|
|
1512
1518
|
<div className={styles['table-scroll']}>
|
|
@@ -1796,6 +1802,8 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
1796
1802
|
</div>
|
|
1797
1803
|
);
|
|
1798
1804
|
}
|
|
1805
|
+
case 'timeline':
|
|
1806
|
+
return <TimelineWidget widgetData={widgetData} widget={widget} />;
|
|
1799
1807
|
default:
|
|
1800
1808
|
return null;
|
|
1801
1809
|
}
|
|
@@ -3038,9 +3038,6 @@ function GenericDetail({
|
|
|
3038
3038
|
form: processedForm,
|
|
3039
3039
|
};
|
|
3040
3040
|
|
|
3041
|
-
console.log('[GenericDetail] activeTabConfig.bulkUpload:', activeTabConfig.bulkUpload);
|
|
3042
|
-
console.log('[GenericDetail] processedConfig.form.bulkUpload:', processedConfig.form.bulkUpload);
|
|
3043
|
-
|
|
3044
3041
|
return (
|
|
3045
3042
|
<>
|
|
3046
3043
|
{activeTabConfig.dropzone &&
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import moment from 'moment';
|
|
4
|
+
import ExcelStyleGrid from './ExcelStyleGrid';
|
|
5
|
+
import { useExcelGrid } from '../hooks/useExcelGrid';
|
|
6
|
+
import styles from '../styles/GenericDashboard.module.scss';
|
|
7
|
+
|
|
8
|
+
const TimelineWidget = ({ widgetData, widget }) => {
|
|
9
|
+
const { dateColumns, facilitiesByCategory, gridData, facilityColors, processDataForLeadCentricGrid, processDataForGrid } = useExcelGrid();
|
|
10
|
+
const [showScrollHint, setShowScrollHint] = useState(true);
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
|
|
13
|
+
const handleProjectClick = (leadId) => {
|
|
14
|
+
if (leadId) {
|
|
15
|
+
navigate(`/leads/${leadId}`);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Process the timeline data when widget data changes
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (widgetData && widgetData.length > 0) {
|
|
22
|
+
const startDate = widget.startDate || moment().startOf('month').toDate();
|
|
23
|
+
const endDate = widget.endDate || moment().add(5, 'months').endOf('month').toDate();
|
|
24
|
+
|
|
25
|
+
// Use grouping type from widget config: 'lead' for lead-centric, 'facility' for facility-centric
|
|
26
|
+
const groupingType = widget.groupingType || 'lead';
|
|
27
|
+
|
|
28
|
+
if (groupingType === 'facility') {
|
|
29
|
+
processDataForGrid(widgetData, startDate, endDate);
|
|
30
|
+
} else {
|
|
31
|
+
processDataForLeadCentricGrid(widgetData, startDate, endDate);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}, [widgetData, widget.startDate, widget.endDate, widget.groupingType, processDataForLeadCentricGrid, processDataForGrid]);
|
|
35
|
+
|
|
36
|
+
if (!widgetData || widgetData.length === 0) {
|
|
37
|
+
return (
|
|
38
|
+
<div className={styles.noData}>
|
|
39
|
+
No timeline data available
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className={styles['timeline-container']} style={{ width: '100%', maxWidth: '100%', overflow: 'hidden' }}>
|
|
46
|
+
<ExcelStyleGrid
|
|
47
|
+
gridData={gridData}
|
|
48
|
+
dateColumns={dateColumns}
|
|
49
|
+
facilitiesByCategory={facilitiesByCategory}
|
|
50
|
+
facilityColors={facilityColors}
|
|
51
|
+
showScrollHint={showScrollHint}
|
|
52
|
+
setShowScrollHint={setShowScrollHint}
|
|
53
|
+
maxWidth="100%"
|
|
54
|
+
maxHeight={widget.height || '500px'}
|
|
55
|
+
onProjectClick={handleProjectClick}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default TimelineWidget;
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import moment from 'moment';
|
|
3
|
+
|
|
4
|
+
export const useExcelGrid = () => {
|
|
5
|
+
const [dateColumns, setDateColumns] = useState([]);
|
|
6
|
+
const [facilitiesByCategory, setFacilitiesByCategory] = useState({});
|
|
7
|
+
const [gridData, setGridData] = useState([]);
|
|
8
|
+
const [facilityColors, setFacilityColors] = useState({});
|
|
9
|
+
|
|
10
|
+
// Generate date columns for the grid
|
|
11
|
+
const generateDateColumns = (startDate, endDate) => {
|
|
12
|
+
const dates = [];
|
|
13
|
+
const current = moment(startDate).utcOffset('+08:00');
|
|
14
|
+
const end = moment(endDate).utcOffset('+08:00');
|
|
15
|
+
|
|
16
|
+
while (current.isSameOrBefore(end, 'day')) {
|
|
17
|
+
dates.push({
|
|
18
|
+
date: current.format('YYYY-MM-DD'),
|
|
19
|
+
display: current.format('DD/MM'),
|
|
20
|
+
fullDate: current.clone()
|
|
21
|
+
});
|
|
22
|
+
current.add(1, 'day');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return dates;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Generate colors for facility categories
|
|
29
|
+
const generateFacilityColors = (facilities) => {
|
|
30
|
+
const colors = [
|
|
31
|
+
'#a8c8ec', // Pastel Blue
|
|
32
|
+
'#b8e6b8', // Pastel Green
|
|
33
|
+
'#f8b4b4', // Pastel Red
|
|
34
|
+
'#fce68a', // Pastel Yellow
|
|
35
|
+
'#d1c4e9', // Pastel Purple
|
|
36
|
+
'#ffcc9c', // Pastel Orange
|
|
37
|
+
'#9fdfcc', // Pastel Teal
|
|
38
|
+
'#f4c2c2', // Pastel Pink
|
|
39
|
+
'#c5c5c5', // Light Gray
|
|
40
|
+
'#b3e0e0', // Pastel Cyan
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const facilityColors = {};
|
|
44
|
+
const facilityKeys = Object.keys(facilities);
|
|
45
|
+
|
|
46
|
+
facilityKeys.forEach((facility, index) => {
|
|
47
|
+
facilityColors[facility] = colors[index % colors.length];
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return facilityColors;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Group facilities by category
|
|
54
|
+
const groupFacilitiesByCategory = (data) => {
|
|
55
|
+
const grouped = {};
|
|
56
|
+
|
|
57
|
+
data.forEach(item => {
|
|
58
|
+
const facility = item.facility || 'Uncategorized';
|
|
59
|
+
if (!grouped[facility]) {
|
|
60
|
+
grouped[facility] = [];
|
|
61
|
+
}
|
|
62
|
+
grouped[facility].push(item);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return grouped;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Convert segmented data to grid format
|
|
69
|
+
const convertToGridData = (segmentedData, dateColumns, facilityColors) => {
|
|
70
|
+
const gridRows = [];
|
|
71
|
+
const facilityGroups = groupFacilitiesByCategory(segmentedData);
|
|
72
|
+
|
|
73
|
+
Object.keys(facilityGroups).forEach(facility => {
|
|
74
|
+
const facilityItems = facilityGroups[facility];
|
|
75
|
+
|
|
76
|
+
facilityItems.forEach(item => {
|
|
77
|
+
const row = {
|
|
78
|
+
id: item.id,
|
|
79
|
+
facility: facility,
|
|
80
|
+
resourceName: item.id,
|
|
81
|
+
client: item.client,
|
|
82
|
+
project: item.project,
|
|
83
|
+
lead_id: item.lead_id,
|
|
84
|
+
status: item.status,
|
|
85
|
+
confirmed: item.confirmed,
|
|
86
|
+
cells: {}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Initialize all cells as empty
|
|
90
|
+
dateColumns.forEach(dateCol => {
|
|
91
|
+
row.cells[dateCol.date] = {
|
|
92
|
+
hasData: false,
|
|
93
|
+
color: '#f8f9fa',
|
|
94
|
+
data: null
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Fill in data for occupied dates
|
|
99
|
+
if (item.data && item.data.length >= 2) {
|
|
100
|
+
const startDate = moment(item.data[0].x).utcOffset('+08:00');
|
|
101
|
+
const endDate = moment(item.data[1].x).utcOffset('+08:00');
|
|
102
|
+
|
|
103
|
+
const current = startDate.clone();
|
|
104
|
+
while (current.isSameOrBefore(endDate, 'day')) {
|
|
105
|
+
const dateKey = current.format('YYYY-MM-DD');
|
|
106
|
+
if (row.cells[dateKey]) {
|
|
107
|
+
row.cells[dateKey] = {
|
|
108
|
+
hasData: true,
|
|
109
|
+
color: item.data[0].color || item.color || '#007bff',
|
|
110
|
+
facility: facility,
|
|
111
|
+
data: {
|
|
112
|
+
resource: item.id,
|
|
113
|
+
client: item.client,
|
|
114
|
+
project: item.project,
|
|
115
|
+
status: item.status,
|
|
116
|
+
confirmed: item.confirmed,
|
|
117
|
+
date: current.format('DD-MM-YYYY')
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
current.add(1, 'day');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
gridRows.push(row);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return gridRows;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Transform data for lead-centric view (floating dock)
|
|
133
|
+
const transformDataForLeadCentricView = (segmentedData) => {
|
|
134
|
+
const leadGroups = {};
|
|
135
|
+
const allFacilities = new Set();
|
|
136
|
+
|
|
137
|
+
// Group by lead and collect all facilities
|
|
138
|
+
segmentedData.forEach(item => {
|
|
139
|
+
const leadKey = `${item.client} - ${item.project || item.id.split(' (')[0]}`;
|
|
140
|
+
|
|
141
|
+
if (!leadGroups[leadKey]) {
|
|
142
|
+
leadGroups[leadKey] = {
|
|
143
|
+
id: leadKey,
|
|
144
|
+
client: item.client,
|
|
145
|
+
project: item.project || item.id.split(' (')[0],
|
|
146
|
+
lead_id: item.lead_id,
|
|
147
|
+
status: item.status,
|
|
148
|
+
confirmed: item.confirmed,
|
|
149
|
+
facilities: {}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Extract facility from the item
|
|
154
|
+
const facility = item.facility || item.id.match(/\(([^)]+)\)$/)?.[1] || 'Unknown';
|
|
155
|
+
allFacilities.add(facility);
|
|
156
|
+
|
|
157
|
+
// Store facility booking data
|
|
158
|
+
if (!leadGroups[leadKey].facilities[facility]) {
|
|
159
|
+
leadGroups[leadKey].facilities[facility] = [];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
leadGroups[leadKey].facilities[facility].push({
|
|
163
|
+
data: item.data,
|
|
164
|
+
confirmed: item.confirmed
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Custom sorting function for wharf facilities (Jetty first, then AMC 1-6)
|
|
169
|
+
const sortWharfFacilities = (facilities) => {
|
|
170
|
+
return facilities.sort((a, b) => {
|
|
171
|
+
// Jetty facilities first
|
|
172
|
+
const isAJetty = a.toLowerCase().includes('jetty');
|
|
173
|
+
const isBJetty = b.toLowerCase().includes('jetty');
|
|
174
|
+
|
|
175
|
+
if (isAJetty && !isBJetty) return -1;
|
|
176
|
+
if (!isAJetty && isBJetty) return 1;
|
|
177
|
+
|
|
178
|
+
// Both are jetties, sort alphabetically
|
|
179
|
+
if (isAJetty && isBJetty) {
|
|
180
|
+
return a.localeCompare(b);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// AMC facilities sorting (AMC 1-6)
|
|
184
|
+
const isAAMC = a.startsWith('AMC ');
|
|
185
|
+
const isBAMC = b.startsWith('AMC ');
|
|
186
|
+
|
|
187
|
+
if (isAAMC && !isBAMC) return -1;
|
|
188
|
+
if (!isAAMC && isBAMC) return 1;
|
|
189
|
+
|
|
190
|
+
// Both are AMC, sort numerically
|
|
191
|
+
if (isAAMC && isBAMC) {
|
|
192
|
+
const numA = parseInt(a.split(' ')[1]) || 0;
|
|
193
|
+
const numB = parseInt(b.split(' ')[1]) || 0;
|
|
194
|
+
return numA - numB;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Default alphabetical sort for other facilities
|
|
198
|
+
return a.localeCompare(b);
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
leadGroups: Object.values(leadGroups),
|
|
204
|
+
facilities: sortWharfFacilities(Array.from(allFacilities))
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Convert lead-centric data to grid format
|
|
209
|
+
const convertLeadCentricToGridData = (leadGroups, facilities, dateColumns) => {
|
|
210
|
+
const gridRows = [];
|
|
211
|
+
|
|
212
|
+
leadGroups.forEach(lead => {
|
|
213
|
+
const row = {
|
|
214
|
+
id: lead.id,
|
|
215
|
+
facility: 'Lead', // All leads are grouped under "Lead" category
|
|
216
|
+
resourceName: lead.id,
|
|
217
|
+
client: lead.client,
|
|
218
|
+
project: lead.project,
|
|
219
|
+
lead_id: lead.lead_id,
|
|
220
|
+
status: lead.status,
|
|
221
|
+
confirmed: lead.confirmed,
|
|
222
|
+
cells: {}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Initialize all cells as empty
|
|
226
|
+
dateColumns.forEach(dateCol => {
|
|
227
|
+
row.cells[dateCol.date] = {
|
|
228
|
+
hasData: false,
|
|
229
|
+
color: '#f8f9fa',
|
|
230
|
+
data: null
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Fill in facility bookings for each date
|
|
235
|
+
facilities.forEach(facility => {
|
|
236
|
+
if (lead.facilities[facility]) {
|
|
237
|
+
lead.facilities[facility].forEach(booking => {
|
|
238
|
+
if (booking.data && booking.data.length >= 2) {
|
|
239
|
+
const startDate = moment(booking.data[0].x).utcOffset('+08:00');
|
|
240
|
+
const endDate = moment(booking.data[1].x).utcOffset('+08:00');
|
|
241
|
+
|
|
242
|
+
const current = startDate.clone();
|
|
243
|
+
while (current.isSameOrBefore(endDate, 'day')) {
|
|
244
|
+
const dateKey = current.format('YYYY-MM-DD');
|
|
245
|
+
if (row.cells[dateKey]) {
|
|
246
|
+
// If cell already has data, we need to combine facilities
|
|
247
|
+
if (row.cells[dateKey].hasData) {
|
|
248
|
+
// Combine multiple facilities for the same date
|
|
249
|
+
const existingResource = row.cells[dateKey].data.resource;
|
|
250
|
+
const combinedResource = existingResource.includes(facility)
|
|
251
|
+
? existingResource
|
|
252
|
+
: `${existingResource}, ${facility}`;
|
|
253
|
+
|
|
254
|
+
row.cells[dateKey].data.resource = combinedResource;
|
|
255
|
+
// Use the highest confirmation status
|
|
256
|
+
row.cells[dateKey].data.confirmed = Math.max(
|
|
257
|
+
row.cells[dateKey].data.confirmed || 0,
|
|
258
|
+
booking.confirmed || 0
|
|
259
|
+
);
|
|
260
|
+
} else {
|
|
261
|
+
// First facility for this date
|
|
262
|
+
row.cells[dateKey] = {
|
|
263
|
+
hasData: true,
|
|
264
|
+
color: '#007bff',
|
|
265
|
+
facility: facility,
|
|
266
|
+
data: {
|
|
267
|
+
resource: facility, // Now facility is the resource
|
|
268
|
+
client: lead.client,
|
|
269
|
+
project: lead.project,
|
|
270
|
+
status: lead.status,
|
|
271
|
+
confirmed: booking.confirmed,
|
|
272
|
+
date: current.format('DD-MM-YYYY')
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
current.add(1, 'day');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
gridRows.push(row);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
return gridRows;
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Process data for grid (original facility-centric)
|
|
291
|
+
const processDataForGrid = (segmentedData, startDate, endDate) => {
|
|
292
|
+
// Generate date columns for the grid
|
|
293
|
+
const dates = generateDateColumns(startDate, endDate);
|
|
294
|
+
setDateColumns(dates);
|
|
295
|
+
|
|
296
|
+
// Group facilities
|
|
297
|
+
const grouped = groupFacilitiesByCategory(segmentedData);
|
|
298
|
+
setFacilitiesByCategory(grouped);
|
|
299
|
+
|
|
300
|
+
// Generate colors for facilities
|
|
301
|
+
const colors = generateFacilityColors(grouped);
|
|
302
|
+
setFacilityColors(colors);
|
|
303
|
+
|
|
304
|
+
// Convert to grid data
|
|
305
|
+
const grid = convertToGridData(segmentedData, dates, colors);
|
|
306
|
+
setGridData(grid);
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
dateColumns: dates,
|
|
310
|
+
facilitiesByCategory: grouped,
|
|
311
|
+
facilityColors: colors,
|
|
312
|
+
gridData: grid
|
|
313
|
+
};
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Process data for lead-centric grid (floating dock)
|
|
317
|
+
const processDataForLeadCentricGrid = (segmentedData, startDate, endDate) => {
|
|
318
|
+
// Generate date columns for the grid
|
|
319
|
+
const dates = generateDateColumns(startDate, endDate);
|
|
320
|
+
setDateColumns(dates);
|
|
321
|
+
|
|
322
|
+
// Transform data to lead-centric structure
|
|
323
|
+
const { leadGroups, facilities } = transformDataForLeadCentricView(segmentedData);
|
|
324
|
+
|
|
325
|
+
// Create grouped structure with single "Lead" category
|
|
326
|
+
const grouped = { 'Lead': leadGroups };
|
|
327
|
+
setFacilitiesByCategory(grouped);
|
|
328
|
+
|
|
329
|
+
// Generate colors (single color for leads)
|
|
330
|
+
const colors = { 'Lead': '#a8c8ec' };
|
|
331
|
+
setFacilityColors(colors);
|
|
332
|
+
|
|
333
|
+
// Convert to grid data
|
|
334
|
+
const grid = convertLeadCentricToGridData(leadGroups, facilities, dates);
|
|
335
|
+
setGridData(grid);
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
dateColumns: dates,
|
|
339
|
+
facilitiesByCategory: grouped,
|
|
340
|
+
facilityColors: colors,
|
|
341
|
+
gridData: grid,
|
|
342
|
+
facilities: facilities
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
dateColumns,
|
|
348
|
+
facilitiesByCategory,
|
|
349
|
+
gridData,
|
|
350
|
+
facilityColors,
|
|
351
|
+
setDateColumns,
|
|
352
|
+
setFacilitiesByCategory,
|
|
353
|
+
setGridData,
|
|
354
|
+
setFacilityColors,
|
|
355
|
+
generateDateColumns,
|
|
356
|
+
generateFacilityColors,
|
|
357
|
+
groupFacilitiesByCategory,
|
|
358
|
+
convertToGridData,
|
|
359
|
+
processDataForGrid,
|
|
360
|
+
processDataForLeadCentricGrid,
|
|
361
|
+
transformDataForLeadCentricView,
|
|
362
|
+
convertLeadCentricToGridData
|
|
363
|
+
};
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
export default useExcelGrid;
|