diff --git a/sigap-website/actions/auth/sign-in.ts b/sigap-website/actions/auth/sign-in.ts
index bc0811c..e0644d2 100644
--- a/sigap-website/actions/auth/sign-in.ts
+++ b/sigap-website/actions/auth/sign-in.ts
@@ -1,34 +1,53 @@
"use server";
-import { encodedRedirect } from "@/utils/utils";
+
import { createClient } from "@/utils/supabase/server";
-import { redirect } from "next/navigation";
-export const signInAction = async (formData: FormData) => {
- const email = formData.get("email") as string;
+export const signInAction = async (formData: { email: string }) => {
const supabase = await createClient();
+ const encodeEmail = encodeURIComponent(formData.email);
- if (!email || email === "") {
- return encodedRedirect("error", "/sign-in", "Email is required");
+ try {
+ // First, check for existing session
+ const {
+ data: { session },
+ error: sessionError,
+ } = await supabase.auth.getSession();
+
+ // If there's an active session and the email matches
+ if (session && session.user.email === formData.email) {
+ return {
+ success: true,
+ message: "Already logged in",
+ redirectTo: "/protected/dashboard", // or wherever you want to redirect logged in users
+ };
+ }
+
+ // If no active session or different email, proceed with OTP
+ const { data, error } = await supabase.auth.signInWithOtp({
+ email: formData.email,
+ options: {
+ shouldCreateUser: false,
+ },
+ });
+
+ if (error) {
+ return {
+ success: false,
+ error: error.message,
+ redirectTo: `/verify-otp?email=${encodeEmail}`,
+ };
+ }
+
+ return {
+ success: true,
+ message: "OTP has been sent to your email",
+ redirectTo: `/verify-otp?email=${encodeEmail}`,
+ };
+ } catch (error) {
+ return {
+ success: false,
+ error: "An unexpected error occurred",
+ redirectTo: "/sign-in",
+ };
}
-
- const { data, error } = await supabase.auth.signInWithOtp({
- email,
- options: {
- shouldCreateUser: true,
- },
- });
-
- if (error && error.message.includes("User not found")) {
- // Encode email parameter untuk keamanan
- const encodedEmail = encodeURIComponent(email);
- return redirect(`/sign-in?email=${encodedEmail}`);
- }
-
- if (error) {
- return encodedRedirect("error", "/sign-in", error.message);
- }
-
- // Redirect dengan email parameter
- const encodedEmail = encodeURIComponent(email);
- return redirect(`/verify-otp?email=${encodedEmail}`);
-};
+};
\ No newline at end of file
diff --git a/sigap-website/components/auth/login-form.tsx b/sigap-website/components/auth/login-form.tsx
index 1b38c67..de88eeb 100644
--- a/sigap-website/components/auth/login-form.tsx
+++ b/sigap-website/components/auth/login-form.tsx
@@ -1,103 +1,103 @@
-import { cn } from "@/lib/utils";
-import { Button } from "@/components/ui/button";
-import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
-} from "@/components/ui/card";
-import { Input } from "@/components/ui/input";
-import { Label } from "@/components/ui/label";
-import { SubmitButton } from "../submit-button";
-import { signInAction } from "@/actions/auth/sign-in";
+// import { cn } from "@/lib/utils";
+// import { Button } from "@/components/ui/button";
+// import {
+// Card,
+// CardContent,
+// CardDescription,
+// CardHeader,
+// CardTitle,
+// } from "@/components/ui/card";
+// import { Input } from "@/components/ui/input";
+// import { Label } from "@/components/ui/label";
+// import { SubmitButton } from "../submit-button";
+// import { signInAction } from "@/actions/auth/sign-in";
-export function LoginForm({
- className,
- ...props
-}: React.ComponentPropsWithoutRef<"div">) {
- return (
-
-
-
- Welcome back
-
- Login with your Apple or Google account
-
-
-
-
-
-
-
-
- );
-}
+// export function LoginForm({
+// className,
+// ...props
+// }: React.ComponentPropsWithoutRef<"div">) {
+// return (
+//
+//
+//
+// Welcome back
+//
+// Login with your Apple or Google account
+//
+//
+//
+//
+//
+//
+//
+//
+// );
+// }
diff --git a/sigap-website/src/applications/entities/models/user.model.ts b/sigap-website/src/applications/entities/models/user.model.ts
new file mode 100644
index 0000000..2d4a1b3
--- /dev/null
+++ b/sigap-website/src/applications/entities/models/user.model.ts
@@ -0,0 +1,72 @@
+import { z } from "zod";
+
+export const Roles = {
+ ADMIN: "admin",
+ USER: "user",
+ STAFF: "staff",
+};
+
+export type Roles = typeof Roles;
+
+export const userSchema = z.object({
+ id: z.string(),
+ email: z.string(),
+ emailVerified: z.boolean().default(false),
+ password: z.string().nullable(),
+ firstName: z.string().nullable(),
+ lastName: z.string().nullable(),
+ avatar: z.string().nullable(),
+ role: z.nativeEnum(Roles).default(Roles.USER),
+ createdAt: z.date(),
+ updatedAt: z.date(),
+ lastSignedIn: z.date().nullable(),
+ metadata: z.any().nullable(),
+ profile: z
+ .object({
+ id: z.string(),
+ userId: z.string(),
+ bio: z.string().nullable(),
+ phone: z.string().nullable(),
+ address: z.string().nullable(),
+ city: z.string().nullable(),
+ country: z.string().nullable(),
+ birthDate: z.date().nullable(),
+ })
+ .nullable(),
+});
+
+export type User = z.infer;
+
+export const userInsertSchema = userSchema.pick({
+ email: true,
+ password: true,
+ firstName: true,
+ lastName: true,
+ role: true,
+ profile: true,
+});
+
+export type UserInsert = z.infer;
+
+export const signInSchema = z.object({
+ email: z.string(),
+ password: z.string(),
+ phone: z.string(),
+ username: z.string(),
+});
+
+export type SignIn = z.infer;
+
+export const signInWithPasswordlessSchema = signInSchema.pick({
+ email: true,
+});
+
+export type SignInWithOtp = z.infer;
+
+export interface SignInResponse {
+ success: boolean;
+ message?: string;
+ error?: string;
+ errors?: Record;
+ redirectTo?: string;
+}
diff --git a/sigap-website/src/applications/repositories/signin.repository.ts b/sigap-website/src/applications/repositories/signin.repository.ts
new file mode 100644
index 0000000..3ae62ca
--- /dev/null
+++ b/sigap-website/src/applications/repositories/signin.repository.ts
@@ -0,0 +1,5 @@
+import { SignInResponse, SignInWithOtp } from "../entities/models/user.model";
+
+export interface SignInRepository {
+ signInWithPasswordless(email: SignInWithOtp): Promise;
+}
diff --git a/sigap-website/src/applications/usecases/signin/signin.usecases.ts b/sigap-website/src/applications/usecases/signin/signin.usecases.ts
new file mode 100644
index 0000000..f18b45e
--- /dev/null
+++ b/sigap-website/src/applications/usecases/signin/signin.usecases.ts
@@ -0,0 +1,10 @@
+import { SignInResponse, SignInWithOtp } from "../../entities/models/user.model";
+import { SignInRepository } from "../../repositories/signin.repository";
+
+export class SignInUseCase {
+ constructor(private signInRepository: SignInRepository) {}
+
+ async executeSignInWithPasswordless(email: SignInWithOtp): Promise {
+ return this.signInRepository.signInWithPasswordless(email);
+ }
+}
diff --git a/sigap-website/src/infrastructure/hooks/use-signin.ts b/sigap-website/src/infrastructure/hooks/use-signin.ts
new file mode 100644
index 0000000..62af726
--- /dev/null
+++ b/sigap-website/src/infrastructure/hooks/use-signin.ts
@@ -0,0 +1,113 @@
+import { toast } from "@/hooks/use-toast";
+import { useState } from "react";
+import { ContactRepositoryImpl } from "../repositories/contact-us.repository.impl";
+import { validateContactForm } from "../validators/contact-us.validator";
+import { SignInWithOtp } from "@/src/applications/entities/models/user.model";
+import { SignInRepositoryImpl } from "../repositories/signin.repository.impl";
+import { validateSignInWithOtp } from "../validators/signin.validator";
+import Router from "next/router";
+import { useNavigations } from "@/hooks/use-navigations";
+
+export const useSignInForm = () => {
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [formData, setFormData] = useState({
+ email: "",
+ });
+ const [errors, setErrors] = useState<
+ Partial>
+ >({});
+ const { router } = useNavigations();
+
+ const contactRepository = new SignInRepositoryImpl();
+
+ // Handle input change
+ const handleChange = (
+ e: React.ChangeEvent
+ ) => {
+ const { id, value } = e.target;
+ setFormData((prev) => ({ ...prev, [id]: value }));
+
+ if (errors[id as keyof SignInWithOtp]) {
+ setErrors((prev) => {
+ const newErrors = { ...prev };
+ delete newErrors[id as keyof SignInWithOtp];
+ return newErrors;
+ });
+ }
+ };
+
+ // Handle select change
+ const handleSelectChange = (value: string) => {
+ setFormData((prev) => ({ ...prev, typeMessage: value }));
+
+ // Clear error when selecting
+ if (errors.email) {
+ setErrors((prev) => {
+ const newErrors = { ...prev };
+ delete newErrors.email;
+ return newErrors;
+ });
+ }
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ const validation = validateSignInWithOtp(formData);
+ if (!validation.success) {
+ setErrors(validation.errors);
+ return;
+ }
+
+ setIsSubmitting(true);
+ try {
+ const response = await contactRepository.signInWithPasswordless(formData);
+
+ if (!response.success) {
+ if (response.errors) {
+ setErrors(response.errors);
+ } else {
+ toast({
+ title: "Error",
+ description: response.error || "An unexpected error occurred.",
+ variant: "destructive",
+ });
+ }
+ return;
+ }
+ // Add redirect handling
+ if (response.redirectTo) {
+ router.push(response.redirectTo);
+ }
+
+ toast({
+ title: "Success",
+ description:
+ response.message || `OTP has been sent to ${formData.email}`,
+ });
+
+ setFormData({
+ email: "",
+ });
+ setErrors({});
+ } catch (error) {
+
+ toast({
+ title: "Error",
+ description: "An unexpected error occurred. Please try again later.",
+ variant: "destructive",
+ });
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ return {
+ formData,
+ errors,
+ isSubmitting,
+ setFormData,
+ handleChange,
+ handleSelectChange,
+ handleSubmit,
+ };
+};
diff --git a/sigap-website/src/infrastructure/repositories/signin.repository.impl.ts b/sigap-website/src/infrastructure/repositories/signin.repository.impl.ts
new file mode 100644
index 0000000..9805dc4
--- /dev/null
+++ b/sigap-website/src/infrastructure/repositories/signin.repository.impl.ts
@@ -0,0 +1,23 @@
+import { signInAction } from "@/actions/auth/sign-in";
+import {
+ SignInResponse,
+ SignInWithOtp,
+} from "@/src/applications/entities/models/user.model";
+import { SignInRepository } from "@/src/applications/repositories/signin.repository";
+import Router from "next/router";
+
+export class SignInRepositoryImpl implements SignInRepository {
+ async signInWithPasswordless(email: SignInWithOtp): Promise {
+ try {
+ const result = await signInAction(email);
+
+ return result;
+ } catch (error) {
+ console.error("Error in SignInRepositoryImpl:", error);
+ return {
+ success: false,
+ error: "An unexpected error occurred. Please try again later.",
+ };
+ }
+ }
+}
diff --git a/sigap-website/src/infrastructure/validators/signin.validator.ts b/sigap-website/src/infrastructure/validators/signin.validator.ts
new file mode 100644
index 0000000..10a98f6
--- /dev/null
+++ b/sigap-website/src/infrastructure/validators/signin.validator.ts
@@ -0,0 +1,27 @@
+import { ContactUsInsert } from "@/src/applications/entities/models/contact-us.model";
+import { SignInWithOtp } from "@/src/applications/entities/models/user.model";
+import { TValidator } from "@/utils/validator";
+
+/**
+ * Validate the contact form
+ * @param {Object} formData - The form data to validate
+ * @returns {Object} - Validation result with success flag and errors object
+ */
+export const validateSignInWithOtp = (
+ formData: SignInWithOtp
+): { success: boolean; errors: Record } => {
+ const errors: Record = {};
+ let isValid = true;
+
+ // Validate email
+ const emailResult = TValidator.validateEmail(formData.email);
+ if (!emailResult.success) {
+ errors.email = emailResult.error!;
+ isValid = false;
+ }
+
+ return {
+ success: isValid,
+ errors: isValid ? {} : errors,
+ };
+};