The OAuth flow uses grant_type=mobile with a server-issued grant (app/api/auth/token/route.js); no client_id/client_secret is sent at runtime. These vars were dead — removed from Dockerfile args/env, docker-compose, nixpacks, and docs. Also silences the Coolify 'variable not set' build warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
891 lines
23 KiB
Markdown
891 lines
23 KiB
Markdown
# 🏥 نوبت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
|