import React, { useState } from "react"; import { useSupabaseClient } from "@supabase/auth-helpers-react"; import { ButtonContent, CaptionText, Conditional, dateToTimeString, PrimaryButton, SecondaryButton, } from "ui"; import { useUserShiftStart } from "../../hooks"; import { useQueryClient } from "react-query"; import { CirclePlay, CircleStop } from "lucide-react"; interface ShiftStatus { isOnShift: boolean; onShiftSince: string | null | undefined; isOnBreak: boolean; onBreakSince: string | null | undefined; } export const UserShiftStateButton = () => { const { data: shiftStatus, isError, error: loadShiftStatusError, } = useUserShiftStart(); const [error, setError] = useState<string>(); const supabaseClient = useSupabaseClient(); const queryClient = useQueryClient(); const logUserAction = async ( action: string, details: object | null, userId: string, ) => { const { error: insertError } = await supabaseClient .from("UserHistory") .insert([{ user: userId, action, detail: details }]); if (insertError) { setError("Failed to log user action"); } }; const toggleShiftStatus = async () => { const { data: sessionData } = await supabaseClient.auth.getSession(); if (sessionData?.session) { const userId = sessionData.session.user.id; const now = new Date().toISOString(); let newStatus: string; let details: { duration?: string; start?: string; end?: string } | null = null; if (shiftStatus?.isOnShift && shiftStatus.onShiftSince) { const shiftStart = new Date(shiftStatus.onShiftSince); const shiftEnd = new Date(); const shiftDuration = shiftEnd.getTime() - shiftStart.getTime(); details = { duration: dateToTimeString(shiftDuration), start: shiftStart.toISOString(), end: shiftEnd.toISOString(), }; newStatus = "SHIFT END"; if (shiftStatus.isOnBreak && shiftStatus.onBreakSince) { const breakStart = new Date(shiftStatus.onBreakSince); const breakEnd = new Date(); const breakDuration = breakEnd.getTime() - breakStart.getTime(); const breakDetails = { duration: dateToTimeString(breakDuration), start: breakStart.toISOString(), end: breakEnd.toISOString(), }; await logUserAction("BREAK END", breakDetails, userId); } } else { newStatus = "SHIFT START"; } const { error: updateError } = await supabaseClient .from("UserLastWorkedOn") .update({ shift_start: shiftStatus?.isOnShift ? null : now, break_start: null, }) .eq("user", userId); if (updateError) { setError("Failed to toggle shift status"); return; } await logUserAction(newStatus, details, userId); queryClient.invalidateQueries({ queryKey: ["userShiftStatus"] }); } }; const toggleBreakStatus = async () => { const { data: sessionData } = await supabaseClient.auth.getSession(); if (sessionData?.session) { const userId = sessionData.session.user.id; const now = new Date().toISOString(); let newStatus: string; let details: { duration?: string; start?: string; end?: string } | null = null; if (shiftStatus?.isOnBreak && shiftStatus.onBreakSince) { const breakStart = new Date(shiftStatus.onBreakSince); const breakEnd = new Date(); const breakDuration = breakEnd.getTime() - breakStart.getTime(); details = { duration: dateToTimeString(breakDuration), start: breakStart.toISOString(), end: breakEnd.toISOString(), }; newStatus = "BREAK END"; } else { newStatus = "BREAK START"; } const { error: updateError } = await supabaseClient .from("UserLastWorkedOn") .update({ break_start: shiftStatus?.isOnBreak ? null : now, }) .eq("user", userId); if (updateError) { setError("Failed to toggle break status"); return; } await logUserAction(newStatus, details, userId); queryClient.invalidateQueries({ queryKey: ["userShiftStatus"] }); } }; return ( <Conditional condition={!isError} elseChildren={ <CaptionText> Error: {isError ? `${loadShiftStatusError}` : ""} {error} </CaptionText> } > <CaptionText className="mt-1"> You are {shiftStatus?.isOnShift ? "On" : "Off"} shift </CaptionText> <PrimaryButton onClick={toggleShiftStatus} className="min-w-32 rounded-none rounded-bl-md rounded-tl-md" > <ButtonContent className="gap-2"> <div>{shiftStatus?.isOnShift ? "End" : "Start"} Shift</div> <Conditional condition={!shiftStatus?.isOnShift} elseChildren={<CircleStop size={16} />} > <CirclePlay size={16} /> </Conditional> </ButtonContent> </PrimaryButton> <SecondaryButton onClick={toggleBreakStatus} disabled={!shiftStatus?.isOnShift} className="min-w-32 rounded-none rounded-br-md rounded-tr-md" > <ButtonContent className="gap-2"> <div>{shiftStatus?.isOnBreak ? "End" : "Start"} Break</div> <Conditional condition={!shiftStatus?.isOnBreak} elseChildren={<CircleStop size={16} />} > <CirclePlay size={16} /> </Conditional> </ButtonContent> </SecondaryButton> </Conditional> ); };
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter