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.
This commit is contained in:
@@ -18,12 +18,22 @@ import { ActiveBadge } from '../components/ui/StatusBadge';
|
||||
import { numericField } from '../lib/forms';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
|
||||
// هر پرسنل حساب کاربری ورود دارد، پس موبایل همان نامکاربری است و اجباری.
|
||||
const schema = z.object({
|
||||
full_name: z.string().min(2, 'نام حداقل ۲ کاراکتر باید باشد'),
|
||||
phone: z.string().optional(),
|
||||
phone: z.string().regex(/^09\d{9}$/, 'شماره موبایل معتبر (۱۱ رقمی) وارد کنید'),
|
||||
job_title: z.string().optional(),
|
||||
address: z.string().optional(),
|
||||
national_code: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
}).superRefine((data, ctx) => {
|
||||
if (data.password && data.password.length < 8) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['password'],
|
||||
message: 'رمز عبور حداقل ۸ کاراکتر باشد',
|
||||
});
|
||||
}
|
||||
});
|
||||
type StaffFormData = z.infer<typeof schema>;
|
||||
|
||||
@@ -91,6 +101,7 @@ export default function StaffPage() {
|
||||
job_title: s.job_title ?? '',
|
||||
address: s.address ?? '',
|
||||
national_code: s.national_code ?? '',
|
||||
password: '',
|
||||
});
|
||||
setEditTarget(s);
|
||||
};
|
||||
@@ -131,6 +142,15 @@ export default function StaffPage() {
|
||||
header: 'وضعیت',
|
||||
render: (s) => <ActiveBadge active={s.active} />,
|
||||
},
|
||||
{
|
||||
key: 'has_account',
|
||||
header: 'حساب کاربری',
|
||||
render: (s) => (
|
||||
<span className={`badge ${s.has_account ? 'green' : ''}`} style={{ fontSize: 12 }}>
|
||||
{s.has_account ? 'دارد' : 'ندارد'}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
header: 'تاریخ ثبت',
|
||||
@@ -275,8 +295,9 @@ function StaffFormFields({ form }: { form: ReturnType<typeof useForm<StaffFormDa
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
|
||||
<div className="field">
|
||||
<label>تلفن</label>
|
||||
<label>موبایل (نام کاربری ورود) *</label>
|
||||
<input {...numericField(register('phone'), 11)} placeholder="09121234567" />
|
||||
{errors.phone && <span className="field-error">{errors.phone.message}</span>}
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>کد ملی</label>
|
||||
@@ -287,6 +308,16 @@ function StaffFormFields({ form }: { form: ReturnType<typeof useForm<StaffFormDa
|
||||
<label>آدرس</label>
|
||||
<input {...register('address')} placeholder="آدرس محل سکونت" />
|
||||
</div>
|
||||
|
||||
<div className="field" style={{ borderTop: '1px solid var(--border)', paddingTop: 12 }}>
|
||||
<label>رمز عبور ورود به پنل</label>
|
||||
<input type="password" autoComplete="new-password" {...register('password')} placeholder="حداقل ۸ کاراکتر — خالی یعنی بدون تغییر" />
|
||||
{errors.password && <span className="field-error">{errors.password.message}</span>}
|
||||
<div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 4 }}>
|
||||
برای هر پرسنل حساب کاربری ساخته میشود: با همین شماره موبایل وارد پنل میشود و فقط
|
||||
داشبورد و سرویسهای خودش را میبیند.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user