# Branching - [ ] Create the feature/authentication branch based on develop # Adding Models Create three files inside this folder: 📂 Suggested Folder: shared/models/authentication - [ ] `AuthResponseDto.ts` ```ts export interface AuthResponseDto {   token: string;   phoneNumber: string;   roles: string[]; } ``` - [ ] `LoginRequestDto.ts` ```ts export interface LoginRequestDto {   phoneNumber: string;   password: string; } ``` - [ ] `RegisterRequestDto.ts` ```ts export interface RegisterRequestDto {   phoneNumber: string;   password: string;   confirmPassword: string; } ``` # Adding Authentication API Calls in `agent.ts` - [ ] Add the `Auth` object to `agent.ts`: ```ts const Auth = { register: (data: RegisterRequestDto) =>     request.post<{ token: string }>('/auth/register', data), login: (data: LoginRequestDto) =>     request.post<{ token: string }>('/auth/login', data), }; ``` - [ ] Ensure `agent.ts` ends like this: ```tsx const agent = {     TransportationSearch,     Cities,     Auth } ``` # Creating `authStore.ts` Suggested Folder 📂 Suggested Folder: shared/store/ - [ ] Create authStore.ts ```tsx import { AuthResponseDto } from '@/shared/models/authentication/AuthResponseDto'; import {create} from 'zustand'; interface User {   phoneNumber: string;   roles: string[]; } interface AuthState {   isLoggedIn: boolean;   user: User | null;   token: string | null;   login: (response: AuthResponseDto) => void;   logout: () => void;   setToken: (token: string) => void; } export const useAuthStore = create((set) => ({   isLoggedIn: false,   user: null,   token: null,   login: (response) =>   set(() => ({     token: response.token,     user: {       phoneNumber: response.phoneNumber,       roles: response.roles     },     isLoggedIn: true,   })),   logout: () =>     set(() => ({       token: null,       user: null,       isLoggedIn: false,     })),   setToken: (token) =>     set((state) => ({       token,       isLoggedIn: !!token,       user: state.user,     })), })); ``` ## About `authStore.ts` This file defines a centralized **authentication state store** using [`Zustand`](https://github.com/pmndrs/zustand), a minimal and scalable state management library for React. It helps manage login state, user data, and authentication token across your application. --- ## 🔹 `User` Interface ```ts interface User { phoneNumber: string; roles: string[]; } ``` This interface defines the shape of the `user` object stored in the auth state. It currently includes: - `phoneNumber`: A string representing the user's phone number. - `roles`: An array representing user roles --- ## 🔹 `AuthState` Interface ```ts interface AuthState { isLoggedIn: boolean; user: User | null; token: string | null; login: (response: AuthResponseDto) => void; logout: () => void; setToken: (token: string) => void; } ``` This defines the overall structure of the authentication store: - `isLoggedIn`: Indicates whether a user is logged in. - `user`: Stores user-specific data if authenticated; otherwise `null`. - `token`: JWT or access token from the server. - `login()`: Accepts an `AuthResponseDto` and updates the state. - `logout()`: Clears all authentication-related data. - `setToken()`: Sets the token and toggles login status accordingly. --- ## 🔹 Zustand Store Definition ```ts export const useAuthStore = create((set) => ({ ... }); ``` Creates a global auth store using Zustand. `create()` accepts a function that receives `set` (used to update state) and returns the initial store state and methods. --- ## 🔹 Initial State ```ts isLoggedIn: false, user: null, token: null, ``` These lines define the initial, default state for an unauthenticated user. --- ## 🔹 `login()` Method ```ts login: (response) => set(() => ({ token: response.token, user: { phoneNumber: response.phoneNumber, roles: response.roles }, isLoggedIn: true, })), ``` - Accepts an `AuthResponseDto` object after a successful login. - Extracts the `token`, `phoneNumber`, and `roles`, and sets them in state. - Marks the user as `isLoggedIn: true`. --- ## 🔹 `logout()` Method ```ts logout: () => set(() => ({ token: null, user: null, isLoggedIn: false, })), ``` - Clears all auth-related data (token and user). - Effectively logs the user out by setting `isLoggedIn` to `false`. --- ## 🔹 `setToken()` Method ```ts setToken: (token) => set((state) => ({ token, isLoggedIn: !!token, user: state.user, })), ``` - Updates the token in the store. - Sets `isLoggedIn` based on whether a non-empty token exists. - Retains the current `user` object. --- # Add LoginModal 📂 Suggested Folder: shared/features/authentication/modals ## Example ```tsx import React, { useState } from "react"; import { useAuthStore } from "@/store/authStore"; import agent from "@/shared/api/agent"; import { LoginRequestDto } from "@/shared/models/authentication/LoginRequestDto"; interface LoginModalProps {   onClose: () => void; } const LoginModal: React.FC = ({ onClose }) => {     const login = useAuthStore((state) => state.login);   const [form, setForm] = useState({     phoneNumber: "",     password: "",   });   const [error, setError] = useState(null);   const validate = () => {     const phoneRegex = /^(?:\+98|0)?9\d{9}$/;     if (!phoneRegex.test(form.phoneNumber)) {       return "Invalid phone number format";     }     if (!form.password || form.password.length < 8) {       return "Password must be at least 8 characters";     }     return null;   };   const handleSubmit = async () => {     const validationError = validate();     if (validationError) {       setError(validationError);       return;     }     try {       const response = await agent.Auth.login(form);       login(response);       setError(null);       onClose();     } catch (err: any) {       setError(err.response?.data?.message || "Login failed");     }   };   return (    
     
       

Login

        setForm({ ...form, phoneNumber: e.target.value })}           style={styles.input}         />         setForm({ ...form, password: e.target.value })}           style={styles.input}         />                 {error && (          

            {error}          

        )}              
   
  ); }; const styles: { [key: string]: React.CSSProperties } = { //ADD STYLES }; export default LoginModal; ``` ## About LoginModal This component provides a modal UI that allows users to log in using their **phone number and password**. It integrates with the authentication store and API to perform login logic and handle errors. --- ## 🔹 Props Interface ```tsx interface LoginModalProps { onClose: () => void; } ``` - `onClose`: A callback to be called when the modal should be closed (e.g., user clicks "Cancel" or logs in successfully). --- ## 🔹 Component Setup ```tsx const LoginModal: React.FC = ({ onClose }) => { ``` Defines a functional React component with the `onClose` prop destructured. ### 🔸 Accessing Auth Store ```tsx const login = useAuthStore((state) => state.login); ``` Retrieves the `login` method from Zustand’s `authStore` so that the global auth state can be updated after successful login. ### 🔸 Local Form State ```tsx const [form, setForm] = useState({ phoneNumber: "", password: "", }); ``` Initializes `form` state with empty values for the phone number and password. ### 🔸 Error Handling State ```tsx const [error, setError] = useState(null); ``` Stores any error messages resulting from validation or login attempt. --- ## 🔹 Validation Logic ```tsx const validate = () => { const phoneRegex = /^(?:\+98|0)?9\d{9}$/; if (!phoneRegex.test(form.phoneNumber)) { return "Invalid phone number format"; } if (!form.password || form.password.length < 8) { return "Password must be at least 8 characters"; } return null; }; ``` - Validates the phone number format (Iranian phone format in this case). - Ensures password is at least 8 characters long. - Returns a string error message or `null` if validation passes. --- ## 🔹 Submit Handler ```tsx const handleSubmit = async () => { const validationError = validate(); if (validationError) { setError(validationError); return; } try { const response = await agent.Auth.login(form); login(response); setError(null); onClose(); } catch (err: any) { setError(err.response?.data?.message || "Login failed"); } }; ``` - Calls `validate()` and prevents submission if there's an error. - Calls the backend API using `agent.Auth.login()`. - On success: updates auth state, clears error, closes modal. - On failure: shows error message. --- ## 🔹 UI Layout ```tsx return (

Login

{error &&

{error}

}
); ``` ### Elements: - **Phone Number Input** - **Password Input** - **Login Button**: Triggers `handleSubmit`. - **Error Message**: Shown only if there's an error. - **Cancel Button**: Triggers `onClose` callback. --- ## 🔹 Styles Placeholder ```tsx const styles: { [key: string]: React.CSSProperties } = { // Add modal styles here }; ``` This placeholder defines inline CSS styles for the modal. Each style (e.g., `overlay`, `modal`, `input`, `button`) should be defined here. --- ## ✅ Summary This modal: - Provides a simple, reusable login form. - Validates input before calling the API. - Updates global auth state via Zustand. - Handles success/failure states. - Uses modal-friendly inline styles (with room for improvement). --- # Add RegisterModal - [ ] Create RegisterModal 📂 Suggested Folder: shared/features/authentication/modals ```tsx import agent from "@/shared/api/agent"; import { RegisterRequestDto } from "@/shared/models/authentication/RegisterRequestDto"; import { useAuthStore } from "@/store/authStore"; import React, { useState } from "react"; interface Props {   onClose: () => void; } const RegisterModal: React.FC = ({ onClose }) => {   const [form, setForm] = useState({     phoneNumber: "",     password: "",     confirmPassword: "",   });   const [error, setError] = useState(null);   const login = useAuthStore((state) => state.login);     const validate = () => {     const { phoneNumber, password, confirmPassword } = form;     if (!phoneNumber || !password || !confirmPassword) {       return "All fields are required.";     }     if (!/^\d{11}$/.test(phoneNumber)) {       return "Phone number must be 11 digits.";     }     if (password.length < 6) {       return "Password must be at least 6 characters.";     }     if (password !== confirmPassword) {       return "Passwords do not match.";     }     return null;   };   const handleChange = (e: React.ChangeEvent) => {     setForm({ ...form, [e.target.name]: e.target.value });   };   const handleSubmit = async (e: React.FormEvent) => {     e.preventDefault();     setError(null);         const validationError = validate();     if (validationError) {       setError(validationError);       return;     }     try {       // Use the form as RegisterRequestDto explicitly       const requestData: RegisterRequestDto = {         phoneNumber: form.phoneNumber,         password: form.password,         confirmPassword: form.confirmPassword,       };       const response = await agent.Auth.register(requestData);       login(response);       setForm({ phoneNumber: "", password: "", confirmPassword: "" });       onClose();     } catch (err: any) {       setError(err.response?.data?.message || "Registration failed.");     }   };   return (    
     
       

Register

       
                                        {error && (            

              {error}            

          )}                  
             
   
  ); }; const styles: { [key: string]: React.CSSProperties } = { //ADD STYLES }; export default RegisterModal; ``` ## About RegisterModal ## **Component Structure** ### 1. **Props** ```tsx interface Props {   onClose: () => void; } ``` - The modal only expects one prop: `onClose`, a function to close the modal (e.g., hide it from the screen). --- ### 2. **State Management** ```tsx const [form, setForm] = useState({   phoneNumber: "",   password: "",   confirmPassword: "", }); ``` - Initializes the form state for inputs, based on the `RegisterRequestDto` shape. ```tsx const [error, setError] = useState(null); ``` - Stores any validation or server error message to display in the UI. ```tsx const login = useAuthStore((state) => state.login); ``` - Accesses the `login` method from your global auth store, to automatically log in the user after successful registration. --- ### 3. **Validation Logic** ```tsx const validate = () => {   // Checks for empty fields   // Validates phone number format (must be 11 digits)   // Ensures password length is sufficient   // Confirms password and confirmation match }; ``` - Ensures client-side validation before making a request to the server. --- ### 4. **Input Handling** ```tsx const handleChange = (e: React.ChangeEvent) => {   setForm({ ...form, [e.target.name]: e.target.value }); }; ``` - Updates the correct field in the `form` object dynamically based on the input `name`. --- ### 5. **Form Submission** ```tsx const handleSubmit = async (e: React.FormEvent) => {   e.preventDefault();   setError(null);   const validationError = validate();   // If validation passes, submit the data to the backend   // If backend response is successful, log in and close modal   // If it fails, show error message }; ``` - Prevents default form submission - Validates inputs - Sends the data to `agent.Auth.register` - On success: logs in user and clears form - On failure: shows error from server --- ### 6. **JSX Render** ```tsx
...
``` - **Modal Overlay**: darkened background behind the modal - **Modal Box**: contains title, form, and buttons ### Inside `
`: - Inputs for:   - `phoneNumber`   - `password`   - `confirmPassword` - Submit button for Register - Error message display (if any) - Cancel button that calls `onClose` --- # Merge - [ ] Create a PR and merge the current branch with develop