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/lib/affinities.tsx b/app/lib/affinities.tsx new file mode 100644 index 0000000..6c1023e --- /dev/null +++ b/app/lib/affinities.tsx @@ -0,0 +1,5 @@ +/* Copyright (C) 2024 Manuel Bustillo*/ + +export class Affinities { + [key:string]: number; +} \ No newline at end of file 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 new file mode 100644 index 0000000..f541e4d --- /dev/null +++ b/app/ui/components/form/affinitySlider.tsx @@ -0,0 +1,33 @@ +/* Copyright (C) 2024 Manuel Bustillo*/ + +import { Slider } from 'primereact/slider'; + +export default function AffinitySlider({ value, onChange }: { value: number, onChange: (value: number) => void }) { + + const label = (value: number) => { + if (value < 0.2) { + return 'Nemesis'; + } else if (value < 0.5) { + return 'Enemies'; + } else if (value < 0.9) { + return 'Bad vibes'; + } else if (value < 1.1) { + return 'Neutral'; + } else if (value < 1.5) { + return 'Good vibes'; + } else if (value < 1.8) { + return 'Good friends'; + } else { + return 'Besties'; + } + } + + return ( + <> + onChange(e.value)} className='w-80 bg-gray-400' /> + + {label(value)} + + + ) +} \ No newline at end of file diff --git a/app/ui/components/group-form-dialog.tsx b/app/ui/components/group-form-dialog.tsx index d652a13..cc96348 100644 --- a/app/ui/components/group-form-dialog.tsx +++ b/app/ui/components/group-form-dialog.tsx @@ -2,18 +2,17 @@ 'use client'; +import { AbstractApi } from '@/app/api/abstract-api'; +import { Group, GroupSerializer } from '@/app/lib/group'; import { classNames } from '@/app/ui/components/button'; -import { Dialog } from 'primereact/dialog'; import { ColorPicker } from 'primereact/colorpicker'; +import { Dialog } from 'primereact/dialog'; import { Dropdown } from 'primereact/dropdown'; import { FloatLabel } from 'primereact/floatlabel'; import { InputText } from 'primereact/inputtext'; import { useState } from 'react'; -import { Group, GroupSerializer } from '@/app/lib/group'; -import { ApiError } from 'next/dist/server/api-utils'; -import { AbstractApi } from '@/app/api/abstract-api'; -export default function GroupFormDialog({groups, onCreate, onHide, group, visible }: { +export default function GroupFormDialog({ groups, onCreate, onHide, group, visible }: { groups: Group[], onCreate?: () => void, onHide: () => void, diff --git a/app/ui/groups/table.tsx b/app/ui/groups/table.tsx index 41af307..253c271 100644 --- a/app/ui/groups/table.tsx +++ b/app/ui/groups/table.tsx @@ -2,18 +2,18 @@ 'use client'; -import { Group, GroupSerializer } from '@/app/lib/group'; -import TableOfContents from '../components/table-of-contents'; -import { MapPinIcon, PencilIcon, TrashIcon } from '@heroicons/react/24/outline'; import { AbstractApi } from '@/app/api/abstract-api'; -import { TreeTable } from 'primereact/treetable'; +import { Group, GroupSerializer } from '@/app/lib/group'; +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(); @@ -23,6 +23,7 @@ export default function GroupsTable({ groups, onUpdate, onEdit }: {
{ api.destroy(serializer, group, onUpdate) }} /> onEdit(group)} /> + onEditAffinities(group)} />
);