251210:1709 Frontend: reeactor organization and run build
Some checks failed
Spec Validation / validate-markdown (push) Has been cancelled
Spec Validation / validate-diagrams (push) Has been cancelled
Spec Validation / check-todos (push) Has been cancelled

This commit is contained in:
admin
2025-12-10 17:09:11 +07:00
parent aa96cd90e3
commit c8a0f281ef
140 changed files with 3780 additions and 1473 deletions

View File

@@ -0,0 +1,211 @@
"use client";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
useCreateOrganization,
useUpdateOrganization,
} from "@/hooks/use-master-data";
import { useEffect } from "react";
import { Organization } from "@/types/organization";
// Organization role types matching database
const ORGANIZATION_ROLES = [
{ value: "1", label: "Owner" },
{ value: "2", label: "Designer" },
{ value: "3", label: "Consultant" },
{ value: "4", label: "Contractor" },
{ value: "5", label: "Third Party" },
] as const;
const organizationSchema = z.object({
organizationCode: z.string().min(1, "Organization Code is required"),
organizationName: z.string().min(1, "Organization Name is required"),
roleId: z.string().optional(),
isActive: z.boolean().optional(),
});
type OrganizationFormData = z.infer<typeof organizationSchema>;
interface OrganizationDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
organization?: Organization | null;
}
export function OrganizationDialog({
open,
onOpenChange,
organization,
}: OrganizationDialogProps) {
const createOrg = useCreateOrganization();
const updateOrg = useUpdateOrganization();
const {
register,
handleSubmit,
reset,
setValue,
watch,
control,
formState: { errors },
} = useForm<OrganizationFormData>({
resolver: zodResolver(organizationSchema),
defaultValues: {
organizationCode: "",
organizationName: "",
roleId: "",
isActive: true,
},
});
useEffect(() => {
if (organization) {
reset({
organizationCode: organization.organizationCode,
organizationName: organization.organizationName,
roleId: organization.roleId?.toString() || "",
isActive: organization.isActive,
});
} else {
reset({
organizationCode: "",
organizationName: "",
roleId: "",
isActive: true,
});
}
}, [organization, reset, open]);
const onSubmit = (data: OrganizationFormData) => {
const submitData = {
...data,
roleId: data.roleId ? parseInt(data.roleId) : undefined,
};
if (organization) {
updateOrg.mutate(
{ id: organization.id, data: submitData },
{ onSuccess: () => onOpenChange(false) }
);
} else {
createOrg.mutate(submitData, {
onSuccess: () => onOpenChange(false),
});
}
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>
{organization ? "Edit Organization" : "New Organization"}
</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Organization Code *</Label>
<Input
placeholder="e.g. OWNER"
{...register("organizationCode")}
/>
{errors.organizationCode && (
<p className="text-sm text-red-500">
{errors.organizationCode.message}
</p>
)}
</div>
<div className="space-y-2">
<Label>Role</Label>
<Select
value={watch("roleId")}
onValueChange={(value) => setValue("roleId", value)}
>
<SelectTrigger>
<SelectValue placeholder="Select role" />
</SelectTrigger>
<SelectContent>
{ORGANIZATION_ROLES.map((role) => (
<SelectItem key={role.value} value={role.value}>
{role.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<Label>Organization Name *</Label>
<Input
placeholder="e.g. Project Owner Co., Ltd."
{...register("organizationName")}
/>
{errors.organizationName && (
<p className="text-sm text-red-500">
{errors.organizationName.message}
</p>
)}
</div>
<div className="flex items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<Label>Active Status</Label>
<p className="text-sm text-muted-foreground">
Enable or disable this organization
</p>
</div>
<Controller
control={control}
name="isActive"
render={({ field }) => (
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
)}
/>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
>
Cancel
</Button>
<Button
type="submit"
disabled={createOrg.isPending || updateOrg.isPending}
>
{organization ? "Save Changes" : "Create Organization"}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}