Define a dialog to configure the affinities between groups
This commit is contained in:
parent
d307ff6927
commit
52fb808d45
@ -3,7 +3,9 @@
|
|||||||
'use client';
|
'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 { Guest, GuestSerializer } from '@/app/lib/guest';
|
||||||
|
import AffinitiesFormDialog from '@/app/ui/components/affinities-form-dialog';
|
||||||
import { classNames } from '@/app/ui/components/button';
|
import { classNames } from '@/app/ui/components/button';
|
||||||
import GroupFormDialog from '@/app/ui/components/group-form-dialog';
|
import GroupFormDialog from '@/app/ui/components/group-form-dialog';
|
||||||
import GuestFormDialog from '@/app/ui/components/guest-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 GuestsTable from '@/app/ui/guests/table';
|
||||||
import { TabPanel, TabView } from 'primereact/tabview';
|
import { TabPanel, TabView } from 'primereact/tabview';
|
||||||
import { Suspense, useState } from 'react';
|
import { Suspense, useState } from 'react';
|
||||||
import { Group, GroupSerializer } from '@/app/lib/group';
|
|
||||||
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
@ -34,6 +35,8 @@ export default function Page() {
|
|||||||
const [groups, setGroups] = useState<Array<Group>>([]);
|
const [groups, setGroups] = useState<Array<Group>>([]);
|
||||||
const [groupBeingEdited, setGroupBeingEdited] = useState<Group | undefined>(undefined);
|
const [groupBeingEdited, setGroupBeingEdited] = useState<Group | undefined>(undefined);
|
||||||
|
|
||||||
|
const [groupAffinitiesBeingEditted, setGroupAffinitiesBeingEditted] = useState<Group | undefined>(undefined);
|
||||||
|
|
||||||
const [guestsLoaded, setGuestsLoaded] = useState(false);
|
const [guestsLoaded, setGuestsLoaded] = useState(false);
|
||||||
const [guests, setGuests] = useState<Array<Guest>>([]);
|
const [guests, setGuests] = useState<Array<Guest>>([]);
|
||||||
const [guestBeingEdited, setGuestBeingEdited] = useState<Guest | undefined>(undefined);
|
const [guestBeingEdited, setGuestBeingEdited] = useState<Guest | undefined>(undefined);
|
||||||
@ -77,11 +80,19 @@ export default function Page() {
|
|||||||
onHide={() => { setGroupBeingEdited(undefined) }}
|
onHide={() => { setGroupBeingEdited(undefined) }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<AffinitiesFormDialog
|
||||||
|
groups={groups}
|
||||||
|
group={groupAffinitiesBeingEditted}
|
||||||
|
visible={groupAffinitiesBeingEditted !== undefined}
|
||||||
|
onHide={() => { setGroupAffinitiesBeingEditted(undefined) }}
|
||||||
|
/>
|
||||||
|
|
||||||
<Suspense fallback={<SkeletonTable />}>
|
<Suspense fallback={<SkeletonTable />}>
|
||||||
<GroupsTable
|
<GroupsTable
|
||||||
groups={groups}
|
groups={groups}
|
||||||
onUpdate={refreshGroups}
|
onUpdate={refreshGroups}
|
||||||
onEdit={(group) => setGroupBeingEdited(group)}
|
onEdit={(group) => setGroupBeingEdited(group)}
|
||||||
|
onEditAffinities={(group) => setGroupAffinitiesBeingEditted(group)}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
69
app/ui/components/affinities-form-dialog.tsx
Normal file
69
app/ui/components/affinities-form-dialog.tsx
Normal file
@ -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<Affinities>({});
|
||||||
|
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 (
|
||||||
|
<Dialog header="Update affinities" visible={visible} style={{ width: '60vw' }} onHide={onHide}>
|
||||||
|
{!isLoadingAffinities && <div className="card justify-evenly py-5 bg-gray-200 flex flex-col">
|
||||||
|
<span className="text-center p-4">Describe the affinity with the rest of the groups</span>
|
||||||
|
|
||||||
|
{
|
||||||
|
groups.filter((currentGroup) => currentGroup.id !== group?.id).map((group) =>
|
||||||
|
<div key={group.id} className="flex flex-row hover:bg-gray-300 px-3 py-2 items-center">
|
||||||
|
<span className="w-1/3 text-right px-4">{group.name}</span>
|
||||||
|
<AffinitySlider value={group.id && affinities[group.id] || 1} onChange={(value) => setAffinities({ ...affinities, [group.id || "default"]: value })} />
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
|
||||||
|
<button className={classNames('primary')} onClick={submitAffinities} >Update</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
@ -10,10 +10,7 @@ import { Dialog } from 'primereact/dialog';
|
|||||||
import { Dropdown } from 'primereact/dropdown';
|
import { Dropdown } from 'primereact/dropdown';
|
||||||
import { FloatLabel } from 'primereact/floatlabel';
|
import { FloatLabel } from 'primereact/floatlabel';
|
||||||
import { InputText } from 'primereact/inputtext';
|
import { InputText } from 'primereact/inputtext';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import AffinitySlider from './form/affinitySlider';
|
|
||||||
import { Affinities } from '@/app/lib/affinities';
|
|
||||||
import { getSlug } from '@/app/lib/utils';
|
|
||||||
|
|
||||||
export default function GroupFormDialog({ groups, onCreate, onHide, group, visible }: {
|
export default function GroupFormDialog({ groups, onCreate, onHide, group, visible }: {
|
||||||
groups: Group[],
|
groups: Group[],
|
||||||
@ -27,19 +24,6 @@ export default function GroupFormDialog({ groups, onCreate, onHide, group, visib
|
|||||||
const [icon, setIcon] = useState(group?.icon || '');
|
const [icon, setIcon] = useState(group?.icon || '');
|
||||||
const [color, setColor] = useState<string>(group?.color || '');
|
const [color, setColor] = useState<string>(group?.color || '');
|
||||||
const [parentId, setParentId] = useState(group?.parentId || '');
|
const [parentId, setParentId] = useState(group?.parentId || '');
|
||||||
const [affinities, setAffinities] = useState<Affinities>({});
|
|
||||||
|
|
||||||
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<Group>();
|
const api = new AbstractApi<Group>();
|
||||||
const serializer = new GroupSerializer();
|
const serializer = new GroupSerializer();
|
||||||
@ -105,18 +89,6 @@ export default function GroupFormDialog({ groups, onCreate, onHide, group, visib
|
|||||||
{group?.id !== undefined ? 'Update' : 'Create'}
|
{group?.id !== undefined ? 'Update' : 'Create'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="card justify-evenly py-5 bg-gray-200 flex flex-col">
|
|
||||||
<span className="text-center p-4">Describe the affinity with the rest of the groups</span>
|
|
||||||
|
|
||||||
{
|
|
||||||
groups.map((group) =>
|
|
||||||
<div key={group.id} className="flex flex-row hover:bg-gray-300 px-3 py-2 items-center">
|
|
||||||
<span className="w-1/3 text-right px-4">{group.name}</span>
|
|
||||||
<AffinitySlider value={group.id && affinities[group.id] || 2} onChange={(value) => setAffinities({...affinities, [group.id || "default"]:value})} />
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -4,15 +4,16 @@
|
|||||||
|
|
||||||
import { AbstractApi } from '@/app/api/abstract-api';
|
import { AbstractApi } from '@/app/api/abstract-api';
|
||||||
import { Group, GroupSerializer } from '@/app/lib/group';
|
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 { Column } from 'primereact/column';
|
||||||
import { TreeNode } from 'primereact/treenode';
|
import { TreeNode } from 'primereact/treenode';
|
||||||
import { TreeTable } from 'primereact/treetable';
|
import { TreeTable } from 'primereact/treetable';
|
||||||
|
|
||||||
export default function GroupsTable({ groups, onUpdate, onEdit }: {
|
export default function GroupsTable({ groups, onUpdate, onEdit, onEditAffinities }: {
|
||||||
groups: Group[],
|
groups: Group[],
|
||||||
onUpdate: () => void,
|
onUpdate: () => void,
|
||||||
onEdit: (group: Group) => void,
|
onEdit: (group: Group) => void,
|
||||||
|
onEditAffinities: (group: Group) => void,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
const api = new AbstractApi<Group>();
|
const api = new AbstractApi<Group>();
|
||||||
@ -22,6 +23,7 @@ export default function GroupsTable({ groups, onUpdate, onEdit }: {
|
|||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<TrashIcon className='size-6 cursor-pointer' onClick={() => { api.destroy(serializer, group, onUpdate) }} />
|
<TrashIcon className='size-6 cursor-pointer' onClick={() => { api.destroy(serializer, group, onUpdate) }} />
|
||||||
<PencilIcon className='size-6 cursor-pointer' onClick={() => onEdit(group)} />
|
<PencilIcon className='size-6 cursor-pointer' onClick={() => onEdit(group)} />
|
||||||
|
<AdjustmentsHorizontalIcon className='size-6 cursor-pointer' onClick={() => onEditAffinities(group)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user