Skip to content

Commit a0cbe77

Browse files
authored
Merge pull request #117 from CivicDataLab/112-implement-auto-save-in-dam
Implement Auto Save Mechanism in access model creation
2 parents 2b08420 + 966c32e commit a0cbe77

4 files changed

Lines changed: 113 additions & 64 deletions

File tree

app/[locale]/dashboard/organization/[organizationId]/dataset/[id]/edit/components/AccessModelForm.tsx

Lines changed: 71 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import {
1717
} from 'opub-ui';
1818

1919
import { GraphQL } from '@/lib/api';
20+
import { cn } from '@/lib/utils';
2021
import { Icons } from '@/components/icons';
22+
import styles from '../edit.module.scss';
2123
import ResourceSelector from './ResourceSelector';
2224

2325
interface AccessModelProps {
@@ -131,6 +133,9 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
131133
resources: [],
132134
accessModelId: '',
133135
});
136+
const [previousAccessModelData, setPreviousAccessModelData] =
137+
useState(accessModelData);
138+
134139
const [selectedResources, setSelectedResources] = useState<string[]>([]);
135140
const [showSelectAll, setShowSelectAll] = useState(false);
136141

@@ -161,7 +166,7 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
161166

162167
accessModelDetailsRefetch();
163168
// Update accessModelData with the received data
164-
setAccessModelData({
169+
const newData = {
165170
dataset: params.id,
166171
name: name,
167172
description: description,
@@ -171,7 +176,10 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
171176
resource: resource.resource.id,
172177
fields: resource.fields.map((field: any) => +field.id),
173178
})),
174-
});
179+
};
180+
181+
setAccessModelData(newData);
182+
setPreviousAccessModelData(newData);
175183

176184
// Update selectedResources and selectedFields based on modelResources
177185
const selectedResourcesIds = modelResources.map((resource: any) => ({
@@ -203,13 +211,14 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
203211
setSelectedFields(resourceDetails);
204212
const newResources = resourceDetails.map((resource: any) => ({
205213
resource: resource.value,
206-
fields: resource.schema.map((field: any) => field.id),
214+
fields: resource.schema.map((field: any) => +field.id),
207215
}));
208216

209217
setAccessModelData((prevData: any) => ({
210218
...prevData,
211219
resources: newResources,
212220
}));
221+
213222
if (resourceDetails.length === 0) {
214223
setAccessModelData((prevData: any) => ({
215224
...prevData,
@@ -221,6 +230,8 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
221230
resources: [...prevData.resources],
222231
}));
223232
}
233+
234+
handleSave({ ...accessModelData, resources: newResources });
224235
};
225236

226237
const handleRemoveResource = (resourceId: any) => {
@@ -235,12 +246,16 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
235246
);
236247

237248
// Remove the corresponding resource from accessModelData.resources
249+
const updatedResources = accessModelData.resources.filter(
250+
(resource: any) => resource.resource !== resourceId
251+
);
252+
238253
setAccessModelData((prevData: any) => ({
239254
...prevData,
240-
resources: prevData.resources.filter(
241-
(resource: any) => resource.resource !== resourceId
242-
),
255+
resources: updatedResources,
243256
}));
257+
258+
handleSave({ ...accessModelData, resources: updatedResources });
244259
};
245260

246261
const handleSelectAll = () => {
@@ -255,26 +270,29 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
255270
setSelectedResources(allResources);
256271
setShowSelectAll(false);
257272

273+
const updatedResources = allResources.map((resource: any) => ({
274+
resource: resource.value,
275+
fields: resource.schema.map((option: any) => option.value),
276+
}));
277+
258278
setAccessModelData((prevData) => ({
259279
...prevData,
260-
resources: allResources.map((resource: any) => ({
261-
resource: resource.value,
262-
fields: resource.schema.map((option: any) => option.value),
263-
})),
280+
resources: updatedResources,
264281
}));
282+
283+
handleSave({ ...accessModelData, resources: updatedResources });
265284
};
266285

267286
const { mutate, isLoading: editMutationLoading } = useMutation(
268287
(data: { accessModelInput: EditAccessModelInput }) =>
269288
GraphQL(editaccessModel, data),
270289
{
271290
onSuccess: (res: any) => {
272-
toast('Access Model Saved');
291+
// toast('Access Model Saved');
273292
accessModelDetailsRefetch();
274293
accessModelListRefetch();
275294
setAccessModelId(res?.editAccessModel?.id);
276-
277-
// setList(true);
295+
setPreviousAccessModelData(accessModelData);
278296
},
279297
onError: (err: any) => {
280298
toast(`Received ${err} during access model saving`);
@@ -284,6 +302,28 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
284302

285303
const [isSheetOpen, setIsSheetOpen] = useState(false);
286304

305+
const handleSave = (updatedData: any) => {
306+
if (
307+
JSON.stringify(updatedData) !== JSON.stringify(previousAccessModelData)
308+
) {
309+
mutate({
310+
accessModelInput: {
311+
name: updatedData.name,
312+
dataset: params.id,
313+
description: updatedData.description,
314+
type: updatedData.type as AccessTypes,
315+
resources: updatedData.resources,
316+
accessModelId: accessModelId || null,
317+
},
318+
});
319+
}
320+
};
321+
322+
const handleChange = (field: string, value: any) => {
323+
const updatedData = { ...accessModelData, [field]: value };
324+
setAccessModelData(updatedData);
325+
};
326+
287327
return (
288328
<div className="rounded-2 border-2 border-solid border-baseGraySlateSolid6 px-6 py-8">
289329
<div className="mb-6 flex flex-wrap items-center justify-between gap-6">
@@ -343,11 +383,12 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
343383
(item: any, index: any) => (
344384
<div
345385
key={index}
346-
className=" rounded-1 border-1 border-solid border-baseGraySlateSolid6 px-6 py-3 "
386+
className={`rounded-1 border-1 border-solid border-baseGraySlateSolid6 px-6 py-3 ${accessModelId === item.id ? ' bg-baseGraySlateSolid5' : ''}`}
347387
>
348388
<Button
349389
kind={'tertiary'}
350390
className="flex w-full justify-start"
391+
disabled={accessModelId === item.id}
351392
onClick={() => {
352393
setAccessModelId(item.id);
353394
setIsSheetOpen(false);
@@ -363,41 +404,27 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
363404
</Sheet>
364405
</div>
365406
<Divider />
366-
{isLoading ||
367-
editMutationLoading ||
368-
accessModelDetailsLoading ||
369-
accessModelListLoading ? (
407+
{isLoading ? (
370408
<div className="mt-8 flex justify-center">
371409
<Spinner />
372410
</div>
373411
) : (
374412
<div className="mt-6 flex flex-col gap-8">
375-
<div className="text-center">
376-
<Button
377-
onClick={() =>
378-
mutate({
379-
accessModelInput: {
380-
name: accessModelData.name,
381-
dataset: accessModelData.dataset,
382-
description: accessModelData.description,
383-
type: accessModelData.type as AccessTypes,
384-
accessModelId: accessModelId || null,
385-
resources: accessModelData.resources,
386-
},
387-
})
388-
}
389-
>
390-
Save Access Type
391-
</Button>
413+
<div className="flex justify-end gap-2">
414+
<Text color="highlight">Auto Save </Text>
415+
{editMutationLoading ? (
416+
<Spinner />
417+
) : (
418+
<Icon source={Icons.checkmark} />
419+
)}
392420
</div>
393421
<div className="flex flex-col gap-6">
394422
<div className="flex gap-6">
395423
<div className=" w-4/5">
396424
<TextField
397425
value={accessModelData.name}
398-
onChange={(e) =>
399-
setAccessModelData({ ...accessModelData, name: e })
400-
}
426+
onChange={(e) => handleChange('name', e)}
427+
onBlur={() => handleSave(accessModelData)}
401428
label="Access Type Name"
402429
name="name"
403430
required
@@ -416,27 +443,22 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
416443
defaultValue={'PUBLIC'}
417444
value={accessModelData.type}
418445
placeholder="Select"
419-
onChange={(e) =>
420-
setAccessModelData({ ...accessModelData, type: e })
421-
}
446+
onChange={(e) => handleChange('type', e)}
447+
onBlur={() => handleSave(accessModelData)}
422448
/>
423449
</div>
424450
<TextField
425451
value={accessModelData.description}
426-
onChange={(e) =>
427-
setAccessModelData({
428-
...accessModelData,
429-
description: e,
430-
})
431-
}
452+
onChange={(e) => handleChange('description', e)}
453+
onBlur={() => handleSave(accessModelData)}
432454
label="Description"
433455
name="description"
434456
multiline={4}
435457
/>
436458
</div>
437459

438460
<div className="flex items-end gap-6">
439-
<div className="w-3/4">
461+
<div className={cn(' w-3/4', styles.combobox)}>
440462
<Combobox
441463
displaySelected
442464
label={'Select Fields of the Resource'}
@@ -479,6 +501,7 @@ const AccessModelForm: React.FC<AccessModelProps> = ({
479501
handleRemoveResource={handleRemoveResource}
480502
accessModelData={accessModelData}
481503
setAccessModelData={setAccessModelData}
504+
handleSave={handleSave}
482505
/>
483506
);
484507
})}

app/[locale]/dashboard/organization/[organizationId]/dataset/[id]/edit/components/ResourceSelector.tsx

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ interface ResourceSelectorProps {
1010
handleRemoveResource: (resourceId: string) => void;
1111
accessModelData: any;
1212
setAccessModelData: (data: any) => void;
13+
handleSave: (updatedData: any) => void;
1314
}
1415

1516
const ResourceSelector: React.FC<ResourceSelectorProps> = ({
1617
selectedResource,
1718
handleRemoveResource,
1819
accessModelData,
1920
setAccessModelData,
21+
handleSave,
2022
}) => {
2123
const [selectAllFields, setSelectAllFields] = useState(true);
2224
const [options, setOptions] = useState<{ label: string; value: string }[]>(
@@ -57,10 +59,10 @@ const ResourceSelector: React.FC<ResourceSelectorProps> = ({
5759
);
5860
} else if (selectAllFields) {
5961
setSelectedFields(initialOptions);
60-
setAccessModelData((prevData: any) => ({
61-
...prevData,
62+
const updatedData = {
63+
...accessModelData,
6264
resources: [
63-
...prevData.resources.filter(
65+
...accessModelData.resources.filter(
6466
(resource: any) => resource.resource !== selectedResource.id
6567
),
6668
{
@@ -70,9 +72,17 @@ const ResourceSelector: React.FC<ResourceSelectorProps> = ({
7072
), // Convert to integer
7173
},
7274
],
73-
}));
75+
};
76+
setAccessModelData(updatedData);
77+
handleSave(updatedData);
7478
}
75-
}, [selectedResource, accessModelData, selectAllFields]);
79+
}, [
80+
selectedResource,
81+
accessModelData,
82+
selectAllFields,
83+
setAccessModelData,
84+
handleSave,
85+
]);
7686

7787
const handleFieldSelection = (selectedOptions: any) => {
7888
const updatedFields = selectedOptions.map((option: any) => ({
@@ -82,18 +92,21 @@ const ResourceSelector: React.FC<ResourceSelectorProps> = ({
8292

8393
setSelectedFields(updatedFields);
8494

85-
setAccessModelData((prevData: any) => ({
86-
...prevData,
95+
const updatedData = {
96+
...accessModelData,
8797
resources: [
88-
...prevData.resources.filter(
98+
...accessModelData.resources.filter(
8999
(resource: any) => resource.resource !== selectedResource.id
90100
),
91101
{
92102
resource: selectedResource.id,
93103
fields: updatedFields.map((field: any) => parseInt(field.value, 10)), // Convert to integer
94104
},
95105
],
96-
}));
106+
};
107+
108+
setAccessModelData(updatedData);
109+
handleSave(updatedData);
97110

98111
setSelectAllFields(updatedFields.length === options.length);
99112
};
@@ -103,18 +116,21 @@ const ResourceSelector: React.FC<ResourceSelectorProps> = ({
103116
setSelectAllFields(!selectAllFields);
104117
setSelectedFields(updatedFields);
105118

106-
setAccessModelData((prevData: any) => ({
107-
...prevData,
119+
const updatedData = {
120+
...accessModelData,
108121
resources: [
109-
...prevData.resources.filter(
122+
...accessModelData.resources.filter(
110123
(resource: any) => resource.resource !== selectedResource.id
111124
),
112125
{
113126
resource: selectedResource.id,
114127
fields: updatedFields.map((field: any) => parseInt(field.value, 10)), // Convert to integer
115128
},
116129
],
117-
}));
130+
};
131+
132+
setAccessModelData(updatedData);
133+
handleSave(updatedData);
118134
};
119135

120136
return (
@@ -132,9 +148,9 @@ const ResourceSelector: React.FC<ResourceSelectorProps> = ({
132148
</Button>
133149
</div>
134150
<div className="flex flex-wrap gap-6 px-8">
135-
<div className="flex w-3/5 flex-col gap-4">
151+
<div className="flex w-full flex-col gap-4 2xl:w-3/5">
136152
<div className="relative mr-4 flex items-center">
137-
<div>
153+
<div className={cn('mt-1 w-full', styles.combobox)}>
138154
<Combobox
139155
displaySelected
140156
label="Select Fields of the Resource"
@@ -145,7 +161,7 @@ const ResourceSelector: React.FC<ResourceSelectorProps> = ({
145161
onChange={(e: any) => handleFieldSelection(e)}
146162
/>
147163
</div>
148-
<div className="absolute right-0" style={{ top: '-4px' }}>
164+
<div className="absolute right-0" style={{ top: '1px' }}>
149165
<Checkbox
150166
name="Select All Fields"
151167
checked={selectAllFields}

app/[locale]/dashboard/organization/[organizationId]/dataset/[id]/edit/edit.module.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@
99
width: 140px;
1010
}
1111
}
12+
13+
.combobox {
14+
[role='presentation'] {
15+
z-index: 1000 !important;
16+
}
17+
}

0 commit comments

Comments
 (0)