datastake-daf 0.6.767 → 0.6.769

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.
Files changed (79) hide show
  1. package/dist/components/index.js +1007 -730
  2. package/dist/layouts/index.js +495 -459
  3. package/dist/pages/index.js +7914 -6836
  4. package/dist/style/datastake/mapbox-gl.css +330 -0
  5. package/dist/utils/index.js +481 -457
  6. package/package.json +1 -1
  7. package/src/@daf/core/components/Charts/ColumnChart/index.jsx +10 -0
  8. package/src/@daf/core/components/Charts/LineChart/index.jsx +14 -0
  9. package/src/@daf/core/components/Dashboard/Map/ChainIcon/Markers/StakeholderMarker.js +5 -2
  10. package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +67 -27
  11. package/src/@daf/core/components/Dashboard/Map/hook.js +26 -32
  12. package/src/@daf/core/components/Dashboard/Widget/ActivityIndicators/index.jsx +2 -0
  13. package/src/@daf/core/components/Dashboard/Widget/StatCard/StatCard.stories.js +226 -0
  14. package/src/@daf/core/components/Dashboard/Widget/StatCard/index.js +103 -0
  15. package/src/@daf/core/components/Dashboard/Widget/StatCard/style.js +83 -0
  16. package/src/@daf/core/components/Icon/configs/Down.js +8 -0
  17. package/src/@daf/core/components/Icon/configs/Up.js +8 -0
  18. package/src/@daf/core/components/Icon/configs/index.js +4 -0
  19. package/src/@daf/core/components/Icon/configs/partnerIcon.js +1 -1
  20. package/src/@daf/core/components/Screens/BaseScreen/index.jsx +1 -1
  21. package/src/@daf/core/components/Screens/TableScreen/TablePageWithTabs/index.jsx +1 -1
  22. package/src/@daf/core/components/Sidenav/Menu.jsx +4 -4
  23. package/src/@daf/core/components/UI/MissingTagButton/index.jsx +36 -0
  24. package/src/@daf/pages/Dashboards/SupplyChain/components/SupplyChainMap/index.js +0 -2
  25. package/src/@daf/pages/Documents/config.js +0 -10
  26. package/src/@daf/pages/Documents/index.jsx +51 -108
  27. package/src/@daf/pages/Events/Activities/config.js +1 -11
  28. package/src/@daf/pages/Events/Activities/index.jsx +47 -105
  29. package/src/@daf/pages/Events/Incidents/config.js +1 -11
  30. package/src/@daf/pages/Events/Incidents/index.jsx +47 -105
  31. package/src/@daf/pages/Events/config.js +18 -34
  32. package/src/@daf/pages/Events/index.jsx +49 -111
  33. package/src/@daf/pages/Locations/MineSite/config.js +0 -10
  34. package/src/@daf/pages/Locations/MineSite/index.jsx +47 -105
  35. package/src/@daf/pages/Locations/config.js +4 -16
  36. package/src/@daf/pages/Locations/index.jsx +53 -110
  37. package/src/@daf/pages/Stakeholders/Operators/config.js +0 -10
  38. package/src/@daf/pages/Stakeholders/Operators/index.jsx +47 -105
  39. package/src/@daf/pages/Stakeholders/Workers/config.js +0 -10
  40. package/src/@daf/pages/Stakeholders/Workers/index.jsx +47 -105
  41. package/src/@daf/pages/Stakeholders/config.js +3 -15
  42. package/src/@daf/pages/Stakeholders/index.jsx +53 -109
  43. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/index.jsx +43 -0
  44. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/CommunityStats/helper.js +60 -0
  45. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/CommunityStats/index.jsx +36 -0
  46. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/GenderDistribution/helper.js +117 -0
  47. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/GenderDistribution/index.jsx +49 -0
  48. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/JobsTimeline/index.jsx +212 -0
  49. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/index.jsx +72 -0
  50. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/CyclePartners/helper.js +91 -0
  51. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/CyclePartners/index.jsx +50 -0
  52. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/helper.js +134 -0
  53. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/index.jsx +49 -0
  54. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/index.jsx +112 -0
  55. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/index.jsx +498 -0
  56. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/KeyInformation/index.jsx +49 -0
  57. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/PlantingLocations/index.jsx +120 -0
  58. package/src/@daf/pages/Summary/Activities/PlantingCycle/config.js +5 -10
  59. package/src/@daf/pages/Summary/Activities/PlantingCycle/helper.js +218 -0
  60. package/src/@daf/pages/Summary/Activities/PlantingCycle/index.jsx +22 -32
  61. package/src/@daf/pages/Summary/Activities/Restoration/components/ActivityImagery/index.jsx +29 -0
  62. package/src/@daf/pages/Summary/Activities/Restoration/components/ActivityLocation/index.jsx +94 -0
  63. package/src/@daf/pages/Summary/Activities/Restoration/components/WorkersDistribution/index.jsx +49 -0
  64. package/src/@daf/pages/Summary/Activities/Restoration/index.jsx +16 -138
  65. package/src/@daf/pages/TablePage/config.js +78 -0
  66. package/src/@daf/{core/components/Screens/TableScreen/TableWithTabsAndCreate → pages/TablePage}/create.jsx +6 -5
  67. package/src/@daf/pages/TablePage/hook.js +123 -0
  68. package/src/@daf/pages/TablePage/index.jsx +142 -0
  69. package/src/index.js +2 -0
  70. package/src/@daf/core/components/Screens/TableScreen/TableWithTabsAndCreate/index.jsx +0 -115
  71. package/src/@daf/pages/Documents/create.jsx +0 -105
  72. package/src/@daf/pages/Events/Activities/create.jsx +0 -104
  73. package/src/@daf/pages/Events/Incidents/create.jsx +0 -104
  74. package/src/@daf/pages/Events/create.jsx +0 -104
  75. package/src/@daf/pages/Locations/MineSite/create.jsx +0 -104
  76. package/src/@daf/pages/Locations/create.jsx +0 -104
  77. package/src/@daf/pages/Stakeholders/Operators/create.jsx +0 -104
  78. package/src/@daf/pages/Stakeholders/Workers/create.jsx +0 -104
  79. package/src/@daf/pages/Stakeholders/create.jsx +0 -105
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datastake-daf",
3
- "version": "0.6.767",
3
+ "version": "0.6.769",
4
4
  "dependencies": {
5
5
  "@ant-design/icons": "^5.2.5",
6
6
  "@antv/g2": "^5.1.1",
@@ -104,6 +104,8 @@ export default function ColumnChart({
104
104
  width,
105
105
  legendConfig = {},
106
106
  isPdf = false,
107
+ yAxis: customYAxis,
108
+ xAxis: customXAxis,
107
109
  ...rest
108
110
  }) {
109
111
  const containerRef = React.useRef(null);
@@ -146,6 +148,7 @@ export default function ColumnChart({
146
148
  label: {
147
149
  formatter: formattedXAxis,
148
150
  },
151
+ ...(customXAxis || {}),
149
152
  },
150
153
  yAxis: {
151
154
  ...(isPercentage
@@ -163,7 +166,12 @@ export default function ColumnChart({
163
166
  (s) => `${s},`
164
167
  ) + " %"
165
168
  : formattedYAxis,
169
+ ...(customYAxis?.label || {}),
166
170
  },
171
+ // Merge customYAxis properties (excluding label which is already merged above)
172
+ ...(customYAxis ? Object.fromEntries(
173
+ Object.entries(customYAxis).filter(([key]) => key !== 'label')
174
+ ) : {}),
167
175
  },
168
176
  legend: false,
169
177
  ...(showBackground && isPercentage && {
@@ -196,6 +204,8 @@ export default function ColumnChart({
196
204
  color,
197
205
  token.colorPrimary9,
198
206
  groupField,
207
+ customYAxis,
208
+ customXAxis,
199
209
  ]);
200
210
 
201
211
  React.useEffect(() => {
@@ -88,6 +88,8 @@ export default function LineChart({
88
88
  isPdf = false,
89
89
  legendConfig = {},
90
90
  width,
91
+ yAxis: customYAxis,
92
+ xAxis: customXAxis,
91
93
  ...rest
92
94
  }) {
93
95
  const containerRef = React.useRef(null);
@@ -129,7 +131,12 @@ export default function LineChart({
129
131
  style: {
130
132
  fontSize: !autoHideXLabel ? 10 : undefined,
131
133
  },
134
+ ...(customXAxis?.label || {}),
132
135
  },
136
+ // Merge customXAxis properties (excluding label which is already merged above)
137
+ ...(customXAxis ? Object.fromEntries(
138
+ Object.entries(customXAxis).filter(([key]) => key !== 'label')
139
+ ) : {}),
133
140
  },
134
141
  yAxis: {
135
142
  ...(isPercentage
@@ -143,7 +150,12 @@ export default function LineChart({
143
150
  formatter: isPercentage
144
151
  ? (v) => `${v}`.replace(/\d{1,3}(?=(\d{3})+$)/g, (s) => `${s},`) + " %"
145
152
  : formattedYAxis,
153
+ ...(customYAxis?.label || {}),
146
154
  },
155
+ // Merge customYAxis properties (excluding label which is already merged above)
156
+ ...(customYAxis ? Object.fromEntries(
157
+ Object.entries(customYAxis).filter(([key]) => key !== 'label')
158
+ ) : {}),
147
159
  },
148
160
  smooth: isSmooth,
149
161
  meta: {
@@ -183,6 +195,8 @@ export default function LineChart({
183
195
  tooltipConfig,
184
196
  isPdf,
185
197
  t,
198
+ customYAxis,
199
+ customXAxis,
186
200
  ]);
187
201
 
188
202
  React.useEffect(() => {
@@ -90,7 +90,7 @@ export default function StakeholderIcon({
90
90
  useEffect(() => {
91
91
  linkNodesData.map((node) => {
92
92
  const isConnectingToStakeholder = node.isStakeholder;
93
- const id = `${data.datastakeId}-${node.stakeholderId}`;
93
+ const id = `${data.datastakeId}-${node.stakeholderId || node.datastakeId}`;
94
94
  const targetsParentId = node.parentId;
95
95
  const targetMarkerIndex = node.stakeholdersIndex;
96
96
  const isSibling = targetsParentId === parentId;
@@ -234,7 +234,10 @@ export default function StakeholderIcon({
234
234
  onClickLink(data);
235
235
  },
236
236
  })}
237
- getPopupContainer={() => document.getElementById("map")}
237
+ getPopupContainer={(triggerNode) => {
238
+ const mapElement = document.getElementById("map");
239
+ return mapElement || triggerNode.parentElement || document.body;
240
+ }}
238
241
  >
239
242
  <StakeholderMarker
240
243
  className={`${data.type} ${
@@ -32,7 +32,8 @@ export default function LocationIcon({
32
32
  activeMarker,
33
33
  setActiveMarker,
34
34
  }) {
35
- const root = useRef(null);
35
+ const rootsMapRef = useRef(new Map());
36
+ const markersRef = useRef([])
36
37
  const isSelected = selectedMarkersId.includes(data.datastakeId);
37
38
  const Marker = useMemo(() => {
38
39
  if (isMineSite(data.type)) {
@@ -75,7 +76,22 @@ export default function LocationIcon({
75
76
  }, [data.stakeholders, zoom]);
76
77
 
77
78
  useEffect(() => {
78
- stakeholdersOfLocation.map((stakeholder, index) => {
79
+ const currentRoots = rootsMapRef.current;
80
+ const currentMarkers = markersRef.current;
81
+
82
+ currentMarkers.forEach(marker => {
83
+ if (mapRef.hasLayer(marker)) {
84
+ mapRef.removeLayer(marker);
85
+ }
86
+ });
87
+ currentRoots.forEach(root => {
88
+ root.unmount();
89
+ });
90
+ currentRoots.clear();
91
+ markersRef.current = [];
92
+
93
+ // Create new markers
94
+ stakeholdersOfLocation.forEach((stakeholder, index) => {
79
95
  const markerId = `${stakeholder.datastakeId}`;
80
96
  const { x, y, radius, center } = getStakeholderPosition({
81
97
  zoom,
@@ -94,6 +110,7 @@ export default function LocationIcon({
94
110
  const pathLocLatLng = mapRef.layerPointToLatLng(pathLocPoint);
95
111
  const isForceOpen = activeMarker?.datastakeId === data.datastakeId;
96
112
  const iconSize = isSmallMarker(zoom) || isExtraSmallMarker(zoom) ? [11, 11] : [25, 25];
113
+
97
114
  const marker = L.marker(stakeholderLatLng, {
98
115
  icon: L.divIcon({
99
116
  html: `<div id="${markerId}"></div>`,
@@ -102,31 +119,38 @@ export default function LocationIcon({
102
119
  }),
103
120
  }).addTo(mapRef);
104
121
 
105
- const div = document.getElementById(markerId);
106
- root.current = createRoot(div);
122
+ markersRef.current.push(marker);
107
123
 
108
- root.current.render(
109
- <StakeholderIcon
110
- data={stakeholder}
111
- zoom={zoom}
112
- allData={allData}
113
- link={link}
114
- parentId={data.datastakeId}
115
- renderTooltip={renderTooltip}
116
- onClickLink={onClickLink}
117
- selectedMarkersId={selectedMarkersId}
118
- handleSelectMarker={handleSelectMarker}
119
- mapRef={mapRef}
120
- radius={radius}
121
- index={index}
122
- x={x}
123
- y={y}
124
- openPopupIdRef={openPopupIdRef}
125
- polylinesRef={polylinesRef}
126
- isForceOpen={isForceOpen}
127
- activeMarker={activeMarker}
128
- />,
129
- );
124
+ setTimeout(() => {
125
+ const div = document.getElementById(markerId);
126
+ if (div && !rootsMapRef.current.has(markerId)) {
127
+ const root = createRoot(div);
128
+ rootsMapRef.current.set(markerId, root);
129
+
130
+ root.render(
131
+ <StakeholderIcon
132
+ data={stakeholder}
133
+ zoom={zoom}
134
+ allData={allData}
135
+ link={link}
136
+ parentId={data.datastakeId}
137
+ renderTooltip={renderTooltip}
138
+ onClickLink={onClickLink}
139
+ selectedMarkersId={selectedMarkersId}
140
+ handleSelectMarker={handleSelectMarker}
141
+ mapRef={mapRef}
142
+ radius={radius}
143
+ index={index}
144
+ x={x}
145
+ y={y}
146
+ openPopupIdRef={openPopupIdRef}
147
+ polylinesRef={polylinesRef}
148
+ isForceOpen={isForceOpen}
149
+ activeMarker={activeMarker}
150
+ />,
151
+ );
152
+ }
153
+ }, 0);
130
154
 
131
155
  setMapMarkers((prev) => {
132
156
  const array = [
@@ -162,6 +186,19 @@ export default function LocationIcon({
162
186
  listOfPolylines: polylinesRef.current,
163
187
  });
164
188
  });
189
+
190
+ return () => {
191
+ markersRef.current.forEach(marker => {
192
+ if (mapRef.hasLayer(marker)) {
193
+ mapRef.removeLayer(marker);
194
+ }
195
+ });
196
+ rootsMapRef.current.forEach(root => {
197
+ root.unmount();
198
+ });
199
+ rootsMapRef.current.clear();
200
+ markersRef.current = [];
201
+ };
165
202
  }, [stakeholdersOfLocation, selectedMarkersId, activeMarker]);
166
203
 
167
204
  linkedNodesData.map((node) => {
@@ -213,7 +250,10 @@ export default function LocationIcon({
213
250
  // (!openPopupIdRef.current || openPopupIdRef.current === data.datastakeId) &&
214
251
  // isHovering
215
252
  // }
216
- getPopupContainer={() => document.getElementById("map")}
253
+ getPopupContainer={(triggerNode) => {
254
+ const mapElement = document.getElementById("map");
255
+ return mapElement || triggerNode.parentElement || document.body;
256
+ }}
217
257
  >
218
258
  <div style={{ position: "relative", display: "inline-block" }}>
219
259
  {(isSelected || selectedMarkersId.length === 0) && (
@@ -82,43 +82,37 @@ export const useMap = ({
82
82
  const highlightTable = {};
83
83
 
84
84
  for (const [node] of graph) {
85
- const visited = new Set();
86
- const queue = [node];
87
-
88
- while (queue.length) {
89
- const current = queue.shift();
90
- if (visited.has(current)) continue;
91
-
92
- const currentType = nodeTypes.get(current);
93
-
94
- if (current !== node && isLocation(currentType)) continue;
95
-
96
- visited.add(current);
97
-
98
- for (const neighbor of graph.get(current)) {
99
- if (!visited.has(neighbor)) queue.push(neighbor);
100
- }
85
+ const highlighted = new Set();
86
+
87
+ highlighted.add(node);
88
+
89
+ const nodeIsStakeholder = !isLocation(nodeTypes.get(node));
90
+ if (nodeIsStakeholder && stakeToLoc.has(node)) {
91
+ const parentLoc = stakeToLoc.get(node);
92
+ highlighted.add(parentLoc);
101
93
  }
102
-
103
- if (!isLocation(nodeTypes.get(node)) && stakeToLoc.has(node)) {
104
- visited.add(stakeToLoc.get(node));
105
- }
106
-
107
- visited.add(node);
108
-
109
- const extraLocs = new Set();
110
- for (const n of visited) {
111
- for (const neighbor of graph.get(n) || []) {
112
- if (isLocation(nodeTypes.get(neighbor))) {
113
- extraLocs.add(neighbor);
94
+
95
+ for (const neighbor of graph.get(node) || []) {
96
+ const neighborIsStakeholder = !isLocation(nodeTypes.get(neighbor));
97
+
98
+ if (neighborIsStakeholder && stakeToLoc.has(neighbor)) {
99
+ const neighborParent = stakeToLoc.get(neighbor);
100
+
101
+ if (
102
+ (isLocation(nodeTypes.get(node)) && neighborParent === node) ||
103
+ (nodeIsStakeholder && stakeToLoc.get(node) === neighborParent)
104
+ ) {
105
+ highlighted.add(neighbor);
106
+ } else {
107
+ highlighted.add(neighbor);
108
+ highlighted.add(neighborParent);
114
109
  }
110
+ } else {
111
+ highlighted.add(neighbor);
115
112
  }
116
113
  }
117
- for (const loc of extraLocs) {
118
- visited.add(loc);
119
- }
120
114
 
121
- highlightTable[node] = [...visited];
115
+ highlightTable[node] = [...highlighted];
122
116
  }
123
117
 
124
118
  return highlightTable;
@@ -48,6 +48,7 @@ export default function ActivityIndicatorsWidget({
48
48
  }, [config, filterMode]);
49
49
 
50
50
  const component = (
51
+ <section>
51
52
  <Widget
52
53
  loading={loading}
53
54
  className={formatClassname(["flex-1 with-border-header h-w-btn-header no-p-body", widgetClassName])}
@@ -81,6 +82,7 @@ export default function ActivityIndicatorsWidget({
81
82
  ))}
82
83
  </Style>
83
84
  </Widget>
85
+ </section>
84
86
  );
85
87
 
86
88
  if (noMinWidth) {
@@ -0,0 +1,226 @@
1
+ import ThemeLayout from "../../../ThemeLayout";
2
+ import StatCard from "./index";
3
+ import Widget from "../index.jsx";
4
+ import DashboardLayout from "../../DashboardLayout/index.jsx";
5
+
6
+ export default {
7
+ title: "Dashboard/Widgets/StatCard",
8
+ component: StatCard,
9
+ tags: ["autodocs"],
10
+ decorators: [
11
+ (Story) => (
12
+ <ThemeLayout>
13
+ <Story />
14
+ </ThemeLayout>
15
+ ),
16
+ ],
17
+ };
18
+
19
+ export const Primary = {
20
+ name: "StatCard - With All Props",
21
+ args: {
22
+ title: "Activity Participants",
23
+ value: "325",
24
+ icon: "Users",
25
+ change: {
26
+ value: 20,
27
+ direction: "up",
28
+ },
29
+ },
30
+ };
31
+
32
+ export const WithPositiveChange = {
33
+ name: "With Positive Change",
34
+ args: {
35
+ title: "Activity Participants",
36
+ value: "325",
37
+ icon: "Users",
38
+ change: {
39
+ value: 20,
40
+ direction: "up",
41
+ },
42
+ },
43
+ };
44
+
45
+ export const WithNegativeChange = {
46
+ name: "With Negative Change",
47
+ args: {
48
+ title: "Total Revenue",
49
+ value: "$12,450",
50
+ icon: "Package",
51
+ change: {
52
+ value: 15,
53
+ direction: "down",
54
+ },
55
+ },
56
+ };
57
+
58
+ export const WithoutChange = {
59
+ name: "Without Change Indicator",
60
+ args: {
61
+ title: "Total Projects",
62
+ value: "48",
63
+ icon: "Lock",
64
+ },
65
+ };
66
+
67
+ export const WithoutIcon = {
68
+ name: "Without Icon",
69
+ args: {
70
+ title: "Active Users",
71
+ value: "1,234",
72
+ change: {
73
+ value: 12,
74
+ direction: "up",
75
+ },
76
+ },
77
+ };
78
+
79
+ export const CustomChangeColors = {
80
+ name: "Custom Change Colors",
81
+ args: {
82
+ title: "Custom Metric",
83
+ value: "999",
84
+ icon: "Package",
85
+ change: {
86
+ value: 5,
87
+ direction: "up",
88
+ color: "#E0F2FE",
89
+ borderColor: "#7DD3FC",
90
+ textColor: "#0369A1",
91
+ },
92
+ },
93
+ };
94
+
95
+ export const MultipleCards = {
96
+ name: "Multiple Stat Cards",
97
+ render: () => (
98
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: "16px" }}>
99
+ <StatCard
100
+ title="Activity Participants"
101
+ value="325"
102
+ icon="Users"
103
+ change={{
104
+ value: 20,
105
+ direction: "up",
106
+ }}
107
+ />
108
+ <StatCard
109
+ title="Total Revenue"
110
+ value="$12,450"
111
+ icon="Package"
112
+ change={{
113
+ value: 15,
114
+ direction: "down",
115
+ }}
116
+ />
117
+ <StatCard
118
+ title="Total Projects"
119
+ value="48"
120
+ icon="Lock"
121
+ />
122
+ <StatCard
123
+ title="Active Users"
124
+ value="1,234"
125
+ icon="User"
126
+ change={{
127
+ value: 8,
128
+ direction: "up",
129
+ }}
130
+ />
131
+ </div>
132
+ ),
133
+ };
134
+
135
+ export const InWidget = {
136
+ name: "Inside Widget",
137
+ render: () => (
138
+ <Widget title="Key Statistics" className="with-border-header">
139
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: "16px" }}>
140
+ <StatCard
141
+ title="Activity Participants"
142
+ value="325"
143
+ icon="Users"
144
+ change={{
145
+ value: 20,
146
+ direction: "up",
147
+ }}
148
+ />
149
+ <StatCard
150
+ title="Total Revenue"
151
+ value="$12,450"
152
+ icon="Package"
153
+ change={{
154
+ value: 15,
155
+ direction: "down",
156
+ }}
157
+ />
158
+ <StatCard
159
+ title="Total Projects"
160
+ value="48"
161
+ icon="Lock"
162
+ />
163
+ </div>
164
+ </Widget>
165
+ ),
166
+ };
167
+
168
+ export const InDashboard = {
169
+ name: "In Dashboard Layout",
170
+ render: () => (
171
+ <DashboardLayout>
172
+ <section>
173
+ <Widget>
174
+ <div
175
+ style={{
176
+ height: "500px",
177
+ backgroundColor: "#f0f0f0",
178
+ display: "flex",
179
+ alignItems: "center",
180
+ justifyContent: "center",
181
+ }}
182
+ >
183
+ Placeholder
184
+ </div>
185
+ </Widget>
186
+ <Widget title="Statistics Overview" className="with-border-header">
187
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", gap: "16px" }}>
188
+ <StatCard
189
+ title="Activity Participants"
190
+ value="325"
191
+ icon="Users"
192
+ change={{
193
+ value: 20,
194
+ direction: "up",
195
+ }}
196
+ />
197
+ <StatCard
198
+ title="Total Revenue"
199
+ value="$12,450"
200
+ icon="Package"
201
+ change={{
202
+ value: 15,
203
+ direction: "down",
204
+ }}
205
+ />
206
+ <StatCard
207
+ title="Total Projects"
208
+ value="48"
209
+ icon="Lock"
210
+ />
211
+ <StatCard
212
+ title="Active Users"
213
+ value="1,234"
214
+ icon="User"
215
+ change={{
216
+ value: 8,
217
+ direction: "up",
218
+ }}
219
+ />
220
+ </div>
221
+ </Widget>
222
+ </section>
223
+ </DashboardLayout>
224
+ ),
225
+ };
226
+
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import { Tooltip } from 'antd';
3
+ import CustomIcon from '../../../Icon/CustomIcon.jsx';
4
+ import Style from './style.js';
5
+
6
+ const StatCard = ({
7
+ title,
8
+ value,
9
+ icon,
10
+ iconTooltip,
11
+ change = null, // Object with { value, direction?, color?, borderColor?, textColor? }
12
+ }) => {
13
+ // Default colors for positive change (green)
14
+ const getChangeColors = () => {
15
+ if (!change) return {};
16
+
17
+ const isPositive = change.direction !== 'down';
18
+ const defaultPositive = {
19
+ backgroundColor: '#ECFDF3',
20
+ borderColor: '#A7F3D0',
21
+ textColor: '#52C41A',
22
+ };
23
+ const defaultNegative = {
24
+ backgroundColor: '#FEF3F2',
25
+ borderColor: '#FECACA',
26
+ textColor: '#D92D20',
27
+ };
28
+
29
+ const defaults = isPositive ? defaultPositive : defaultNegative;
30
+
31
+ return {
32
+ changeColor: change.color || defaults.backgroundColor,
33
+ changeBorderColor: change.borderColor || defaults.borderColor,
34
+ changeTextColor: change.textColor || defaults.textColor,
35
+ };
36
+ };
37
+
38
+ const changeColors = getChangeColors();
39
+ const isPositive = change?.direction !== 'down';
40
+
41
+ // Check if change value is 0 (handle both number and string formats like "0", "0%", "0.0%")
42
+ const isChangeZero = () => {
43
+ if (!change || !change.value) return true;
44
+ const valueStr = String(change.value).replace(/[%,]/g, '').trim();
45
+ const numValue = parseFloat(valueStr);
46
+ return isNaN(numValue) || numValue === 0;
47
+ };
48
+
49
+ const shouldShowChange = change && !isChangeZero();
50
+
51
+ return (
52
+ <Style {...changeColors}>
53
+ <div className="stat-card-header">
54
+ <h3 className="stat-card-title">{title}</h3>
55
+ {icon && (
56
+ <div className="stat-card-icon">
57
+ <CustomIcon name={icon} width={24} height={24} />
58
+ </div>
59
+ )}
60
+ </div>
61
+ <div className="stat-card-content">
62
+ <h2 className="stat-card-value">{value}</h2>
63
+ {shouldShowChange && (
64
+ <Tooltip
65
+ title={
66
+ change.tooltipText ||
67
+ (isPositive
68
+ ? `Increased by ${change.value}`
69
+ : `Decreased by ${change.value}`)
70
+ }
71
+ >
72
+ <div className="stat-card-change">
73
+ <div className="change-icon">
74
+ {isPositive ? (
75
+ <>
76
+ <CustomIcon
77
+ name="Up"
78
+ width={10}
79
+ height={10}
80
+ color={changeColors.changeTextColor}
81
+ />
82
+ </>
83
+ ) : (
84
+ <>
85
+ <CustomIcon
86
+ name="Down"
87
+ width={10}
88
+ height={10}
89
+ color={changeColors.changeTextColor}
90
+ />
91
+ </>
92
+ )}
93
+ </div>
94
+ <span className="change-value">{change.value}</span>
95
+ </div>
96
+ </Tooltip>
97
+ )}
98
+ </div>
99
+ </Style>
100
+ );
101
+ }
102
+
103
+ export default StatCard;