AIvoices/frontend-nextjs/app/components/Settings/AppSettings.tsx
2025-04-26 01:59:26 +01:00

205 lines
No EOL
8 KiB
TypeScript

"use client";
import { connectUserToDevice, signOutAction } from "@/app/actions";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { LogOut } from "lucide-react";
import GeneralUserForm from "./UserForm";
import { Slider } from "@/components/ui/slider";
import { updateUser } from "@/db/users";
import _ from "lodash";
import { createClient } from "@/utils/supabase/client";
import React, { useCallback } from "react";
import { doesUserHaveADevice, updateDevice } from "@/db/devices";
import { useToast } from "@/components/ui/use-toast";
interface AppSettingsProps {
selectedUser: IUser;
heading: React.ReactNode;
}
const skipDeviceRegistration = process.env.NEXT_PUBLIC_SKIP_DEVICE_REGISTRATION === "True";
const AppSettings: React.FC<AppSettingsProps> = ({
selectedUser,
heading,
}) => {
const supabase = createClient();
const { toast } = useToast();
const [isConnected, setIsConnected] = React.useState(false);
const doctorFormRef = React.useRef<{ submitForm: () => void } | null>(null);
const userFormRef = React.useRef<{ submitForm: () => void } | null>(null);
const [deviceCode, setDeviceCode] = React.useState("");
const [error, setError] = React.useState("");
const handleSave = () => {
if (selectedUser.user_info.user_type === "doctor") {
doctorFormRef.current?.submitForm();
} else {
userFormRef.current?.submitForm();
}
};
const checkIfUserHasDevice = useCallback(async () => {
setIsConnected(
await doesUserHaveADevice(supabase, selectedUser.user_id)
);
}, [selectedUser.user_id, supabase]);
React.useEffect(() => {
checkIfUserHasDevice();
}, [checkIfUserHasDevice]);
const [volume, setVolume] = React.useState([
selectedUser.device?.volume ?? 50,
]);
const debouncedUpdateVolume = _.debounce(async () => {
if (selectedUser.device?.device_id) {
await updateDevice(
supabase,
{ volume: volume[0] },
selectedUser.device.device_id
);
}
}, 1000); // Adjust the debounce delay as needed
const updateVolume = (value: number[]) => {
setVolume(value);
debouncedUpdateVolume();
};
const onSave = async (values: any, userType: "doctor" | "user", userId: string) => {
await updateUser(
supabase,
{
supervisee_age: values.supervisee_age,
supervisee_name: values.supervisee_name,
supervisee_persona: values.supervisee_persona,
user_info: {
user_type: userType,
user_metadata: values,
},
},
userId);
toast({
description: "Your prefereces have been saved!",
});
}
return (
<>
<GeneralUserForm
selectedUser={selectedUser}
userId={selectedUser.user_id}
heading={heading}
onSave={onSave}
onClickCallback={() => handleSave()}
/>
<div className="space-y-4 max-w-screen-sm mt-12">
<h2 className="text-lg font-semibold border-b border-gray-200 pb-2">
Device settings
</h2>
{skipDeviceRegistration && <div className="flex flex-col text-purple-500 text-xs gap-2">You don't need to register your device because NEXT_PUBLIC_SKIP_DEVICE_REGISTRATION is set to True.</div>}
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<div className="flex flex-row items-center gap-2">
<Label className="text-sm font-medium text-gray-700">
Register your device
</Label>
<div
className={`rounded-full flex-shrink-0 h-2 w-2 ${
isConnected ? 'bg-green-500' : 'bg-amber-500'
}`}
/>
</div>
<div className="flex flex-row items-center gap-2 mt-2">
<Input
value={deviceCode}
disabled={isConnected || skipDeviceRegistration}
onChange={(e) => setDeviceCode(e.target.value)}
placeholder={isConnected ? "**********" : "Enter your device code"}
maxLength={100}
/>
<Button
size="sm"
variant="outline"
disabled={isConnected || skipDeviceRegistration}
onClick={async () => {
const result = await connectUserToDevice(selectedUser.user_id, deviceCode);
if (!result) {
setError("Error registering device");
}
checkIfUserHasDevice();
}}
>
Register
</Button>
</div>
<p className="text-xs text-gray-400">
{isConnected ? <span className="font-medium text-gray-800">Registered!</span> :
error ? <span className="text-red-500">{error}.</span> :
"Enter your device code to register it."
}
</p>
</div>
<div className="flex flex-col gap-2 mt-2">
<Label className="text-sm font-medium text-gray-700">
Logged in as
</Label>
<Input
// autoFocus
disabled
value={selectedUser?.email}
className="max-w-screen-sm h-10 bg-white"
autoComplete="on"
style={{
fontSize: 16,
}}
/>
</div>
{isConnected && <div className="flex flex-col gap-4 mt-6">
<Label className="text-sm font-medium text-gray-700">
Device volume
</Label>
<div className="flex flex-row gap-2 items-center flex-nowrap">
<Slider
value={volume}
onValueChange={updateVolume}
className="sm:w-1/2"
defaultValue={[50]}
max={100}
min={1}
step={1}
/>
<p className="text-gray-500 text-sm">{volume}%</p>
</div>
</div>}
<form
action={signOutAction}
className="flex flex-row justify-between mt-4"
>
<Button
variant="destructive_outline"
size="sm"
className="font-medium flex flex-row items-center rounded-full gap-2 "
>
<LogOut size={18} strokeWidth={2} />
<span>Logout</span>
</Button>
</form>
</div>
</div>
</>
);
};
export default AppSettings;