From 52fb808d45af67771a20e7c9ad18efa56bd52ab8 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sat, 28 Dec 2024 14:18:14 +0100 Subject: [PATCH] Define a dialog to configure the affinities between groups --- app/[slug]/dashboard/guests/page.tsx | 17 ++++- app/ui/components/affinities-form-dialog.tsx | 69 ++++++++++++++++++++ app/ui/components/form/affinitySlider.tsx | 6 +- app/ui/components/group-form-dialog.tsx | 30 +-------- app/ui/groups/table.tsx | 6 +- 5 files changed, 91 insertions(+), 37 deletions(-) create mode 100644 app/ui/components/affinities-form-dialog.tsx diff --git a/app/[slug]/dashboard/guests/page.tsx b/app/[slug]/dashboard/guests/page.tsx index beb73f8..0d68edc 100644 --- a/app/[slug]/dashboard/guests/page.tsx +++ b/app/[slug]/dashboard/guests/page.tsx @@ -2,8 +2,10 @@ 'use client'; -import { AbstractApi, } from '@/app/api/abstract-api'; +import { AbstractApi, } from '@/app/api/abstract-api'; +import { Group, GroupSerializer } from '@/app/lib/group'; import { Guest, GuestSerializer } from '@/app/lib/guest'; +import AffinitiesFormDialog from '@/app/ui/components/affinities-form-dialog'; import { classNames } from '@/app/ui/components/button'; import GroupFormDialog from '@/app/ui/components/group-form-dialog'; import GuestFormDialog from '@/app/ui/components/guest-form-dialog'; @@ -12,7 +14,6 @@ import SkeletonTable from '@/app/ui/guests/skeleton-row'; import GuestsTable from '@/app/ui/guests/table'; import { TabPanel, TabView } from 'primereact/tabview'; import { Suspense, useState } from 'react'; -import { Group, GroupSerializer } from '@/app/lib/group'; export default function Page() { @@ -34,6 +35,8 @@ export default function Page() { const [groups, setGroups] = useState>([]); const [groupBeingEdited, setGroupBeingEdited] = useState(undefined); + const [groupAffinitiesBeingEditted, setGroupAffinitiesBeingEditted] = useState(undefined); + const [guestsLoaded, setGuestsLoaded] = useState(false); const [guests, setGuests] = useState>([]); const [guestBeingEdited, setGuestBeingEdited] = useState(undefined); @@ -77,11 +80,19 @@ export default function Page() { onHide={() => { setGroupBeingEdited(undefined) }} /> + { setGroupAffinitiesBeingEditted(undefined) }} + /> + }> - setGroupBeingEdited(group)} + onEditAffinities={(group) => setGroupAffinitiesBeingEditted(group)} /> diff --git a/app/ui/components/affinities-form-dialog.tsx b/app/ui/components/affinities-form-dialog.tsx new file mode 100644 index 0000000..7428233 --- /dev/null +++ b/app/ui/components/affinities-form-dialog.tsx @@ -0,0 +1,69 @@ +/* Copyright (C) 2024 Manuel Bustillo*/ + +'use client'; + +import { Affinities } from '@/app/lib/affinities'; +import { Group } from '@/app/lib/group'; +import { getCsrfToken, getSlug } from '@/app/lib/utils'; +import { classNames } from '@/app/ui/components/button'; +import { Dialog } from 'primereact/dialog'; +import { useEffect, useState } from 'react'; +import AffinitySlider from './form/affinitySlider'; + +export default function AffinitiesFormDialog({ groups, group, visible, onHide }: { + groups: Group[], + group?: Group, + visible: boolean, + onHide: () => void, +}) { + const [affinities, setAffinities] = useState({}); + const [isLoadingAffinities, setIsLoadingAffinities] = useState(true); + + useEffect(() => { + setIsLoadingAffinities(true); + if (group?.id === undefined) { + setAffinities({}); + setIsLoadingAffinities(false) + } else { + fetch(`/api/${getSlug()}/groups/${group?.id}/affinities`) + .then((response) => response.json()) + .then((data) => { + setAffinities(data); + setIsLoadingAffinities(false) + }); + } + }, [group]); + + function submitAffinities() { + const formattedAffinities = Object.entries(affinities).map(([groupId, affinity]) => ({ group_id: groupId, affinity: affinity })); + fetch(`/api/${getSlug()}/groups/${group?.id}/affinities/bulk_update`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': getCsrfToken(), + }, + body: JSON.stringify({ affinities: formattedAffinities }) + }).then(() => { + onHide(); + }); + } + + return ( + + {!isLoadingAffinities &&
+ Describe the affinity with the rest of the groups + + { + groups.filter((currentGroup) => currentGroup.id !== group?.id).map((group) => +
+ {group.name} + setAffinities({ ...affinities, [group.id || "default"]: value })} /> +
) + } + + +
+ } +
+ ); +} \ No newline at end of file diff --git a/app/ui/components/form/affinitySlider.tsx b/app/ui/components/form/affinitySlider.tsx index b434535..56bef94 100644 --- a/app/ui/components/form/affinitySlider.tsx +++ b/app/ui/components/form/affinitySlider.tsx @@ -1,6 +1,6 @@ import { Slider } from 'primereact/slider'; -export default function AffinitySlider({value , onChange }: { value: number, onChange: (value: number) => void }) { +export default function AffinitySlider({ value, onChange }: { value: number, onChange: (value: number) => void }) { const label = (value: number) => { if (value < 0.2) { @@ -22,9 +22,9 @@ export default function AffinitySlider({value , onChange }: { value: number, onC return ( <> - onChange(e.value)} className='w-80 bg-gray-400' /> + onChange(e.value)} className='w-80 bg-gray-400' /> - { label(value) } + {label(value)} ) diff --git a/app/ui/components/group-form-dialog.tsx b/app/ui/components/group-form-dialog.tsx index 226d40b..cc96348 100644 --- a/app/ui/components/group-form-dialog.tsx +++ b/app/ui/components/group-form-dialog.tsx @@ -10,10 +10,7 @@ import { Dialog } from 'primereact/dialog'; import { Dropdown } from 'primereact/dropdown'; import { FloatLabel } from 'primereact/floatlabel'; import { InputText } from 'primereact/inputtext'; -import { useEffect, useState } from 'react'; -import AffinitySlider from './form/affinitySlider'; -import { Affinities } from '@/app/lib/affinities'; -import { getSlug } from '@/app/lib/utils'; +import { useState } from 'react'; export default function GroupFormDialog({ groups, onCreate, onHide, group, visible }: { groups: Group[], @@ -27,19 +24,6 @@ export default function GroupFormDialog({ groups, onCreate, onHide, group, visib const [icon, setIcon] = useState(group?.icon || ''); const [color, setColor] = useState(group?.color || ''); const [parentId, setParentId] = useState(group?.parentId || ''); - const [affinities, setAffinities] = useState({}); - - useEffect(() => { - if (group?.id === undefined) { - setAffinities({}); - } else { - fetch(`/api/${getSlug()}/groups/${group?.id}/affinities`) - .then((response) => response.json()) - .then((data) => { - setAffinities(data); - }); - } - }, []); const api = new AbstractApi(); const serializer = new GroupSerializer(); @@ -105,18 +89,6 @@ export default function GroupFormDialog({ groups, onCreate, onHide, group, visib {group?.id !== undefined ? 'Update' : 'Create'} - -
- Describe the affinity with the rest of the groups - - { - groups.map((group) => -
- {group.name} - setAffinities({...affinities, [group.id || "default"]:value})} /> -
) - } -
); diff --git a/app/ui/groups/table.tsx b/app/ui/groups/table.tsx index 4088cfa..253c271 100644 --- a/app/ui/groups/table.tsx +++ b/app/ui/groups/table.tsx @@ -4,15 +4,16 @@ import { AbstractApi } from '@/app/api/abstract-api'; import { Group, GroupSerializer } from '@/app/lib/group'; -import { PencilIcon, TrashIcon } from '@heroicons/react/24/outline'; +import { AdjustmentsHorizontalIcon, PencilIcon, TrashIcon } from '@heroicons/react/24/outline'; import { Column } from 'primereact/column'; import { TreeNode } from 'primereact/treenode'; import { TreeTable } from 'primereact/treetable'; -export default function GroupsTable({ groups, onUpdate, onEdit }: { +export default function GroupsTable({ groups, onUpdate, onEdit, onEditAffinities }: { groups: Group[], onUpdate: () => void, onEdit: (group: Group) => void, + onEditAffinities: (group: Group) => void, }) { const api = new AbstractApi(); @@ -22,6 +23,7 @@ export default function GroupsTable({ groups, onUpdate, onEdit }: {
{ api.destroy(serializer, group, onUpdate) }} /> onEdit(group)} /> + onEditAffinities(group)} />
);