datastake-daf 0.6.765 → 0.6.767

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 (64) hide show
  1. package/dist/components/index.js +482 -245
  2. package/dist/layouts/index.js +0 -3
  3. package/dist/pages/index.js +299 -241
  4. package/dist/services/index.js +18 -0
  5. package/dist/utils/index.js +328 -5
  6. package/package.json +4 -2
  7. package/src/@daf/core/components/Charts/BarChart/index.jsx +4 -0
  8. package/src/@daf/core/components/Dashboard/Map/ChainIcon/Markers/StakeholderMarker.js +1 -1
  9. package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +1 -9
  10. package/src/@daf/core/components/Dashboard/Map/StakeholderIcon/index.js +3 -0
  11. package/src/@daf/core/components/Dashboard/Map/hook.js +31 -1
  12. package/src/@daf/core/components/Graphs/StakeholderMappings/index.jsx +0 -2
  13. package/src/@daf/core/components/Graphs/TradeRelationship/index.jsx +49 -11
  14. package/src/@daf/core/components/Graphs/components/BaseGraph.jsx +10 -7
  15. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/KeyIndicatorsWidget/config.js +2 -2
  16. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/KeyIndicatorsWidget/index.jsx +1 -1
  17. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/UserActivity/helper.js +4 -1
  18. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/UserActivity/index.jsx +5 -0
  19. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/UserGrowth/index.jsx +1 -0
  20. package/src/@daf/core/components/Screens/Admin/AdminDashboard/components/UserStatistics/index.jsx +34 -26
  21. package/src/@daf/core/components/Screens/Admin/AdminDashboard/index.jsx +56 -52
  22. package/src/@daf/core/components/Screens/Admin/AdminScreens/Dashboard.jsx +52 -67
  23. package/src/@daf/core/components/Screens/Admin/AdminScreens/Location.jsx +96 -0
  24. package/src/@daf/core/components/Screens/Admin/AdminScreens/Subjects.jsx +96 -0
  25. package/src/@daf/core/components/Screens/Admin/AdminScreens/index.js +2 -1
  26. package/src/@daf/core/components/Screens/Admin/adminRoutes.js +116 -89
  27. package/src/@daf/core/components/Screens/TableScreen/TableWithTabsAndCreate/create.jsx +106 -0
  28. package/src/@daf/core/components/Screens/TableScreen/TableWithTabsAndCreate/index.jsx +115 -0
  29. package/src/@daf/core/components/Table/MoreTags/index.jsx +17 -5
  30. package/src/@daf/hooks/useMapHelper.js +5 -0
  31. package/src/@daf/layouts/AuthLayout/components/Navbar/index.jsx +0 -1
  32. package/src/@daf/pages/Dashboards/SupplyChain/components/ChartsContainer/components/Identification/hook.js +10 -10
  33. package/src/@daf/pages/Dashboards/SupplyChain/components/SupplyChainMap/index.js +11 -5
  34. package/src/@daf/pages/Dashboards/UserDashboard/components/ContributionsGraph/helper.js +1 -14
  35. package/src/@daf/pages/Dashboards/UserDashboard/components/ContributionsGraph/hook.js +12 -5
  36. package/src/@daf/pages/Dashboards/UserDashboard/components/MineSites/index.jsx +1 -1
  37. package/src/@daf/pages/Dashboards/UserDashboard/components/Triangulation/hook.js +1 -1
  38. package/src/@daf/pages/Data/Channels/columns.js +175 -0
  39. package/src/@daf/pages/Data/Channels/config.js +0 -0
  40. package/src/@daf/pages/Data/Channels/create.jsx +0 -0
  41. package/src/@daf/pages/Data/Channels/index.jsx +0 -0
  42. package/src/@daf/pages/Events/Activities/columns.js +1 -4
  43. package/src/@daf/pages/Events/helper.js +2 -2
  44. package/src/@daf/pages/Events/index.jsx +1 -1
  45. package/src/@daf/pages/Locations/MineSite/columns.js +6 -4
  46. package/src/@daf/pages/Stakeholders/Operators/columns.js +4 -2
  47. package/src/@daf/pages/Summary/Activities/PlantingCycle/index.jsx +1 -0
  48. package/src/@daf/pages/Summary/Minesite/components/StakeholderMapping/config.js +2 -2
  49. package/src/@daf/pages/Summary/Minesite/components/StakeholderMapping/helper.js +7 -7
  50. package/src/@daf/pages/Summary/Minesite/components/StakeholderMapping/index.js +3 -2
  51. package/src/@daf/pages/Summary/Minesite/index.jsx +3 -1
  52. package/src/@daf/pages/Summary/Operator/components/Governance/index.js +0 -1
  53. package/src/@daf/pages/Summary/Operator/components/KeyInformation/config.js +33 -21
  54. package/src/@daf/pages/Summary/Operator/components/TradeRelationships/helper.js +13 -13
  55. package/src/@daf/pages/Summary/Operator/components/TradeRelationships/hook.js +8 -8
  56. package/src/@daf/pages/Summary/Operator/components/TradeRelationships/index.js +10 -3
  57. package/src/@daf/pages/Summary/Operator/index.jsx +3 -0
  58. package/src/@daf/pages/Summary/components/InformationAvailability/index.js +4 -3
  59. package/src/@daf/services/AdminService.js +14 -0
  60. package/src/constants/locales/en/translation.js +109 -2
  61. package/src/constants/locales/fr/translation.js +109 -1
  62. package/src/constants/locales/sp/translation.js +104 -1
  63. package/src/index.js +2 -0
  64. package/dist/style/datastake/mapbox-gl.css +0 -330
@@ -21,6 +21,7 @@ function TradeRelationship({
21
21
  onFilterChange = () => {},
22
22
  renderTooltipItems = () => [],
23
23
  getTotal = () => 0,
24
+ onRenderComplete = () => {},
24
25
  }) {
25
26
  const reactFlowWrapper = useRef(null);
26
27
  const [nodes, setNodes] = useNodesState([]);
@@ -30,6 +31,27 @@ function TradeRelationship({
30
31
  // const [initCenter, setInitCenter] = useState(true);
31
32
  const [associatedNodes, setAssociatedNodes] = useState(null);
32
33
 
34
+ const isFullyRenderedRef = useRef(false);
35
+ const [isFullyRendered, setIsFullyRendered] = useState(false);
36
+
37
+ useEffect(() => {
38
+ isFullyRenderedRef.current = false;
39
+ setIsFullyRendered(false);
40
+ setActiveNode(null);
41
+ }, [data]);
42
+
43
+ useEffect(() => {
44
+ if (nodes.length > 0 && edges.length > 0 && !isFullyRenderedRef.current) {
45
+ const timeoutId = setTimeout(() => {
46
+ isFullyRenderedRef.current = true;
47
+ setIsFullyRendered(true);
48
+ onRenderComplete(true);
49
+ }, 200);
50
+
51
+ return () => clearTimeout(timeoutId);
52
+ }
53
+ }, [nodes.length, edges.length, associatedNodes]);
54
+
33
55
  useEffect(() => {
34
56
  setActiveNode(null);
35
57
  }, [data]);
@@ -58,7 +80,7 @@ function TradeRelationship({
58
80
  [edges, activeNode, associatedNodes]
59
81
  );
60
82
 
61
- useEffect(() => {
83
+ useEffect(() => {
62
84
  let yInit = 0;
63
85
  let xInit = 0;
64
86
  const isBilateral = data?.sources?.length >= 1;
@@ -71,10 +93,9 @@ function TradeRelationship({
71
93
  setEdges([]);
72
94
  return;
73
95
  }
74
-
75
96
  const _nodes = [
76
97
  {
77
- id: data.id,
98
+ id: data.id?.toString(),
78
99
  type: "expandedNode",
79
100
  position: {
80
101
  x: xInit,
@@ -89,10 +110,10 @@ function TradeRelationship({
89
110
  if (isBilateral) {
90
111
  (data.sources || []).forEach((source) => {
91
112
  const hasPrev = false;
92
- _edges.push({
113
+ const edge = {
93
114
  id: `e-${data.id}-${source}`,
94
- source: source,
95
- type: "default",
115
+ source: source?.toString(),
116
+ type: "defaultEdge",
96
117
  target: data?.id?.toString(),
97
118
  sourceHandle: "left",
98
119
  targetHandle: "right",
@@ -106,7 +127,8 @@ function TradeRelationship({
106
127
  tooltipTitle,
107
128
  moreLeft: false,
108
129
  },
109
- });
130
+ };
131
+ _edges.push(edge);
110
132
  });
111
133
  }
112
134
 
@@ -164,7 +186,7 @@ function TradeRelationship({
164
186
  ? false
165
187
  : true;
166
188
 
167
- _edges.push({
189
+ const edge = {
168
190
  id: `e-${ch.id}-${source}`,
169
191
  source: hasPrev ? source : ch?.id?.toString(),
170
192
  type: isCustom ? "verticalPath" : "defaultEdge",
@@ -182,7 +204,8 @@ function TradeRelationship({
182
204
  tooltipTitle,
183
205
  moreLeft: prevChildren.length > children.length,
184
206
  },
185
- });
207
+ };
208
+ _edges.push(edge);
186
209
 
187
210
  if (isCustom) {
188
211
  customIndex += 1;
@@ -230,11 +253,25 @@ function TradeRelationship({
230
253
  maxHeight: 0,
231
254
  });
232
255
 
256
+ // Check for potential ID mismatches
257
+ const nodeIds = _nodes.map(n => n.id);
258
+ const edgeIssues = _edges.filter(e => {
259
+ const sourceExists = nodeIds.includes(e.source);
260
+ const targetExists = nodeIds.includes(e.target);
261
+ return !sourceExists || !targetExists;
262
+ });
263
+
264
+ // Set nodes first
233
265
  setNodes(_nodes);
234
- setEdges(_edges);
266
+
267
+ const timeoutId = setTimeout(() => {
268
+ setEdges(_edges);
269
+ }, 100); // 100ms is imperceptible to users but ensures React Flow is ready
270
+
271
+ // Cleanup to prevent memory leaks if component unmounts quickly
272
+ return () => clearTimeout(timeoutId);
235
273
  }, [data, activeNode]);
236
274
 
237
- // Used to find associated nodes, when a node is selected
238
275
  useEffect(() => {
239
276
  if (activeNode) {
240
277
  let _associatedNodesRight = [activeNode];
@@ -293,6 +330,7 @@ function TradeRelationship({
293
330
 
294
331
  return (
295
332
  <BaseGraph
333
+ key={JSON.stringify(nodes) + JSON.stringify(edges)}
296
334
  nodes={mappedNodes}
297
335
  edges={mappedEdges}
298
336
  maxZoom={maxZoom}
@@ -33,7 +33,6 @@ const BaseGraph = forwardRef(function BaseGraph(
33
33
  withDuration = true,
34
34
  onFilterChange,
35
35
  isPdf,
36
- zoomOutTransition = false,
37
36
  ...props
38
37
  },
39
38
  ref,
@@ -50,18 +49,22 @@ const BaseGraph = forwardRef(function BaseGraph(
50
49
  return result;
51
50
  }, [nodes.length, mandatoryNodesToFit?.length, mandatoryNodesToFit]);
52
51
 
52
+ // In BaseGraph.jsx, replace the useEffect with:
53
53
  useEffect(() => {
54
54
  if (nodesToFit.length === 0) return;
55
55
 
56
- requestAnimationFrame(() => {
56
+ // Use setTimeout instead of requestAnimationFrame to ensure nodes are rendered
57
+ const timer = setTimeout(() => {
57
58
  fitView({
58
59
  padding: 0.4,
59
60
  nodes: [...nodesToFit],
60
- duration: withDuration ? 300 : undefined,
61
- maxZoom: 0.8,
61
+ // duration: withDuration ? 300 : undefined,
62
+ maxZoom: 0.9,
62
63
  });
63
- });
64
- }, [JSON.stringify(nodesToFit), withDuration]);
64
+ }, 100); // Small delay to ensure nodes are rendered
65
+
66
+ return () => clearTimeout(timer);
67
+ }, [nodesToFit.length, nodesToFit.map(n => `${n.id}-${n.width}-${n.height}`).join(','), withDuration]);
65
68
 
66
69
  return (
67
70
  <ComponentWithFocus>
@@ -87,7 +90,7 @@ const BaseGraph = forwardRef(function BaseGraph(
87
90
  fitView={true} // zoom out on default
88
91
  fitViewOptions={{
89
92
  padding: 0.2, //zoom out on default
90
- ...(zoomOutTransition ? { duration: withDuration ? 300 : undefined } : {}),
93
+ duration: withDuration ? 300 : undefined,
91
94
  }}
92
95
  {...props}
93
96
  >
@@ -8,7 +8,7 @@ export function getConfig(data, goTo, t) {
8
8
  return {
9
9
  label: (
10
10
  <div className="flex">
11
- <div className="flex-1">{t(`admin::${item.title}`)}</div>
11
+ <div className="flex-1">{t(`${item.title}`)}</div>
12
12
  <div className="cursor-pointer" onClick={() => goTo(item.goToPath)}>
13
13
  <CustomIcon name="LinkNewTab" width={16} height={16} color={iconColor} />
14
14
  </div>
@@ -17,4 +17,4 @@ export function getConfig(data, goTo, t) {
17
17
  render: () => <span>{renderNumber(item.valueToShow)}</span>,
18
18
  };
19
19
  });
20
- }
20
+ }
@@ -17,4 +17,4 @@ export default function KeyIndicatorsWidget({ data = [], loading, goTo, t }) {
17
17
  />
18
18
  </div>
19
19
  );
20
- }
20
+ }
@@ -53,6 +53,9 @@ export const getColor = (users, items) => {
53
53
 
54
54
  export function useConfig(data) {
55
55
  const config = useMemo(() => {
56
+ // Ensure data is an array to prevent "data.find is not a function" error
57
+ const safeData = Array.isArray(data) ? data : [];
58
+
56
59
  const start = moment().add(-1, "years").format("MMM YY");
57
60
  let now = moment(start, "MMM YY");
58
61
  const _config = [];
@@ -65,7 +68,7 @@ export function useConfig(data) {
65
68
  while (true) {
66
69
  DAYS.forEach((d) => {
67
70
  if (d === nowMonth.format("dd") && now.format("MMM YY") === nowMonth.format("MMM YY")) {
68
- const users = data.find((d) => d._id === nowMonth.format("YYYY-MM-DD"))?.count || 0;
71
+ const users = safeData.find((d) => d._id === nowMonth.format("YYYY-MM-DD"))?.count || 0;
69
72
  conf.push({ date: nowMonth.format("DD MMM YY"), active: true, users: users });
70
73
  nowMonth.add(1, "days");
71
74
  } else {
@@ -10,8 +10,13 @@ const DAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
10
10
 
11
11
  export default function UserActivity({ loading, data = [], t }) {
12
12
  const config = useConfig(data);
13
+
13
14
  const { isCollapsed } = useResizeContext();
14
15
 
16
+
17
+
18
+ // console.log("userActivityyyyy",data);
19
+
15
20
  return (
16
21
  <Widget
17
22
  title={t(`admin::user-activity`)}
@@ -10,6 +10,7 @@ export default function UserGrowth({ loading, data = [], t, translationKeys, fet
10
10
 
11
11
  useUserGrowth({ container, data, t, translationKeys });
12
12
 
13
+
13
14
  useEffect(() => {
14
15
  if (typeof fetchUserGrowth === "function") {
15
16
  fetchUserGrowth(selectValue);
@@ -4,32 +4,40 @@ import TopContributors from "./TopContributors/index.jsx";
4
4
  import Widget from "../../../../../Dashboard/Widget/index.jsx";
5
5
 
6
6
  export default function UserStatistics({
7
- fetchUserGrowth,
8
- loading,
9
- loadingUserGrowth,
10
- data,
11
- t,
12
- translationKeys,
7
+ fetchUserGrowth,
8
+ loading,
9
+ loadingUserGrowth,
10
+ data,
11
+ t,
12
+ translationKeys,
13
13
  }) {
14
- return (
15
- <Widget
16
- title={t(`${translationKeys}-admin::usage-statistics`)}
17
- className="with-border-header"
18
- >
19
- <div className="flex flex-column gap-6">
20
- <UserActivity loading={loading} data={data.userActivityData} t={t} />
14
+ // console.log("UserStatistics:loading", loading);
15
+ // console.log("UserStatistics:loadingUserGrowth", loadingUserGrowth);
16
+ // console.log("UserStatistics:data", data);
17
+ // console.log("UserStatistics:userActivityData", data?.userActivityData);
18
+ // console.log("UserStatistics:topContributorsData", data?.topContributorsData);
19
+ // console.log("UserStatistics:userGrowthData", data?.userGrowthData);
21
20
 
22
- <div className="flex flex-row flex-col-mobile gap-6">
23
- <TopContributors data={data.topContributorsData} loading={loading} t={t} />
24
- <UserGrowth
25
- t={t}
26
- loading={loadingUserGrowth}
27
- data={data.userGrowthData}
28
- fetchUserGrowth={fetchUserGrowth}
29
- translationKeys={translationKeys}
30
- />
31
- </div>
32
- </div>
33
- </Widget>
34
- );
21
+
22
+ return (
23
+ <Widget
24
+ title={t(`${translationKeys}-admin::usage-statistics`)}
25
+ className="with-border-header"
26
+ >
27
+ <div className="flex flex-column gap-6">
28
+ <UserActivity loading={loading} data={data.userActivityData || []} t={t} />
29
+
30
+ <div className="flex flex-row flex-col-mobile gap-6">
31
+ <TopContributors data={data.topContributorsData || []} loading={loading} t={t} />
32
+ <UserGrowth
33
+ t={t}
34
+ loading={loadingUserGrowth}
35
+ data={data.userGrowthData || []}
36
+ fetchUserGrowth={fetchUserGrowth}
37
+ translationKeys={translationKeys}
38
+ />
39
+ </div>
40
+ </div>
41
+ </Widget>
42
+ );
35
43
  }
@@ -5,60 +5,64 @@ import KeyIndicatorsWidget from "./components/KeyIndicatorsWidget/index.jsx";
5
5
  import UserStatistics from "./components/UserStatistics/index.jsx";
6
6
 
7
7
  export default function AdminDashboard({
8
- actionWidgetConfig,
9
- loading,
10
- data,
11
- goTo,
12
- adminTranslationIdentifier,
13
- t,
14
- loadingUserGrowth,
15
- fetchUserGrowth = () => {},
8
+ actionWidgetConfig,
9
+ loading,
10
+ data,
11
+ goTo,
12
+ adminTranslationIdentifier,
13
+ t,
14
+ loadingUserGrowth,
15
+ fetchUserGrowth = () => {},
16
+ userGrowthData,
16
17
  }) {
17
- const { keyIndicatorsData } = data;
18
+ const keyIndicatorsCards = Array.isArray(data?.keyIndicatorsData)
19
+ ? data.keyIndicatorsData
20
+ : [];
18
21
 
19
- return (
20
- <div className="daf-analysis admin-dashboard">
21
- <Header title={t(`${adminTranslationIdentifier}::dashboard`)} />
22
- <div className="content">
23
- <div className="view-content">
24
- <div className="daf-analysis-layout">
25
- <div className="sections-cont w-pt">
26
- <section>
27
- {actionWidgetConfig.map((widgetConfig) => {
28
- return (
29
- <ActionWidget
30
- key={widgetConfig.title}
31
- {...widgetConfig}
32
- onClick={() => goTo(widgetConfig.goToPath)}
33
- title={t(`admin::${widgetConfig.title}`)}
34
- />
35
- );
36
- })}
37
- </section>
22
+ console.log("AdminDashboard:data", data);
38
23
 
39
- <section>
40
- <KeyIndicatorsWidget
41
- data={keyIndicatorsData}
42
- loading={loading}
43
- goTo={goTo}
44
- t={t}
45
- />
46
- </section>
24
+ return (
25
+ <div className="daf-analysis admin-dashboard">
26
+ <Header title={t(`${adminTranslationIdentifier}::dashboard`)} />
27
+ <div className="content">
28
+ <div className="view-content">
29
+ <div className="daf-analysis-layout">
30
+ <div className="sections-cont w-pt">
31
+ <section>
32
+ {actionWidgetConfig.map((widgetConfig) => (
33
+ <ActionWidget
34
+ key={widgetConfig.title}
35
+ {...widgetConfig}
36
+ onClick={() => goTo(widgetConfig.goToPath)}
37
+ title={t(`${widgetConfig.title}`)}
38
+ />
39
+ ))}
40
+ </section>
47
41
 
48
- <section>
49
- <UserStatistics
50
- data={data}
51
- loading={loading}
52
- t={t}
53
- loadingUserGrowth={loadingUserGrowth}
54
- fetchUserGrowth={fetchUserGrowth}
55
- translationKeys={adminTranslationIdentifier}
56
- ></UserStatistics>
57
- </section>
58
- </div>
59
- </div>
60
- </div>
61
- </div>
62
- </div>
63
- );
42
+ <section>
43
+ <KeyIndicatorsWidget
44
+ data={keyIndicatorsCards}
45
+ loading={loading}
46
+ goTo={goTo}
47
+ t={t}
48
+ />
49
+ </section>
50
+
51
+ <section>
52
+ <UserStatistics
53
+ data={{ ...data }}
54
+ userGrowthData={userGrowthData}
55
+ loading={loading}
56
+ t={t}
57
+ loadingUserGrowth={loadingUserGrowth}
58
+ fetchUserGrowth={fetchUserGrowth}
59
+ translationKeys={adminTranslationIdentifier}
60
+ />
61
+ </section>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ );
64
68
  }
@@ -1,77 +1,62 @@
1
1
  import React, { useMemo } from "react";
2
2
  import AdminDashboard from "../AdminDashboard/index.jsx";
3
3
 
4
- /**
5
- * Admin Dashboard Screen Wrapper
6
- * This is a ready-to-use route component that can be configured per application
7
- *
8
- * @param {Object} config - Application configuration
9
- * @param {string} config.appName - Application name for translations (e.g., "wazi", "tazama")
10
- * @param {Function} config.goTo - Navigation function (useNavigate)
11
- * @param {Function} config.t - Translation function (useTranslation)
12
- * @param {Function} config.getRedirectLink - Function to get redirect links
13
- * @param {Function} config.getActionWidgetsConfig - Function that returns action widgets config
14
- * @param {Function} config.getKeyIndicatorsConfig - Function that returns key indicators config
15
- * @param {Function} config.useWidgetFetch - Hook to fetch dashboard data
16
- */
17
4
  export default function AdminDashboardScreen({ config }) {
18
- const {
19
- appName = "app",
20
- goTo,
21
- t,
22
- getRedirectLink,
23
- getActionWidgetsConfig,
24
- getKeyIndicatorsConfig,
25
- useWidgetFetch,
26
- } = config;
5
+ const {
6
+ appName = "app",
7
+ goTo,
8
+ t,
9
+ getRedirectLink,
10
+ getActionWidgetsConfig,
11
+ getKeyIndicatorsConfig,
12
+ useWidgetFetch,
13
+ } = config;
27
14
 
28
- const {
29
- data,
30
- loading,
31
- userGrowthData,
32
- fetchUserGrowth,
33
- userGrowthDataLoading,
34
- } = useWidgetFetch();
15
+ const {
16
+ data,
17
+ loading,
18
+ userGrowthData,
19
+ fetchUserGrowth,
20
+ userGrowthDataLoading,
21
+ } = useWidgetFetch();
35
22
 
36
- const actionsWidgetsConfig = useMemo(
37
- () => getActionWidgetsConfig({ getRedirectLink }),
38
- [getRedirectLink]
39
- );
23
+ console.log({userGrowthData});
40
24
 
41
- const keyIndicatorsConfig = useMemo(
42
- () =>
43
- getKeyIndicatorsConfig({
44
- getRedirectLink,
45
- keyIndicatorsData: data?.keyInformation,
46
- }),
47
- [data?.keyInformation, getRedirectLink]
48
- );
25
+ const actionsWidgetsConfig = useMemo(
26
+ () => getActionWidgetsConfig({ getRedirectLink }),
27
+ [getRedirectLink, getActionWidgetsConfig]
28
+ );
49
29
 
50
- // Dummy data for top contributors if not provided
51
- const TOTAL_DUMMY = [
52
- { label: "Contributor 1", Score: 0 },
53
- { label: "Contributor 2", Score: 0 },
54
- { label: "Contributor 3", Score: 0 },
55
- { label: "Contributor 4", Score: 0 },
56
- { label: "Contributor 5", Score: 0 },
57
- ];
30
+ const keyIndicatorsConfig = useMemo(
31
+ () =>
32
+ getKeyIndicatorsConfig({
33
+ getRedirectLink,
34
+ keyIndicators: data?.keyIndicators ?? data?.keyInformation,
35
+ }),
36
+ [data?.keyIndicators, data?.keyInformation, getRedirectLink, getKeyIndicatorsConfig]
37
+ );
38
+
39
+ const safeUserActivity = Array.isArray(data?.data?.userActivity) ? data.data.userActivity : [];
40
+ const safeTopContributors = Array.isArray(data?.topContributors) ? data.topContributors : [];
41
+ const safeUserGrowth = Array.isArray(data?.userGrowthData) ? data.userGrowthData : [];
58
42
 
59
- return (
60
- <AdminDashboard
61
- t={t}
62
- loading={loading}
63
- goTo={goTo}
64
- actionWidgetConfig={actionsWidgetsConfig}
65
- loadingUserGrowth={userGrowthDataLoading}
66
- fetchUserGrowth={fetchUserGrowth}
67
- data={{
68
- keyIndicatorsData: keyIndicatorsConfig,
69
- userActivityData: data?.userActivity,
70
- topContributorsData: data?.topContributors || TOTAL_DUMMY,
71
- userGrowthData: userGrowthData,
72
- }}
73
- adminTranslationIdentifier={appName}
74
- />
75
- );
76
- }
77
43
 
44
+ return (
45
+ <AdminDashboard
46
+ t={t}
47
+ goTo={goTo}
48
+ loading={loading}
49
+ actionWidgetConfig={actionsWidgetsConfig}
50
+ loadingUserGrowth={userGrowthDataLoading}
51
+ fetchUserGrowth={fetchUserGrowth}
52
+ adminTranslationIdentifier={appName}
53
+ data={{
54
+ keyIndicatorsData: keyIndicatorsConfig,
55
+ userActivityData: safeUserActivity,
56
+ topContributorsData: safeTopContributors,
57
+ userGrowthData: safeUserGrowth,
58
+ data: data,
59
+ }}
60
+ />
61
+ );
62
+ }
@@ -0,0 +1,96 @@
1
+ import React, { useCallback } from "react";
2
+ import { message } from "antd";
3
+ import LocationTable from "../AdminTables/LocationTable/index.jsx";
4
+
5
+ /**
6
+ * Admin Location Screen Wrapper
7
+ * This is a ready-to-use route component that can be configured per application
8
+ *
9
+ * @param {Object} config - Application configuration
10
+ * @param {string} config.appName - Application name
11
+ * @param {string} config.module - Module identifier (APP constant)
12
+ * @param {Function} config.goTo - Navigation function
13
+ * @param {Function} config.t - Translation function
14
+ * @param {Object} config.location - Router location object
15
+ * @param {boolean} config.isMobile - Is mobile viewport
16
+ * @param {Function} config.getRedirectLink - Function to get redirect links
17
+ * @param {Object} config.AdminService - Admin service with methods
18
+ * @param {Object} config.options - Options object with categories, countries, etc.
19
+ * @param {Function} config.handleError - Error handling function
20
+ * @param {number} config.defaultPageSize - Default pagination page size
21
+ * @param {string} config.view - View identifier
22
+ * @param {string} config.headerTitle - Header title for the table
23
+ * @param {Array} config.breadcrumbs - Breadcrumbs configuration
24
+ * @param {any} config.refetchTrigger - Trigger to refetch data
25
+ * @param {Function} config.getData - Optional custom getData function
26
+ */
27
+ export default function AdminLocationScreen({ config }) {
28
+ const {
29
+ appName = "app",
30
+ module,
31
+ goTo,
32
+ t,
33
+ location,
34
+ isMobile,
35
+ getRedirectLink,
36
+ AdminService,
37
+ options,
38
+ handleError,
39
+ defaultPageSize = 20,
40
+ view,
41
+ headerTitle = "location",
42
+ breadcrumbs,
43
+ refetchTrigger,
44
+ getData
45
+ } = config;
46
+
47
+ const handleMergeLocations = useCallback(
48
+ async (mergeData) => {
49
+ try {
50
+ if (AdminService.mergeLocations) {
51
+ await AdminService.mergeLocations(mergeData);
52
+ message.success(t("Locations merged successfully"));
53
+ } else {
54
+ console.warn("AdminService.mergeLocations is not implemented");
55
+ message.success(t("Locations merged successfully"));
56
+ }
57
+ } catch (err) {
58
+ handleError?.(err);
59
+ }
60
+ },
61
+ [AdminService, t, handleError]
62
+ );
63
+
64
+ // Use custom getData if provided, otherwise use default AdminService.getLocations
65
+ const getLocations = useCallback((params) => {
66
+ if (getData) {
67
+ return getData(params);
68
+ }
69
+ return AdminService.getLocations(params);
70
+ }, [getData, AdminService]);
71
+
72
+ return (
73
+ <LocationTable
74
+ t={t}
75
+ goTo={goTo}
76
+ getRedirectLink={getRedirectLink}
77
+ location={location}
78
+ module={module}
79
+ headerTitle={headerTitle}
80
+ getData={getLocations}
81
+ isMobile={isMobile}
82
+ defaultPageSize={defaultPageSize}
83
+ view={view}
84
+ breadcrumbs={breadcrumbs}
85
+ mergeSubjectsFunction={handleMergeLocations}
86
+ refetchTrigger={refetchTrigger}
87
+ config={{
88
+ options: {
89
+ category: options?.category,
90
+ countries: options?.countries,
91
+ },
92
+ }}
93
+ />
94
+ );
95
+ }
96
+