سامانه جامع مدیریت کلینیک دندانپزشکی با React.js
سامانه جامع مدیریت کلینیک دندانپزشکی با React.js
در ادامه، نسخه کامل و جامع سامانه مدیریت کلینیک دندانپزشکی با React.js ارائه میشود:
ساختار کامل پروژه
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
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
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
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
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
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
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
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
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
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
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
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
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
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
/* 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
{ "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 طراحی شده است.
نظرات (۰)
هیچ نظری هنوز ثبت نشده است