The two months rendered reversed (current month on the left). Pin the calendar row to dir=rtl and render the base month first so the current month sits on the right and the next month on the left, matching the design. Pin each month header to dir=ltr so the nav arrows stay on the expected outer edges regardless of the RTL flip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🏥 نوبت724 (Nobat724) - سیستم نوبتدهی آنلاین پزشکی
📋 فهرست مطالب
- معرفی پروژه
- معماری و تکنولوژی
- ساختار پروژه
- ویژگیهای کلیدی
- راهاندازی پروژه
- سیستم Multi-Domain
- احراز هویت و مجوزها
- مسیرها و صفحات
- کامپوننتهای اصلی
- API و سرویسها
- 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 پشتیبانی میکند که به هر شهر اجازه میدهد دامنه مستقل خود را داشته باشد:
// در 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. سیستم احراز هویت
// 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. سیستم رزرو نوبت
فرآیند رزرو نوبت شامل مراحل زیر است:
- انتخاب تاریخ و زمان (
components/appointment/date/) - ورود یا ثبتنام (در صورت نیاز)
- وارد کردن اطلاعات بیمار (
components/appointment/detail/) - پرداخت (
components/appointment/paying/) - تایید نهایی (
components/appointment/successPay/)
4. پنل مدیریتی
پنل مدیریتی برای نمایندگان شامل:
- داشبورد: نمای کلی آمار
- حساب کاربری: مدیریت پروفایل
- افزودن پزشک: ثبت پزشک جدید
- مدیریت نوبتها: مشاهده و مدیریت نوبتها
محافظت مسیر:
// 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 (اختیاری)
نصب و راهاندازی
# 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
NEXT_PUBLIC_CLIENT_ID=your_client_id
NEXT_PUBLIC_CLIENT_SECRET=your_client_secret
DEV_MODE=TRUE
# 5. اجرای پروژه در حالت Development
npm run dev
# یا
yarn dev
# پروژه در http://localhost:3000 اجرا میشود
اسکریپتهای موجود
{
"dev": "cross-env HOST=yazd-nobat.localhost PORT=3000 next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
راهاندازی با Docker
# 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 → یزد
فرآیند تشخیص شهر
// 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 داینامیک بر اساس شهر
// app/layout.js
export async function generateMetadata() {
const { matchedCity } = getStateInfo();
return {
title: matchedCity ? matchedCity.title : "نوبت724",
description: matchedCity ? matchedCity.description : "توضیحات پیشفرض",
keywords: matchedCity ? matchedCity.keywords : "کلمات کلیدی پیشفرض",
};
}
🔐 احراز هویت و مجوزها
فرآیند ورود
- ورود شماره موبایل (
/login) - دریافت کد OTP
- تایید کد (
/login-verify) - ذخیره توکنها در Cookie
- Redirect به Dashboard یا Panel
Cookies استفاده شده
access_token: JWT Tokenrefresh_token: Refresh Tokenuuid: شناسه کاربرuserInfo: اطلاعات کاربر
محافظت از مسیرها
مثال - محافظت از Panel:
// 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:
// 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)
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:
// 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)
// تبدیل اعداد به فرمت عربی
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 برای استایلدهی استفاده میکند:
// 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
// mui/index.js
const theme = createTheme({
direction: "rtl",
typography: {
fontFamily: "IRANSans, Arial",
},
palette: {
primary: { main: "#5559CE" },
// ...
},
});
Dark Mode
// app/Providers.js
export function Providers({ children }) {
const router = usePathname();
return (
<ThemeProvider
attribute={router.includes("/panel") ? "class" : "data-"}
defaultTheme="system"
enableSystem
>
{children}
</ThemeProvider>
);
}
🐳 Deployment
متغیرهای محیطی مورد نیاز
# API Configuration
NEXT_PUBLIC_API_URL=https://api.nobat724.com
NEXT_PUBLIC_CLIENT_ID=your_client_id
NEXT_PUBLIC_CLIENT_SECRET=your_client_secret
# Development Mode (TRUE/FALSE)
DEV_MODE=FALSE
# Application Port
APP_PORT=3000
# Node Environment
NODE_ENV=production
Build Production
# Build
npm run build
# Start Production Server
npm run start
Docker Deployment
# 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
# 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. افزودن صفحه جدید
// app/new-page/page.js
import Layout from "@/components/layout/StLayout";
export default function NewPage() {
return (
<Layout>
<div>محتوای صفحه جدید</div>
</Layout>
);
}
2. افزودن API Endpoint جدید
// services/response.js
export const request = {
// ...
getNewData: () => api.get("api/v1/new-endpoint"),
postNewData: (data) => api.post("api/v1/new-endpoint", data),
};
3. افزودن کامپوننت جدید
// components/NewComponent/index.js
"use client"; // اگر Client Component است
function NewComponent({ data }) {
return <div>{/* محتوای کامپوننت */}</div>;
}
export default NewComponent;
4. کار با تاریخ شمسی
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
"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
- Material-UI Documentation
- Tailwind CSS Documentation
- CASL Documentation
- Jalali Moment
👥 تیم توسعه
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