# 🏥 نوبت724 (Nobat724) - سیستم نوبت‌دهی آنلاین پزشکی

## 📋 فهرست مطالب

- [معرفی پروژه](#معرفی-پروژه)
- [معماری و تکنولوژی](#معماری-و-تکنولوژی)
- [ساختار پروژه](#ساختار-پروژه)
- [ویژگی‌های کلیدی](#ویژگیهای-کلیدی)
- [راه‌اندازی پروژه](#راهاندازی-پروژه)
- [سیستم Multi-Domain](#سیستم-multi-domain)
- [احراز هویت و مجوزها](#احراز-هویت-و-مجوزها)
- [مسیرها و صفحات](#مسیرها-و-صفحات)
- [کامپوننت‌های اصلی](#کامپوننتهای-اصلی)
- [API و سرویس‌ها](#api-و-سرویسها)
- [Deployment](#deployment)

---

## 🎯 معرفی پروژه

**نوبت724** یک پلتفرم جامع و پیشرفته برای نوبت‌دهی آنلاین پزشکی است که امکان رزرو نوبت از پزشکان و کلینیک‌ها را به صورت آنلاین فراهم می‌کند.

### ویژگی‌های اصلی:

- ✅ **Multi-Domain Architecture**: پشتیبانی از دامنه‌های مختلف برای شهرهای مختلف
- ✅ **رزرو نوبت آنلاین**: سیستم کامل رزرو نوبت با تقویم شمسی
- ✅ **پنل مدیریتی**: پنل کامل برای نمایندگان و پزشکان
- ✅ **احراز هویت**: سیستم ورود و احراز هویت با OTP
- ✅ **پرداخت آنلاین**: سیستم پرداخت یکپارچه
- ✅ **Responsive Design**: طراحی کاملاً واکنش‌گرا با Tailwind CSS
- ✅ **Dark Mode**: پشتیبانی کامل از حالت تاریک
- ✅ **SEO Optimized**: بهینه‌سازی کامل SEO با متادیتای داینامیک

---

## 🏗️ معماری و تکنولوژی

### Frontend Framework

- **Next.js 14.2.20** (App Router)
- **React 18**
- **Server Components** و **Client Components**

### UI/UX

- **Material-UI (MUI) v7**: کامپوننت‌های UI
- **Tailwind CSS 3.4**: استایل‌دهی
- **Emotion**: CSS-in-JS
- **Dark Mode**: با `next-themes`
- **Responsive Design**: Mobile-first approach

### State Management & Data Fetching

- **React Context API**: مدیریت state سراسری
- **Axios**: درخواست‌های HTTP
- **Server-Side Rendering (SSR)**

### Authentication & Authorization

- **CASL**: مدیریت دسترسی‌ها
- **JWT Tokens**: احراز هویت
- **Cookie-based Auth**: ذخیره توکن‌ها

### کتابخانه‌های تخصصی

- **jalali-moment & moment-jalaali**: تبدیل تاریخ شمسی
- **jalaali-react-date-picker**: Date Picker فارسی
- **react-toastify**: نوتیفیکیشن‌ها
- **html2canvas & jspdf**: تولید PDF
- **qrcode.react**: تولید QR Code
- **@maptiler/sdk**: نمایش نقشه

### Development Tools

- **Docker & Docker Compose**: Containerization
- **ESLint**: Code Quality
- **Prettier**: Code Formatting

---

## 📁 ساختار پروژه

```
nobat724_front/
├── app/                          # Next.js App Directory
│   ├── layout.js                 # Root Layout با Metadata داینامیک
│   ├── page.js                   # صفحه اصلی
│   ├── globals.css              # استایل‌های سراسری
│   ├── about-us/                # درباره ما
│   ├── appointment/[doctorId]/  # صفحه رزرو نوبت (Dynamic Route)
│   ├── blog/[slug]/             # صفحه جزئیات بلاگ
│   ├── blogs/                   # لیست بلاگ‌ها
│   ├── clinic/[slug]/           # صفحه جزئیات کلینیک
│   ├── clinics/                 # لیست کلینیک‌ها
│   ├── contact-us/              # تماس با ما
│   ├── dashboard/               # داشبورد عمومی
│   ├── doctor/[slug]/           # صفحه جزئیات پزشک
│   ├── doctors/                 # لیست پزشکان
│   ├── login/                   # ورود
│   ├── login-verify/            # تایید OTP
│   ├── panel/(layout)/          # پنل مدیریتی (Route Group)
│   │   ├── dashboard/           # داشبورد پنل
│   │   ├── user-account/        # حساب کاربری
│   │   ├── add-doctor/          # افزودن پزشک
│   │   └── turns/               # مدیریت نوبت‌ها
│   ├── specialties/             # تخصص‌های پزشکی
│   └── component/               # کامپوننت‌های مشترک App
│
├── components/                   # کامپوننت‌های اصلی
│   ├── home/                    # کامپوننت‌های صفحه اصلی
│   ├── appointment/             # کامپوننت‌های رزرو نوبت
│   ├── doctor/                  # کامپوننت‌های پزشک
│   ├── doctors/                 # کامپوننت‌های لیست پزشکان
│   ├── clinic/                  # کامپوننت‌های کلینیک
│   ├── clinics/                 # کامپوننت‌های لیست کلینیک‌ها
│   ├── panel/                   # کامپوننت‌های پنل
│   ├── layout/                  # Layout کامپوننت‌ها
│   ├── layoutPanel/             # Layout پنل
│   ├── register/                # ثبت‌نام و ورود
│   └── icons/                   # آیکون‌های سفارشی
│
├── lib/                         # کتابخانه‌ها و Utilities
│   ├── ability.js              # تعریف دسترسی‌ها (CASL)
│   ├── auth.js                 # احراز هویت
│   ├── getStateInfo.js         # دریافت اطلاعات استان/شهر (Server)
│   ├── getStateInfoClient.js   # دریافت اطلاعات استان/شهر (Client)
│   ├── getCanonicalUrl.js      # تولید Canonical URL
│   └── req.js                  # Helper برای Fetch
│
├── services/                    # سرویس‌های API
│   ├── api.js                  # تنظیمات Axios
│   ├── response.js             # API Endpoints
│   └── clinicApi.js            # APIهای مربوط به کلینیک
│
├── context/                     # React Context
│   └── ProvinceProvider.js     # Context استان/شهر
│
├── data/                        # فایل‌های JSON استاتیک
│   ├── city.json               # اطلاعات شهرها (Multi-Domain)
│   ├── state.json              # اطلاعات استان‌ها
│   ├── specialties.json        # تخصص‌های پزشکی
│   ├── doctors.json            # Mock Data پزشکان
│   └── clinics.json            # Mock Data کلینیک‌ها
│
├── helper/                      # توابع کمکی
│   └── index.js                # Utility Functions
│
├── utils/                       # Utilities
│   └── index.js                # توابع کمکی (removeToken و...)
│
├── hooks/                       # Custom Hooks
│   └── useCanonicalUrl.js      # Hook برای Canonical URL
│
├── mui/                         # تنظیمات MUI
│   └── index.js                # Theme Configuration
│
├── public/                      # فایل‌های استاتیک
│   ├── assets/                 # تصاویر و آیکون‌ها
│   └── fonts/                  # فونت‌ها
│
├── middleware.js                # Next.js Middleware
├── next.config.js              # تنظیمات Next.js
├── tailwind.config.js          # تنظیمات Tailwind
├── docker-compose.yml          # Docker Compose
├── dockerfile                  # Dockerfile
└── package.json                # Dependencies
```

---

## ✨ ویژگی‌های کلیدی

### 1. Multi-Domain Architecture

پروژه از **معماری Multi-Domain** پشتیبانی می‌کند که به هر شهر اجازه می‌دهد دامنه مستقل خود را داشته باشد:

```javascript
// در data/city.json
{
  "id": "600",
  "domain": "nobat724.com",      // دامنه اصلی
  "site_name": "نوبت724",
  "title": "رزرو نوبت پزشکی",
  // ...
}
```

**نحوه عملکرد:**

- در `lib/getStateInfo.js`: بررسی subdomain و تطبیق با شهر
- در `app/layout.js`: تولید Metadata داینامیک بر اساس شهر
- در `context/ProvinceProvider.js`: مدیریت Context شهر/استان

### 2. سیستم احراز هویت

```javascript
// lib/auth.js - دریافت اطلاعات کاربر از Cookie
export async function getUser() {
  const cookieStore = cookies();
  const raw =
    cookieStore.get("access_token") &&
    cookieStore.get("refresh_token") &&
    cookieStore.get("uuid") &&
    cookieStore.get("userInfo");
  return raw ? raw : null;
}

// lib/ability.js - تعریف دسترسی‌ها
export function defineAbilitiesFor(user) {
  if (user) {
    can("access", "Dashboard");
    const parsedData = JSON.parse(user.value);
    if (parsedData.roles.find((item) => item === "representation")) {
      can("access", "Panel");
    }
  } else {
    can("access", "Login");
  }
}
```

### 3. سیستم رزرو نوبت

فرآیند رزرو نوبت شامل مراحل زیر است:

1. **انتخاب تاریخ و زمان** (`components/appointment/date/`)
2. **ورود یا ثبت‌نام** (در صورت نیاز)
3. **وارد کردن اطلاعات بیمار** (`components/appointment/detail/`)
4. **پرداخت** (`components/appointment/paying/`)
5. **تایید نهایی** (`components/appointment/successPay/`)

### 4. پنل مدیریتی

پنل مدیریتی برای نمایندگان شامل:

- **داشبورد**: نمای کلی آمار
- **حساب کاربری**: مدیریت پروفایل
- **افزودن پزشک**: ثبت پزشک جدید
- **مدیریت نوبت‌ها**: مشاهده و مدیریت نوبت‌ها

**محافظت مسیر:**

```javascript
// app/panel/(layout)/layout.js
async function LayoutPanel({ children }) {
  const user = await getUser();
  const ability = defineAbilitiesFor(user);

  if (!ability.can("access", "Panel")) {
    return redirect("/");
  }

  return <Content>{children}</Content>;
}
```

---

## 🚀 راه‌اندازی پروژه

### پیش‌نیازها

- **Node.js** 18+
- **npm** یا **yarn**
- **Docker** (اختیاری)

### نصب و راه‌اندازی

```bash
# 1. کلون کردن پروژه
git clone [repository-url]
cd nobat724_front

# 2. نصب وابستگی‌ها
npm install
# یا
yarn install

# 3. ایجاد فایل .env.local
cp .env.example .env.local

# 4. تنظیم متغیرهای محیطی
# ویرایش .env.local:
NEXT_PUBLIC_API_URL=http://localhost:8000
DEV_MODE=TRUE

# 5. اجرای پروژه در حالت Development
npm run dev
# یا
yarn dev

# پروژه در http://localhost:3000 اجرا می‌شود
```

### اسکریپت‌های موجود

```json
{
  "dev": "cross-env HOST=yazd-nobat.localhost PORT=3000 next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint"
}
```

### راه‌اندازی با Docker

```bash
# Build و اجرا
docker-compose up -d

# مشاهده لاگ‌ها
docker-compose logs -f

# توقف
docker-compose down
```

---

## 🌐 سیستم Multi-Domain

### ساختار دامنه‌ها

پروژه از **subdomain-based routing** استفاده می‌کند:

```
nobat724.com          → سایت اصلی (همه شهرها)
tabriz-nobat.ir       → تبریز
urmia-nobat.ir        → ارومیه
yazd-nobat.ir         → یزد
```

### فرآیند تشخیص شهر

```javascript
// lib/getStateInfo.js (Server-Side)
export function getStateInfo() {
  const headersList = headers();
  const host = headersList.get("host") || "";
  const subdomain = host.split(".")[0];

  const matchedCity = citiesData.find((city) =>
    city.domain.includes(subdomain)
  );

  return { matchedCity, matchedState };
}

// context/ProvinceProvider.js (Client-Side)
export const ProvinceProvider = ({ children }) => {
  const [hostName, setHostName] = useState("");
  const isProvinceInclude = citiesData.find((city) =>
    hostName.includes(city.domain.split(".")[0])
  );

  return (
    <ProvinceContext.Provider value={{ isProvinceInclude }}>
      {children}
    </ProvinceContext.Provider>
  );
};
```

### Metadata داینامیک بر اساس شهر

```javascript
// app/layout.js
export async function generateMetadata() {
  const { matchedCity } = getStateInfo();

  return {
    title: matchedCity ? matchedCity.title : "نوبت724",
    description: matchedCity ? matchedCity.description : "توضیحات پیش‌فرض",
    keywords: matchedCity ? matchedCity.keywords : "کلمات کلیدی پیش‌فرض",
  };
}
```

---

## 🔐 احراز هویت و مجوزها

### فرآیند ورود

1. **ورود شماره موبایل** (`/login`)
2. **دریافت کد OTP**
3. **تایید کد** (`/login-verify`)
4. **ذخیره توکن‌ها در Cookie**
5. **Redirect به Dashboard یا Panel**

### Cookies استفاده شده

- `access_token`: JWT Token
- `refresh_token`: Refresh Token
- `uuid`: شناسه کاربر
- `userInfo`: اطلاعات کاربر

### محافظت از مسیرها

**مثال - محافظت از Panel:**

```javascript
// app/panel/(layout)/layout.js
async function LayoutPanel({ children }) {
  const user = await getUser();
  const ability = defineAbilitiesFor(user);

  if (!ability.can("access", "Panel")) {
    return redirect("/");
  }

  return <Content>{children}</Content>;
}
```

**مثال - محافظت از صفحه Login:**

```javascript
// app/login/page.js
async function LogIn() {
  const user = await getUser();
  const ability = defineAbilitiesFor(user);

  if (!ability.can("access", "Login")) {
    return redirect("/");
  }

  return <ContentLogin />;
}
```

---

## 📍 مسیرها و صفحات

### صفحات عمومی (Public)

| مسیر             | توضیحات        | فایل                        |
| ---------------- | -------------- | --------------------------- |
| `/`              | صفحه اصلی      | `app/page.js`               |
| `/doctors`       | لیست پزشکان    | `app/doctors/page.js`       |
| `/doctor/[slug]` | جزئیات پزشک    | `app/doctor/[slug]/page.js` |
| `/clinics`       | لیست کلینیک‌ها | `app/clinics/page.js`       |
| `/clinic/[slug]` | جزئیات کلینیک  | `app/clinic/[slug]/page.js` |
| `/specialties`   | تخصص‌های پزشکی | `app/specialties/page.js`   |
| `/blogs`         | لیست بلاگ‌ها   | `app/blogs/page.js`         |
| `/blog/[slug]`   | جزئیات بلاگ    | `app/blog/[slug]/page.js`   |
| `/about-us`      | درباره ما      | `app/about-us/page.js`      |
| `/contact-us`    | تماس با ما     | `app/contact-us/page.js`    |

### صفحات رزرو نوبت

| مسیر                      | توضیحات        |
| ------------------------- | -------------- |
| `/appointment/[doctorId]` | صفحه رزرو نوبت |

### صفحات احراز هویت

| مسیر            | توضیحات   | محافظت            |
| --------------- | --------- | ----------------- |
| `/login`        | ورود      | فقط کاربران مهمان |
| `/login-verify` | تایید OTP | فقط کاربران مهمان |

### پنل مدیریتی (Protected)

| مسیر                  | توضیحات        | دسترسی    |
| --------------------- | -------------- | --------- |
| `/panel/dashboard`    | داشبورد        | نمایندگان |
| `/panel/user-account` | حساب کاربری    | نمایندگان |
| `/panel/add-doctor`   | افزودن پزشک    | نمایندگان |
| `/panel/turns`        | مدیریت نوبت‌ها | نمایندگان |

---

## 🧩 کامپوننت‌های اصلی

### Layout Components

**1. Root Layout (`app/layout.js`)**

- Metadata داینامیک
- Theme Provider
- Province Provider
- Google Analytics
- Custom Toastify

**2. Public Layout (`components/layout/StLayout.js`)**

- Header
- Footer
- ScrollToTop

**3. Panel Layout (`components/layoutPanel/`)**

- Sidebar
- Header
- User Detail

### Feature Components

**Home Page (`components/home/`)**

- Search Bar
- Text Header
- Frequent Searches

**Doctor List (`components/doctors/`)**

- Filter Sidebar
- Doctor Cards
- Pagination

**Doctor Detail (`components/doctor/`)**

- Doctor Info
- Appointment List
- Comments
- Share

**Appointment (`components/appointment/`)**

- Date Picker
- Time Slots
- Patient Details
- Payment
- Success/Fail Pages

**Panel (`components/panel/`)**

- Dashboard Stats
- Doctor Management
- Appointment Management
- User Profile

---

## 🔌 API و سرویس‌ها

### تنظیمات Axios (`services/api.js`)

```javascript
const BASE_URL = process.env.NEXT_PUBLIC_API_URL;
const token = Cookies.get("access_token");

const api = axios.create({
  baseURL: BASE_URL,
  headers: {
    "Content-Type": "application/json",
    Authorization: token ? `Bearer ${token}` : "",
  },
});

api.interceptors.response.use(
  (response) => response.data,
  (error) => Promise.reject(error)
);
```

### API Endpoints (`services/response.js`)

**دسته‌بندی Endpoints:**

```javascript
// Authentication
postLogin(data);
getUserInfo(headers);

// Doctors
getDoctors(params);
getDoctor(slug);
postDoctor(data);
getDoctorServices();

// Clinics
getClinics(params);
getClinic(slug);
getClinicDoctors(slug, params);

// Appointments
getAppointment(doctor_id, date);
postAppointment(data);
getAppointmentNotAvailable(doctor_id);
getAppointmentWeeklySchedule(uuid);
postAppointmentWeeklySchedule();
patchAppointmentWeeklySchedule(uuid);
deleteAppointmentWeeklySchedule(uuid);

// User Profile
getUserProfile(uuid);
postUserProfile(data);
patchUserProfile(data, uuid, token);

// Categories
getSpecialties();
getInsuranceType();

// File Upload
postImageUpload(data);
```

### Helper Functions (`helper/index.js`)

```javascript
// تبدیل اعداد به فرمت عربی
export const numberToArStyle = (num) => num?.toLocaleString("ar-AE") || "";

// تبدیل تاریخ میلادی به شمسی
export const convertToJalali = (date) => {
  const jalaliDate = moment(date).format("jYYYY/jM/jD");
  // ...
};

// بررسی URL فعال
export const isActiveURL = (link, name) => link === name;

// آپلود تصویر
export const uploadImage = async (event) => {
  // ...
};

// ساخت Query String برای فیلتر پزشکان
export const buildDoctorParams = (searchParams) => {
  // ...
};
```

---

## 🎨 استایل‌دهی

### Tailwind CSS

پروژه از **Tailwind CSS** برای استایل‌دهی استفاده می‌کند:

```javascript
// tailwind.config.js
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  darkMode: "class",
  theme: {
    extend: {
      // Custom colors, fonts, etc.
    },
  },
};
```

### Material-UI Theme

```javascript
// mui/index.js
const theme = createTheme({
  direction: "rtl",
  typography: {
    fontFamily: "IRANSans, Arial",
  },
  palette: {
    primary: { main: "#5559CE" },
    // ...
  },
});
```

### Dark Mode

```javascript
// app/Providers.js
export function Providers({ children }) {
  const router = usePathname();

  return (
    <ThemeProvider
      attribute={router.includes("/panel") ? "class" : "data-"}
      defaultTheme="system"
      enableSystem
    >
      {children}
    </ThemeProvider>
  );
}
```

---

## 🐳 Deployment

### متغیرهای محیطی مورد نیاز

```env
# API Configuration
NEXT_PUBLIC_API_URL=https://api.nobat724.com

# Development Mode (TRUE/FALSE)
DEV_MODE=FALSE

# Application Port
APP_PORT=3000

# Node Environment
NODE_ENV=production
```

### Build Production

```bash
# Build
npm run build

# Start Production Server
npm run start
```

### Docker Deployment

```bash
# Build Image
docker build -t nobat724-front .

# Run Container
docker run -p 3000:3000 \
  -e NEXT_PUBLIC_API_URL=https://api.nobat724.com \
  -e DEV_MODE=FALSE \
  nobat724-front
```

### Docker Compose

```yaml
# docker-compose.yml
version: "3.8"

services:
  frontend:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - "${APP_PORT}:3000"
    environment:
      - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
      - DEV_MODE=${DEV_MODE}
```

---

## 📝 نکات توسعه

### 1. افزودن صفحه جدید

```javascript
// app/new-page/page.js
import Layout from "@/components/layout/StLayout";

export default function NewPage() {
  return (
    <Layout>
      <div>محتوای صفحه جدید</div>
    </Layout>
  );
}
```

### 2. افزودن API Endpoint جدید

```javascript
// services/response.js
export const request = {
  // ...
  getNewData: () => api.get("api/v1/new-endpoint"),
  postNewData: (data) => api.post("api/v1/new-endpoint", data),
};
```

### 3. افزودن کامپوننت جدید

```javascript
// components/NewComponent/index.js
"use client"; // اگر Client Component است

function NewComponent({ data }) {
  return <div>{/* محتوای کامپوننت */}</div>;
}

export default NewComponent;
```

### 4. کار با تاریخ شمسی

```javascript
import moment from "jalali-moment";

// تبدیل میلادی به شمسی
const jalaliDate = moment("2024-01-15").format("jYYYY/jMM/jDD");

// تبدیل شمسی به میلادی
const gregorianDate = moment("1402/10/25", "jYYYY/jMM/jDD").format(
  "YYYY-MM-DD"
);
```

### 5. استفاده از Context

```javascript
"use client";
import { useProvince } from "@/context/ProvinceProvider";

function MyComponent() {
  const { isProvinceInclude } = useProvince();

  return <div>{isProvinceInclude && <p>شهر: {isProvinceInclude.name}</p>}</div>;
}
```

---

## 🔧 Troubleshooting

### مشکلات رایج

**1. خطای Hydration**

```
Error: Text content does not match server-rendered HTML
```

**راه‌حل:** استفاده از `useEffect` برای محتوای Client-Side

**2. خطای Authentication**

```
Unauthorized (401)
```

**راه‌حل:** بررسی توکن در Cookie و تنظیم Header

**3. مشکل Dark Mode**

```
Flash of unstyled content
```

**راه‌حل:** استفاده صحیح از `next-themes` و `ThemeProvider`

---

## 📚 منابع مفید

- [Next.js Documentation](https://nextjs.org/docs)
- [Material-UI Documentation](https://mui.com/)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
- [CASL Documentation](https://casl.js.org/v6/en/)
- [Jalali Moment](https://github.com/jalaali/moment-jalaali)

---

## 👥 تیم توسعه

**Senior Frontend Developer**: شما! 🎉

**Tech Stack Expertise:**

- ✅ Next.js 14 (App Router)
- ✅ React.js 18
- ✅ Material-UI v7
- ✅ Tailwind CSS
- ✅ Server Components & Client Components
- ✅ Authentication & Authorization
- ✅ Multi-Domain Architecture
- ✅ Persian/Jalali Date Handling

---

## 📞 پشتیبانی

برای هرگونه سوال یا مشکل:

- 📧 Email: nobat724@gmail.com
- 🌐 Website: https://nobat724.com

---

**تاریخ آخرین به‌روزرسانی:** نوامبر 2025

**نسخه پروژه:** 0.1.0
