add admin checks for admin pages and run biome formating 🔨
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 11m8s

This commit is contained in:
2025-07-25 13:31:12 +05:00
parent aedf7cdf7d
commit 9b2f2c1528
127 changed files with 6577 additions and 6334 deletions

View File

@ -4,7 +4,15 @@ import { Loader2, Plus } from "lucide-react";
import { useActionState, useEffect, useState } from "react"; // Import useActionState
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { addDeviceAction } from "@/queries/devices";
@ -19,7 +27,6 @@ export type AddDeviceFormState = {
payload?: FormData;
};
export const initialState: AddDeviceFormState = {
message: "",
fieldErrors: {},
@ -30,7 +37,7 @@ export default function AddDeviceDialogForm({ user_id }: { user_id?: string }) {
const [state, formAction, pending] = useActionState(
addDeviceAction,
initialState
initialState,
);
useEffect(() => {
@ -53,7 +60,11 @@ export default function AddDeviceDialogForm({ user_id }: { user_id?: string }) {
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button className="gap-2 items-center" disabled={pending} variant="default">
<Button
className="gap-2 items-center"
disabled={pending}
variant="default"
>
Add Device
<Plus size={16} />
</Button>
@ -71,9 +82,7 @@ export default function AddDeviceDialogForm({ user_id }: { user_id?: string }) {
<div className="grid gap-4 py-4">
<div className="flex flex-col gap-2">
<div>
<Label htmlFor="device_name">
Device Name
</Label>
<Label htmlFor="device_name">Device Name</Label>
<Input
placeholder="eg: iPhone X"
type="text"
@ -90,13 +99,13 @@ export default function AddDeviceDialogForm({ user_id }: { user_id?: string }) {
</div>
<div>
<Label htmlFor="mac_address">
Mac Address
</Label>
<Label htmlFor="mac_address">Mac Address</Label>
<Input
placeholder="Mac address of your device"
name="mac_address"
defaultValue={(state?.payload?.get("mac_address") || "") as string}
defaultValue={
(state?.payload?.get("mac_address") || "") as string
}
id="mac_address"
className="col-span-3"
/>
@ -117,4 +126,4 @@ export default function AddDeviceDialogForm({ user_id }: { user_id?: string }) {
</DialogContent>
</Dialog>
);
}
}

View File

@ -2,78 +2,85 @@
import { Loader2, MoveLeft } from "lucide-react";
import { useActionState, useEffect } from "react";
import { toast } from "sonner";
import { type UpdateUserFormState, updateUserAgreement } from "@/actions/user-actions";
import {
type UpdateUserFormState,
updateUserAgreement,
} from "@/actions/user-actions";
import { Button } from "@/components/ui/button";
import { FloatingLabelInput } from "@/components/ui/floating-label";
import type { UserProfile } from "@/lib/types/user";
export default function UserAgreementForm({ user }: { user: UserProfile }) {
const initialState: UpdateUserFormState = {
message: "",
fieldErrors: {},
payload: new FormData(),
};
const [state, formAction, isPending] = useActionState(
updateUserAgreement,
initialState,
);
const initialState: UpdateUserFormState = {
message: "",
fieldErrors: {},
payload: new FormData(),
};
const [state, formAction, isPending] = useActionState(
updateUserAgreement,
initialState,
);
useEffect(() => {
if (state.message) {
if (state.fieldErrors) {
Object.entries(state.fieldErrors).forEach(([field, errors]) => {
errors.forEach((error) => {
toast.error(`Error in ${field}: ${error}`);
});
});
} else {
toast.success("Success", {
description: "User agreement updated successfully",
closeButton: true,
});
}
}
}, [state]);
useEffect(() => {
if (state.message) {
if (state.fieldErrors) {
Object.entries(state.fieldErrors).forEach(([field, errors]) => {
errors.forEach((error) => {
toast.error(`Error in ${field}: ${error}`);
});
});
} else {
toast.success("Success", {
description: "User agreement updated successfully",
closeButton: true,
});
}
}
}, [state]);
return (
<div id="user-update-form">
<Button
onClick={() => window.history.back()}
variant="outline"
className="mb-4"
>
<MoveLeft />
Go Back
</Button>
<form action={formAction}>
<h4 className="p-2 rounded font-semibold text-muted-foreground">
Upload User agreement
</h4>
<div className="border border-dashed border-sarLinkOrange p-4 rounded-lg max-w-2xl">
<fieldset
disabled={isPending}
className="space-y-1 my-2 grid grid-cols-1 md:grid-cols-2 gap-4"
>
<input type="hidden" name="userId" value={user.id} />
<FloatingLabelInput
type="file"
size={10}
name="agreement"
label="Agreement Document"
accept=".pdf"
/>
</fieldset>
return (
<div id="user-update-form">
<Button
onClick={() => window.history.back()}
variant="outline"
className="mb-4"
>
<MoveLeft />
Go Back
</Button>
<form action={formAction}>
<h4 className="p-2 rounded font-semibold text-muted-foreground">
Upload User agreement
</h4>
<div className="border border-dashed border-sarLinkOrange p-4 rounded-lg max-w-2xl">
<fieldset
disabled={isPending}
className="space-y-1 my-2 grid grid-cols-1 md:grid-cols-2 gap-4"
>
<input type="hidden" name="userId" value={user.id} />
<FloatingLabelInput
type="file"
size={10}
name="agreement"
label="Agreement Document"
accept=".pdf"
/>
</fieldset>
<Button
disabled={isPending}
type="submit"
variant={"secondary"}
className=""
>
{isPending ? <Loader2 className="animate-spin" /> : "Update Agreement"}
</Button>
</div>
</form>
</div>
);
<Button
disabled={isPending}
type="submit"
variant={"secondary"}
className=""
>
{isPending ? (
<Loader2 className="animate-spin" />
) : (
"Update Agreement"
)}
</Button>
</div>
</form>
</div>
);
}

View File

@ -20,7 +20,6 @@ import type { UserProfile } from "@/lib/types/user";
import { cn } from "@/lib/utils";
import { Textarea } from "../ui/textarea";
export type RejectUserFormState = {
message: string;
fieldErrors?: {
@ -37,7 +36,10 @@ export const initialState: RejectUserFormState = {
export default function UserRejectDialog({ user }: { user: UserProfile }) {
const [open, setOpen] = useState(false);
const [state, formAction, isPending] = useActionState(rejectUser, initialState);
const [state, formAction, isPending] = useActionState(
rejectUser,
initialState,
);
useEffect(() => {
if (state.message && state !== initialState) {
@ -52,7 +54,6 @@ export default function UserRejectDialog({ user }: { user: UserProfile }) {
}
}, [state]);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
@ -87,7 +88,10 @@ export default function UserRejectDialog({ user }: { user: UserProfile }) {
<div className="grid gap-4 py-4">
<div className="flex flex-col items-start gap-2">
<input type="hidden" name="userId" value={user.id} />
<Label htmlFor="reason" className="text-right text-muted-foreground">
<Label
htmlFor="reason"
className="text-right text-muted-foreground"
>
Rejection details
</Label>
<Textarea

View File

@ -17,83 +17,83 @@ import type { UserProfile } from "@/lib/types/user";
// } from "@/components/ui/select";
export default function UserUpdateForm({ user }: { user: UserProfile }) {
const initialState: UpdateUserFormState = {
message: "",
fieldErrors: {},
payload: new FormData(),
};
const [state, formAction, isPending] = useActionState(
updateUser,
initialState,
);
const initialState: UpdateUserFormState = {
message: "",
fieldErrors: {},
payload: new FormData(),
};
const [state, formAction, isPending] = useActionState(
updateUser,
initialState,
);
useEffect(() => {
if (state.message) {
if (state.fieldErrors) {
Object.entries(state.fieldErrors).forEach(([field, errors]) => {
errors.forEach((error) => {
toast.error(`Error in ${field}: ${error}`);
});
});
} else {
toast.success("Success", {
description: "User updated successfully",
closeButton: true,
});
}
}
}, [state]);
useEffect(() => {
if (state.message) {
if (state.fieldErrors) {
Object.entries(state.fieldErrors).forEach(([field, errors]) => {
errors.forEach((error) => {
toast.error(`Error in ${field}: ${error}`);
});
});
} else {
toast.success("Success", {
description: "User updated successfully",
closeButton: true,
});
}
}
}, [state]);
return (
<div id="user-update-form">
<Button
onClick={() => window.history.back()}
variant="outline"
className="mb-4"
>
<MoveLeft />
Go Back
</Button>
<form action={formAction}>
<h4 className="p-2 rounded font-semibold text-muted-foreground">
Update User Information
</h4>
<div className="border border-dashed border-sarLinkOrange p-4 rounded-lg max-w-2xl">
<fieldset
disabled={isPending}
className="space-y-1 my-2 grid grid-cols-1 md:grid-cols-2 gap-4"
>
<input type="hidden" name="userId" value={user.id} />
<FloatingLabelInput
defaultValue={
user?.id_card || (state.payload?.get("id_card") as string)
}
size={10}
name="id_card"
label="ID Card"
/>
<FloatingLabelInput
defaultValue={
user?.first_name || (state.payload?.get("first_name") as string)
}
name="first_name"
label="First Name"
/>
<FloatingLabelInput
defaultValue={
user?.last_name || (state.payload?.get("last_name") as string)
}
name="last_name"
label="Last Name"
/>
<FloatingLabelInput
defaultValue={
user?.address || (state.payload?.get("address") as string)
}
name="address"
label="House Name"
/>
{/* <Select>
return (
<div id="user-update-form">
<Button
onClick={() => window.history.back()}
variant="outline"
className="mb-4"
>
<MoveLeft />
Go Back
</Button>
<form action={formAction}>
<h4 className="p-2 rounded font-semibold text-muted-foreground">
Update User Information
</h4>
<div className="border border-dashed border-sarLinkOrange p-4 rounded-lg max-w-2xl">
<fieldset
disabled={isPending}
className="space-y-1 my-2 grid grid-cols-1 md:grid-cols-2 gap-4"
>
<input type="hidden" name="userId" value={user.id} />
<FloatingLabelInput
defaultValue={
user?.id_card || (state.payload?.get("id_card") as string)
}
size={10}
name="id_card"
label="ID Card"
/>
<FloatingLabelInput
defaultValue={
user?.first_name || (state.payload?.get("first_name") as string)
}
name="first_name"
label="First Name"
/>
<FloatingLabelInput
defaultValue={
user?.last_name || (state.payload?.get("last_name") as string)
}
name="last_name"
label="Last Name"
/>
<FloatingLabelInput
defaultValue={
user?.address || (state.payload?.get("address") as string)
}
name="address"
label="House Name"
/>
{/* <Select>
<SelectTrigger>
<SelectValue placeholder="Select an island" />
</SelectTrigger>
@ -123,29 +123,29 @@ export default function UserUpdateForm({ user }: { user: UserProfile }) {
</SelectGroup>
</SelectContent>
</Select> */}
<FloatingLabelInput
defaultValue={user?.dob}
name="dob"
type="date"
label="DOB"
/>
<FloatingLabelInput
defaultValue={user?.mobile}
name="mobile"
label="Phone Number"
/>
</fieldset>
<FloatingLabelInput
defaultValue={user?.dob}
name="dob"
type="date"
label="DOB"
/>
<FloatingLabelInput
defaultValue={user?.mobile}
name="mobile"
label="Phone Number"
/>
</fieldset>
<Button
disabled={isPending}
type="submit"
variant={"secondary"}
className="col-span-2 w-full"
>
{isPending ? <Loader2 className="animate-spin" /> : "Update User"}
</Button>
</div>
</form>
</div>
);
<Button
disabled={isPending}
type="submit"
variant={"secondary"}
className="col-span-2 w-full"
>
{isPending ? <Loader2 className="animate-spin" /> : "Update User"}
</Button>
</div>
</form>
</div>
);
}

View File

@ -32,7 +32,9 @@ export function UserVerifyDialog({ user }: { user: UserProfile }) {
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle className="text-muted-foreground">Verify User</DialogTitle>
<DialogTitle className="text-muted-foreground">
Verify User
</DialogTitle>
<DialogDescription>
Are you sure you want to verify the following user?
<span className="inline-block my-4">
@ -60,19 +62,24 @@ export function UserVerifyDialog({ user }: { user: UserProfile }) {
setOpen(true);
setDisabled(true);
const res = await verifyUser(String(userId));
if (res.ok) toast.success('User Verified!');
else toast.warning(res.error, {
description: <div>
<p className="italic">The following fields do not match</p>
<ul className="list-disc list-inside p-2">
{res.mismatch_fields?.map((field) => (
<li key={field}>{field}</li>
))}
</ul>
</div>,
closeButton: true,
duration: 10000,
});
if (res.ok) toast.success("User Verified!");
else
toast.warning(res.error, {
description: (
<div>
<p className="italic">
The following fields do not match
</p>
<ul className="list-disc list-inside p-2">
{res.mismatch_fields?.map((field) => (
<li key={field}>{field}</li>
))}
</ul>
</div>
),
closeButton: true,
duration: 10000,
});
setDisabled(false);
}}
>