Files
clinicpro/assets/admin/pages/StaffMyServicesPage.tsx
T
hamed 57aeb40934 feat: add staff role functionality with dashboard access and service management
- Implemented SidebarStaff component tests to ensure staff users see only their dashboard and services.
- Created StaffMyServicesPage to display assigned services for staff users.
- Added migration to link clinic staff rows to user accounts for ROLE_STAFF access.
- Defined StaffPermissions class for static permissions related to staff role.
- Introduced StaffRouteGuardSubscriber to restrict API access for staff users.
- Developed StaffAccountService for managing staff user accounts and linking them to clinic staff.
- Added comprehensive tests for StaffAccountService to validate user creation, mobile number handling, and account attachment.
- Implemented tests for staff dashboard access to ensure proper permissions and access control.
- Created tests for staff login context to verify correct environment visibility based on user roles.
2026-07-30 10:18:41 +03:30

80 lines
2.8 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { WrenchScrewdriverIcon } from '@heroicons/react/24/outline';
import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api';
import type { StaffAssignedService } from '../types';
import { formatRial } from '../lib/utils';
import DataTable, { type Column } from '../components/ui/DataTable';
import PageHeader from '../components/ui/PageHeader';
interface StaffDashboardData {
services: StaffAssignedService[];
}
const EMPTY: StaffAssignedService[] = [];
/**
* سرویس‌های تخصیص‌یافته به پرسنل — فقط خواندنی.
* داده از همان اندپوینت داشبورد پرسنل می‌آید؛ نقش staff اندپوینت دیگری ندارد.
*/
export default function StaffMyServicesPage() {
const { data, isLoading } = useQuery({
queryKey: ['dashboard-staff'],
queryFn: () => api.get<ApiResponse<StaffDashboardData>>('/api/v1/dashboard/staff'),
staleTime: 60_000,
});
const services = data?.data?.services ?? EMPTY;
const columns: Column<StaffAssignedService>[] = [
{
key: 'name',
header: 'سرویس',
render: (s) => (
<div>
<div style={{ fontWeight: 600, fontSize: 14 }}>{s.name}</div>
<div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 2 }}>{s.section_name}</div>
</div>
),
},
{
key: 'price_rials',
header: 'تعرفه',
render: (s) => <span style={{ fontSize: 13 }}>{formatRial(s.price_rials)}</span>,
},
{
key: 'duration_minutes',
header: 'مدت',
render: (s) => (
<span style={{ fontSize: 13, color: 'var(--text-3)' }}>
{s.duration_minutes ? `${s.duration_minutes} دقیقه` : '—'}
</span>
),
},
];
return (
<>
<PageHeader
title="سرویس‌های من"
description="سرویس‌هایی که به شما تخصیص داده شده است"
backTo="/admin/dashboard"
/>
<div className="card">
{services.length === 0 && !isLoading ? (
<div style={{ textAlign: 'center', padding: '60px 24px', color: 'var(--text-3)' }}>
<WrenchScrewdriverIcon style={{ width: 48, margin: '0 auto 16px', display: 'block', opacity: 0.4 }} />
<div style={{ fontWeight: 600, fontSize: 15, marginBottom: 8, color: 'var(--text-2)' }}>
هنوز سرویسی به شما تخصیص نیافته
</div>
<div style={{ fontSize: 13 }}>پس از تخصیص سرویس توسط مطب/کلینیک، اینجا نمایش داده می‌شود.</div>
</div>
) : (
<DataTable columns={columns} data={services} loading={isLoading} />
)}
</div>
</>
);
}