Tech Table & History components completely rewritten & cleaned up. Just need to implement the realtime subscription now.

This commit is contained in:
2025-06-13 11:38:33 -05:00
parent c96bdab91b
commit b80bf9cd3f
20 changed files with 639 additions and 545 deletions

View File

@ -9,13 +9,10 @@ import {
updateUserStatus,
type UserWithStatus,
} from '@/lib/hooks/status';
import {
Drawer,
DrawerTrigger,
Progress,
} from '@/components/ui';
import { Drawer, DrawerTrigger, Progress } from '@/components/ui';
import { toast } from 'sonner';
import { HistoryDrawer } from '@/components/status';
import type { Profile } from '@/utils/supabase';
import type { RealtimeChannel } from '@supabase/supabase-js';
type TechTableProps = {
@ -33,10 +30,10 @@ export const TechTable = ({
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 [usersWithStatuses, setUsersWithStatuses] =
useState<UserWithStatus[]>(initialStatuses);
const [selectedHistoryUser, setSelectedHistoryUser] =
useState<Profile | null>(null);
const fetchRecentUsersWithStatuses = useCallback(async () => {
try {
@ -44,7 +41,7 @@ export const TechTable = ({
if (!response.success) throw new Error(response.error);
return response.data;
} catch (error) {
toast.error(`Error fetching technicians: ${error as Error}`)
toast.error(`Error fetching technicians: ${error as Error}`);
return [];
}
}, []);
@ -78,19 +75,24 @@ export const TechTable = ({
} else {
const result = await updateStatuses(selectedIds, statusInput);
if (!result.success) throw new Error(result.error);
toast.success(`Status updated for ${selectedIds.length} selected users.`);
toast.success(
`Status updated for ${selectedIds.length} selected users.`,
);
}
setSelectedIds([]);
setStatusInput('');
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
const errorMessage =
error instanceof Error ? error.message : String(error);
toast.error(`Failed to update status: ${errorMessage}`);
}
}, [isAuthenticated, statusInput, selectedIds, usersWithStatuses, profile]);
}, [isAuthenticated, statusInput, selectedIds]);
const handleCheckboxChange = (id: string) => {
setSelectedIds((prev) =>
prev.includes(id) ? prev.filter(prevId => prevId !== id) : [...prev, id]
prev.includes(id)
? prev.filter((prevId) => prevId !== id)
: [...prev, id],
);
};
@ -98,7 +100,7 @@ export const TechTable = ({
if (selectAll) {
setSelectedIds([]);
} else {
setSelectedIds(usersWithStatuses.map(tech => tech.user.id));
setSelectedIds(usersWithStatuses.map((tech) => tech.user.id));
}
setSelectAll(!selectAll);
};
@ -106,7 +108,7 @@ export const TechTable = ({
useEffect(() => {
setSelectAll(
selectedIds.length === usersWithStatuses.length &&
usersWithStatuses.length > 0
usersWithStatuses.length > 0,
);
}, [selectedIds.length, usersWithStatuses.length]);
@ -116,9 +118,9 @@ export const TechTable = ({
hour: 'numeric',
minute: 'numeric',
});
const day = date.getDate()
const day = date.getDate();
const month = date.toLocaleString('default', { month: 'long' });
return `${time} = ${month} ${day}`;
return `${time} - ${month} ${day}`;
};
if (loading) {
@ -131,7 +133,10 @@ export const TechTable = ({
return (
<div className={className}>
<table className={`w-full text-center border-collapse ${tvMode ? 'text-4xl lg:text-5xl' : 'text-base lg:text-lg'}`}>
<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 && (
@ -150,7 +155,7 @@ export const TechTable = ({
<DrawerTrigger className='hover:underline'>
Status
</DrawerTrigger>
<HistoryDrawer user_id='' />
<HistoryDrawer />
</Drawer>
</th>
<th className='py-3 px-4 border font-semibold'>Updated At</th>
@ -171,7 +176,9 @@ export const TechTable = ({
type='checkbox'
className='scale-125 cursor-pointer'
checked={selectedIds.includes(userWithStatus.user.id)}
onChange={() => handleCheckboxChange(userWithStatus.user.id)}
onChange={() =>
handleCheckboxChange(userWithStatus.user.id)
}
/>
</td>
)}
@ -182,15 +189,12 @@ export const TechTable = ({
<Drawer>
<DrawerTrigger
className='text-left w-full p-2 rounded hover:bg-muted transition-colors'
onClick={() => setSelectedHistoryUserId(userWithStatus.user.id)}
onClick={() => setSelectedHistoryUser(userWithStatus.user)}
>
{userWithStatus.status}
</DrawerTrigger>
{selectedHistoryUserId === userWithStatus.user.id && (
<HistoryDrawer
key={selectedHistoryUserId}
user_id={selectedHistoryUserId}
/>
{selectedHistoryUser === userWithStatus.user && (
<HistoryDrawer user={selectedHistoryUser} />
)}
</Drawer>
</td>
@ -201,7 +205,43 @@ export const TechTable = ({
))}
</tbody>
</table>
{!tvMode && (
<div className='mx-auto flex flex-row items-center justify-center py-5 gap-4'>
<input
autoFocus
type='text'
placeholder='New Status'
className={
'min-w-[120px] lg:min-w-[400px] py-2 px-3 rounded-xl \
border bg-background lg:text-2xl focus:outline-none \
focus:ring-2 focus:ring-primary'
}
value={statusInput}
onChange={(e) => setStatusInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
updateStatus().catch((error) => {
toast.error(`Failed to update status: ${error as Error}`);
});
}
}}
/>
<button
type='submit'
className={
'min-w-[100px] lg:min-w-[160px] py-2 px-4 rounded-xl \
text-center font-semibold lg:text-2xl bg-primary \
text-primary-foreground hover:bg-primary/90 \
transition-colors disabled:opacity-50 \
disabled:cursor-not-allowed'
}
onClick={() => void updateStatus()}
disabled={!statusInput.trim()}
>
Update
</button>
</div>
)}
</div>
);
};