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