feat: implement realistic data seeding for doctors, clinics, and secretaries
- Added seed_realistic_data.php to clean existing data and populate the database with realistic entries for doctors, clinics, and secretaries. - Created a structured approach to generate 100 doctors per city with diverse specialties and services. - Implemented database cleanup routines to ensure a fresh start for data seeding. - Enhanced the DoctorSecretaryRepository with improved comments for clarity.
This commit is contained in:
@@ -3,155 +3,308 @@ import {
|
||||
ArrowsRightLeftIcon,
|
||||
BanknotesIcon,
|
||||
BuildingOffice2Icon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
Cog6ToothIcon,
|
||||
CalendarDaysIcon,
|
||||
ChartBarIcon,
|
||||
ChatBubbleLeftEllipsisIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
Cog6ToothIcon,
|
||||
CreditCardIcon,
|
||||
DevicePhoneMobileIcon,
|
||||
DocumentTextIcon,
|
||||
FolderOpenIcon,
|
||||
HeartIcon,
|
||||
IdentificationIcon,
|
||||
KeyIcon,
|
||||
LockClosedIcon,
|
||||
StarIcon,
|
||||
TagIcon,
|
||||
UserCircleIcon,
|
||||
UserGroupIcon,
|
||||
UsersIcon,
|
||||
UserPlusIcon,
|
||||
UsersIcon,
|
||||
WrenchScrewdriverIcon,
|
||||
FolderOpenIcon,
|
||||
IdentificationIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import { useSubscription } from "../../hooks/useSubscription";
|
||||
import { useAuthStore } from "../../stores/authStore";
|
||||
import { useUiStore } from "../../stores/uiStore";
|
||||
import { useSubscription } from "../../hooks/useSubscription";
|
||||
|
||||
type SectionItem = { to: string; icon: React.ElementType; label: string; feature?: string };
|
||||
type SectionItem = {
|
||||
to: string;
|
||||
icon: React.ElementType;
|
||||
label: string;
|
||||
feature?: string;
|
||||
};
|
||||
type Section = { label: string; items: SectionItem[] };
|
||||
|
||||
function buildSections(primaryRole: string | null, dbUuid: string | null): Section[] {
|
||||
if (primaryRole === 'admin') {
|
||||
function buildSections(
|
||||
primaryRole: string | null,
|
||||
dbUuid: string | null,
|
||||
): Section[] {
|
||||
if (primaryRole === "admin") {
|
||||
return [
|
||||
{
|
||||
label: 'عمومی',
|
||||
label: "عمومی",
|
||||
items: [
|
||||
{ to: '/admin/dashboard', icon: ChartBarIcon, label: 'داشبورد' },
|
||||
{ to: '/admin/users', icon: UserGroupIcon, label: 'کاربران' },
|
||||
{ to: '/admin/doctors', icon: HeartIcon, label: 'پزشکان' },
|
||||
{ to: '/admin/clinics', icon: BuildingOffice2Icon, label: 'کلینیکها' },
|
||||
{
|
||||
to: "/admin/dashboard",
|
||||
icon: ChartBarIcon,
|
||||
label: "داشبورد",
|
||||
},
|
||||
{
|
||||
to: "/admin/users",
|
||||
icon: UserGroupIcon,
|
||||
label: "کاربران",
|
||||
},
|
||||
{ to: "/admin/doctors", icon: HeartIcon, label: "پزشکان" },
|
||||
{
|
||||
to: "/admin/clinics",
|
||||
icon: BuildingOffice2Icon,
|
||||
label: "کلینیکها",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'مدیریت',
|
||||
label: "مدیریت",
|
||||
items: [
|
||||
{ to: '/admin/appointments', icon: CalendarDaysIcon, label: 'نوبتها' },
|
||||
{ to: '/admin/payments', icon: CreditCardIcon, label: 'پرداختها' },
|
||||
{ to: '/admin/settlements', icon: BanknotesIcon, label: 'تسویهحساب' },
|
||||
{ to: '/admin/pre-registrations', icon: ClipboardDocumentCheckIcon, label: 'درخواستهای ثبتنام' },
|
||||
{
|
||||
to: "/admin/appointments",
|
||||
icon: CalendarDaysIcon,
|
||||
label: "نوبتها",
|
||||
},
|
||||
{
|
||||
to: "/admin/payments",
|
||||
icon: CreditCardIcon,
|
||||
label: "پرداختها",
|
||||
},
|
||||
{
|
||||
to: "/admin/settlements",
|
||||
icon: BanknotesIcon,
|
||||
label: "تسویهحساب",
|
||||
},
|
||||
{
|
||||
to: "/admin/pre-registrations",
|
||||
icon: ClipboardDocumentCheckIcon,
|
||||
label: "درخواستهای ثبتنام",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'محتوا',
|
||||
label: "محتوا",
|
||||
items: [
|
||||
{ to: '/admin/comments', icon: ChatBubbleLeftEllipsisIcon, label: 'نظرات' },
|
||||
{ to: '/admin/ratings', icon: StarIcon, label: 'امتیازها' },
|
||||
{ to: '/admin/blogs', icon: DocumentTextIcon, label: 'بلاگ' },
|
||||
{ to: '/admin/sms', icon: DevicePhoneMobileIcon, label: 'پیامک' },
|
||||
{
|
||||
to: "/admin/comments",
|
||||
icon: ChatBubbleLeftEllipsisIcon,
|
||||
label: "نظرات",
|
||||
},
|
||||
{ to: "/admin/ratings", icon: StarIcon, label: "امتیازها" },
|
||||
{
|
||||
to: "/admin/blogs",
|
||||
icon: DocumentTextIcon,
|
||||
label: "بلاگ",
|
||||
},
|
||||
{
|
||||
to: "/admin/sms",
|
||||
icon: DevicePhoneMobileIcon,
|
||||
label: "پیامک",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'سیستم',
|
||||
label: "سیستم",
|
||||
items: [
|
||||
{ to: '/admin/categories', icon: TagIcon, label: 'دستهبندیها' },
|
||||
{ to: '/admin/representations', icon: UsersIcon, label: 'نمایندگان' },
|
||||
{ to: '/admin/secretaries', icon: KeyIcon, label: 'منشیها' },
|
||||
{ to: '/admin/admin-subscription', icon: CreditCardIcon, label: 'اشتراکها' },
|
||||
{ to: '/admin/settings', icon: Cog6ToothIcon, label: 'تنظیمات' },
|
||||
{
|
||||
to: "/admin/categories",
|
||||
icon: TagIcon,
|
||||
label: "دستهبندیها",
|
||||
},
|
||||
{
|
||||
to: "/admin/representations",
|
||||
icon: UsersIcon,
|
||||
label: "نمایندگان",
|
||||
},
|
||||
{
|
||||
to: "/admin/secretaries",
|
||||
icon: KeyIcon,
|
||||
label: "منشیها",
|
||||
},
|
||||
{
|
||||
to: "/admin/admin-subscription",
|
||||
icon: CreditCardIcon,
|
||||
label: "اشتراکها",
|
||||
},
|
||||
{
|
||||
to: "/admin/settings",
|
||||
icon: Cog6ToothIcon,
|
||||
label: "تنظیمات",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (primaryRole === 'clinic') {
|
||||
const clinicTo = dbUuid ? `/admin/clinics/${dbUuid}` : '/admin/my-clinic';
|
||||
if (primaryRole === "clinic") {
|
||||
const clinicTo = dbUuid
|
||||
? `/admin/clinics/${dbUuid}`
|
||||
: "/admin/my-clinic";
|
||||
return [
|
||||
{
|
||||
label: 'عمومی',
|
||||
label: "عمومی",
|
||||
items: [
|
||||
{ to: '/admin/dashboard', icon: ChartBarIcon, label: 'داشبورد' },
|
||||
{ to: clinicTo, icon: BuildingOffice2Icon, label: 'کلینیک من' },
|
||||
{
|
||||
to: "/admin/dashboard",
|
||||
icon: ChartBarIcon,
|
||||
label: "داشبورد",
|
||||
},
|
||||
{
|
||||
to: clinicTo,
|
||||
icon: BuildingOffice2Icon,
|
||||
label: "کلینیک من",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'مدیریت',
|
||||
label: "مدیریت",
|
||||
items: [
|
||||
{ to: '/admin/appointments', icon: CalendarDaysIcon, label: 'نوبتها' },
|
||||
{ to: '/admin/my-patients', icon: FolderOpenIcon, label: 'پرونده بیماران', feature: 'patient_records' },
|
||||
{ to: '/admin/staff', icon: UserPlusIcon, label: 'پرسنل' },
|
||||
{ to: '/admin/my-secretaries', icon: IdentificationIcon, label: 'منشیان' },
|
||||
{ to: '/admin/clinic-services', icon: WrenchScrewdriverIcon, label: 'سرویسها', feature: 'services' },
|
||||
{ to: '/admin/sms-wallet', icon: DevicePhoneMobileIcon, label: 'کیف پول پیامک', feature: 'sms_panel' },
|
||||
{
|
||||
to: "/admin/appointments",
|
||||
icon: CalendarDaysIcon,
|
||||
label: "نوبتها",
|
||||
},
|
||||
{
|
||||
to: "/admin/my-patients",
|
||||
icon: FolderOpenIcon,
|
||||
label: "پرونده بیماران",
|
||||
feature: "patient_records",
|
||||
},
|
||||
{ to: "/admin/staff", icon: UserPlusIcon, label: "پرسنل" },
|
||||
{
|
||||
to: "/admin/my-secretaries",
|
||||
icon: IdentificationIcon,
|
||||
label: "منشی ها",
|
||||
},
|
||||
{
|
||||
to: "/admin/clinic-services",
|
||||
icon: WrenchScrewdriverIcon,
|
||||
label: "سرویسها",
|
||||
feature: "services",
|
||||
},
|
||||
{
|
||||
to: "/admin/sms-wallet",
|
||||
icon: DevicePhoneMobileIcon,
|
||||
label: "کیف پول پیامک",
|
||||
feature: "sms_panel",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'اشتراک',
|
||||
label: "اشتراک",
|
||||
items: [
|
||||
{ to: '/admin/subscription', icon: CreditCardIcon, label: 'پنل اشتراکی' },
|
||||
{
|
||||
to: "/admin/subscription",
|
||||
icon: CreditCardIcon,
|
||||
label: "پنل اشتراکی",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (primaryRole === 'doctor') {
|
||||
if (primaryRole === "doctor") {
|
||||
return [
|
||||
{
|
||||
label: 'عمومی',
|
||||
label: "عمومی",
|
||||
items: [
|
||||
{ to: '/admin/dashboard', icon: ChartBarIcon, label: 'داشبورد' },
|
||||
{
|
||||
to: "/admin/dashboard",
|
||||
icon: ChartBarIcon,
|
||||
label: "داشبورد",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'پروفایل',
|
||||
label: "پروفایل",
|
||||
items: [
|
||||
{ to: '/admin/profile', icon: UserCircleIcon, label: 'پروفایل من' },
|
||||
{
|
||||
to: "/admin/profile",
|
||||
icon: UserCircleIcon,
|
||||
label: "پروفایل من",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'مدیریت',
|
||||
label: "مدیریت",
|
||||
items: [
|
||||
{ to: '/admin/appointments', icon: CalendarDaysIcon, label: 'نوبتهای من' },
|
||||
{ to: '/admin/my-patients', icon: FolderOpenIcon, label: 'پرونده بیماران', feature: 'patient_records' },
|
||||
{ to: '/admin/staff', icon: UserPlusIcon, label: 'پرسنل' },
|
||||
{ to: '/admin/my-secretaries', icon: IdentificationIcon, label: 'منشیان' },
|
||||
{ to: '/admin/clinic-services', icon: WrenchScrewdriverIcon, label: 'سرویسها', feature: 'services' },
|
||||
{ to: '/admin/sms-wallet', icon: DevicePhoneMobileIcon, label: 'کیف پول پیامک', feature: 'sms_panel' },
|
||||
{
|
||||
to: "/admin/appointments",
|
||||
icon: CalendarDaysIcon,
|
||||
label: "نوبتهای من",
|
||||
},
|
||||
{
|
||||
to: "/admin/my-patients",
|
||||
icon: FolderOpenIcon,
|
||||
label: "پرونده بیماران",
|
||||
feature: "patient_records",
|
||||
},
|
||||
{ to: "/admin/staff", icon: UserPlusIcon, label: "پرسنل" },
|
||||
{
|
||||
to: "/admin/my-secretaries",
|
||||
icon: IdentificationIcon,
|
||||
label: "منشی ها",
|
||||
},
|
||||
{
|
||||
to: "/admin/clinic-services",
|
||||
icon: WrenchScrewdriverIcon,
|
||||
label: "سرویسها",
|
||||
feature: "services",
|
||||
},
|
||||
{
|
||||
to: "/admin/sms-wallet",
|
||||
icon: DevicePhoneMobileIcon,
|
||||
label: "کیف پول پیامک",
|
||||
feature: "sms_panel",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'اشتراک',
|
||||
label: "اشتراک",
|
||||
items: [
|
||||
{ to: '/admin/subscription', icon: CreditCardIcon, label: 'پنل اشتراکی' },
|
||||
{
|
||||
to: "/admin/subscription",
|
||||
icon: CreditCardIcon,
|
||||
label: "پنل اشتراکی",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (primaryRole === 'secretary') {
|
||||
if (primaryRole === "secretary") {
|
||||
return [
|
||||
{
|
||||
label: 'عمومی',
|
||||
label: "عمومی",
|
||||
items: [
|
||||
{ to: '/admin/dashboard', icon: ChartBarIcon, label: 'داشبورد' },
|
||||
{
|
||||
to: "/admin/dashboard",
|
||||
icon: ChartBarIcon,
|
||||
label: "داشبورد",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'مدیریت',
|
||||
label: "مدیریت",
|
||||
items: [
|
||||
{ to: '/admin/appointments', icon: CalendarDaysIcon, label: 'نوبتها' },
|
||||
{ to: '/admin/my-patients', icon: FolderOpenIcon, label: 'پرونده بیماران', feature: 'patient_records' },
|
||||
{
|
||||
to: "/admin/appointments",
|
||||
icon: CalendarDaysIcon,
|
||||
label: "نوبتها",
|
||||
},
|
||||
{
|
||||
to: "/admin/my-patients",
|
||||
icon: FolderOpenIcon,
|
||||
label: "پرونده بیماران",
|
||||
feature: "patient_records",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -159,18 +312,24 @@ function buildSections(primaryRole: string | null, dbUuid: string | null): Secti
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'عمومی',
|
||||
items: [{ to: '/admin/dashboard', icon: ChartBarIcon, label: 'داشبورد' }],
|
||||
label: "عمومی",
|
||||
items: [
|
||||
{
|
||||
to: "/admin/dashboard",
|
||||
icon: ChartBarIcon,
|
||||
label: "داشبورد",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const ROLE_LABELS: Record<string, string> = {
|
||||
admin: 'مدیر کل',
|
||||
clinic: 'مالک کلینیک',
|
||||
doctor: 'پزشک',
|
||||
secretary: 'منشی',
|
||||
user: 'کاربر',
|
||||
admin: "مدیر کل",
|
||||
clinic: "مالک کلینیک",
|
||||
doctor: "پزشک",
|
||||
secretary: "منشی",
|
||||
user: "کاربر",
|
||||
};
|
||||
|
||||
const HUES = [256, 205, 162, 295, 272];
|
||||
@@ -186,13 +345,14 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function Sidebar({ mobileOpen: _m, onMobileClose: _c }: Props) {
|
||||
const sidebarOpen = useUiStore((s) => s.sidebarOpen);
|
||||
const { logout, primaryRole, userName, availableContexts, dbUuid } = useAuthStore();
|
||||
const sidebarOpen = useUiStore((s) => s.sidebarOpen);
|
||||
const { logout, primaryRole, userName, availableContexts, dbUuid } =
|
||||
useAuthStore();
|
||||
const { hasFeature } = useSubscription();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const sections = buildSections(primaryRole, dbUuid);
|
||||
const initials = (userName ?? 'U').charAt(0).toUpperCase();
|
||||
const initials = (userName ?? "U").charAt(0).toUpperCase();
|
||||
|
||||
return (
|
||||
<aside className="sidebar">
|
||||
@@ -212,23 +372,56 @@ export default function Sidebar({ mobileOpen: _m, onMobileClose: _c }: Props) {
|
||||
{sections.map((section) => (
|
||||
<div className="nav-group" key={section.label}>
|
||||
<span className="nav-label">{section.label}</span>
|
||||
{section.items.map(({ to, icon: Icon, label, feature }) => {
|
||||
const isLocked = feature ? !hasFeature(feature) : false;
|
||||
const dest = isLocked ? '/admin/subscription' : to;
|
||||
return (
|
||||
<NavLink
|
||||
key={to}
|
||||
to={dest}
|
||||
title={isLocked ? 'نیاز به ارتقاء پنل' : (!sidebarOpen ? label : undefined)}
|
||||
className={({ isActive }) => `nav-item${isActive && !isLocked ? ' active' : ''}${isLocked ? ' locked' : ''}`}
|
||||
style={isLocked ? { opacity: 0.55 } : undefined}
|
||||
>
|
||||
<Icon style={{ width: 19, height: 19, flexShrink: 0 }} />
|
||||
<span>{label}</span>
|
||||
{isLocked && <LockClosedIcon style={{ width: 12, height: 12, marginRight: 'auto', flexShrink: 0 }} />}
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
{section.items.map(
|
||||
({ to, icon: Icon, label, feature }) => {
|
||||
const isLocked = feature
|
||||
? !hasFeature(feature)
|
||||
: false;
|
||||
const dest = isLocked
|
||||
? "/admin/subscription"
|
||||
: to;
|
||||
return (
|
||||
<NavLink
|
||||
key={to}
|
||||
to={dest}
|
||||
title={
|
||||
isLocked
|
||||
? "نیاز به ارتقاء پنل"
|
||||
: !sidebarOpen
|
||||
? label
|
||||
: undefined
|
||||
}
|
||||
className={({ isActive }) =>
|
||||
`nav-item${isActive && !isLocked ? " active" : ""}${isLocked ? " locked" : ""}`
|
||||
}
|
||||
style={
|
||||
isLocked
|
||||
? { opacity: 0.55 }
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
style={{
|
||||
width: 19,
|
||||
height: 19,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<span>{label}</span>
|
||||
{isLocked && (
|
||||
<LockClosedIcon
|
||||
style={{
|
||||
width: 12,
|
||||
height: 12,
|
||||
marginRight: "auto",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</NavLink>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -238,10 +431,14 @@ export default function Sidebar({ mobileOpen: _m, onMobileClose: _c }: Props) {
|
||||
<span className="nav-label">محیط کاری</span>
|
||||
<NavLink
|
||||
to="/admin/select-context"
|
||||
title={!sidebarOpen ? 'تغییر محیط' : undefined}
|
||||
className={({ isActive }) => `nav-item${isActive ? ' active' : ''}`}
|
||||
title={!sidebarOpen ? "تغییر محیط" : undefined}
|
||||
className={({ isActive }) =>
|
||||
`nav-item${isActive ? " active" : ""}`
|
||||
}
|
||||
>
|
||||
<ArrowsRightLeftIcon style={{ width: 19, height: 19, flexShrink: 0 }} />
|
||||
<ArrowsRightLeftIcon
|
||||
style={{ width: 19, height: 19, flexShrink: 0 }}
|
||||
/>
|
||||
<span>تغییر محیط</span>
|
||||
</NavLink>
|
||||
</div>
|
||||
@@ -252,20 +449,37 @@ export default function Sidebar({ mobileOpen: _m, onMobileClose: _c }: Props) {
|
||||
<div className="sidebar-foot">
|
||||
<div
|
||||
className="user-chip"
|
||||
onClick={() => { logout(); navigate('/admin/login'); }}
|
||||
onClick={() => {
|
||||
logout();
|
||||
navigate("/admin/login");
|
||||
}}
|
||||
title="خروج از سیستم"
|
||||
>
|
||||
<div
|
||||
className="avatar sm"
|
||||
style={{ background: avatarBg(userName ?? 'U'), flexShrink: 0 }}
|
||||
style={{
|
||||
background: avatarBg(userName ?? "U"),
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{initials}
|
||||
</div>
|
||||
<div className="user-meta">
|
||||
<b>{userName ?? 'کاربر'}</b>
|
||||
<span>{ROLE_LABELS[primaryRole ?? ''] ?? primaryRole ?? ''}</span>
|
||||
<b>{userName ?? "کاربر"}</b>
|
||||
<span>
|
||||
{ROLE_LABELS[primaryRole ?? ""] ??
|
||||
primaryRole ??
|
||||
""}
|
||||
</span>
|
||||
</div>
|
||||
<ArrowLeftOnRectangleIcon style={{ width: 16, height: 16, flexShrink: 0, color: 'var(--text-3)' }} />
|
||||
<ArrowLeftOnRectangleIcon
|
||||
style={{
|
||||
width: 16,
|
||||
height: 16,
|
||||
flexShrink: 0,
|
||||
color: "var(--text-3)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user