
# 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<AuthState>((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<AuthState>((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<LoginModalProps> = ({ onClose }) => {
  
  const login = useAuthStore((state) => state.login);

  const [form, setForm] = useState<LoginRequestDto>({
    phoneNumber: "",
    password: "",
  });

  const [error, setError] = useState<string | null>(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 (
    <div style={styles.overlay}>
      <div style={styles.modal}>
        <h2 style={{ marginBottom: "1rem" }}>Login</h2>
        <input
          type="text"
          placeholder="Phone Number"
          value={form.phoneNumber}
          onChange={(e) => setForm({ ...form, phoneNumber: e.target.value })}
          style={styles.input}
        />

        <input
          type="password"
          placeholder="Password"
          value={form.password}
          onChange={(e) => setForm({ ...form, password: e.target.value })}
          style={styles.input}
        />
        <button onClick={handleSubmit} style={styles.button}>
          Login
        </button>
        {error && (
          <p style={{ color: "red", marginTop: "0.5rem", fontWeight: "bold" }}>
            {error}
          </p>
        )}

        <button
          onClick={onClose}
          style={{
            ...styles.button,
            marginTop: "0.5rem",
            backgroundColor: "#ccc",
            color: "#333",
          }}
        >
          Cancel
        </button>
      </div>
    </div>
  );
};

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<LoginModalProps> = ({ 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<LoginRequestDto>({
  phoneNumber: "",
  password: "",
});
```

Initializes `form` state with empty values for the phone number and password.

### 🔸 Error Handling State

```tsx
const [error, setError] = useState<string | null>(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 (
  <div style={styles.overlay}>
    <div style={styles.modal}>
      <h2>Login</h2>
      <input ... />
      <input ... />
      <button onClick={handleSubmit}>Login</button>
      {error && <p>{error}</p>}
      <button onClick={onClose}>Cancel</button>
    </div>
  </div>
);
```

### 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<Props> = ({ onClose }) => {

  const [form, setForm] = useState<RegisterRequestDto>({
    phoneNumber: "",
    password: "",
    confirmPassword: "",
  });

  const [error, setError] = useState<string | null>(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<HTMLInputElement>) => {
    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 (
    <div style={styles.overlay}>
      <div style={styles.modal}>
        <h2 style={{ marginBottom: "1rem" }}>Register</h2>
        <form
          onSubmit={handleSubmit}
          style={{ display: "flex", flexDirection: "column" }}
        >

          <input
            type="text"
            name="phoneNumber"
            value={form.phoneNumber}
            onChange={handleChange}
            placeholder="Phone Number"
            style={styles.input}
          />

          <input
            type="password"
            name="password"
            value={form.password}
            onChange={handleChange}
            placeholder="Password"
            style={styles.input}
          />

          <input
            type="password"
            name="confirmPassword"
            value={form.confirmPassword}
            onChange={handleChange}
            placeholder="Confirm Password"
            style={styles.input}
          />

          {error && (

            <p
              style={{ color: "red", marginTop: "0.5rem", fontWeight: "bold" }}
            >
              {error}
            </p>
          )}

          <button type="submit" style={styles.button}>
            Register
          </button>

        </form>

        <button

          onClick={onClose}

          style={{
            ...styles.button,
            marginTop: "0.5rem",
            backgroundColor: "#ccc",
            color: "#333",
          }}
        >
          Cancel
        </button>
      </div>
    </div>
  );
};

  

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<RegisterRequestDto>({
  phoneNumber: "",
  password: "",
  confirmPassword: "",
});
```

- Initializes the form state for inputs, based on the `RegisterRequestDto` shape.

```tsx
const [error, setError] = useState<string | null>(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<HTMLInputElement>) => {
  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
<div style={styles.overlay}>...</div>
```

- **Modal Overlay**: darkened background behind the modal
- **Modal Box**: contains title, form, and buttons

### Inside `<form>`:

- Inputs for:  
      - `phoneNumber`  
      - `password`  
      - `confirmPassword`
    
- Submit button for Register
    
- Error message display (if any)
    
- Cancel button that calls `onClose`
    
---

# Handle login/logout and register buttons in navbar
- [ ] implement a way for showing login and register buttons in navbar when user is not signed in
- [ ] when clicked, the button should show the related modal, for the user to sign in or register
# Merge
- [ ] Create a PR and merge the current branch with develop
