fix: conflict when pull main

This commit is contained in:
Sabda Yagra 2025-10-02 09:29:25 +07:00
commit 1b476ca121
13 changed files with 205 additions and 105 deletions

View File

@ -186,7 +186,7 @@ const usePendingApprovalColumns = () => {
View View
</DropdownMenuItem> </DropdownMenuItem>
</Link> </Link>
{canApprove && ( {/* {canApprove && (
<DropdownMenuItem <DropdownMenuItem
className="p-2 border-b text-green-700 bg-green-50 group rounded-none" className="p-2 border-b text-green-700 bg-green-50 group rounded-none"
onClick={() => { onClick={() => {
@ -197,7 +197,7 @@ const usePendingApprovalColumns = () => {
<CheckCircle className="w-4 h-4 me-1.5" /> <CheckCircle className="w-4 h-4 me-1.5" />
Approve Approve
</DropdownMenuItem> </DropdownMenuItem>
)} )} */}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
); );

View File

@ -186,7 +186,7 @@ const usePendingApprovalColumns = () => {
View View
</DropdownMenuItem> </DropdownMenuItem>
</Link> </Link>
{canApprove && ( {/* {canApprove && (
<DropdownMenuItem <DropdownMenuItem
className="p-2 border-b text-green-700 bg-green-50 group rounded-none" className="p-2 border-b text-green-700 bg-green-50 group rounded-none"
onClick={() => { onClick={() => {
@ -197,7 +197,7 @@ const usePendingApprovalColumns = () => {
<CheckCircle className="w-4 h-4 me-1.5" /> <CheckCircle className="w-4 h-4 me-1.5" />
Approve Approve
</DropdownMenuItem> </DropdownMenuItem>
)} )} */}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
); );

View File

@ -186,7 +186,7 @@ const usePendingApprovalColumns = () => {
View View
</DropdownMenuItem> </DropdownMenuItem>
</Link> </Link>
{canApprove && ( {/* {canApprove && (
<DropdownMenuItem <DropdownMenuItem
className="p-2 border-b text-green-700 bg-green-50 group rounded-none" className="p-2 border-b text-green-700 bg-green-50 group rounded-none"
onClick={() => { onClick={() => {
@ -197,7 +197,7 @@ const usePendingApprovalColumns = () => {
<CheckCircle className="w-4 h-4 me-1.5" /> <CheckCircle className="w-4 h-4 me-1.5" />
Approve Approve
</DropdownMenuItem> </DropdownMenuItem>
)} )} */}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
); );

View File

@ -192,7 +192,7 @@ const usePendingApprovalColumns = () => {
View View
</DropdownMenuItem> </DropdownMenuItem>
</Link> </Link>
{canApprove && ( {/* {canApprove && (
<DropdownMenuItem <DropdownMenuItem
className="p-2 border-b text-green-700 bg-green-50 group rounded-none" className="p-2 border-b text-green-700 bg-green-50 group rounded-none"
onClick={() => { onClick={() => {
@ -203,7 +203,7 @@ const usePendingApprovalColumns = () => {
<CheckCircle className="w-4 h-4 me-1.5" /> <CheckCircle className="w-4 h-4 me-1.5" />
Approve Approve
</DropdownMenuItem> </DropdownMenuItem>
)} )} */}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
); );

View File

@ -294,31 +294,19 @@ export default function FormVideoDetail() {
async function save() { async function save() {
const data = { const data = {
mediaUploadId: id, action: status == "2" ? "approve" : status == "3" ? "revision" : "reject",
statusId: status,
message: description, message: description,
files: isUserMabesApprover ? getPlacement() : [],
}; };
setModalOpen(false); setModalOpen(false);
loading(); loading();
const response = await submitApproval(data); const response = await submitApproval(id, data);
if (response?.error) { if (response?.error) {
error(response.message); error(response.message);
return false; return false;
} }
const dataReject = {
listFiles: rejectedFiles,
};
const resReject = await rejectFiles(dataReject);
if (resReject?.error) {
error(resReject.message);
return false;
}
close(); close();
submitApprovalSuccesss(); submitApprovalSuccesss();
@ -403,7 +391,7 @@ export default function FormVideoDetail() {
confirmButtonText: "OK", confirmButtonText: "OK",
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
router.push("/contributor/content/video"); router.push("/admin/content/video");
} }
}); });
}; };

View File

@ -366,32 +366,19 @@ export default function FormAudioDetail() {
async function save() { async function save() {
const data = { const data = {
mediaUploadId: id, action: status == "2" ? "approve" : status == "3" ? "revision" : "reject",
statusId: status,
message: description, message: description,
// files: [],
files: isUserMabesApprover ? getPlacement() : [],
}; };
setModalOpen(false); setModalOpen(false);
loading(); loading();
const response = await submitApproval(data); const response = await submitApproval(id, data);
if (response?.error) { if (response?.error) {
error(response.message); error(response.message);
return false; return false;
} }
const dataReject = {
listFiles: rejectedFiles,
};
const resReject = await rejectFiles(dataReject);
if (resReject?.error) {
error(resReject.message);
return false;
}
close(); close();
submitApprovalSuccesss(); submitApprovalSuccesss();
@ -471,7 +458,7 @@ export default function FormAudioDetail() {
confirmButtonColor: "#3085d6", confirmButtonColor: "#3085d6",
confirmButtonText: "OK", confirmButtonText: "OK",
}).then(() => { }).then(() => {
router.push("/in/contributor/content/audio"); router.push("/admin/content/audio");
}); });
}; };

View File

@ -321,32 +321,19 @@ export default function FormTeksDetail() {
async function save() { async function save() {
const data = { const data = {
mediaUploadId: id, action: status == "2" ? "approve" : status == "3" ? "revision" : "reject",
statusId: status,
message: description, message: description,
files: isUserMabesApprover ? getPlacement() : [],
}; };
setModalOpen(false);
setModalOpen(false);
loading(); loading();
const response = await submitApproval(data); const response = await submitApproval(id, data);
if (response?.error) { if (response?.error) {
error(response.message); error(response.message);
return false; return false;
} }
const dataReject = {
listFiles: rejectedFiles,
};
const resReject = await rejectFiles(dataReject);
if (resReject?.error) {
error(resReject.message);
return false;
}
close(); close();
submitApprovalSuccesss(); submitApprovalSuccesss();

View File

@ -452,34 +452,19 @@ export default function FormImageDetail() {
async function save() { async function save() {
const data = { const data = {
mediaUploadId: id, action: status == "2" ? "approve" : status == "3" ? "revision" : "reject",
statusId: status,
message: description, message: description,
files: isUserMabesApprover ? getPlacement() : [],
}; };
setModalOpen(false); setModalOpen(false);
loading(); loading();
const response = await submitApproval(data); const response = await submitApproval(id, data);
if (response?.error) { if (response?.error) {
error(response.message); error(response.message);
return false; return false;
} }
const dataReject = {
listFiles: rejectedFiles,
};
console.log("reject", dataReject);
const resReject = await rejectFiles(dataReject);
if (resReject?.error) {
error(resReject.message);
return false;
}
close(); close();
submitApprovalSuccesss(); submitApprovalSuccesss();
@ -551,7 +536,7 @@ export default function FormImageDetail() {
confirmButtonText: "OK", confirmButtonText: "OK",
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
router.push("/contributor/content/image"); router.push("/admin/content/image");
} }
}); });
}; };

View File

@ -9,9 +9,11 @@ import { Input } from "../ui/input";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { Label } from "../ui/label"; import { Label } from "../ui/label";
import { RadioGroup, RadioGroupItem } from "../ui/radio-group"; import { RadioGroup, RadioGroupItem } from "../ui/radio-group";
import { registerTenant } from "@/service/auth";
// ✅ import services // ✅ import services
import { requestOTP, createUser } from "@/service/auth"; import { requestOTP, createUser } from "@/service/auth";
import { EyeFilledIcon, EyeSlashFilledIcon } from "../icons";
export default function SignUp() { export default function SignUp() {
const router = useRouter(); const router = useRouter();
@ -21,6 +23,32 @@ export default function SignUp() {
const [role, setRole] = useState("umum"); const [role, setRole] = useState("umum");
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [otp, setOtp] = useState(["", "", "", "", "", ""]); const [otp, setOtp] = useState(["", "", "", "", "", ""]);
const [membership, setMembership] = useState("");
const [certNumber, setCertNumber] = useState("");
const [membershipType, setMembershipType] = useState("");
const [nrp, setNrp] = useState("");
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [whatsapp, setWhatsapp] = useState("");
const [namaTenant, setNamaTenant] = useState("");
const [tenantPassword, setTenantPassword] = useState("");
const [confirmTenantPassword, setConfirmTenantPassword] = useState("");
const [firstNameKontributor, setFirstNameKontributor] = useState("");
const [lastNameKontributor, setLastNameKontributor] = useState("");
const [whatsappKontributor, setWhatsappKontributor] = useState("");
const [namaPerusahaan, setNamaPerusahaan] = useState("");
const [kategoriPerusahaan, setKategoriPerusahaan] = useState("");
const [kontributorPassword, setKontributorPassword] = useState("");
const [confirmKontributorPassword, setConfirmKontributorPassword] =
useState("");
const [isLoading, setIsLoading] = useState(false);
const [formErrors, setFormErrors] = useState<any>({});
// di atas, bareng useState lainnya
const [isVisible, setIsVisible] = useState(false);
const [isVisibleSetup, setIsVisibleSetup] = useState([false, false]);
// fungsi helper
const toggleVisibility = () => setIsVisible(!isVisible);
// data user lengkap // data user lengkap
const [fullname, setFullname] = useState(""); const [fullname, setFullname] = useState("");
@ -54,6 +82,15 @@ export default function SignUp() {
console.error("Error send otp:", err); console.error("Error send otp:", err);
MySwal.fire("Error", "Terjadi kesalahan server", "error"); MySwal.fire("Error", "Terjadi kesalahan server", "error");
} }
// If role is tenant, handle tenant registration directly
if (role === "tenant") {
handleTenantRegistration(e);
return;
}
// For other roles, proceed with OTP flow
setStep("otp");
}; };
// 🔹 Handle OTP Verification (dummy → lanjut form) // 🔹 Handle OTP Verification (dummy → lanjut form)
@ -73,26 +110,25 @@ export default function SignUp() {
const handleRegister = async (e: React.FormEvent) => { const handleRegister = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
const payload = { const payload = {
address, address,
clientId: "web-app", // contoh default clientId: "web-app", // contoh default
dateOfBirth, dateOfBirth,
email, email,
fullName: fullname, // 🔥 fix disini (bukan fullname) fullName: fullname, // 🔥 fix disini (bukan fullname)
genderType, genderType,
identityGroup: "", identityGroup: "",
identityGroupNumber: "", identityGroupNumber: "",
identityNumber: "", identityNumber: "",
identityType: "", identityType: "",
lastEducation: "", lastEducation: "",
password, password,
phoneNumber, phoneNumber,
userLevelId: 1, userLevelId: 1,
userRoleId: 1, userRoleId: 1,
username, username,
workType, workType,
}; };
try { try {
const res = await createUser(payload); const res = await createUser(payload);
@ -117,6 +153,124 @@ export default function SignUp() {
if (value && nextInput) nextInput.focus(); if (value && nextInput) nextInput.focus();
}; };
// Form validation functions
const validateEmail = (email: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const validatePhone = (phone: string) => {
const phoneRegex = /^[0-9+\-\s()]+$/;
return phoneRegex.test(phone) && phone.length >= 10;
};
const validatePassword = (password: string) => {
return password.length >= 8;
};
const validateTenantForm = () => {
const errors: any = {};
if (!firstName.trim()) {
errors.firstName = "First name is required";
}
if (!lastName.trim()) {
errors.lastName = "Last name is required";
}
if (!email.trim()) {
errors.email = "Email is required";
} else if (!validateEmail(email)) {
errors.email = "Please enter a valid email address";
}
if (!whatsapp.trim()) {
errors.whatsapp = "WhatsApp number is required";
} else if (!validatePhone(whatsapp)) {
errors.whatsapp = "Please enter a valid phone number";
}
if (!namaTenant.trim()) {
errors.namaTenant = "Tenant name is required";
}
if (!tenantPassword) {
errors.tenantPassword = "Password is required";
} else if (!validatePassword(tenantPassword)) {
errors.tenantPassword = "Password must be at least 8 characters";
}
if (!confirmTenantPassword) {
errors.confirmTenantPassword = "Please confirm your password";
} else if (tenantPassword !== confirmTenantPassword) {
errors.confirmTenantPassword = "Passwords do not match";
}
setFormErrors(errors);
return Object.keys(errors).length === 0;
};
const handleTenantRegistration = async (e: React.FormEvent) => {
e.preventDefault();
if (!validateTenantForm()) {
return;
}
setIsLoading(true);
setFormErrors({});
try {
const registrationData = {
adminUser: {
address: "Jakarta", // Default address as per API requirement
email: email,
fullname: `${firstName} ${lastName}`,
password: tenantPassword,
phoneNumber: whatsapp,
username: `${firstName}-${lastName}`, // Using firstName + lastName as username
},
client: {
clientType: "sub_client", // Hardcoded as per API requirement
name: namaTenant,
parentClientId: "78356d32-52fa-4dfc-b836-6cebf4e3eead", // Hardcoded as per API requirement
},
};
const response = await registerTenant(registrationData);
if (response.error) {
MySwal.fire({
title: "Registration Failed",
text: response.message || "An error occurred during registration",
icon: "error",
confirmButtonText: "OK",
});
} else {
MySwal.fire({
title: "Registration Successful",
text: "Your tenant account has been created successfully!",
icon: "success",
confirmButtonText: "OK",
}).then(() => {
// Redirect to login page or dashboard
router.push("/auth");
});
}
} catch (error) {
console.error("Registration error:", error);
MySwal.fire({
title: "Registration Failed",
text: "An unexpected error occurred. Please try again.",
icon: "error",
confirmButtonText: "OK",
});
} finally {
setIsLoading(false);
}
};
return ( return (
<div className="min-h-screen flex"> <div className="min-h-screen flex">
{/* Left Side Logo */} {/* Left Side Logo */}
@ -185,12 +339,6 @@ export default function SignUp() {
{/* Step 3: Form Lengkap */} {/* Step 3: Form Lengkap */}
{step === "form" && ( {step === "form" && (
<form onSubmit={handleRegister} className="space-y-4"> <form onSubmit={handleRegister} className="space-y-4">
<Input
type="text"
placeholder="Nama Lengkap"
value={fullname}
onChange={(e) => setFullname(e.target.value)}
/>
<Input <Input
type="text" type="text"
placeholder="Username" placeholder="Username"
@ -216,9 +364,9 @@ export default function SignUp() {
onChange={(e) => setDateOfBirth(e.target.value)} onChange={(e) => setDateOfBirth(e.target.value)}
/> />
<select <select
className="w-full border rounded-md p-2"
value={genderType} value={genderType}
onChange={(e) => setGenderType(e.target.value)} onChange={(e) => setGenderType(e.target.value)}
className="w-full border rounded-md p-2"
> >
<option value="male">Laki-laki</option> <option value="male">Laki-laki</option>
<option value="female">Perempuan</option> <option value="female">Perempuan</option>

View File

@ -97,6 +97,7 @@ const nextConfig = {
{ protocol: "https", hostname: "avatars.githubusercontent.com" }, { protocol: "https", hostname: "avatars.githubusercontent.com" },
{ protocol: "https", hostname: "i.pravatar.cc" }, { protocol: "https", hostname: "i.pravatar.cc" },
{ protocol: "https", hostname: "dev.mikulnews.com" }, { protocol: "https", hostname: "dev.mikulnews.com" },
{ protocol: "https", hostname: "kontenhumas.com" },
{ protocol: "https", hostname: "netidhub.com" }, { protocol: "https", hostname: "netidhub.com" },
{ protocol: "https", hostname: "kontenhumas.com" }, { protocol: "https", hostname: "kontenhumas.com" },
], ],

View File

@ -208,3 +208,7 @@ export async function requestOTP(data: { email: string; name: string }) {
const url = "users/otp-request"; const url = "users/otp-request";
return httpPostInterceptor(url, data); return httpPostInterceptor(url, data);
} }
export async function registerTenant(data: any) {
const url = "clients/with-user";
return httpPost(url, data);
}

View File

@ -285,8 +285,8 @@ export async function convertSPIT(data: any) {
return httpPostInterceptor(url, data); return httpPostInterceptor(url, data);
} }
export async function submitApproval(data: any) { export async function submitApproval(id: string, data: any) {
const url = "article-approval-flows/1/multi-branch-approve"; const url = `article-approval-flows/articles/${id}/approve`;
return httpPostInterceptor(url, data); return httpPostInterceptor(url, data);
} }

View File

@ -176,7 +176,7 @@ export async function listArticles(
categoryId?: string, categoryId?: string,
sortBy = "createdAt" sortBy = "createdAt"
) { ) {
let url = `articles?page=${page}&totalPage=${totalPage}`; let url = `articles?page=${page}&totalPage=${totalPage}&isPublish=true`;
if (typeId !== undefined) url += `&typeId=${typeId}`; if (typeId !== undefined) url += `&typeId=${typeId}`;
if (search) url += `&title=${encodeURIComponent(search)}`; if (search) url += `&title=${encodeURIComponent(search)}`;