- Implemented GET /api/v1/dashboard/clinic to return clinic stats and today's schedule for clinic owners. - Implemented GET /api/v1/dashboard/doctor to return doctor's stats and today's schedule for doctors. - Implemented GET /api/v1/dashboard/secretary to return stats and conditional appointments for secretaries. feat(migrations): create user_active_context and mobile_verification_otp tables - Added migration to create user_active_context table for tracking active user sessions. - Added migration to create mobile_verification_otp table for handling mobile number verification. feat(migrations): create site_config table for application settings - Added migration to create site_config table to store various site configuration settings. feat(appointments): create MyAppointmentsController for user-specific appointments - Added MyAppointmentsController to handle fetching user-specific appointments with pagination and filtering. feat(auth): implement NotificationMobileController for mobile number verification - Added NotificationMobileController to handle OTP requests and verification for mobile number changes. feat(auth): create MobileVerificationOtp entity for OTP management - Created MobileVerificationOtp entity to manage OTP records for mobile verification. feat(auth): create UserActiveContext entity for user session management - Created UserActiveContext entity to manage user active sessions. feat(config): implement SiteConfigController for managing site settings - Added SiteConfigController to handle fetching and updating site configuration settings. feat(config): create SiteConfig entity and repository for configuration management - Created SiteConfig entity and repository to manage site configuration data.
93 lines
3.4 KiB
TypeScript
93 lines
3.4 KiB
TypeScript
import { useNavigate } from 'react-router-dom';
|
|
import { useState } from 'react';
|
|
import { useAuthStore, ContextItem } from '../stores/authStore';
|
|
|
|
const ROLE_LABELS: Record<string, string> = {
|
|
admin: 'مدیر کل',
|
|
clinic: 'مالک کلینیک',
|
|
doctor: 'پزشک',
|
|
secretary: 'منشی',
|
|
user: 'کاربر',
|
|
};
|
|
|
|
const TYPE_ICONS: Record<string, string> = {
|
|
doctor: '🏥',
|
|
clinic: '🏢',
|
|
};
|
|
|
|
export default function SelectContextPage() {
|
|
const { availableContexts, switchContext } = useAuthStore();
|
|
const navigate = useNavigate();
|
|
const [loading, setLoading] = useState<string | null>(null);
|
|
|
|
const handleSelect = async (ctx: ContextItem) => {
|
|
setLoading(ctx.db_uuid);
|
|
await switchContext(ctx.db_uuid);
|
|
navigate('/admin/dashboard', { replace: true });
|
|
};
|
|
|
|
return (
|
|
<div style={{
|
|
minHeight: '100vh',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: 'var(--surface-alt, #f4f6f9)',
|
|
padding: '2rem',
|
|
}}>
|
|
<div style={{ width: '100%', maxWidth: 480 }}>
|
|
<div className="card card-pad" style={{ textAlign: 'center' }}>
|
|
<h2 style={{ marginBottom: '.25rem', fontSize: '1.25rem', fontWeight: 600 }}>
|
|
انتخاب محیط کاری
|
|
</h2>
|
|
<p className="muted" style={{ marginBottom: '1.5rem', fontSize: '.875rem' }}>
|
|
لطفاً محیط کاری مورد نظر خود را انتخاب کنید
|
|
</p>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '.75rem' }}>
|
|
{availableContexts.map((ctx) => (
|
|
<button
|
|
key={ctx.db_uuid}
|
|
onClick={() => handleSelect(ctx)}
|
|
disabled={loading !== null}
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '1rem',
|
|
padding: '.875rem 1rem',
|
|
border: '1.5px solid var(--border, #e2e8f0)',
|
|
borderRadius: '0.625rem',
|
|
background: loading === ctx.db_uuid ? 'var(--surface-alt, #f4f6f9)' : '#fff',
|
|
cursor: loading !== null ? 'wait' : 'pointer',
|
|
textAlign: 'right',
|
|
transition: 'border-color .15s, box-shadow .15s',
|
|
opacity: loading !== null && loading !== ctx.db_uuid ? 0.5 : 1,
|
|
}}
|
|
onMouseEnter={e => (e.currentTarget.style.borderColor = 'var(--primary, #6366f1)')}
|
|
onMouseLeave={e => (e.currentTarget.style.borderColor = 'var(--border, #e2e8f0)')}
|
|
>
|
|
<span style={{ fontSize: '1.5rem', lineHeight: 1 }}>
|
|
{TYPE_ICONS[ctx.type] ?? '👤'}
|
|
</span>
|
|
<div style={{ flex: 1 }}>
|
|
<div style={{ fontWeight: 600, fontSize: '.9375rem', marginBottom: '.125rem' }}>
|
|
{ctx.name}
|
|
</div>
|
|
<div style={{ fontSize: '.8125rem' }}>
|
|
<span className="badge blue" style={{ fontSize: '.75rem' }}>
|
|
{ROLE_LABELS[ctx.role] ?? ctx.role}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{loading === ctx.db_uuid && (
|
|
<span className="skeleton" style={{ width: 20, height: 20, borderRadius: '50%' }} />
|
|
)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|