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 */}
{/* Back Link */}
);
}