Add form to create a new group

This commit is contained in:
Manuel Bustillo 2024-12-08 12:45:15 +01:00
parent 98d3afcd8e
commit ad0b09c3df
5 changed files with 152 additions and 8 deletions

View File

@ -7,6 +7,7 @@ import { loadGuests } from '@/app/api/guests';
import { Group, Guest } from '@/app/lib/definitions';
import { classNames } from '@/app/ui/components/button';
import GuestFormDialog from '@/app/ui/components/guest-form-dialog';
import GroupFormDialog from '@/app/ui/components/group-form-dialog';
import GroupsTable from '@/app/ui/groups/table';
import SkeletonTable from '@/app/ui/guests/skeleton-row';
import GuestsTable from '@/app/ui/guests/table';
@ -31,6 +32,7 @@ export default function Page() {
const [groupsLoaded, setGroupsLoaded] = useState(false);
const [groups, setGroups] = useState<Array<Group>>([]);
const [groupBeingEdited, setGroupBeingEdited] = useState<Group | undefined>(undefined);
const [guestsLoaded, setGuestsLoaded] = useState(false);
const [guests, setGuests] = useState<Array<Guest>>([]);
@ -59,7 +61,18 @@ export default function Page() {
</div>
</ TabPanel>
<TabPanel header="Groups" leftIcon="pi pi-sitemap mx-2">
<div className="flex w-full items-center justify-between">
<div className="flex flex-col w-full items-center justify-between">
<button onClick={() => setGroupBeingEdited({})} className={classNames('primary')}>Add new</button>
<GroupFormDialog
key={groupBeingEdited?.id}
groups={groups}
onCreate={() => { refreshGroups(); setGroupBeingEdited(undefined) }}
group={groupBeingEdited}
visible={groupBeingEdited !== undefined}
onHide={() => { setGroupBeingEdited(undefined) }}
/>
<Suspense fallback={<SkeletonTable />}>
<GroupsTable groups={groups} />
</Suspense>

View File

@ -1,7 +1,7 @@
/* Copyright (C) 2024 Manuel Bustillo*/
import { Group } from '@/app/lib/definitions';
import { getSlug } from '../lib/utils';
import { getCsrfToken, getSlug } from '../lib/utils';
export function loadGroups(onLoad?: (groups: Group[]) => void) {
fetch(`/api/${getSlug()}/groups`)
@ -25,4 +25,43 @@ export function loadGroups(onLoad?: (groups: Group[]) => void) {
}, (error) => {
return [];
});
}
export function updateGroup(group: Group) {
return fetch(`/api/${getSlug()}/groups/${group.id}`,
{
method: 'PUT',
body: JSON.stringify({ group: {
name: group.name,
color: group.color,
icon: group.icon,
parent_id: group.parentId
} }),
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': getCsrfToken(),
}
})
.catch((error) => console.error(error));
}
export function createGroup(group: Group, onCreate?: () => void) {
fetch(`/api/${getSlug()}/groups`, {
method: 'POST',
body: JSON.stringify({
name: group.name,
color: group.color,
icon: group.icon,
parent_id: group.parentId
}),
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': getCsrfToken(),
}
})
.then((response) => response.json())
.then((data) => {
onCreate && onCreate();
})
.catch((error) => console.error(error));
}

View File

@ -32,11 +32,12 @@ export type TableArrangement = {
}
export type Group = {
id: string;
name: string;
guest_count: number;
icon: string;
children: Group[];
id?: string;
name?: string;
guest_count?: number;
icon?: string;
children?: Group[];
parentId?: string;
color?: string;
attendance?: AttendanceSummary
};

View File

@ -0,0 +1,91 @@
/* Copyright (C) 2024 Manuel Bustillo*/
'use client';
import { createGroup, updateGroup } from '@/app/api/groups';
import { Group } from '@/app/lib/definitions';
import { classNames } from '@/app/ui/components/button';
import { Dialog } from 'primereact/dialog';
import { ColorPicker } from 'primereact/colorpicker';
import { Dropdown } from 'primereact/dropdown';
import { FloatLabel } from 'primereact/floatlabel';
import { InputText } from 'primereact/inputtext';
import { useState } from 'react';
export default function GroupFormDialog({groups, onCreate, onHide, group, visible }: {
groups: Group[],
onCreate?: () => void,
onHide: () => void,
group?: Group,
visible: boolean,
}) {
const [name, setName] = useState(group?.name || '');
const [icon, setIcon] = useState(group?.icon || '');
const [color, setColor] = useState<string>(group?.color || '');
const [parentId, setParentId] = useState(group?.parentId || '');
function resetForm() {
setName('');
setIcon('');
setColor('');
setParentId('');
}
function submitGroup() {
if (!(name)) {
return
}
if (group?.id !== undefined) {
group.name = name;
group.icon = icon;
group.color = color;
group.parentId = parentId;
updateGroup(group).then(() => {
resetForm();
onCreate && onCreate();
});
} else {
group && createGroup({name, icon, color, parentId}, () => {
resetForm();
onCreate && onCreate();
});
}
}
return (
<>
<Dialog header="Add group" visible={visible} style={{ width: '60vw' }} onHide={onHide}>
<div className="card flex justify-evenly py-5">
<FloatLabel>
<InputText id="name" className='rounded-sm' value={name} onChange={(e) => setName(e.target.value)} />
<label htmlFor="name">Name</label>
</FloatLabel>
<FloatLabel>
<InputText id="icon" className='rounded-sm' value={icon} onChange={(e) => setIcon(e.target.value)} />
<label htmlFor="icon">Icon</label>
</FloatLabel>
<FloatLabel>
<ColorPicker value={color} format='hex' onChange={(e) => setColor(`#${e.value}`)} />
<label htmlFor="color" />
</FloatLabel>
<FloatLabel>
<Dropdown id="parentId" className='rounded-sm min-w-32' value={parentId} onChange={(e) => setParentId(e.target.value)} options={
groups.map((group) => {
return { label: group.name, value: group.id };
})
} />
<label htmlFor="parentId">Parent</label>
</FloatLabel>
<button className={classNames('primary')} onClick={submitGroup} disabled={!(name.length > 0)}>
{group?.id !== undefined ? 'Update' : 'Create'}
</button>
</div>
</Dialog>
</>
);
}

View File

@ -54,7 +54,7 @@ export default function GuestFormDialog({ groups, onCreate, onHide, guest, visib
return (
<>
<Dialog header="Add guest" visible={visible} style={{ width: '50vw' }} onHide={onHide}>
<Dialog header="Add guest" visible={visible} style={{ width: '60vw' }} onHide={onHide}>
<div className="card flex justify-evenly py-5">
<FloatLabel>
<InputText id="username" className='rounded-sm' value={name} onChange={(e) => setName(e.target.value)} />