diff --git a/apps/frontend/src/index.css b/apps/frontend/src/index.css index 6f4ed59..c6f68d5 100644 --- a/apps/frontend/src/index.css +++ b/apps/frontend/src/index.css @@ -69,5 +69,5 @@ } :root { - --primary-color: #2555FF + --primary-color: #2555FF; } \ No newline at end of file diff --git a/apps/frontend/src/main.tsx b/apps/frontend/src/main.tsx index 6e738f1..6ba9c8f 100644 --- a/apps/frontend/src/main.tsx +++ b/apps/frontend/src/main.tsx @@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client"; import App from "./App.tsx"; import "./index.css"; import "./styles/tailwind.css"; -import "./styles/fonts/manrope.css"; +import "./styles/fonts/inter.css"; ReactDOM.createRoot(document.getElementById("root")!).render( diff --git a/apps/frontend/src/routes/__root.tsx b/apps/frontend/src/routes/__root.tsx index 91b0ffe..4dd18f8 100644 --- a/apps/frontend/src/routes/__root.tsx +++ b/apps/frontend/src/routes/__root.tsx @@ -8,7 +8,7 @@ interface RouteContext { export const Route = createRootRouteWithContext()({ component: () => ( -
+
diff --git a/apps/frontend/src/routes/register/index.lazy.tsx b/apps/frontend/src/routes/register/index.lazy.tsx new file mode 100644 index 0000000..3468815 --- /dev/null +++ b/apps/frontend/src/routes/register/index.lazy.tsx @@ -0,0 +1,322 @@ +import { createLazyFileRoute, useNavigate } from "@tanstack/react-router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { Input } from '@/shadcn/components/ui/input.tsx'; +import { Button } from '@/shadcn/components/ui/button.tsx'; +import { Alert } from '@/shadcn/components/ui/alert.tsx'; +import { Checkbox } from "@/shadcn/components/ui/checkbox"; +import { Form, FormField, FormControl, FormLabel, FormMessage, FormItem } from '@/shadcn/components/ui/form.tsx'; +import { TbArrowNarrowRight } from 'react-icons/tb'; +import client from "../../honoClient"; + +// Define the schema for validation +const formSchema = z.object({ + name: z.string().min(1, "Kolom ini wajib diisi"), + username: z.string().min(1, "Kolom ini wajib diisi"), + email: z.string().email("Alamat email tidak valid").min(1, "Kolom ini wajib diisi"), + password: z.string().min(6, "Kata sandi harus minimal 6 karakter"), + companyName: z.string().min(1, "Kolom ini wajib diisi"), + position: z.string().min(1, "Kolom ini wajib diisi"), + workExperience: z.string().min(1, "Kolom ini wajib diisi"), + address: z.string().min(1, "Kolom ini wajib diisi"), + phoneNumber: z.string().min(1, "Kolom ini wajib diisi"), + terms: z.boolean().refine((val) => val, "Anda harus menyetujui persyaratan dan layanan"), +}); + +// Define the form type +type FormSchema = z.infer; + +export const Route = createLazyFileRoute("/register/")({ + component: RegisterPage, +}); + +export default function RegisterPage() { + const [errorFields, setErrorFields] = useState> | null>(null); + const [errorMessage, setErrorMessage] = useState(null); + + const navigate = useNavigate(); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + name: "", + username: "", + email: "", + password: "", + companyName: "", + position: "", + workExperience: "", + address: "", + phoneNumber: "", + terms: false, + }, + }); + + const handleSubmit = async (values: FormSchema) => { + try { + const res = await client.register.$post({ + json: values, + }); + + if (res.ok) { + // Redirect to login page on success + navigate({ to: "/login", replace: true }); + } else { + // Handle non-200 responses from backend + const errorData = await res.json(); + throw new Error(errorData.message || "An unknown error occurred"); + } + } catch (error: any) { + const message = error.message; + + if (message.includes("Email atau username sudah terdaftar")) { + setErrorFields({ + email: "Email sudah terdaftar", + username: "Username sudah terdaftar", + }); + } else if (message.includes("Nomor telepon sudah terdaftar")) { + setErrorFields({ + phoneNumber: "Nomor telepon sudah terdaftar", + }); + } else { + setErrorMessage(message); + } + } + }; + + return ( +
+ {/* Image */} +
+
+ + + + +
+
+ + {/* Logo */} +
+ Amati Logo +
+ + {/* Main content */} +
+ + {/* Form column */} +
+ + {/* Title and Already have an account */} +
+

Daftar Akun

+

+ Sudah punya akun?{' '} + + Sign In now + +

+
+ +
+ + {errorMessage && ( + +

{errorMessage}

+
+ )} + + {Object.keys(errorFields || {}).length > 0 && ( + + {Object.values(errorFields || {}).map((msg, idx) => ( +

{msg}

+ ))} +
+ )} + + {/* Form fields */} +
+
+ ( + + Nama Lengkap + + + + + + )} /> +
+ + ( + + Username + + + + + + )} /> + + ( + + Email + + + + + + )} /> + + ( + + Kata Sandi + + + + + + )} /> + + ( + + Nama Perusahaan + + + + + + )} /> + + ( + + Jabatan + + + + + + )} /> + + ( + + Pengalaman Kerja + + + + + + )} /> + + ( + + Alamat + + + + + + )} /> + + ( + + Nomor Telepon + + + + + + )} /> + + ( + + +
+ form.setValue("terms", !!checked)} + className={`border ${form.formState.errors.terms ? "border-red-500" : "border-[#00000099]"}`} + onChange={form.register("terms").onChange} + onBlur={form.register("terms").onBlur} + name="terms" + ref={form.register("terms").ref} + id="terms" + /> + +
+
+ {form.formState.errors.terms && ( + {form.formState.errors.terms.message} + )} +
+ )} + /> +
+ +
+
+ +
+
+
+ +
+
+
+ ); +} diff --git a/apps/frontend/src/styles/fonts/inter.css b/apps/frontend/src/styles/fonts/inter.css new file mode 100644 index 0000000..3fa8fb1 --- /dev/null +++ b/apps/frontend/src/styles/fonts/inter.css @@ -0,0 +1,6 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'); + +.font-inter { + font-family: "Inter", sans-serif; + font-optical-sizing: auto; +}