feat: redesign StaffPage with KPI bar, improved table layout, and enhanced form fields
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { PencilIcon, EyeIcon, EyeSlashIcon, PlusIcon } from '@heroicons/react/24/outline';
|
||||
import { PencilIcon, EyeIcon, EyeSlashIcon, PlusIcon, UserGroupIcon } from '@heroicons/react/24/outline';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
@@ -38,6 +38,9 @@ export default function StaffPage() {
|
||||
});
|
||||
|
||||
const staff = data?.data ?? EMPTY;
|
||||
const total = staff.length;
|
||||
const active = staff.filter((s) => s.active).length;
|
||||
const inactive = total - active;
|
||||
|
||||
const createForm = useForm<StaffFormData>({ resolver: zodResolver(schema) });
|
||||
const editForm = useForm<StaffFormData>({ resolver: zodResolver(schema) });
|
||||
@@ -86,10 +89,36 @@ export default function StaffPage() {
|
||||
};
|
||||
|
||||
const columns: Column<ClinicStaff>[] = [
|
||||
{ key: 'full_name', header: 'نام و نام خانوادگی' },
|
||||
{ key: 'job_title', header: 'سمت', render: (s) => s.job_title ?? '—' },
|
||||
{ key: 'phone', header: 'تلفن', render: (s) => s.phone ?? '—' },
|
||||
{ key: 'national_code', header: 'کد ملی', render: (s) => s.national_code ?? '—' },
|
||||
{
|
||||
key: 'full_name',
|
||||
header: 'پرسنل',
|
||||
render: (s) => (
|
||||
<div>
|
||||
<div style={{ fontWeight: 600, fontSize: 14 }}>{s.full_name}</div>
|
||||
{s.job_title && (
|
||||
<div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 2 }}>{s.job_title}</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
header: 'تلفن',
|
||||
render: (s) => (
|
||||
<span style={{ fontSize: 13, direction: 'ltr', display: 'inline-block' }}>
|
||||
{s.phone ?? '—'}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'national_code',
|
||||
header: 'کد ملی',
|
||||
render: (s) => (
|
||||
<span style={{ fontSize: 13, direction: 'ltr', display: 'inline-block' }}>
|
||||
{s.national_code ?? '—'}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'active',
|
||||
header: 'وضعیت',
|
||||
@@ -98,7 +127,9 @@ export default function StaffPage() {
|
||||
{
|
||||
key: 'created_at',
|
||||
header: 'تاریخ ثبت',
|
||||
render: (s) => formatDate(s.created_at),
|
||||
render: (s) => (
|
||||
<span style={{ fontSize: 13, color: 'var(--text-3)' }}>{formatDate(s.created_at)}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'uuid',
|
||||
@@ -135,8 +166,37 @@ export default function StaffPage() {
|
||||
}
|
||||
/>
|
||||
|
||||
{/* KPI bar */}
|
||||
{!isLoading && (
|
||||
<div className="stat-grid" style={{ marginBottom: 16, gridTemplateColumns: 'repeat(3, 1fr)' }}>
|
||||
<div className="card card-pad" style={{ textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 28, fontWeight: 800, color: 'var(--text-1)' }}>{total}</div>
|
||||
<div style={{ fontSize: 13, color: 'var(--text-3)', marginTop: 2 }}>کل پرسنل</div>
|
||||
</div>
|
||||
<div className="card card-pad" style={{ textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 28, fontWeight: 800, color: 'oklch(0.55 0.16 162)' }}>{active}</div>
|
||||
<div style={{ fontSize: 13, color: 'var(--text-3)', marginTop: 2 }}>فعال</div>
|
||||
</div>
|
||||
<div className="card card-pad" style={{ textAlign: 'center' }}>
|
||||
<div style={{ fontSize: 28, fontWeight: 800, color: 'oklch(0.55 0.18 25)' }}>{inactive}</div>
|
||||
<div style={{ fontSize: 13, color: 'var(--text-3)', marginTop: 2 }}>غیرفعال</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="card">
|
||||
<DataTable columns={columns} data={staff} loading={isLoading} />
|
||||
{staff.length === 0 && !isLoading ? (
|
||||
<div style={{ textAlign: 'center', padding: '60px 24px', color: 'var(--text-3)' }}>
|
||||
<UserGroupIcon 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, marginBottom: 20 }}>اولین عضو تیم خود را اضافه کنید</div>
|
||||
<button className="btn primary sm" onClick={() => setCreateOpen(true)}>
|
||||
<PlusIcon style={{ width: 16 }} /> افزودن پرسنل
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<DataTable columns={columns} data={staff} loading={isLoading} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modal ایجاد */}
|
||||
@@ -187,22 +247,26 @@ function StaffFormFields({ form }: { form: ReturnType<typeof useForm<StaffFormDa
|
||||
const { register, formState: { errors } } = form;
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<div className="field">
|
||||
<label>نام و نام خانوادگی *</label>
|
||||
<input {...register('full_name')} placeholder="علی رضایی" />
|
||||
{errors.full_name && <span className="field-error">{errors.full_name.message}</span>}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
|
||||
<div className="field">
|
||||
<label>نام و نام خانوادگی *</label>
|
||||
<input {...register('full_name')} placeholder="علی رضایی" />
|
||||
{errors.full_name && <span className="field-error">{errors.full_name.message}</span>}
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>سمت</label>
|
||||
<input {...register('job_title')} placeholder="پرستار" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>سمت</label>
|
||||
<input {...register('job_title')} placeholder="پرستار" />
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>تلفن</label>
|
||||
<input {...register('phone')} placeholder="09121234567" dir="ltr" />
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>کد ملی</label>
|
||||
<input {...register('national_code')} placeholder="0012345678" dir="ltr" />
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
|
||||
<div className="field">
|
||||
<label>تلفن</label>
|
||||
<input {...register('phone')} placeholder="09121234567" dir="ltr" />
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>کد ملی</label>
|
||||
<input {...register('national_code')} placeholder="0012345678" dir="ltr" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>آدرس</label>
|
||||
|
||||
Reference in New Issue
Block a user