کانال نوحه و مداحی ناب

سامانه جامع مدیریت کلینیک دندانپزشکی با React.js

سامانه جامع مدیریت کلینیک دندانپزشکی با React.js

در ادامه، نسخه کامل و جامع سامانه مدیریت کلینیک دندانپزشکی با React.js ارائه می‌شود:

ساختار کامل پروژه

text
src/
├── components/
│   ├── common/
│   │   ├── Layout/
│   │   │   ├── Header.js
│   │   │   ├── Sidebar.js
│   │   │   ├── Footer.js
│   │   │   └── Breadcrumb.js
│   │   ├── UI/
│   │   │   ├── Button.js
│   │   │   ├── Modal.js
│   │   │   ├── Table.js
│   │   │   ├── Pagination.js
│   │   │   ├── SearchBox.js
│   │   │   ├── FilterPanel.js
│   │   │   ├── LoadingSpinner.js
│   │   │   ├── Toast.js
│   │   │   └── ConfirmDialog.js
│   │   └── Forms/
│   │       ├── PatientForm.js
│   │       ├── DentistForm.js
│   │       ├── VisitForm.js
│   │       ├── TreatmentPlanForm.js
│   │       ├── InvoiceForm.js
│   │       └── InsuranceForm.js
│   ├── patients/
│   │   ├── PatientList.js
│   │   ├── PatientCard.js
│   │   ├── PatientProfile.js
│   │   ├── PatientTeethChart.js
│   │   └── PatientInsurance.js
│   ├── dentists/
│   │   ├── DentistList.js
│   │   ├── DentistCard.js
│   │   ├── DentistSchedule.js
│   │   └── DentistCalendar.js
│   ├── visits/
│   │   ├── VisitList.js
│   │   ├── VisitForm.js
│   │   ├── VisitDetails.js
│   │   ├── TreatmentList.js
│   │   └── DentalChart.js
│   ├── treatment-plans/
│   │   ├── TreatmentPlanList.js
│   │   ├── TreatmentPlanForm.js
│   │   ├── TreatmentPlanDetails.js
│   │   └── TreatmentTimeline.js
│   ├── financial/
│   │   ├── InvoiceList.js
│   │   ├── PaymentForm.js
│   │   ├── FinancialReport.js
│   │   └── InsuranceCalculator.js
│   ├── radiology/
│   │   ├── RadiographList.js
│   │   ├── RadiographUpload.js
│   │   └── RadiographViewer.js
│   ├── appointments/
│   │   ├── AppointmentList.js
│   │   ├── AppointmentForm.js
│   │   ├── CalendarView.js
│   │   └── ScheduleManager.js
│   └── dashboard/
│       ├── DashboardStats.js
│       ├── RecentActivity.js
│       ├── QuickActions.js
│       └── UpcomingAppointments.js
├── pages/
│   ├── Dashboard.js
│   ├── Patients/
│   │   ├── PatientsList.js
│   │   ├── AddPatient.js
│   │   ├── EditPatient.js
│   │   └── PatientDetail.js
│   ├── Dentists/
│   │   ├── DentistsList.js
│   │   ├── AddDentist.js
│   │   └── EditDentist.js
│   ├── Visits/
│   │   ├── VisitsList.js
│   │   ├── AddVisit.js
│   │   └── VisitDetail.js
│   ├── TreatmentPlans/
│   │   ├── TreatmentPlansList.js
│   │   ├── CreateTreatmentPlan.js
│   │   └── TreatmentPlanDetail.js
│   ├── Financial/
│   │   ├── Invoices.js
│   │   ├── Payments.js
│   │   └── Reports.js
│   ├── Radiology/
│   │   ├── Radiographs.js
│   │   └── UploadRadiograph.js
│   └── Appointments/
│       ├── Appointments.js
│       ├── Calendar.js
│       └── Schedule.js
├── services/
│   ├── api/
│   │   ├── apiClient.js
│   │   ├── patientService.js
│   │   ├── dentistService.js
│   │   ├── visitService.js
│   │   ├── treatmentPlanService.js
│   │   ├── financialService.js
│   │   ├── radiologyService.js
│   │   ├── appointmentService.js
│   │   └── reportService.js
│   └── utils/
│       ├── formatters.js
│       ├── validators.js
│       └── constants.js
├── hooks/
│   ├── useApi.js
│   ├── useForm.js
│   ├── usePagination.js
│   └── useLocalStorage.js
├── context/
│   └── AppContext.js
├── styles/
│   ├── main.css
│   └── components.css
└── App.js

فایل‌های اصلی پروژه

1. فایل اصلی App.js

javascript
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { AppProvider } from './context/AppContext';
import Layout from './components/common/Layout/Layout';
import Dashboard from './pages/Dashboard';

// Patient Routes
import PatientsList from './pages/Patients/PatientsList';
import AddPatient from './pages/Patients/AddPatient';
import EditPatient from './pages/Patients/EditPatient';
import PatientDetail from './pages/Patients/PatientDetail';

// Dentist Routes
import DentistsList from './pages/Dentists/DentistsList';
import AddDentist from './pages/Dentists/AddDentist';
import EditDentist from './pages/Dentists/EditDentist';

// Visit Routes
import VisitsList from './pages/Visits/VisitsList';
import AddVisit from './pages/Visits/AddVisit';
import VisitDetail from './pages/Visits/VisitDetail';

// Treatment Plan Routes
import TreatmentPlansList from './pages/TreatmentPlans/TreatmentPlansList';
import CreateTreatmentPlan from './pages/TreatmentPlans/CreateTreatmentPlan';
import TreatmentPlanDetail from './pages/TreatmentPlans/TreatmentPlanDetail';

// Financial Routes
import Invoices from './pages/Financial/Invoices';
import Payments from './pages/Financial/Payments';
import Reports from './pages/Financial/Reports';

// Radiology Routes
import Radiographs from './pages/Radiology/Radiographs';
import UploadRadiograph from './pages/Radiology/UploadRadiograph';

// Appointment Routes
import Appointments from './pages/Appointments/Appointments';
import Calendar from './pages/Appointments/Calendar';
import Schedule from './pages/Appointments/Schedule';

import './styles/main.css';
import './styles/components.css';

function App() {
  return (
    <AppProvider>
      <Router>
        <Layout>
          <Routes>
            {/* Dashboard */}
            <Route path="/" element={<Dashboard />} />
            
            {/* Patient Routes */}
            <Route path="/patients" element={<PatientsList />} />
            <Route path="/patients/new" element={<AddPatient />} />
            <Route path="/patients/edit/:id" element={<EditPatient />} />
            <Route path="/patients/:id" element={<PatientDetail />} />
            
            {/* Dentist Routes */}
            <Route path="/dentists" element={<DentistsList />} />
            <Route path="/dentists/new" element={<AddDentist />} />
            <Route path="/dentists/edit/:id" element={<EditDentist />} />
            
            {/* Visit Routes */}
            <Route path="/visits" element={<VisitsList />} />
            <Route path="/visits/new" element={<AddVisit />} />
            <Route path="/visits/:id" element={<VisitDetail />} />
            
            {/* Treatment Plan Routes */}
            <Route path="/treatment-plans" element={<TreatmentPlansList />} />
            <Route path="/treatment-plans/new" element={<CreateTreatmentPlan />} />
            <Route path="/treatment-plans/:id" element={<TreatmentPlanDetail />} />
            
            {/* Financial Routes */}
            <Route path="/financial/invoices" element={<Invoices />} />
            <Route path="/financial/payments" element={<Payments />} />
            <Route path="/financial/reports" element={<Reports />} />
            
            {/* Radiology Routes */}
            <Route path="/radiology" element={<Radiographs />} />
            <Route path="/radiology/upload" element={<UploadRadiograph />} />
            
            {/* Appointment Routes */}
            <Route path="/appointments" element={<Appointments />} />
            <Route path="/appointments/calendar" element={<Calendar />} />
            <Route path="/appointments/schedule" element={<Schedule />} />
          </Routes>
        </Layout>
      </Router>
    </AppProvider>
  );
}

export default App;

2. سرویس‌های API

services/api/apiClient.js

javascript
Copy
Download
const BASE_URL = 'http://localhost/api';

export const apiClient = {
  async request(endpoint, options = {}) {
    const url = `${BASE_URL}${endpoint}`;
    const config = {
      headers: {
        'Content-Type': 'application/json',
        ...options.headers,
      },
      ...options,
    };

    if (config.body) {
      config.body = JSON.stringify(config.body);
    }

    try {
      const response = await fetch(url, config);
      const data = await response.json();
      
      if (!response.ok) {
        throw new Error(data.message || 'خطا در ارتباط با سرور');
      }
      
      return data;
    } catch (error) {
      console.error('API Error:', error);
      throw error;
    }
  },

  get(endpoint) {
    return this.request(endpoint);
  },

  post(endpoint, data) {
    return this.request(endpoint, {
      method: 'POST',
      body: data,
    });
  },

  put(endpoint, data) {
    return this.request(endpoint, {
      method: 'PUT',
      body: data,
    });
  },

  delete(endpoint) {
    return this.request(endpoint, {
      method: 'DELETE',
    });
  },
};

services/api/patientService.js

javascript
import { apiClient } from './apiClient';

export const patientService = {
  // گرفتن لیست بیماران
  getPatients: (filters = {}) => {
    return apiClient.post('/patients', filters);
  },

  // گرفتن اطلاعات بیمار
  getPatientInfo: (patientId) => {
    return apiClient.get(`/patients/${patientId}`);
  },

  // ایجاد بیمار جدید
  createPatient: (patientData) => {
    return apiClient.post('/patients', patientData);
  },

  // به‌روزرسانی بیمار
  updatePatient: (patientId, patientData) => {
    return apiClient.put(`/patients/${patientId}`, patientData);
  },

  // حذف بیمار
  deletePatient: (patientId) => {
    return apiClient.delete(`/patients/${patientId}`);
  },

  // افزودن بیمه به بیمار
  addPatientInsurance: (patientId, insuranceData) => {
    return apiClient.post(`/patients/${patientId}/insurances`, insuranceData);
  },

  // گرفتن صورتحساب‌های بیمار
  getPatientInvoices: (patientId) => {
    return apiClient.get(`/patients/${patientId}/invoices`);
  },

  // گرفتن طرح‌های درمان بیمار
  getPatientTreatmentPlans: (patientId) => {
    return apiClient.get(`/patients/${patientId}/treatment-plans`);
  },

  // گرفتن عکس‌های رادیولوژی بیمار
  getPatientRadiographs: (patientId) => {
    return apiClient.get(`/patients/${patientId}/radiographs`);
  },

  // ثبت وضعیت دندان‌های بیمار
  recordPatientTeeth: (patientId, teethData) => {
    return apiClient.post(`/patients/${patientId}/teeth`, teethData);
  }
};

services/api/dentistService.js

javascript
import { apiClient } from './apiClient';

export const dentistService = {
  // گرفتن لیست دندانپزشکان
  getDentists: (filters = {}) => {
    return apiClient.post('/dentists', filters);
  },

  // ایجاد دندانپزشک جدید
  createDentist: (dentistData) => {
    return apiClient.post('/dentists', dentistData);
  },

  // به‌روزرسانی دندانپزشک
  updateDentist: (dentistId, dentistData) => {
    return apiClient.put(`/dentists/${dentistId}`, dentistData);
  },

  // حذف دندانپزشک
  deleteDentist: (dentistId) => {
    return apiClient.delete(`/dentists/${dentistId}`);
  },

  // گرفتن برنامه زمانی دندانپزشک
  getDentistSchedule: (dentistId, dateRange = {}) => {
    return apiClient.post(`/dentists/${dentistId}/schedule`, dateRange);
  },

  // گرفتن قرارملاقات‌های دندانپزشک
  getDentistAppointments: (dentistId, filters = {}) => {
    return apiClient.post(`/dentists/${dentistId}/appointments`, filters);
  }
};

services/api/visitService.js

javascript
import { apiClient } from './apiClient';

export const visitService = {
  // گرفتن لیست ویزیت‌ها
  getVisits: (filters = {}) => {
    return apiClient.post('/visits', filters);
  },

  // ایجاد ویزیت جدید
  createVisit: (visitData) => {
    return apiClient.post('/visits', visitData);
  },

  // گرفتن اطلاعات ویزیت
  getVisitInfo: (visitId) => {
    return apiClient.get(`/visits/${visitId}`);
  },

  // به‌روزرسانی ویزیت
  updateVisit: (visitId, visitData) => {
    return apiClient.put(`/visits/${visitId}`, visitData);
  },

  // حذف ویزیت
  deleteVisit: (visitId) => {
    return apiClient.delete(`/visits/${visitId}`);
  },

  // ثبت درمان دندانپزشکی
  recordTreatment: (treatmentData) => {
    return apiClient.post('/visits/treatments', treatmentData);
  },

  // گرفتن درمان‌های ویزیت
  getVisitTreatments: (visitId) => {
    return apiClient.get(`/visits/${visitId}/treatments`);
  }
};

3. هوک‌های سفارشی

hooks/useApi.js

javascript
import { useState, useEffect } from 'react';

export const useApi = (apiFunction, immediate = true, initialParams = []) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const execute = async (...params) => {
    try {
      setLoading(true);
      setError(null);
      const response = await apiFunction(...params);
      setData(response);
      return response;
    } catch (err) {
      setError(err.message);
      throw err;
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    if (immediate) {
      execute(...initialParams);
    }
  }, []);

  return { data, loading, error, execute, setData };
};

hooks/usePagination.js

javascript
import { useState, useMemo } from 'react';

export const usePagination = (initialPage = 1, initialPageSize = 20) => {
  const [page, setPage] = useState(initialPage);
  const [pageSize, setPageSize] = useState(initialPageSize);

  const pagination = useMemo(() => ({
    page,
    pageSize,
    skip: (page - 1) * pageSize
  }), [page, pageSize]);

  return {
    page,
    pageSize,
    pagination,
    setPage,
    setPageSize,
    nextPage: () => setPage(prev => prev + 1),
    prevPage: () => setPage(prev => Math.max(1, prev - 1)),
    goToPage: (pageNum) => setPage(Math.max(1, pageNum)),
    reset: () => {
      setPage(1);
      setPageSize(initialPageSize);
    }
  };
};

4. کامپوننت‌های اصلی

components/common/Layout/Layout.js

javascript
import React from 'react';
import Header from './Header';
import Sidebar from './Sidebar';
import Footer from './Footer';
import './Layout.css';

const Layout = ({ children }) => {
  return (
    <div className="layout">
      <Header />
      <div className="layout-body">
        <Sidebar />
        <main className="layout-content">
          {children}
        </main>
      </div>
      <Footer />
    </div>
  );
};

export default Layout;

components/common/Layout/Sidebar.js

javascript
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import './Sidebar.css';

const Sidebar = () => {
  const location = useLocation();

  const menuItems = [
    { path: '/', icon: 'fas fa-tachometer-alt', label: 'داشبورد' },
    { path: '/patients', icon: 'fas fa-users', label: 'بیماران' },
    { path: '/dentists', icon: 'fas fa-user-md', label: 'دندانپزشکان' },
    { path: '/visits', icon: 'fas fa-stethoscope', label: 'ویزیت‌ها' },
    { path: '/treatment-plans', icon: 'fas fa-clipboard-list', label: 'طرح‌های درمان' },
    { path: '/appointments', icon: 'fas fa-calendar-alt', label: 'قرارملاقات‌ها' },
    { path: '/financial/invoices', icon: 'fas fa-file-invoice-dollar', label: 'مالی' },
    { path: '/radiology', icon: 'fas fa-x-ray', label: 'رادیولوژی' },
    { path: '/financial/reports', icon: 'fas fa-chart-bar', label: 'گزارش‌ها' }
  ];

  return (
    <aside className="sidebar">
      <nav className="sidebar-nav">
        <ul>
          {menuItems.map(item => (
            <li key={item.path} className={location.pathname === item.path ? 'active' : ''}>
              <Link to={item.path}>
                <i className={item.icon}></i>
                <span>{item.label}</span>
              </Link>
            </li>
          ))}
        </ul>
      </nav>
    </aside>
  );
};

export default Sidebar;

5. صفحات اصلی

pages/Dashboard.js

javascript
import React, { useState, useEffect } from 'react';
import DashboardStats from '../components/dashboard/DashboardStats';
import RecentActivity from '../components/dashboard/RecentActivity';
import QuickActions from '../components/dashboard/QuickActions';
import UpcomingAppointments from '../components/dashboard/UpcomingAppointments';
import { reportService } from '../services/api/reportService';
import './Dashboard.css';

const Dashboard = () => {
  const [stats, setStats] = useState({});
  const [loading, setLoading] = useState(true);
  const [dateRange, setDateRange] = useState({
    startDate: new Date().toISOString().split('T')[0],
    endDate: new Date().toISOString().split('T')[0]
  });

  useEffect(() => {
    fetchDashboardData();
  }, [dateRange]);

  const fetchDashboardData = async () => {
    try {
      setLoading(true);
      const response = await reportService.getClinicStatistics(dateRange);
      if (response.success) {
        setStats(response);
      }
    } catch (error) {
      console.error('Error fetching dashboard data:', error);
    } finally {
      setLoading(false);
    }
  };

  if (loading) {
    return (
      <div className="dashboard-loading">
        <div className="loading-spinner"></div>
        <p>در حال بارگذاری اطلاعات...</p>
      </div>
    );
  }

  return (
    <div className="dashboard">
      <div className="dashboard-header">
        <h1>داشبورد مدیریت کلینیک دندانپزشکی</h1>
        <div className="date-filter">
          <input
            type="date"
            value={dateRange.startDate}
            onChange={(e) => setDateRange({...dateRange, startDate: e.target.value})}
          />
          <span>تا</span>
          <input
            type="date"
            value={dateRange.endDate}
            onChange={(e) => setDateRange({...dateRange, endDate: e.target.value})}
          />
        </div>
      </div>

      <DashboardStats stats={stats} />
      
      <div className="dashboard-content">
        <div className="dashboard-column">
          <QuickActions />
          <RecentActivity />
        </div>
        <div className="dashboard-column">
          <UpcomingAppointments />
        </div>
      </div>
    </div>
  );
};

export default Dashboard;

pages/Patients/PatientsList.js

javascript
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import PatientList from '../../components/patients/PatientList';
import { patientService } from '../../services/api/patientService';
import { usePagination } from '../../hooks/usePagination';
import './PatientsList.css';

const PatientsList = () => {
  const [patients, setPatients] = useState([]);
  const [loading, setLoading] = useState(true);
  const [filters, setFilters] = useState({
    searchTerm: '',
    gender: '',
    status: 'Active'
  });
  
  const { page, pageSize, pagination, setPage } = usePagination();

  useEffect(() => {
    fetchPatients();
  }, [filters, page, pageSize]);

  const fetchPatients = async () => {
    try {
      setLoading(true);
      const response = await patientService.getPatients({
        ...filters,
        page,
        pageSize
      });
      
      if (response.success) {
        setPatients(response.patients || []);
      }
    } catch (error) {
      console.error('Error fetching patients:', error);
    } finally {
      setLoading(false);
    }
  };

  const handleSearch = (searchTerm) => {
    setFilters(prev => ({ ...prev, searchTerm, page: 1 }));
  };

  const handleFilterChange = (newFilters) => {
    setFilters(prev => ({ ...prev, ...newFilters, page: 1 }));
  };

  const handleDeletePatient = async (patientId) => {
    if (window.confirm('آیا از حذف این بیمار اطمینان دارید؟')) {
      try {
        await patientService.deletePatient(patientId);
        fetchPatients(); // Refresh the list
      } catch (error) {
        console.error('Error deleting patient:', error);
        alert('خطا در حذف بیمار');
      }
    }
  };

  return (
    <div className="patients-page">
      <div className="page-header">
        <h1>مدیریت بیماران</h1>
        <Link to="/patients/new" className="btn btn-primary">
          <i className="fas fa-plus"></i>
          افزودن بیمار جدید
        </Link>
      </div>

      <PatientList
        patients={patients}
        loading={loading}
        filters={filters}
        onSearch={handleSearch}
        onFilterChange={handleFilterChange}
        onDeletePatient={handleDeletePatient}
        pagination={pagination}
        onPageChange={setPage}
      />
    </div>
  );
};

export default PatientsList;

pages/Patients/AddPatient.js

javascript
import React from 'react';
import PatientForm from '../../components/common/Forms/PatientForm';
import { patientService } from '../../services/api/patientService';
import { useNavigate } from 'react-router-dom';
import './AddPatient.css';

const AddPatient = () => {
  const navigate = useNavigate();

  const handleSubmit = async (patientData) => {
    try {
      const response = await patientService.createPatient(patientData);
      if (response.success) {
        alert('بیمار با موفقیت ایجاد شد');
        navigate('/patients');
      }
    } catch (error) {
      console.error('Error creating patient:', error);
      alert('خطا در ایجاد بیمار: ' + error.message);
    }
  };

  const handleCancel = () => {
    navigate('/patients');
  };

  return (
    <div className="add-patient-page">
      <div className="page-header">
        <h1>افزودن بیمار جدید</h1>
      </div>
      
      <PatientForm
        onSubmit={handleSubmit}
        onCancel={handleCancel}
        submitButtonText="ایجاد بیمار"
      />
    </div>
  );
};

export default AddPatient;

6. کامپوننت فرم بیمار

components/common/Forms/PatientForm.js

javascript
import React, { useState } from 'react';
import './PatientForm.css';

const PatientForm = ({ patient, onSubmit, onCancel, submitButtonText = "ذخیره" }) => {
  const [formData, setFormData] = useState(patient || {
    national_id: '',
    first_name: '',
    last_name: '',
    birth_date: '',
    gender: 'Male',
    mobile: '',
    email: '',
    address: '',
    city: '',
    province: '',
    postal_code: '',
    emergency_contact_name: '',
    emergency_contact_phone: '',
    emergency_contact_relation: '',
    marital_status: 'Single',
    occupation: '',
    blood_type: '',
    allergies: '',
    medical_conditions: '',
    current_medications: '',
    smoking_status: 'Non-smoker',
    dental_anxiety_level: 'None',
    referred_by: '',
    notes: ''
  });

  const [errors, setErrors] = useState({});

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
    
    // Clear error when user starts typing
    if (errors[name]) {
      setErrors(prev => ({
        ...prev,
        [name]: ''
      }));
    }
  };

  const validateForm = () => {
    const newErrors = {};
    
    if (!formData.national_id.trim()) {
      newErrors.national_id = 'کد ملی الزامی است';
    }
    
    if (!formData.first_name.trim()) {
      newErrors.first_name = 'نام الزامی است';
    }
    
    if (!formData.last_name.trim()) {
      newErrors.last_name = 'نام خانوادگی الزامی است';
    }
    
    if (!formData.birth_date) {
      newErrors.birth_date = 'تاریخ تولد الزامی است';
    }
    
    if (!formData.mobile.trim()) {
      newErrors.mobile = 'شماره موبایل الزامی است';
    }
    
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    
    if (validateForm()) {
      onSubmit(formData);
    }
  };

  return (
    <form className="patient-form" onSubmit={handleSubmit}>
      <div className="form-section">
        <h3>اطلاعات شخصی</h3>
        <div className="form-row">
          <div className="form-group">
            <label>کد ملی *</label>
            <input
              type="text"
              name="national_id"
              value={formData.national_id}
              onChange={handleInputChange}
              className={errors.national_id ? 'error' : ''}
            />
            {errors.national_id && <span className="error-text">{errors.national_id}</span>}
          </div>
          
          <div className="form-group">
            <label>نام *</label>
            <input
              type="text"
              name="first_name"
              value={formData.first_name}
              onChange={handleInputChange}
              className={errors.first_name ? 'error' : ''}
            />
            {errors.first_name && <span className="error-text">{errors.first_name}</span>}
          </div>
          
          <div className="form-group">
            <label>نام خانوادگی *</label>
            <input
              type="text"
              name="last_name"
              value={formData.last_name}
              onChange={handleInputChange}
              className={errors.last_name ? 'error' : ''}
            />
            {errors.last_name && <span className="error-text">{errors.last_name}</span>}
          </div>
        </div>

        <div className="form-row">
          <div className="form-group">
            <label>تاریخ تولد *</label>
            <input
              type="date"
              name="birth_date"
              value={formData.birth_date}
              onChange={handleInputChange}
              className={errors.birth_date ? 'error' : ''}
            />
            {errors.birth_date && <span className="error-text">{errors.birth_date}</span>}
          </div>
          
          <div className="form-group">
            <label>جنسیت *</label>
            <select
              name="gender"
              value={formData.gender}
              onChange={handleInputChange}
            >
              <option value="Male">مرد</option>
              <option value="Female">زن</option>
              <option value="Other">سایر</option>
            </select>
          </div>
          
          <div className="form-group">
            <label>وضعیت تاهل</label>
            <select
              name="marital_status"
              value={formData.marital_status}
              onChange={handleInputChange}
            >
              <option value="Single">مجرد</option>
              <option value="Married">متاهل</option>
              <option value="Divorced">مطلقه</option>
              <option value="Widowed">همسر فوت شده</option>
            </select>
          </div>
        </div>
      </div>

      <div className="form-section">
        <h3>اطلاعات تماس</h3>
        <div className="form-row">
          <div className="form-group">
            <label>شماره موبایل *</label>
            <input
              type="tel"
              name="mobile"
              value={formData.mobile}
              onChange={handleInputChange}
              className={errors.mobile ? 'error' : ''}
            />
            {errors.mobile && <span className="error-text">{errors.mobile}</span>}
          </div>
          
          <div className="form-group">
            <label>ایمیل</label>
            <input
              type="email"
              name="email"
              value={formData.email}
              onChange={handleInputChange}
            />
          </div>
        </div>

        <div className="form-group full-width">
          <label>آدرس</label>
          <textarea
            name="address"
            value={formData.address}
            onChange={handleInputChange}
            rows="3"
          />
        </div>

        <div className="form-row">
          <div className="form-group">
            <label>شهر</label>
            <input
              type="text"
              name="city"
              value={formData.city}
              onChange={handleInputChange}
            />
          </div>
          
          <div className="form-group">
            <label>استان</label>
            <input
              type="text"
              name="province"
              value={formData.province}
              onChange={handleInputChange}
            />
          </div>
          
          <div className="form-group">
            <label>کد پستی</label>
            <input
              type="text"
              name="postal_code"
              value={formData.postal_code}
              onChange={handleInputChange}
            />
          </div>
        </div>
      </div>

      <div className="form-section">
        <h3>اطلاعات اضطراری</h3>
        <div className="form-row">
          <div className="form-group">
            <label>نام تماس اضطراری</label>
            <input
              type="text"
              name="emergency_contact_name"
              value={formData.emergency_contact_name}
              onChange={handleInputChange}
            />
          </div>
          
          <div className="form-group">
            <label>شماره تماس اضطراری</label>
            <input
              type="tel"
              name="emergency_contact_phone"
              value={formData.emergency_contact_phone}
              onChange={handleInputChange}
            />
          </div>
          
          <div className="form-group">
            <label>نسبت</label>
            <input
              type="text"
              name="emergency_contact_relation"
              value={formData.emergency_contact_relation}
              onChange={handleInputChange}
            />
          </div>
        </div>
      </div>

      <div className="form-section">
        <h3>اطلاعات پزشکی</h3>
        <div className="form-row">
          <div className="form-group">
            <label>شغل</label>
            <input
              type="text"
              name="occupation"
              value={formData.occupation}
              onChange={handleInputChange}
            />
          </div>
          
          <div className="form-group">
            <label>گروه خونی</label>
            <select
              name="blood_type"
              value={formData.blood_type}
              onChange={handleInputChange}
            >
              <option value="">انتخاب کنید</option>
              <option value="A+">A+</option>
              <option value="A-">A-</option>
              <option value="B+">B+</option>
              <option value="B-">B-</option>
              <option value="AB+">AB+</option>
              <option value="AB-">AB-</option>
              <option value="O+">O+</option>
              <option value="O-">O-</option>
            </select>
          </div>
          
          <div className="form-group">
            <label>وضعیت سیگار</label>
            <select
              name="smoking_status"
              value={formData.smoking_status}
              onChange={handleInputChange}
            >
              <option value="Non-smoker">عدم مصرف</option>
              <option value="Smoker">مصرف کننده</option>
              <option value="Ex-smoker">سابق</option>
            </select>
          </div>
        </div>

        <div className="form-group full-width">
          <label>حساسیت‌ها</label>
          <textarea
            name="allergies"
            value={formData.allergies}
            onChange={handleInputChange}
            rows="2"
            placeholder="حساسیت‌های دارویی، غذایی و ..."
          />
        </div>

        <div className="form-group full-width">
          <label>بیماری‌های زمینه‌ای</label>
          <textarea
            name="medical_conditions"
            value={formData.medical_conditions}
            onChange={handleInputChange}
            rows="2"
            placeholder="بیماری‌هایی مانند دیابت، فشار خون و ..."
          />
        </div>

        <div className="form-group full-width">
          <label>داروهای مصرفی</label>
          <textarea
            name="current_medications"
            value={formData.current_medications}
            onChange={handleInputChange}
            rows="2"
            placeholder="داروهای مصرفی فعلی"
          />
        </div>
      </div>

      <div className="form-section">
        <h3>اطلاعات دندانپزشکی</h3>
        <div className="form-row">
          <div className="form-group">
            <label>سطح اضطراب دندانپزشکی</label>
            <select
              name="dental_anxiety_level"
              value={formData.dental_anxiety_level}
              onChange={handleInputChange}
            >
              <option value="None">بدون اضطراب</option>
              <option value="Low">کم</option>
              <option value="Medium">متوسط</option>
              <option value="High">زیاد</option>
              <option value="Severe">شدید</option>
            </select>
          </div>
          
          <div className="form-group">
            <label>معرفی شده توسط</label>
            <input
              type="text"
              name="referred_by"
              value={formData.referred_by}
              onChange={handleInputChange}
            />
          </div>
        </div>

        <div className="form-group full-width">
          <label>یادداشت‌ها</label>
          <textarea
            name="notes"
            value={formData.notes}
            onChange={handleInputChange}
            rows="3"
            placeholder="یادداشت‌های اضافی"
          />
        </div>
      </div>

      <div className="form-actions">
        <button type="button" className="btn btn-secondary" onClick={onCancel}>
          انصراف
        </button>
        <button type="submit" className="btn btn-primary">
          {submitButtonText}
        </button>
      </div>
    </form>
  );
};

export default PatientForm;

7. استایل‌های پایه

styles/main.css

css
/* Reset and Base Styles */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Vazirmatn', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  line-height: 1.6;
  color: #333;
  background-color: #f8f9fa;
  direction: rtl;
}

/* Layout Styles */
.layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.layout-body {
  display: flex;
  flex: 1;
}

.layout-content {
  flex: 1;
  padding: 20px;
  margin-right: 250px; /* Sidebar width */
  transition: margin-right 0.3s ease;
}

/* Header Styles */
.header {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  padding: 1rem 2rem;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.header h1 {
  font-size: 1.5rem;
  font-weight: 600;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* Sidebar Styles */
.sidebar {
  position: fixed;
  right: 0;
  top: 0;
  width: 250px;
  height: 100vh;
  background: #2c3e50;
  color: white;
  transform: translateX(0);
  transition: transform 0.3s ease;
  z-index: 1000;
}

.sidebar-nav ul {
  list-style: none;
  padding: 2rem 0;
}

.sidebar-nav li {
  margin-bottom: 0.5rem;
}

.sidebar-nav a {
  display: flex;
  align-items: center;
  padding: 0.75rem 1.5rem;
  color: #bdc3c7;
  text-decoration: none;
  transition: all 0.3s ease;
}

.sidebar-nav a:hover {
  background: #34495e;
  color: white;
}

.sidebar-nav li.active a {
  background: #3498db;
  color: white;
  border-right: 4px solid #2980b9;
}

.sidebar-nav i {
  margin-left: 0.75rem;
  width: 20px;
  text-align: center;
}

/* Footer Styles */
.footer {
  background: #34495e;
  color: #bdc3c7;
  text-align: center;
  padding: 1rem;
  margin-top: auto;
}

/* Button Styles */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 6px;
  font-size: 0.9rem;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: all 0.3s ease;
  gap: 0.5rem;
}

.btn-primary {
  background: #3498db;
  color: white;
}

.btn-primary:hover {
  background: #2980b9;
  transform: translateY(-1px);
}

.btn-secondary {
  background: #95a5a6;
  color: white;
}

.btn-secondary:hover {
  background: #7f8c8d;
}

.btn-success {
  background: #27ae60;
  color: white;
}

.btn-warning {
  background: #f39c12;
  color: white;
}

.btn-danger {
  background: #e74c3c;
  color: white;
}

.btn-danger:hover {
  background: #c0392b;
}

.btn-sm {
  padding: 0.25rem 0.75rem;
  font-size: 0.8rem;
}

/* Card Styles */
.card {
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  margin-bottom: 1.5rem;
  overflow: hidden;
}

.card-header {
  padding: 1rem 1.5rem;
  background: #f8f9fa;
  border-bottom: 1px solid #e9ecef;
  display: flex;
  justify-content: between;
  align-items: center;
}

.card-header h3 {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 600;
}

.card-body {
  padding: 1.5rem;
}

/* Table Styles */
.table {
  width: 100%;
  border-collapse: collapse;
  background: white;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

.table th,
.table td {
  padding: 0.75rem 1rem;
  text-align: right;
  border-bottom: 1px solid #e9ecef;
}

.table th {
  background: #f8f9fa;
  font-weight: 600;
  color: #495057;
}

.table tbody tr:hover {
  background: #f8f9fa;
}

.table-actions {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
}

/* Form Styles */
.form-section {
  background: white;
  border-radius: 8px;
  padding: 1.5rem;
  margin-bottom: 1.5rem;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

.form-section h3 {
  margin-bottom: 1rem;
  padding-bottom: 0.5rem;
  border-bottom: 2px solid #3498db;
  color: #2c3e50;
}

.form-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1rem;
  margin-bottom: 1rem;
}

.form-group {
  display: flex;
  flex-direction: column;
}

.form-group.full-width {
  grid-column: 1 / -1;
}

.form-group label {
  margin-bottom: 0.5rem;
  font-weight: 500;
  color: #495057;
}

.form-group input,
.form-group select,
.form-group textarea {
  padding: 0.75rem;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-size: 0.9rem;
  transition: border-color 0.3s ease;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: none;
  border-color: #3498db;
  box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}

.form-group.error input,
.form-group.error select,
.form-group.error textarea {
  border-color: #e74c3c;
}

.error-text {
  color: #e74c3c;
  font-size: 0.8rem;
  margin-top: 0.25rem;
}

.form-actions {
  display: flex;
  justify-content: flex-start;
  gap: 1rem;
  padding: 1.5rem;
  background: #f8f9fa;
  border-top: 1px solid #e9ecef;
}

/* Loading Spinner */
.loading-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin: 2rem auto;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* Responsive Design */
@media (max-width: 768px) {
  .layout-content {
    margin-right: 0;
    padding: 1rem;
  }
  
  .sidebar {
    transform: translateX(100%);
  }
  
  .sidebar.open {
    transform: translateX(0);
  }
  
  .form-row {
    grid-template-columns: 1fr;
  }
  
  .table {
    font-size: 0.8rem;
  }
  
  .table-actions {
    flex-direction: column;
  }
}

/* Utility Classes */
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-left { text-align: left; }

.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }

.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.mb-3 { margin-bottom: 1.5rem; }

.p-1 { padding: 0.5rem; }
.p-2 { padding: 1rem; }
.p-3 { padding: 1.5rem; }

.d-flex { display: flex; }
.justify-between { justify-content: space-between; }
.align-center { align-items: center; }
.gap-1 { gap: 0.5rem; }
.gap-2 { gap: 1rem; }

/* Status Badges */
.badge {
  display: inline-block;
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-size: 0.75rem;
  font-weight: 500;
}

.badge-success {
  background: #d4edda;
  color: #155724;
}

.badge-warning {
  background: #fff3cd;
  color: #856404;
}

.badge-danger {
  background: #f8d7da;
  color: #721c24;
}

.badge-info {
  background: #d1ecf1;
  color: #0c5460;
}

8. فایل package.json

json
{
  "name": "dental-clinic-management",
  "version": "1.0.0",
  "description": "Comprehensive Dental Clinic Management System",
  "main": "index.js",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.8.0",
    "react-scripts": "5.0.1"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

ویژگی‌های اصلی سیستم

1. مدیریت بیماران

  • ثبت اطلاعات کامل بیماران

  • مدیریت بیمه بیماران

  • نمودار دندان‌ها و وضعیت سلامت دهان

  • سابقه درمانی

  • اطلاعات اضطراری

2. مدیریت دندانپزشکان

  • پروفایل دندانپزشکان

  • برنامه زمانی

  • تخصص‌ها و مهارت‌ها

3. مدیریت ویزیت‌ها

  • ثبت ویزیت‌های جدید

  • ثبت درمان‌های انجام شده

  • یادداشت‌های بالینی

  • پیگیری وضعیت بیمار

4. طرح‌های درمان

  • ایجاد طرح درمان جامع

  • زمان‌بندی جلسات درمان

  • پیگیری پیشرفت درمان

  • محاسبه هزینه‌ها

5. مدیریت مالی

  • صدور فاکتور

  • ثبت پرداخت‌ها

  • گزارش‌های مالی

  • محاسبه بیمه

6. رادیولوژی

  • آپلود عکس‌های رادیولوژی

  • مدیریت پرونده‌های تصویری

  • نمایش و آنالیز تصاویر

7. قرارملاقات‌ها

  • سیستم نوبت‌دهی

  • تقویم قرارملاقات‌ها

  • یادآوری اتوماتیک

  • مدیریت زمان‌بندی

8. گزارش‌گیری

  • آمار کلی کلینیک

  • گزارش‌های مالی

  • گزارش عملکرد دندانپزشکان

  • آمار بیماران

این سیستم کاملاً واکنش‌گرا بوده و برای استفاده در کلینیک‌های دندانپزشکی کوچک تا متوسط طراحی شده است. رابط کاربری به زبان فارسی و مطابق با استانداردهای UI/UX طراحی شده است.

کانال نوحه و مداحی ناب

نظرات (۰)
هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی