feat: enhance various pages with improved UI elements and search functionality

This commit is contained in:
hamed
2026-06-10 13:20:21 +03:30
parent 06ab875693
commit 4b8504df91
8 changed files with 86 additions and 34 deletions
+28 -12
View File
@@ -28,6 +28,7 @@ export default function SmsPage() {
const qc = useQueryClient();
const [activeTab, setActiveTab] = useState<Tab>('samples');
const [page, setPage] = useState(1);
const [approveTarget, setApproveTarget] = useState<SmsTemplate | null>(null);
const [rejectTarget, setRejectTarget] = useState<SmsTemplate | null>(null);
const [rejectReason, setRejectReason] = useState('');
const [addOpen, setAddOpen] = useState(false);
@@ -44,12 +45,11 @@ export default function SmsPage() {
});
const pendingTemplatesQuery = useQuery({
queryKey: ['sms-pending', page],
queryKey: ['sms-pending', activeTab === 'pending' ? page : 1],
queryFn: () =>
api.get<PaginatedResponse<SmsTemplate>>(
`/api/v1/admin/sms/sample-templates?status=pending&page=${page}&limit=${limit}`,
`/api/v1/admin/sms/sample-templates?status=pending&page=${activeTab === 'pending' ? page : 1}&limit=${limit}`,
),
enabled: activeTab === 'pending',
});
const logsQuery = useQuery({
@@ -80,6 +80,7 @@ export default function SmsPage() {
api.post<ApiResponse<null>>(`/api/v1/admin/sms/template/${t.uuid}/approve`, {}),
onSuccess: () => {
toast.success('قالب تأیید شد');
setApproveTarget(null);
qc.invalidateQueries({ queryKey: ['sms-pending'] });
},
onError: (err: Error) => toast.error(err.message),
@@ -145,11 +146,7 @@ export default function SmsPage() {
{ key: 'sent_at', header: 'زمان ارسال', render: (l) => formatDateTime(l.sent_at) },
];
const tabs: { key: Tab; label: string }[] = [
{ key: 'samples', label: 'قالب‌های نمونه' },
{ key: 'pending', label: 'در انتظار تأیید' },
{ key: 'logs', label: 'لاگ‌های ارسال' },
];
const pendingCount = pendingTemplatesQuery.data?.meta?.totalRecords ?? 0;
const handleTabChange = (tab: Tab) => {
setActiveTab(tab);
@@ -174,14 +171,23 @@ export default function SmsPage() {
<div className="cp-card">
<div className="flex border-b border-slate-200 dark:border-gray-700 px-6 pt-4">
{tabs.map((t) => (
{([
{ key: 'samples' as Tab, label: 'قالب‌های نمونه' },
{ key: 'pending' as Tab, label: 'در انتظار تأیید', badge: pendingCount },
{ key: 'logs' as Tab, label: 'لاگ‌های ارسال' },
]).map((t) => (
<button key={t.key} onClick={() => handleTabChange(t.key)}
className={`pb-3 px-4 text-sm font-medium border-b-2 transition-colors -mb-px ${
className={`pb-3 px-4 text-sm font-medium border-b-2 transition-colors -mb-px flex items-center gap-1.5 ${
activeTab === t.key
? 'border-primary-600 text-primary-600'
: 'border-transparent text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200'
}`}>
{t.label}
{t.badge != null && t.badge > 0 && (
<span className="inline-flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full text-[10px] font-bold bg-orange-500 text-white leading-none">
{t.badge}
</span>
)}
</button>
))}
</div>
@@ -219,8 +225,8 @@ export default function SmsPage() {
emptyMessage="هیچ قالبی در انتظار تأیید نیست"
actions={(t) => (
<>
<button onClick={() => approveMutation.mutate(t)}
className="p-1.5 text-gray-400 hover:text-green-600 hover:bg-green-50 rounded-lg transition-colors" title="تأیید">
<button onClick={() => setApproveTarget(t)}
className="cp-action-approve" title="تأیید">
<CheckIcon className="w-4 h-4" />
</button>
<button onClick={() => setRejectTarget(t)}
@@ -311,6 +317,16 @@ export default function SmsPage() {
className="cp-textarea resize-none" />
</Modal>
<ConfirmDialog
open={!!approveTarget}
title="تأیید قالب پیامک"
message={`قالب "${approveTarget?.name}" را تأیید می‌کنید؟`}
confirmLabel="تأیید"
loading={approveMutation.isPending}
onConfirm={() => approveTarget && approveMutation.mutate(approveTarget)}
onCancel={() => setApproveTarget(null)}
/>
<ConfirmDialog
open={!!deleteTarget}
title="حذف قالب"