Complete rewrite of all of the hooks and actions for statuses as well as for the Tech Table. Need to rewrite history component & then I will be happy & ready to continue
This commit is contained in:
207
src/components/status/TechTable.tsx
Normal file
207
src/components/status/TechTable.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
'use client';
|
||||
|
||||
import { createClient } from '@/utils/supabase';
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useAuth, useTVMode } from '@/components/context';
|
||||
import {
|
||||
getRecentUsersWithStatuses,
|
||||
updateStatuses,
|
||||
updateUserStatus,
|
||||
type UserWithStatus,
|
||||
} from '@/lib/hooks/status';
|
||||
import {
|
||||
Drawer,
|
||||
DrawerTrigger,
|
||||
Progress,
|
||||
} from '@/components/ui';
|
||||
import { toast } from 'sonner';
|
||||
import { HistoryDrawer } from '@/components/status';
|
||||
import type { RealtimeChannel } from '@supabase/supabase-js';
|
||||
|
||||
type TechTableProps = {
|
||||
initialStatuses: UserWithStatus[];
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const TechTable = ({
|
||||
initialStatuses = [],
|
||||
className = 'w-full max-w-7xl mx-auto px-4',
|
||||
}: TechTableProps) => {
|
||||
const { isAuthenticated, profile } = useAuth();
|
||||
const { tvMode } = useTVMode();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const [statusInput, setStatusInput] = useState('');
|
||||
const [usersWithStatuses, setUsersWithStatuses] = useState<UserWithStatus[]>(initialStatuses);
|
||||
const [selectedHistoryUserId, setSelectedHistoryUserId] = useState('');
|
||||
|
||||
const supabase = createClient();
|
||||
|
||||
const fetchRecentUsersWithStatuses = useCallback(async () => {
|
||||
try {
|
||||
const response = await getRecentUsersWithStatuses();
|
||||
if (!response.success) throw new Error(response.error);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
toast.error(`Error fetching technicians: ${error as Error}`)
|
||||
return [];
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Initial load
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const data = await fetchRecentUsersWithStatuses();
|
||||
setUsersWithStatuses(data);
|
||||
setLoading(false);
|
||||
};
|
||||
loadData().catch((error) => {
|
||||
console.error('Error loading data:', error);
|
||||
});
|
||||
}, [fetchRecentUsersWithStatuses, isAuthenticated]);
|
||||
|
||||
const updateStatus = useCallback(async () => {
|
||||
if (!isAuthenticated) {
|
||||
toast.error('You must be signed in to update statuses.');
|
||||
return;
|
||||
}
|
||||
if (!statusInput.trim()) {
|
||||
toast.error('Please enter a valid status.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (selectedIds.length === 0) {
|
||||
const result = await updateUserStatus(statusInput);
|
||||
if (!result.success) throw new Error(result.error);
|
||||
toast.success(`Status updated for signed in user.`);
|
||||
} else {
|
||||
const result = await updateStatuses(selectedIds, statusInput);
|
||||
if (!result.success) throw new Error(result.error);
|
||||
toast.success(`Status updated for ${selectedIds.length} selected users.`);
|
||||
}
|
||||
setSelectedIds([]);
|
||||
setStatusInput('');
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
toast.error(`Failed to update status: ${errorMessage}`);
|
||||
}
|
||||
}, [isAuthenticated, statusInput, selectedIds, usersWithStatuses, profile]);
|
||||
|
||||
const handleCheckboxChange = (id: string) => {
|
||||
setSelectedIds((prev) =>
|
||||
prev.includes(id) ? prev.filter(prevId => prevId !== id) : [...prev, id]
|
||||
);
|
||||
};
|
||||
|
||||
const handleSelectAllChange = () => {
|
||||
if (selectAll) {
|
||||
setSelectedIds([]);
|
||||
} else {
|
||||
setSelectedIds(usersWithStatuses.map(tech => tech.user.id));
|
||||
}
|
||||
setSelectAll(!selectAll);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setSelectAll(
|
||||
selectedIds.length === usersWithStatuses.length &&
|
||||
usersWithStatuses.length > 0
|
||||
);
|
||||
}, [selectedIds.length, usersWithStatuses.length]);
|
||||
|
||||
const formatTime = (timestamp: string) => {
|
||||
const date = new Date(timestamp);
|
||||
const time = date.toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
});
|
||||
const day = date.getDate()
|
||||
const month = date.toLocaleString('default', { month: 'long' });
|
||||
return `${time} = ${month} ${day}`;
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className='flex justify-center items-center min-h-[400px]'>
|
||||
<Progress value={33} className='w-64' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<table className={`w-full text-center border-collapse ${tvMode ? 'text-4xl lg:text-5xl' : 'text-base lg:text-lg'}`}>
|
||||
<thead>
|
||||
<tr className='bg-muted'>
|
||||
{!tvMode && (
|
||||
<th className='py-3 px-3 border'>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='scale-125 cursor-pointer'
|
||||
checked={selectAll}
|
||||
onChange={handleSelectAllChange}
|
||||
/>
|
||||
</th>
|
||||
)}
|
||||
<th className='py-3 px-4 border font-semibold'>Name</th>
|
||||
<th className='py-3 px-4 border font-semibold'>
|
||||
<Drawer>
|
||||
<DrawerTrigger className='hover:underline'>
|
||||
Status
|
||||
</DrawerTrigger>
|
||||
<HistoryDrawer user_id='' />
|
||||
</Drawer>
|
||||
</th>
|
||||
<th className='py-3 px-4 border font-semibold'>Updated At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{usersWithStatuses.map((userWithStatus, index) => (
|
||||
<tr
|
||||
key={userWithStatus.user.id}
|
||||
className={`
|
||||
${index % 2 === 0 ? 'bg-muted/50' : 'bg-background'}
|
||||
hover:bg-muted/75 transition-all duration-300
|
||||
`}
|
||||
>
|
||||
{!tvMode && (
|
||||
<td className='py-2 px-3 border'>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='scale-125 cursor-pointer'
|
||||
checked={selectedIds.includes(userWithStatus.user.id)}
|
||||
onChange={() => handleCheckboxChange(userWithStatus.user.id)}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
<td className='py-3 px-4 border font-medium'>
|
||||
{userWithStatus.user.full_name ?? 'Unknown User'}
|
||||
</td>
|
||||
<td className='py-3 px-4 border'>
|
||||
<Drawer>
|
||||
<DrawerTrigger
|
||||
className='text-left w-full p-2 rounded hover:bg-muted transition-colors'
|
||||
onClick={() => setSelectedHistoryUserId(userWithStatus.user.id)}
|
||||
>
|
||||
{userWithStatus.status}
|
||||
</DrawerTrigger>
|
||||
{selectedHistoryUserId === userWithStatus.user.id && (
|
||||
<HistoryDrawer
|
||||
key={selectedHistoryUserId}
|
||||
user_id={selectedHistoryUserId}
|
||||
/>
|
||||
)}
|
||||
</Drawer>
|
||||
</td>
|
||||
<td className='py-3 px-4 border text-muted-foreground'>
|
||||
{formatTime(userWithStatus.created_at)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user