import { json, redirect, type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/node"; import { Form, useActionData, useLoaderData, useNavigation, Link } from "@remix-run/react"; import { useState } from "react"; import { Card, CardContent, CardHeader } from "~/components/ui/card"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Textarea } from "~/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"; import { Alert, AlertDescription } from "~/components/ui/alert"; import { Building, ArrowLeft, ArrowRight, AlertCircle, Loader2, CheckCircle, MapPin, User, FileText, Phone, Upload, Image as ImageIcon } from "lucide-react"; import { formatDateToDDMMYYYY, validatePhoneNumber } from "~/utils/auth-utils"; import pengelolaAuthService from "~/services/auth/pengelola.service"; import { getUserSession, createUserSession } from "~/sessions.server"; // Progress Indicator Component const ProgressIndicator = ({ currentStep = 3, totalSteps = 5 }) => { return (
{Array.from({ length: totalSteps }, (_, index) => { const stepNumber = index + 1; const isActive = stepNumber === currentStep; const isCompleted = stepNumber < currentStep; return (
{isCompleted ? : stepNumber}
{stepNumber < totalSteps && (
)}
); })}
); }; // Interfaces interface LoaderData { userSession: any; } interface CompanyProfileActionData { errors?: { companyname?: string; companyaddress?: string; companyphone?: string; companyemail?: string; companywebsite?: string; taxid?: string; foundeddate?: string; companytype?: string; companydescription?: string; general?: string; }; success?: boolean; } export const loader = async ({ request }: LoaderFunctionArgs): Promise => { const userSession = await getUserSession(request); // Check if user is authenticated and has pengelola role if (!userSession || userSession.role !== "pengelola") { return redirect("/authpengelola"); } // Check if user should be on this step if (userSession.registrationStatus !== "uncomplete") { // Redirect based on current status switch (userSession.registrationStatus) { case "awaiting_approval": return redirect("/authpengelola/waitingapprovalfromadministrator"); case "approved": return redirect("/authpengelola/createanewpin"); case "complete": return redirect("/pengelola/dashboard"); default: break; } } return json({ userSession }); }; export const action = async ({ request }: ActionFunctionArgs): Promise => { const userSession = await getUserSession(request); if (!userSession || userSession.role !== "pengelola") { return redirect("/authpengelola"); } const formData = await request.formData(); // Extract form data sesuai dengan API requirements const companyData = { companyname: formData.get("companyname") as string, companyaddress: formData.get("companyaddress") as string, companyphone: formData.get("companyphone") as string, companyemail: formData.get("companyemail") as string, companywebsite: formData.get("companywebsite") as string, taxid: formData.get("taxid") as string, foundeddate: formData.get("foundeddate") as string, companytype: formData.get("companytype") as string, companydescription: formData.get("companydescription") as string, company_logo: formData.get("company_logo") as File | null }; // Validation const errors: { [key: string]: string } = {}; if (!companyData.companyname?.trim()) { errors.companyname = "Nama perusahaan wajib diisi"; } if (!companyData.companyaddress?.trim()) { errors.companyaddress = "Alamat perusahaan wajib diisi"; } if (!companyData.companyphone?.trim()) { errors.companyphone = "Nomor telepon perusahaan wajib diisi"; } else if (!validatePhoneNumber(companyData.companyphone)) { errors.companyphone = "Format nomor telepon tidak valid (gunakan format 628xxxxxxxxx)"; } if (!companyData.companyemail?.trim()) { errors.companyemail = "Email perusahaan wajib diisi"; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(companyData.companyemail)) { errors.companyemail = "Format email tidak valid"; } if (!companyData.companywebsite?.trim()) { errors.companywebsite = "Website perusahaan wajib diisi"; } else if (!/^https?:\/\/.+\..+/.test(companyData.companywebsite)) { errors.companywebsite = "Format website tidak valid (harus dimulai dengan http:// atau https://)"; } if (!companyData.taxid?.trim()) { errors.taxid = "NPWP/Tax ID wajib diisi"; } if (!companyData.foundeddate?.trim()) { errors.foundeddate = "Tanggal berdiri wajib diisi"; } else { // Validate date format DD-MM-YYYY const dateRegex = /^\d{2}-\d{2}-\d{4}$/; if (!dateRegex.test(companyData.foundeddate)) { errors.foundeddate = "Format tanggal harus DD-MM-YYYY"; } } if (!companyData.companytype?.trim()) { errors.companytype = "Jenis perusahaan wajib dipilih"; } if (!companyData.companydescription?.trim()) { errors.companydescription = "Deskripsi perusahaan wajib diisi"; } if (Object.keys(errors).length > 0) { return json({ errors }, { status: 400 }); } try { // Prepare data untuk API call const apiData = { companyname: companyData.companyname.trim(), companyaddress: companyData.companyaddress.trim(), companyphone: companyData.companyphone.trim(), companyemail: companyData.companyemail.trim(), companywebsite: companyData.companywebsite.trim(), taxid: companyData.taxid.trim(), foundeddate: companyData.foundeddate.trim(), companytype: companyData.companytype.trim(), companydescription: companyData.companydescription.trim(), ...(companyData.company_logo && { company_logo: companyData.company_logo }) }; // Call API untuk create company profile const response = await pengelolaAuthService.createCompanyProfile(apiData); if (response.meta.status === 200 && response.data) { // Update session dengan data terbaru return createUserSession({ request, sessionData: { ...userSession, accessToken: response.data.access_token, refreshToken: response.data.refresh_token, sessionId: response.data.session_id, tokenType: response.data.token_type, registrationStatus: response.data.registration_status, nextStep: response.data.next_step }, redirectTo: "/authpengelola/waitingapprovalfromadministrator" }); } else { return json( { errors: { general: response.meta.message || "Gagal menyimpan profil perusahaan" } }, { status: 400 } ); } } catch (error: any) { console.error("Create company profile error:", error); // Handle specific API errors if (error.response?.data?.meta?.message) { return json( { errors: { general: error.response.data.meta.message } }, { status: error.response.status || 500 } ); } return json( { errors: { general: "Gagal menyimpan profil perusahaan. Silakan coba lagi." } }, { status: 500 } ); } }; export default function CompletingCompanyProfile() { const { userSession } = useLoaderData(); const actionData = useActionData(); const navigation = useNavigation(); const [logoPreview, setLogoPreview] = useState(null); const isSubmitting = navigation.state === "submitting"; // Handle logo file change const handleLogoChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { // Validate file type if (!file.type.startsWith("image/")) { alert("File harus berupa gambar"); return; } // Validate file size (max 2MB) if (file.size > 2 * 1024 * 1024) { alert("Ukuran file maksimal 2MB"); return; } // Create preview const reader = new FileReader(); reader.onload = (event) => { setLogoPreview(event.target?.result as string); }; reader.readAsDataURL(file); } }; return (
{/* Progress Indicator */} {/* Main Card */}

Profil Perusahaan

Lengkapi informasi perusahaan untuk verifikasi admin

{/* Error Alert */} {actionData?.errors?.general && ( {actionData.errors.general} )} {/* Form */}
{/* Company Information Section */}

Informasi Perusahaan

{/* Company Name */}
{actionData?.errors?.companyname && (

{actionData.errors.companyname}

)}
{/* Company Address */}