@dative-gpi/foundation-core-components 1.0.45 → 1.0.47

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 (42) hide show
  1. package/components/autocompletes/FSAutocompleteChart.vue +2 -1
  2. package/components/autocompletes/FSAutocompleteDashboard.vue +1 -1
  3. package/components/autocompletes/FSAutocompleteRole.vue +2 -1
  4. package/components/customProperties/FSMetaField.vue +2 -1
  5. package/components/customProperties/FSMetaValue.vue +2 -1
  6. package/components/customProperties/helpers.ts +2 -1
  7. package/components/entities/FSBaseEntitiesList.vue +50 -0
  8. package/components/entities/FSDialogSelectEntities.vue +171 -0
  9. package/components/entities/FSEntityField.vue +143 -0
  10. package/components/entities/FSSimpleEntitiesList.vue +100 -0
  11. package/components/lists/authTokens/FSBaseAuthTokensList.vue +76 -0
  12. package/components/lists/dashboards/FSBaseDashboardsList.vue +222 -0
  13. package/components/lists/dashboards/FSSimpleDashboardsList.vue +63 -0
  14. package/components/lists/deviceOrganisations/FSBaseDeviceOrganisationsList.vue +260 -0
  15. package/components/lists/deviceOrganisations/FSSimpleDeviceOrganisationsList.vue +44 -0
  16. package/components/lists/folders/FSBaseFoldersList.vue +241 -0
  17. package/components/lists/folders/FSSimpleFoldersList.vue +44 -0
  18. package/components/lists/groups/FSBaseGroupsList.vue +119 -0
  19. package/components/lists/groups/FSSimpleGroupsList.vue +44 -0
  20. package/components/lists/locations/FSBaseLocationsList.vue +79 -0
  21. package/components/lists/locations/FSSimpleLocationsList.vue +44 -0
  22. package/components/lists/models/FSSimpleModelsList.vue +44 -0
  23. package/components/lists/userOrganisations/FSBaseUserOrganisationsList.vue +158 -0
  24. package/components/lists/userOrganisations/FSSimpleUserOrganisationsList.vue +45 -0
  25. package/components/selects/FSAggregationSelector.vue +1 -1
  26. package/components/selects/FSAxisTypeSelector.vue +1 -1
  27. package/components/selects/FSDisplayAsSelector.vue +1 -1
  28. package/components/selects/FSFilterTypeSelector.vue +1 -1
  29. package/components/selects/FSHeatmapRuleSelector.vue +1 -1
  30. package/components/selects/FSOperationOnSelector.vue +1 -1
  31. package/components/selects/FSPlanningTypeSelector.vue +1 -1
  32. package/components/selects/FSPlotPerSelector.vue +1 -1
  33. package/components/selects/{FSSelectSelectedEntities.vue → FSSelectEntityType.vue} +12 -12
  34. package/components/selects/FSSerieTypeSelector.vue +1 -1
  35. package/package.json +7 -7
  36. package/utils/charts.ts +2 -1
  37. package/utils/dashboards.ts +30 -2
  38. package/utils/index.ts +2 -1
  39. package/utils/permissions.ts +80 -0
  40. package/utils/roles.ts +1 -1
  41. package/utils/tables.ts +41 -0
  42. package/utils/users.ts +1 -1
@@ -0,0 +1,241 @@
1
+ <template>
2
+ <FSLoadDataTable
3
+ v-if="fetchingFolders || fetchingDashboardOrganisations || fetchingDashboardShallows"
4
+ />
5
+ <FSDataTable
6
+ v-else
7
+ :items="items"
8
+ :item-to="$props.itemTo"
9
+ :tableCode="$props.tableCode"
10
+ :modelValue="selecteds"
11
+ @update:modelValue="onSelect"
12
+ v-bind="$attrs"
13
+ >
14
+ <template
15
+ v-for="(_, name) in $slots"
16
+ v-slot:[name]="slotData"
17
+ >
18
+ <slot
19
+ :name="name"
20
+ v-bind="slotData"
21
+ />
22
+ </template>
23
+
24
+ <template
25
+ #item.main="{ item }"
26
+ >
27
+ <FSIcon
28
+ v-if="item.id === mainOrganisationDashboardId"
29
+ >
30
+ mdi-account-group-outline
31
+ </FSIcon>
32
+ <FSIcon
33
+ v-if="item.id === mainUserDashboardId"
34
+ >
35
+ mdi-home
36
+ </FSIcon>
37
+ </template>
38
+
39
+ <template
40
+ #item.icon="{ item }"
41
+ >
42
+ <FSIcon>
43
+ {{ item.icon }}
44
+ </FSIcon>
45
+ </template>
46
+
47
+ <template
48
+ #item.tile="{ item, toggleSelect }"
49
+ >
50
+ <FSFolderTileUI
51
+ v-if="item.type == FoldersListType.Folder"
52
+ :bottomColor="item.colors"
53
+ v-bind="item"
54
+ :modelValue="isSelected(item.id)"
55
+ @update:modelValue="toggleSelect(item)"
56
+ :to="$props.itemTo && $props.itemTo(item)"
57
+ />
58
+ <FSDashboardOrganisationTileUI
59
+ v-if="item.type == FoldersListType.Dashboard && item.dashboardType == DashboardType.Organisation"
60
+ :bottomColor="item.colors"
61
+ :modelValue="isSelected(item.id)"
62
+ @update:modelValue="toggleSelect(item)"
63
+ :to="$props.itemTo && $props.itemTo(item)"
64
+ v-bind="item"
65
+ />
66
+ <FSDashboardShallowTileUI
67
+ v-if="item.type == FoldersListType.Dashboard && item.dashboardType == DashboardType.Shallow"
68
+ :bottomColor="item.colors"
69
+ :modelValue="isSelected(item.id)"
70
+ @update:modelValue="toggleSelect(item)"
71
+ :to="$props.itemTo && $props.itemTo(item)"
72
+ v-bind="item"
73
+ />
74
+ </template>
75
+ </FSDataTable>
76
+ </template>
77
+
78
+ <script lang="ts">
79
+ import type { PropType} from "vue";
80
+ import type { RouteLocation } from "vue-router";
81
+ import { computed, defineComponent, onMounted, ref, watch } from "vue";
82
+ import _ from "lodash";
83
+
84
+ import { useOrganisation } from "@dative-gpi/foundation-shared-services/composables";
85
+ import { useDashboardOrganisations, useFolders, useDashboardShallows, useAppOrganisationId, useCurrentUserOrganisation } from "@dative-gpi/foundation-core-services/composables";
86
+
87
+ import { DashboardType } from "@dative-gpi/foundation-shared-domain/enums";
88
+ import { FoldersListType, type FoldersListItem } from "@dative-gpi/foundation-core-components/utils";
89
+ import type { FolderFilters, DashboardOrganisationFilters, DashboardShallowFilters, DashboardInfos } from "@dative-gpi/foundation-core-domain/models";
90
+
91
+ import FSDataTable from "../FSDataTable.vue";
92
+ import FSLoadDataTable from "@dative-gpi/foundation-shared-components/components/lists/FSLoadDataTable.vue";
93
+ import FSIcon from "@dative-gpi/foundation-shared-components/components/FSIcon.vue";
94
+ import FSFolderTileUI from "@dative-gpi/foundation-shared-components/components/tiles/FSFolderTileUI.vue";
95
+ import FSDashboardOrganisationTileUI from "@dative-gpi/foundation-shared-components/components/tiles/FSDashboardOrganisationTileUI.vue";
96
+ import FSDashboardShallowTileUI from "@dative-gpi/foundation-shared-components/components/tiles/FSDashboardShallowTileUI.vue";
97
+
98
+
99
+ export default defineComponent({
100
+ name: "FSBaseFoldersList",
101
+ components: {
102
+ FSDataTable,
103
+ FSLoadDataTable,
104
+ FSIcon,
105
+ FSFolderTileUI,
106
+ FSDashboardOrganisationTileUI,
107
+ FSDashboardShallowTileUI
108
+ },
109
+ props: {
110
+ foldersFilters: {
111
+ type: Object as PropType<FolderFilters>,
112
+ default: undefined,
113
+ required: false
114
+ },
115
+ dashboardOrganisationsFilters: {
116
+ type: Object as PropType<DashboardOrganisationFilters>,
117
+ default: undefined,
118
+ required: false
119
+ },
120
+ dashboardShallowsFilters: {
121
+ type: Object as PropType<DashboardShallowFilters>,
122
+ default: undefined,
123
+ required: false
124
+ },
125
+ modelValue: {
126
+ type: Array as PropType<string[]>,
127
+ required: false,
128
+ default: () => []
129
+ },
130
+ itemTo: {
131
+ type: Function as PropType<(item: DashboardInfos) => Partial<RouteLocation>>,
132
+ required: false
133
+ },
134
+ tableCode: {
135
+ type: String,
136
+ required: true
137
+ }
138
+ },
139
+ emits: ["update:modelValue", "update:type", "update:dashboard-type"],
140
+ setup(props, { emit }) {
141
+
142
+ const { fetch: fetchUserOrganisation, entity: userOrganisation } = useCurrentUserOrganisation();
143
+ const { entity: organisation, get: getOrganisation } = useOrganisation();
144
+ const { organisationId } = useAppOrganisationId();
145
+
146
+ const { entities: dashboardOrganisations, fetching: fetchingDashboardOrganisations, getMany: getManyDashboardOrganisations } = useDashboardOrganisations();
147
+ const { entities: dashboardShallows, fetching: fetchingDashboardShallows, getMany: getManyDashboardShallows } = useDashboardShallows();
148
+ const { entities: folders, fetching: fetchingFolders, getMany: getManyFolders } = useFolders();
149
+
150
+ const selecteds = ref<string[]>([]);
151
+
152
+ const mainUserDashboardId = computed(() => {
153
+ return userOrganisation.value?.mainDashboardId;
154
+ });
155
+
156
+ const mainOrganisationDashboardId = computed(() => {
157
+ return organisation.value?.mainDashboardId;
158
+ });
159
+
160
+ const items = computed((): FoldersListItem[] => {
161
+ return [
162
+ ...folders.value.map(g => ({
163
+ ...g,
164
+ type: FoldersListType.Folder,
165
+ dashboardType: DashboardType.None
166
+ })) as FoldersListItem[],
167
+ ..._.sortBy([
168
+ ...dashboardOrganisations.value.map(d => ({
169
+ ...d,
170
+ type: FoldersListType.Dashboard,
171
+ dashboardType: DashboardType.Organisation
172
+ })) as FoldersListItem[],
173
+ ...dashboardShallows.value.map(d => ({
174
+ ...d,
175
+ type: FoldersListType.Dashboard,
176
+ dashboardType: DashboardType.Shallow
177
+ })) as FoldersListItem[]
178
+ ], d => d.label)
179
+ ]
180
+ })
181
+
182
+ const onSelect = (values: string[]) => {
183
+ selecteds.value = values;
184
+ const selectedItems = items.value.filter(i => selecteds.value!.includes(i.id));
185
+ emit("update:dashboard-type", selectedItems.map(i => i.dashboardType));
186
+ emit("update:modelValue", selectedItems.map(i => i.id));
187
+ emit("update:type", selectedItems.map(i => i.type));
188
+ };
189
+
190
+ const isSelected = (id: string) => {
191
+ return selecteds.value?.includes(id);
192
+ };
193
+
194
+ onMounted(() => {
195
+ fetchUserOrganisation();
196
+ })
197
+
198
+ watch(() => organisationId.value, () => {
199
+ if (organisationId.value) {
200
+ getOrganisation(organisationId.value);
201
+ }
202
+ }, { immediate: true });
203
+
204
+ watch(() => props.foldersFilters, (next, previous) => {
205
+ if ((!next && !previous) || !_.isEqual(next, previous)) {
206
+ getManyFolders(props.foldersFilters, f => f.parentId == props.foldersFilters?.parentId);
207
+ }
208
+ }, { immediate: true });
209
+
210
+ watch(() => props.dashboardOrganisationsFilters, (next, previous) => {
211
+ if ((!next && !previous) || !_.isEqual(next, previous)) {
212
+ getManyDashboardOrganisations(props.dashboardOrganisationsFilters, f => f.folderId == props.foldersFilters?.parentId);
213
+ }
214
+ }, { immediate: true });
215
+
216
+ watch(() => props.dashboardShallowsFilters, (next, previous) => {
217
+ if ((!next && !previous) || !_.isEqual(next, previous)) {
218
+ getManyDashboardShallows(props.dashboardShallowsFilters, f => f.folderId == props.foldersFilters?.parentId);
219
+ }
220
+ }, { immediate: true });
221
+
222
+ watch(() => props.modelValue, (next) => {
223
+ selecteds.value = next;
224
+ }, { immediate: true });
225
+
226
+ return {
227
+ fetchingDashboardOrganisations,
228
+ fetchingDashboardShallows,
229
+ fetchingFolders,
230
+ mainOrganisationDashboardId,
231
+ mainUserDashboardId,
232
+ selecteds,
233
+ items,
234
+ onSelect,
235
+ isSelected,
236
+ FoldersListType,
237
+ DashboardType
238
+ };
239
+ }
240
+ });
241
+ </script>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <FSSimpleList
3
+ :items="folders"
4
+ :loading="fetching"
5
+ v-bind="$attrs"
6
+ />
7
+ </template>
8
+
9
+ <script lang="ts">
10
+ import { defineComponent, type PropType, watch } from "vue";
11
+
12
+ import type { FolderFilters } from "@dative-gpi/foundation-core-domain/models";
13
+ import { useFolders } from "@dative-gpi/foundation-core-services/composables";
14
+
15
+ import FSSimpleList from "@dative-gpi/foundation-shared-components/components/lists/FSSimpleList.vue";
16
+
17
+ export default defineComponent({
18
+ name: "FSSimpleFoldersList",
19
+ components: {
20
+ FSSimpleList,
21
+ },
22
+ props: {
23
+ folderFilters: {
24
+ type: Object as PropType<FolderFilters>,
25
+ required: false,
26
+ default: () => ({})
27
+ }
28
+ },
29
+ setup(props){
30
+ const { entities: folders, getMany, fetching } = useFolders();
31
+
32
+ const fetch = () => {
33
+ getMany(props.folderFilters);
34
+ }
35
+
36
+ watch(() => props.folderFilters, fetch, { immediate: true });
37
+
38
+ return {
39
+ folders,
40
+ fetching
41
+ }
42
+ }
43
+ });
44
+ </script>
@@ -0,0 +1,119 @@
1
+ <template>
2
+ <FSDataTable
3
+ :loading="fetchingGroups"
4
+ :items="groups"
5
+ :tableCode="$props.tableCode"
6
+ :modelValue="$props.modelValue"
7
+ @update:modelValue="$emit('update:modelValue', $event)"
8
+ v-bind="$attrs"
9
+ >
10
+ <template
11
+ v-for="(_, name) in $slots"
12
+ v-slot:[name]="slotData"
13
+ >
14
+ <slot
15
+ :name="name"
16
+ v-bind="slotData"
17
+ />
18
+ </template>
19
+ <template
20
+ #item.imageId="{ item }"
21
+ >
22
+ <FSImage
23
+ v-if="item.imageId"
24
+ height="38px"
25
+ width="38px"
26
+ :imageId="item.imageId"
27
+ />
28
+ </template>
29
+ <template
30
+ #item.icon="{ item }"
31
+ >
32
+ <FSIcon>
33
+ {{ item.icon }}
34
+ </FSIcon>
35
+ </template>
36
+ <template
37
+ #item.tags="{ item }"
38
+ >
39
+ <FSTagGroup
40
+ variant="slide"
41
+ :editable="false"
42
+ :tags="item.tags"
43
+ />
44
+ </template>
45
+ <template
46
+ #item.tile="{ item, toggleSelect }"
47
+ >
48
+ <FSGroupTileUI
49
+ :modelValue="isSelected(item.id)"
50
+ @update:modelValue="toggleSelect(item)"
51
+ v-bind="item"
52
+ />
53
+ </template>
54
+ </FSDataTable>
55
+ </template>
56
+
57
+ <script lang="ts">
58
+ import { defineComponent, watch } from "vue";
59
+ import type { PropType} from "vue";
60
+ import _ from "lodash";
61
+
62
+ import type { GroupFilters } from "@dative-gpi/foundation-core-domain/models";
63
+ import { useGroups } from "@dative-gpi/foundation-core-services/composables";
64
+
65
+ import FSDataTable from "../FSDataTable.vue";
66
+
67
+ import FSIcon from "@dative-gpi/foundation-shared-components/components/FSIcon.vue";
68
+ import FSImage from "@dative-gpi/foundation-shared-components/components/FSImage.vue";
69
+ import FSTagGroup from "@dative-gpi/foundation-shared-components/components/FSTagGroup.vue";
70
+ import FSGroupTileUI from "@dative-gpi/foundation-shared-components/components/tiles/FSGroupTileUI.vue";
71
+
72
+
73
+ export default defineComponent({
74
+ name: "FSBaseGroupsList",
75
+ components: {
76
+ FSDataTable,
77
+ FSImage,
78
+ FSIcon,
79
+ FSGroupTileUI,
80
+ FSTagGroup
81
+ },
82
+ props: {
83
+ groupsFilters: {
84
+ type: Object as PropType<GroupFilters>,
85
+ required: false,
86
+ default: null
87
+ },
88
+ modelValue: {
89
+ type: Array as PropType<string[]>,
90
+ required: false,
91
+ default: () => []
92
+ },
93
+ tableCode: {
94
+ type: String,
95
+ required: true
96
+ }
97
+ },
98
+ emits: ["update:modelValue"],
99
+ setup(props) {
100
+ const { getMany: fetchGroups, fetching: fetchingGroups, entities: groups } = useGroups();
101
+
102
+ const isSelected = (id: string) => {
103
+ return props.modelValue.includes(id);
104
+ };
105
+
106
+ watch(() => props.groupsFilters, (next, previous) => {
107
+ if ((!next && !previous) || !_.isEqual(next, previous)) {
108
+ fetchGroups(props.groupsFilters);
109
+ }
110
+ }, { immediate: true });
111
+
112
+ return {
113
+ fetchingGroups,
114
+ groups,
115
+ isSelected
116
+ };
117
+ }
118
+ });
119
+ </script>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <FSSimpleList
3
+ :items="groups"
4
+ :loading="fetching"
5
+ v-bind="$attrs"
6
+ />
7
+ </template>
8
+
9
+ <script lang="ts">
10
+ import { defineComponent, type PropType, watch } from "vue";
11
+
12
+ import type { GroupFilters } from "@dative-gpi/foundation-core-domain/models";
13
+ import { useGroups } from "@dative-gpi/foundation-core-services/composables";
14
+
15
+ import FSSimpleList from "@dative-gpi/foundation-shared-components/components/lists/FSSimpleList.vue";
16
+
17
+ export default defineComponent({
18
+ name: "FSSimpleGroupsList",
19
+ components: {
20
+ FSSimpleList,
21
+ },
22
+ props: {
23
+ groupFilters: {
24
+ type: Object as PropType<GroupFilters>,
25
+ required: false,
26
+ default: () => ({})
27
+ }
28
+ },
29
+ setup(props){
30
+ const { entities: groups, getMany, fetching } = useGroups();
31
+
32
+ const fetch = () => {
33
+ getMany(props.groupFilters);
34
+ }
35
+
36
+ watch(() => props.groupFilters, fetch, { immediate: true });
37
+
38
+ return {
39
+ groups,
40
+ fetching
41
+ }
42
+ }
43
+ });
44
+ </script>
@@ -0,0 +1,79 @@
1
+ <template>
2
+ <FSDataTable
3
+ mode="table"
4
+ :loading="fetchingLocations"
5
+ :disableIterator="true"
6
+ :items="locations"
7
+ :tableCode="tableCode"
8
+ :modelValue="$props.modelValue"
9
+ @update:modelValue="$emit('update:modelValue', $event)"
10
+ v-bind="$attrs"
11
+ >
12
+ <template
13
+ v-for="(_, name) in $slots"
14
+ v-slot:[name]="slotData"
15
+ >
16
+ <slot
17
+ :name="name"
18
+ v-bind="slotData"
19
+ />
20
+ </template>
21
+ <template
22
+ #item.icon="{ item }"
23
+ >
24
+ <FSIcon >
25
+ {{ item.icon }}
26
+ </FSIcon>
27
+ </template>
28
+ </FSDataTable>
29
+ </template>
30
+
31
+ <script lang="ts">
32
+ import { defineComponent, type PropType, watch } from "vue";
33
+ import _ from "lodash";
34
+
35
+ import { useLocations } from "@dative-gpi/foundation-core-services/composables";
36
+ import type { LocationFilters } from "@dative-gpi/foundation-core-domain/models";
37
+
38
+ import FSDataTable from "../FSDataTable.vue";
39
+ import FSIcon from "@dative-gpi/foundation-shared-components/components/FSIcon.vue";
40
+
41
+ export default defineComponent({
42
+ name: "BaseLocationsList",
43
+ components: {
44
+ FSDataTable,
45
+ FSIcon
46
+ },
47
+ props: {
48
+ locationFilters: {
49
+ type: Object as PropType<LocationFilters>,
50
+ required: false,
51
+ default: null
52
+ },
53
+ modelValue: {
54
+ type: Array as PropType<string[]>,
55
+ default: () => [],
56
+ required: false
57
+ },
58
+ tableCode: {
59
+ type: String,
60
+ required: true
61
+ }
62
+ },
63
+ emits: ["update:modelValue"],
64
+ setup(props) {
65
+ const { getMany: fetchLocations, fetching: fetchingLocations, entities: locations } = useLocations();
66
+
67
+ watch(() => props.locationFilters, (next, previous) => {
68
+ if ((!next && !previous) || !_.isEqual(next, previous)) {
69
+ fetchLocations(props.locationFilters);
70
+ }
71
+ }, { immediate: true });
72
+
73
+ return {
74
+ fetchingLocations,
75
+ locations
76
+ };
77
+ }
78
+ });
79
+ </script>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <FSSimpleList
3
+ :items="locations"
4
+ :loading="fetching"
5
+ v-bind="$attrs"
6
+ />
7
+ </template>
8
+
9
+ <script lang="ts">
10
+ import { defineComponent, type PropType, watch } from "vue";
11
+
12
+ import type { LocationFilters } from "@dative-gpi/foundation-core-domain/models";
13
+ import { useLocations } from "@dative-gpi/foundation-core-services/composables";
14
+
15
+ import FSSimpleList from "@dative-gpi/foundation-shared-components/components/lists/FSSimpleList.vue";
16
+
17
+ export default defineComponent({
18
+ name: "FSSimpleLocationsList",
19
+ components: {
20
+ FSSimpleList,
21
+ },
22
+ props: {
23
+ locationFilters: {
24
+ type: Object as PropType<LocationFilters>,
25
+ required: false,
26
+ default: () => ({})
27
+ }
28
+ },
29
+ setup(props){
30
+ const { entities: locations, getMany, fetching } = useLocations();
31
+
32
+ const fetch = () => {
33
+ getMany(props.locationFilters);
34
+ }
35
+
36
+ watch(() => props.locationFilters, fetch, { immediate: true });
37
+
38
+ return {
39
+ locations,
40
+ fetching
41
+ }
42
+ }
43
+ });
44
+ </script>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <FSSimpleList
3
+ :items="models"
4
+ :loading="fetching"
5
+ v-bind="$attrs"
6
+ />
7
+ </template>
8
+
9
+ <script lang="ts">
10
+ import { defineComponent, type PropType, watch } from "vue";
11
+
12
+ import type { ModelFilters } from "@dative-gpi/foundation-core-domain/models";
13
+ import { useModels } from "@dative-gpi/foundation-core-services/composables";
14
+
15
+ import FSSimpleList from "@dative-gpi/foundation-shared-components/components/lists/FSSimpleList.vue";
16
+
17
+ export default defineComponent({
18
+ name: "FSSimpleModelsList",
19
+ components: {
20
+ FSSimpleList,
21
+ },
22
+ props: {
23
+ modelFilters: {
24
+ type: Object as PropType<ModelFilters>,
25
+ required: false,
26
+ default: () => ({})
27
+ }
28
+ },
29
+ setup(props){
30
+ const { entities: models, getMany, fetching } = useModels();
31
+
32
+ const fetch = () => {
33
+ getMany(props.modelFilters);
34
+ }
35
+
36
+ watch(() => props.modelFilters, fetch, { immediate: true });
37
+
38
+ return {
39
+ models,
40
+ fetching
41
+ }
42
+ }
43
+ });
44
+ </script>