Define a dialog to configure the affinities between groups
This commit is contained in:
		
							parent
							
								
									d307ff6927
								
							
						
					
					
						commit
						52fb808d45
					
				@ -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<Array<Group>>([]);
 | 
			
		||||
  const [groupBeingEdited, setGroupBeingEdited] = useState<Group | undefined>(undefined);
 | 
			
		||||
 | 
			
		||||
  const [groupAffinitiesBeingEditted, setGroupAffinitiesBeingEditted] = useState<Group | undefined>(undefined);
 | 
			
		||||
 | 
			
		||||
  const [guestsLoaded, setGuestsLoaded] = useState(false);
 | 
			
		||||
  const [guests, setGuests] = useState<Array<Guest>>([]);
 | 
			
		||||
  const [guestBeingEdited, setGuestBeingEdited] = useState<Guest | undefined>(undefined);
 | 
			
		||||
@ -77,11 +80,19 @@ export default function Page() {
 | 
			
		||||
              onHide={() => { setGroupBeingEdited(undefined) }}
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            <AffinitiesFormDialog
 | 
			
		||||
              groups={groups}
 | 
			
		||||
              group={groupAffinitiesBeingEditted}
 | 
			
		||||
              visible={groupAffinitiesBeingEditted !== undefined}
 | 
			
		||||
              onHide={() => { setGroupAffinitiesBeingEditted(undefined) }}
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            <Suspense fallback={<SkeletonTable />}>
 | 
			
		||||
              <GroupsTable 
 | 
			
		||||
              <GroupsTable
 | 
			
		||||
                groups={groups}
 | 
			
		||||
                onUpdate={refreshGroups}
 | 
			
		||||
                onEdit={(group) => setGroupBeingEdited(group)}
 | 
			
		||||
                onEditAffinities={(group) => setGroupAffinitiesBeingEditted(group)}
 | 
			
		||||
              />
 | 
			
		||||
            </Suspense>
 | 
			
		||||
          </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>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -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 (
 | 
			
		||||
    <>
 | 
			
		||||
      <Slider value={value} min={0} max={2} step={.1} onChange={(e) =>onChange(e.value)} className='w-80 bg-gray-400' />
 | 
			
		||||
      <Slider value={value} min={0} max={2} step={.1} onChange={(e) => onChange(e.value)} className='w-80 bg-gray-400' />
 | 
			
		||||
      <span className="px-4 w-1/5">
 | 
			
		||||
        { label(value) }
 | 
			
		||||
        {label(value)}
 | 
			
		||||
      </span>
 | 
			
		||||
    </>
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
@ -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<string>(group?.color || '');
 | 
			
		||||
  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 serializer = new GroupSerializer();
 | 
			
		||||
@ -105,18 +89,6 @@ export default function GroupFormDialog({ groups, onCreate, onHide, group, visib
 | 
			
		||||
            {group?.id !== undefined ? 'Update' : 'Create'}
 | 
			
		||||
          </button>
 | 
			
		||||
        </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>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
@ -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<Group>();
 | 
			
		||||
@ -22,6 +23,7 @@ export default function GroupsTable({ groups, onUpdate, onEdit }: {
 | 
			
		||||
    <div className="flex flex-row items-center">
 | 
			
		||||
      <TrashIcon className='size-6 cursor-pointer' onClick={() => { api.destroy(serializer, group, onUpdate) }} />
 | 
			
		||||
      <PencilIcon className='size-6 cursor-pointer' onClick={() => onEdit(group)} />
 | 
			
		||||
      <AdjustmentsHorizontalIcon className='size-6 cursor-pointer' onClick={() => onEditAffinities(group)} />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user