datastake-daf 0.6.778 → 0.6.780

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 (25) hide show
  1. package/dist/components/index.js +403 -355
  2. package/dist/pages/index.js +2120 -235
  3. package/dist/utils/index.js +13 -0
  4. package/package.json +1 -1
  5. package/src/@daf/core/components/Dashboard/Map/ChainIcon/Markers/StakeholderMarker.js +9 -76
  6. package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +116 -8
  7. package/src/@daf/core/components/Dashboard/Map/ChainIcon/utils.js +73 -17
  8. package/src/@daf/core/components/Dashboard/Map/helper.js +1 -0
  9. package/src/@daf/core/components/Dashboard/Map/hook.js +64 -29
  10. package/src/@daf/core/components/Dashboard/Map/style.js +20 -5
  11. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/config.js +4 -2
  12. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/index.jsx +1 -0
  13. package/src/@daf/pages/Summary/Activities/PlantingCycle/components/PlantingLocations/index.jsx +60 -4
  14. package/src/@daf/pages/View/hooks/useCallToGetData.js +73 -0
  15. package/src/@daf/pages/View/hooks/usePrepareForm.js +86 -0
  16. package/src/@daf/pages/View/hooks/useSubmitSubject.js +40 -0
  17. package/src/@daf/pages/View/hooks/useViewActions.js +83 -0
  18. package/src/@daf/pages/View/hooks/useViewPermissions.js +75 -0
  19. package/src/@daf/pages/View/hooks/useViewUrlParams.js +93 -0
  20. package/src/@daf/pages/View/index.jsx +286 -0
  21. package/src/@daf/utils/object.js +3 -1
  22. package/src/pages.js +4 -1
  23. package/src/utils.js +1 -1
  24. package/dist/style/datastake/mapbox-gl.css +0 -330
  25. package/src/@daf/hooks/useViewFormUrlParams.js +0 -84
@@ -50,6 +50,7 @@ const AssociatedInformation = ({
50
50
  }) => {
51
51
  const [activeTab, setActiveTab] = useState(ACTIVITIES_TAB);
52
52
  const [search, setSearch] = useState('');
53
+
53
54
 
54
55
  const searchFields = useMemo(() => searchFieldsMap(activeTab), [activeTab, searchFieldsMap]);
55
56
 
@@ -1,6 +1,9 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { Widget, MineSiteMap } from '../../../../../../../../src/index.js';
3
3
  import { useWidgetFetch } from '../../../../../../hooks/useWidgetFetch.js';
4
+ import { convertDMS } from '../../../../../../../../src/helpers/Map';
5
+ import CustomIcon from '../../../../../../../../src/@daf/core/components/Icon/CustomIcon.jsx';
6
+ import { renderDateFormatted } from '../../../../../../../../src/helpers/Forms';
4
7
 
5
8
  const PlantingLocations = ({
6
9
  id,
@@ -76,6 +79,8 @@ const PlantingLocations = ({
76
79
 
77
80
  const color = "#15FFFFB2"
78
81
 
82
+ const locationName = matchingLocation?.name || locationCheckArrival.name || 'Planting Location';
83
+
79
84
  return {
80
85
  _id: locationCheckArrival._id || event._id || {},
81
86
  area: area,
@@ -83,13 +88,16 @@ const PlantingLocations = ({
83
88
  datastakeId: `LOC-${String(index + 1).padStart(9, '0')}`,
84
89
  gps: gps,
85
90
  id: matchingLocation?.id || locationCheckArrival._id || `event-${index}`,
86
- name: locationCheckArrival.name || matchingLocation?.name || `Event ${index + 1}`,
91
+ name: event.name || t("Activity Start"),
92
+ date: event.date,
87
93
  sources: 1,
88
- subTitle: locationCheckArrival.name || matchingLocation?.name || 'Planting Location',
94
+ subTitle: event.date ? renderDateFormatted(event.date, "DD MMM YY") : locationName,
95
+ plotName: locationName,
96
+ territoryTitle: locationName,
89
97
  type: 'Planting Location'
90
98
  };
91
99
  });
92
- }, [plantingLocationsData]);
100
+ }, [plantingLocationsData, t]);
93
101
 
94
102
  return (
95
103
  <section>
@@ -107,7 +115,55 @@ const PlantingLocations = ({
107
115
  onClickLink={() => { }}
108
116
  onFilterChange={() => { }}
109
117
  primaryLink
110
- renderTooltip={() => { }}
118
+ renderTooltipForLocation={(data) => {
119
+ const coordinates = data.gps?.latitude && data.gps?.longitude
120
+ ? convertDMS(data.gps.latitude, data.gps.longitude)
121
+ : null;
122
+
123
+ if (!coordinates) {
124
+ return [];
125
+ }
126
+
127
+ const iconColor = "#016C6E"; // Activity Start color
128
+
129
+ return [
130
+ {
131
+ label: t("Coordinates"),
132
+ value: (
133
+ <div style={{ display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'nowrap' }}>
134
+ {/* Latitude icon (vertical) */}
135
+ <div style={{ display: 'flex', alignItems: 'center' }}>
136
+ <CustomIcon
137
+ name="SpacingHeight"
138
+ width={14}
139
+ height={14}
140
+ color={iconColor}
141
+ />
142
+ <span style={{ fontWeight: 600, marginLeft: '4px' }}>{coordinates[0]}</span>
143
+ </div>
144
+ {/* Longitude icon (horizontal) */}
145
+ <div style={{ display: 'flex', alignItems: 'center' }}>
146
+ <CustomIcon
147
+ name="SpacingWidth"
148
+ width={14}
149
+ height={14}
150
+ color={iconColor}
151
+ />
152
+ <span style={{ fontWeight: 600, marginLeft: '4px' }}>{coordinates[1]}</span>
153
+ </div>
154
+ </div>
155
+ ),
156
+ },
157
+ ];
158
+ }}
159
+ renderTooltipForTerritory={(data) => {
160
+ return [
161
+ {
162
+ label: t("Plot Name"),
163
+ value: data.plotName || data.name || "--",
164
+ },
165
+ ];
166
+ }}
111
167
  renderTooltipTags={() => { }}
112
168
  type="location-territory"
113
169
  />
@@ -0,0 +1,73 @@
1
+ import { useRef, useEffect } from "react";
2
+ import { hasKeyInObject } from "../../../../@daf/utils/object.js";
3
+ import { getNkey } from "../../../../@daf/core/components/ViewForm/helper.js";
4
+
5
+ export const useCallToGetData = ({
6
+ namespaceConfig,
7
+ namespace,
8
+ allData,
9
+ id,
10
+ isSupported,
11
+ namespaceGet,
12
+ source,
13
+ version,
14
+ user,
15
+ setLoading,
16
+ APP,
17
+ }) => {
18
+ const isFirstRender = useRef(true);
19
+
20
+ const callToGetData = (_doCall = false) => {
21
+ const dKey = namespaceConfig.dataKey;
22
+ const nKey = `${APP}-${getNkey(namespace)}`;
23
+ const doCall = _doCall
24
+ ? true
25
+ : hasKeyInObject(allData, dKey) && hasKeyInObject(allData[dKey], nKey)
26
+ ? allData[dKey][nKey]?.data?.datastakeId !== id
27
+ : true;
28
+ if (doCall) {
29
+ if (isSupported) {
30
+ namespaceGet[namespace]();
31
+ }
32
+ }
33
+ };
34
+
35
+ useEffect(() => {
36
+ if (isFirstRender.current) {
37
+ isFirstRender.current = false;
38
+ return;
39
+ }
40
+ callToGetData(true);
41
+ }, [source, version]);
42
+
43
+ useEffect(() => {
44
+ callToGetData(true);
45
+ }, [id, namespace, user.language]);
46
+
47
+ const onStorageUpdate = (e) => {
48
+ const { key, newValue } = e;
49
+ if (key === `${id}-loading` && newValue) {
50
+ setLoading(newValue);
51
+ }
52
+ if (key === `${id}-updated` && newValue) {
53
+ setLoading(true);
54
+ callToGetData();
55
+ }
56
+ };
57
+
58
+ useEffect(() => {
59
+ window.addEventListener("storage", onStorageUpdate);
60
+ return () => {
61
+ window.removeEventListener("storage", onStorageUpdate);
62
+ };
63
+ }, []);
64
+
65
+ useEffect(() => {
66
+ setLoading(true);
67
+ }, [namespace]);
68
+
69
+
70
+ return {
71
+ callToGetData,
72
+ }
73
+ }
@@ -0,0 +1,86 @@
1
+ import { useState, useEffect } from "react";
2
+ import { hasKeyInObject } from "../../../../@daf/utils/object.js";
3
+
4
+ export const usePrepareForm = ({
5
+ namespaceConfig,
6
+ allData,
7
+ id,
8
+ namespace,
9
+ t,
10
+ mode,
11
+ APP,
12
+ viewConfig,
13
+ }) => {
14
+ const [form, setForm] = useState({});
15
+ const [data, setData] = useState({});
16
+ const [groups, setGroups] = useState({});
17
+ const [linkingForms, setLinkingForms] = useState({});
18
+ const [loading, setLoading] = useState(true);
19
+ const [notFound, setNotFound] = useState(false);
20
+
21
+ const prepareForm = (currentView) => {
22
+ const dKey = namespaceConfig.dataKey;
23
+ const nKey = `${APP}-${currentView}`;
24
+
25
+ if (hasKeyInObject(allData, dKey) && hasKeyInObject(allData[dKey], nKey)) {
26
+ const {
27
+ form = {},
28
+ data = {},
29
+ config = {},
30
+ linkingForms = {},
31
+ } = JSON.parse(JSON.stringify(allData[dKey][nKey] || {}));
32
+
33
+ if (data.datastakeId === id || id === "user") {
34
+ if (viewConfig.linkingSubjects.includes(namespace)) {
35
+ setForm({
36
+ ...form,
37
+ linking: {
38
+ position: 100,
39
+ excludeFromEdit: true,
40
+ label: t("Linked Subjects"),
41
+ template: "linkingSubjects",
42
+ },
43
+ });
44
+ } else {
45
+ setForm(form);
46
+ }
47
+ setData(data);
48
+ setGroups(config.groups || {});
49
+ setLinkingForms(linkingForms);
50
+ setLoading(false);
51
+ setNotFound(false);
52
+ } else if (!data.id) {
53
+ if (mode === "proxy") {
54
+ window.location.reload();
55
+ } else {
56
+ setLoading(false);
57
+ setNotFound(true);
58
+ }
59
+ }
60
+ }
61
+ };
62
+
63
+ const getCertainData = allData[namespaceConfig.dataKey];
64
+
65
+ useEffect(() => {
66
+ if(namespace && namespaceConfig) {
67
+ prepareForm(namespaceConfig.view);
68
+ }
69
+ }, [getCertainData, namespaceConfig]);
70
+
71
+ return {
72
+ form,
73
+ setForm,
74
+ data,
75
+ setData,
76
+ groups,
77
+ setGroups,
78
+ linkingForms,
79
+ setLinkingForms,
80
+ loading,
81
+ setLoading,
82
+ notFound,
83
+ setNotFound,
84
+ prepareForm,
85
+ }
86
+ }
@@ -0,0 +1,40 @@
1
+ import { useState, useCallback } from "react";
2
+
3
+ export const submitSubjectData = async (namespace, data, serviceMap) => {
4
+ const service = serviceMap[namespace];
5
+ if (!service) {
6
+ throw new Error(`No service found for namespace: ${namespace}`);
7
+ }
8
+
9
+ const response = await service.submitStep(
10
+ data,
11
+ data.datastakeId || data.id
12
+ );
13
+ return response.data;
14
+ };
15
+
16
+ export const useSubmitSubject = ({namespace, data, serviceMap}) => {
17
+ const [isDisabled, setIsDisabled] = useState(false);
18
+ const [loading, setLoading] = useState(false);
19
+ const [isPublished, setIsPublished] = useState(false);
20
+
21
+ const submitSubject = useCallback(async () => {
22
+ try {
23
+ setLoading(true);
24
+ const response = await submitSubjectData(namespace, data, serviceMap);
25
+ setIsDisabled(response.published);
26
+ setIsPublished(response.published);
27
+ } catch (error) {
28
+ console.error("Submit error:", error);
29
+ } finally {
30
+ setLoading(false);
31
+ }
32
+ }, [namespace, data]);
33
+
34
+ return {
35
+ submitSubject,
36
+ isDisabled,
37
+ submitLoading: loading,
38
+ isPublished,
39
+ };
40
+ };
@@ -0,0 +1,83 @@
1
+ import { useState, useEffect } from "react";
2
+
3
+ export const useViewActions = ({
4
+ namespace,
5
+ data,
6
+ isSupported,
7
+ canEdit,
8
+ versionUrl,
9
+ sourceUrl,
10
+ getEditLink,
11
+ submitSubject,
12
+ isDisabled,
13
+ setOpenRecordsModal,
14
+ goBackFromSource,
15
+ push,
16
+ getRedirectLink,
17
+ t,
18
+ viewConfig,
19
+ buttonActions,
20
+ }) => {
21
+ const [pageActions, setPageActions] = useState([]);
22
+ const [extraPageActions, setExtraPageActions] = useState([]);
23
+
24
+ useEffect(() => {
25
+ const actions = [];
26
+ const extraActions = [];
27
+
28
+ if (!isSupported) {
29
+ setPageActions([]);
30
+ setExtraPageActions([]);
31
+ return;
32
+ }
33
+
34
+ if (canEdit) {
35
+ if (viewConfig.namespacesWithoutActionButtons.includes(namespace)) {
36
+ if (viewConfig.editOnlyButton.includes(namespace)) {
37
+ if (versionUrl && sourceUrl) {
38
+ actions.push(buttonActions.createBackButton(t, goBackFromSource));
39
+ } else {
40
+ actions.push(buttonActions.createEditButton(t, getEditLink));
41
+ }
42
+ }
43
+ } else {
44
+ if (versionUrl && sourceUrl) {
45
+ actions.push(buttonActions.createBackButton(t, goBackFromSource));
46
+ } else {
47
+ actions.push(buttonActions.createSubmitButton(t, submitSubject, isDisabled, data));
48
+ actions.push(buttonActions.createEditButton(t, getEditLink));
49
+ // actions.push(createRecordsButton(t, setOpenRecordsModal));
50
+ }
51
+ }
52
+ }
53
+
54
+ if (viewConfig.summaryNamespaces.includes(namespace)) {
55
+ extraActions.push(
56
+ buttonActions.createSummaryButton(t, namespace, data, push, getRedirectLink)
57
+ );
58
+ extraActions.push(
59
+ buttonActions.createRecordsButton(t, setOpenRecordsModal)
60
+ );
61
+ }
62
+
63
+ setPageActions(actions);
64
+ setExtraPageActions(extraActions);
65
+ }, [
66
+ namespace,
67
+ data,
68
+ isSupported,
69
+ canEdit,
70
+ versionUrl,
71
+ sourceUrl,
72
+ isDisabled,
73
+ t,
74
+ getEditLink,
75
+ submitSubject,
76
+ goBackFromSource,
77
+ setOpenRecordsModal,
78
+ push,
79
+ getRedirectLink,
80
+ ]);
81
+
82
+ return { pageActions, extraPageActions };
83
+ };
@@ -0,0 +1,75 @@
1
+ import { useMemo, useEffect } from "react";
2
+
3
+ export const useViewPermissions = ({
4
+ data,
5
+ id,
6
+ namespaceOverrides = {
7
+ supportedNamespaces: {},
8
+ canEdit: {},
9
+ },
10
+ namespace,
11
+ user,
12
+ push,
13
+ getRedirectLink,
14
+ namespaceConfig,
15
+ APP,
16
+ viewConfig,
17
+ }) => {
18
+ const baseNamespaceKeys = Object.keys(namespaceConfig);
19
+
20
+ const baseSupportedNamespaces = baseNamespaceKeys.reduce((acc, key) => {
21
+ acc[key] = () => true;
22
+ return acc;
23
+ }, {});
24
+
25
+ const isSupportedNamespaces = useMemo(
26
+ () => ({
27
+ ...baseSupportedNamespaces,
28
+ ...namespaceOverrides.supportedNamespaces,
29
+ }),
30
+ [data, id],
31
+ );
32
+
33
+ const isSupported =
34
+ typeof isSupportedNamespaces[namespace] === "function"
35
+ ? isSupportedNamespaces[namespace]() &&
36
+ viewConfig.supportedNamespaces[APP] &&
37
+ namespaceConfig.supportedNamespaces[APP].includes(namespace)
38
+ : namespaceConfig.supportedNamespaces[APP] && namespaceConfig.supportedNamespaces[APP].includes(namespace);
39
+
40
+
41
+ const isUserData = () => {
42
+ return data && data.authorId && user?.company?.id === data.authorId;
43
+ };
44
+
45
+ const canEdit = useMemo(() => {
46
+ const baseCanEditAction = baseNamespaceKeys.reduce((acc, key) => {
47
+ acc[key] = () => isUserData();
48
+ return acc;
49
+ }, {});
50
+
51
+ const canEditAction = {
52
+ ...baseCanEditAction,
53
+ ...namespaceOverrides.canEdit,
54
+ };
55
+
56
+ return canEditAction[namespace] ? canEditAction[namespace]() : false;
57
+ }, [namespace, data, user]);
58
+
59
+ useEffect(() => {
60
+ if (data) {
61
+ if (typeof isSupportedNamespaces[namespace] === "function") {
62
+ if (!isSupportedNamespaces[namespace]()) {
63
+ push(getRedirectLink(`/app`));
64
+ }
65
+ }
66
+ }
67
+ }, [data, namespace]);
68
+
69
+ return {
70
+ isSupportedNamespaces,
71
+ canEdit,
72
+ isSupported,
73
+ }
74
+
75
+ }
@@ -0,0 +1,93 @@
1
+ import { useState, useEffect, useCallback, useMemo } from "react";
2
+
3
+ export const useViewUrlParams = ({
4
+ params,
5
+ push,
6
+ pathname,
7
+ search,
8
+ searchParams,
9
+ setSearchParams,
10
+ }) => {
11
+ const [namespace, setNamespace] = useState(params?.namespace);
12
+ const [id, setId] = useState(params?.id);
13
+ const [group, setGroup] = useState(params?.group);
14
+ const [subsection, setSubSection] = useState(params?.subsection);
15
+ const sourceUrl = searchParams.get("source");
16
+ const versionUrl = searchParams.get("version");
17
+ const [source, setSource] = useState(sourceUrl || null);
18
+ const [version, setVersion] = useState(versionUrl || null);
19
+
20
+ useEffect(() => {
21
+ if ((id && params.id !== id) || (namespace && namespace !== params.namespace)) {
22
+ setGroup(undefined);
23
+ setSubSection(undefined);
24
+ // setSubGroup(undefined);
25
+ } else {
26
+ setGroup(params.group);
27
+ setSubSection(params.subsection);
28
+ // setSubGroup(params.subgroup);
29
+ }
30
+ setNamespace(params.namespace);
31
+ setId(params.id);
32
+ }, [params]);
33
+
34
+ useEffect(() => {
35
+ if (source && version) {
36
+ const newParams = new URLSearchParams(searchParams);
37
+ newParams.set("source", source);
38
+ newParams.set("version", version);
39
+ setSearchParams(newParams);
40
+ }
41
+ }, [source, version]);
42
+
43
+ const goBackFromSource = useCallback(() => {
44
+ const params = new URLSearchParams(searchParams);
45
+ params.delete("source");
46
+ params.delete("version");
47
+ setSearchParams(params);
48
+ setVersion(null);
49
+ setSource(null);
50
+ }, [searchParams, setSearchParams]);
51
+
52
+ const getEditLink = useCallback((srcId) => {
53
+ const r = new RegExp(`\/view\/`);
54
+ const [previous, extra] = pathname.split(r);
55
+
56
+ if (srcId) {
57
+ push(`${previous}/edit/${extra}?sourceId=${srcId}`);
58
+ return;
59
+ }
60
+
61
+ if (search) {
62
+ push(`${previous}/edit/${extra}${search}`);
63
+ } else {
64
+ push(`${previous}/edit/${extra}`);
65
+ }
66
+ }, [pathname, search, push]);
67
+
68
+ const match = useMemo(
69
+ () => ({
70
+ params,
71
+ path: pathname,
72
+ }),
73
+ [params, pathname],
74
+ );
75
+
76
+ return {
77
+ namespace,
78
+ id,
79
+ group,
80
+ subsection,
81
+ params,
82
+ source,
83
+ setSource,
84
+ sourceUrl,
85
+ version,
86
+ setVersion,
87
+ versionUrl,
88
+ goBackFromSource,
89
+ getEditLink,
90
+ match,
91
+ search,
92
+ };
93
+ }