351 lines
11 KiB
TypeScript
351 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
import { useForm, Controller } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import * as z from "zod";
|
|
import { Card } from "@/components/ui/card";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Button } from "@/components/ui/button";
|
|
import Swal from "sweetalert2";
|
|
import withReactContent from "sweetalert2-react-content";
|
|
import { errorAutoClose, loading, successAutoClose } from "@/lib/swal";
|
|
import { close } from "@/config/swal";
|
|
import { getUserLevelDetail, updateUserLevel } from "@/service/tenant";
|
|
import { UserLevelsCreateRequest, getUserLevels, getProvinces } from "@/service/approval-workflows";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
|
|
const tenantSchema = z.object({
|
|
aliasName: z.string().trim().min(1, { message: "Alias Name wajib diisi" }),
|
|
levelNumber: z
|
|
.number({
|
|
invalid_type_error: "Level Number harus berupa angka",
|
|
})
|
|
.min(1, { message: "Level Number minimal 1" }),
|
|
name: z.string().trim().min(1, { message: "Name wajib diisi" }),
|
|
parentLevelId: z.number().optional(),
|
|
provinceId: z.number().optional(),
|
|
group: z.string().optional(),
|
|
isApprovalActive: z.boolean().default(true),
|
|
isActive: z.boolean().default(true),
|
|
});
|
|
|
|
type TenantSchema = z.infer<typeof tenantSchema>;
|
|
|
|
interface TenantUpdateFormProps {
|
|
id: number;
|
|
initialData?: UserLevelsCreateRequest;
|
|
onSuccess?: () => void;
|
|
onCancel?: () => void;
|
|
}
|
|
|
|
export default function TenantUpdateForm({
|
|
id,
|
|
onSuccess,
|
|
onCancel,
|
|
}: TenantUpdateFormProps) {
|
|
const MySwal = withReactContent(Swal);
|
|
const [loadingData, setLoadingData] = useState(false);
|
|
const [userLevels, setUserLevels] = useState<{id: number; name: string}[]>([]);
|
|
const [provinces, setProvinces] = useState<{id: number; name: string}[]>([]);
|
|
|
|
const {
|
|
control,
|
|
handleSubmit,
|
|
setValue,
|
|
formState: { errors },
|
|
} = useForm<TenantSchema>({
|
|
resolver: zodResolver(tenantSchema),
|
|
defaultValues: {
|
|
aliasName: "",
|
|
levelNumber: 1,
|
|
name: "",
|
|
parentLevelId: undefined,
|
|
provinceId: undefined,
|
|
group: "",
|
|
isApprovalActive: true,
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
async function loadData() {
|
|
setLoadingData(true);
|
|
try {
|
|
const [detailResponse, userLevelsResponse, provincesResponse] = await Promise.all([
|
|
getUserLevelDetail(id),
|
|
getUserLevels(),
|
|
getProvinces(),
|
|
]);
|
|
|
|
if (!detailResponse.error && detailResponse.data) {
|
|
const detail = detailResponse.data;
|
|
setValue("aliasName", detail.aliasName ?? "");
|
|
setValue("levelNumber", detail.levelNumber ?? 1);
|
|
setValue("name", detail.name ?? "");
|
|
setValue("parentLevelId", detail.parentLevelId);
|
|
setValue("provinceId", detail.provinceId);
|
|
setValue("group", detail.group ?? "");
|
|
setValue("isApprovalActive", detail.isApprovalActive ?? true);
|
|
setValue("isActive", detail.isActive ?? true);
|
|
} else {
|
|
console.error("Gagal mengambil detail:", detailResponse.message);
|
|
}
|
|
|
|
if (!userLevelsResponse?.error) {
|
|
setUserLevels(userLevelsResponse?.data?.data || []);
|
|
}
|
|
if (!provincesResponse?.error) {
|
|
setProvinces(provincesResponse?.data?.data || []);
|
|
}
|
|
} catch (err) {
|
|
console.error("Error loading data:", err);
|
|
} finally {
|
|
setLoadingData(false);
|
|
}
|
|
}
|
|
|
|
if (id) loadData();
|
|
}, [id, setValue]);
|
|
|
|
const onSubmit = async (data: TenantSchema) => {
|
|
try {
|
|
loading();
|
|
|
|
const payload: UserLevelsCreateRequest = {
|
|
aliasName: data.aliasName,
|
|
levelNumber: data.levelNumber,
|
|
name: data.name,
|
|
parentLevelId: data.parentLevelId,
|
|
provinceId: data.provinceId,
|
|
group: data.group,
|
|
isApprovalActive: data.isApprovalActive,
|
|
isActive: data.isActive,
|
|
};
|
|
|
|
console.log("Payload dikirim ke API:", payload);
|
|
|
|
const response = await updateUserLevel(Number(id), payload);
|
|
|
|
close();
|
|
|
|
if (response?.error) {
|
|
errorAutoClose(response.message || "Gagal memperbarui data.");
|
|
return;
|
|
}
|
|
|
|
successAutoClose("Data berhasil diperbarui.");
|
|
|
|
setTimeout(() => {
|
|
if (onSuccess) onSuccess();
|
|
}, 3000);
|
|
} catch (err) {
|
|
close();
|
|
errorAutoClose("Terjadi kesalahan saat menyimpan data.");
|
|
console.error("Update user level error:", err);
|
|
}
|
|
};
|
|
|
|
if (loadingData) {
|
|
return (
|
|
<Card className="p-6 w-full lg:w-2/3">
|
|
<div className="flex items-center justify-center py-8">
|
|
<div className="text-center">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto mb-4"></div>
|
|
<p>Loading data...</p>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit(onSubmit)}>
|
|
<Card className="p-6 w-full lg:w-2/3">
|
|
<h2 className="text-lg font-semibold mb-4">
|
|
Update Tenant (User Level)
|
|
</h2>
|
|
|
|
<div className="space-y-4">
|
|
{/* Alias Name */}
|
|
<div>
|
|
<Label>Alias Name</Label>
|
|
<Controller
|
|
control={control}
|
|
name="aliasName"
|
|
render={({ field }) => (
|
|
<Input {...field} placeholder="Masukkan alias name" />
|
|
)}
|
|
/>
|
|
{errors.aliasName && (
|
|
<p className="text-red-500 text-sm">{errors.aliasName.message}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Level Number */}
|
|
<div>
|
|
<Label>Level Number</Label>
|
|
<Controller
|
|
control={control}
|
|
name="levelNumber"
|
|
render={({ field }) => (
|
|
<Input
|
|
type="number"
|
|
{...field}
|
|
onChange={(e) => field.onChange(Number(e.target.value) || 1)}
|
|
placeholder="Masukkan level number"
|
|
/>
|
|
)}
|
|
/>
|
|
{errors.levelNumber && (
|
|
<p className="text-red-500 text-sm">
|
|
{errors.levelNumber.message}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Name */}
|
|
<div>
|
|
<Label>Nama</Label>
|
|
<Controller
|
|
control={control}
|
|
name="name"
|
|
render={({ field }) => (
|
|
<Input {...field} placeholder="Masukkan nama" />
|
|
)}
|
|
/>
|
|
{errors.name && (
|
|
<p className="text-red-500 text-sm">{errors.name.message}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Parent Level */}
|
|
<div>
|
|
<Label>Parent Level</Label>
|
|
<Controller
|
|
control={control}
|
|
name="parentLevelId"
|
|
render={({ field }) => (
|
|
<Select onValueChange={(value) => field.onChange(value && value !== "no-data" ? Number(value) : undefined)} value={field.value?.toString() || ""}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Pilih parent level" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{userLevels.length > 0 ? (
|
|
userLevels.map((level) => (
|
|
<SelectItem key={level.id} value={level.id.toString()}>
|
|
{level.name}
|
|
</SelectItem>
|
|
))
|
|
) : (
|
|
<SelectItem value="no-data" disabled>
|
|
Tidak ada data tersedia
|
|
</SelectItem>
|
|
)}
|
|
</SelectContent>
|
|
</Select>
|
|
)}
|
|
/>
|
|
{errors.parentLevelId && (
|
|
<p className="text-red-500 text-sm">{errors.parentLevelId.message}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Province */}
|
|
<div>
|
|
<Label>Provinsi</Label>
|
|
<Controller
|
|
control={control}
|
|
name="provinceId"
|
|
render={({ field }) => (
|
|
<Select onValueChange={(value) => field.onChange(value && value !== "no-data" ? Number(value) : undefined)} value={field.value?.toString() || ""}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Pilih provinsi" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{provinces.length > 0 ? (
|
|
provinces.map((province) => (
|
|
<SelectItem key={province.id} value={province.id.toString()}>
|
|
{province.name}
|
|
</SelectItem>
|
|
))
|
|
) : (
|
|
<SelectItem value="no-data" disabled>
|
|
Tidak ada data tersedia
|
|
</SelectItem>
|
|
)}
|
|
</SelectContent>
|
|
</Select>
|
|
)}
|
|
/>
|
|
{errors.provinceId && (
|
|
<p className="text-red-500 text-sm">{errors.provinceId.message}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Group */}
|
|
<div>
|
|
<Label>Group</Label>
|
|
<Controller
|
|
control={control}
|
|
name="group"
|
|
render={({ field }) => (
|
|
<Input {...field} placeholder="Masukkan group" />
|
|
)}
|
|
/>
|
|
{errors.group && (
|
|
<p className="text-red-500 text-sm">{errors.group.message}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Approval Active */}
|
|
<div className="flex items-center space-x-2">
|
|
<Controller
|
|
control={control}
|
|
name="isApprovalActive"
|
|
render={({ field }) => (
|
|
<Checkbox
|
|
checked={field.value}
|
|
onCheckedChange={field.onChange}
|
|
/>
|
|
)}
|
|
/>
|
|
<Label>Approval Active</Label>
|
|
{errors.isApprovalActive && (
|
|
<p className="text-red-500 text-sm">{errors.isApprovalActive.message}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Active */}
|
|
<div className="flex items-center space-x-2">
|
|
<Controller
|
|
control={control}
|
|
name="isActive"
|
|
render={({ field }) => (
|
|
<Checkbox
|
|
checked={field.value}
|
|
onCheckedChange={field.onChange}
|
|
/>
|
|
)}
|
|
/>
|
|
<Label>Active</Label>
|
|
{errors.isActive && (
|
|
<p className="text-red-500 text-sm">{errors.isActive.message}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Action Buttons */}
|
|
<div className="mt-6 flex justify-end gap-3">
|
|
<Button type="submit">Update</Button>
|
|
<Button type="button" variant="outline" onClick={() => onCancel?.()}>
|
|
Cancel
|
|
</Button>
|
|
</div>
|
|
</Card>
|
|
</form>
|
|
);
|
|
}
|