Files
clinicpro/assets/admin/components/ui/StatusBadge.tsx
T
hamedandClaude Opus 5 d8aabe5d0a feat(admin): treatment plan tab, staff session screens and the device form editor
Three screens, each reusing what already exists rather than inventing a parallel
look. The treatment plan lives as a tab on the service page next to categories,
because the course belongs to the service; the switch is the protocol's existence
rather than a separate boolean that could disagree with the step list. Each step
asks for days since the previous session, which is how the interval actually
works and what the form should therefore say.

The staff screens are the flow from the reference screenshots: today's sessions,
then a session where each area is started, recorded and closed on its own. Field
inputs are built from the schema the server sends per resource type, so a clinic
adding an RF device sees its own form here without a code change.

StatusBadge gains treatment session and area states rather than a second badge
component sitting beside it, and the resource type modal grows a field editor so
the operator form is configured where the device is defined.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 18:29:49 +03:30

120 lines
5.2 KiB
TypeScript

import React from 'react';
import type { AppointmentStatus, PaymentStatus, SmsTemplateStatus, SettlementStatus, ClaimStatus, InvoiceListStatus } from '../../types';
type BadgeColor = 'green' | 'amber' | 'red' | 'blue' | 'violet' | 'gray';
const appointmentMap: Record<AppointmentStatus, { color: BadgeColor; label: string }> = {
pending: { color: 'blue', label: 'ثبت شده' },
confirmed: { color: 'green', label: 'قطعی شده' },
following_up: { color: 'amber', label: 'در حال پیگیری' },
salon: { color: 'violet', label: 'سالن' },
completed: { color: 'green', label: 'ویزیت شده' },
cancelled_by_user: { color: 'red', label: 'لغو توسط بیمار' },
cancelled_by_doctor: { color: 'red', label: 'لغو شده' },
no_show: { color: 'gray', label: 'غیبت' },
expired: { color: 'gray', label: 'منقضی' },
};
const paymentMap: Record<PaymentStatus, { color: BadgeColor; label: string }> = {
pending: { color: 'amber', label: 'در انتظار' },
success: { color: 'green', label: 'موفق' },
failed: { color: 'red', label: 'ناموفق' },
canceled: { color: 'red', label: 'لغو شده' },
refunded: { color: 'blue', label: 'استرداد' },
};
const smsMap: Record<SmsTemplateStatus, { color: BadgeColor; label: string }> = {
draft: { color: 'gray', label: 'پیش‌نویس' },
pending: { color: 'amber', label: 'در انتظار تأیید' },
approved: { color: 'green', label: 'تأیید شده' },
rejected: { color: 'red', label: 'رد شده' },
};
const settlementMap: Record<SettlementStatus, { color: BadgeColor; label: string }> = {
pending: { color: 'amber', label: 'در انتظار' },
approved: { color: 'green', label: 'تأیید شده' },
rejected: { color: 'red', label: 'رد شده' },
};
const claimMap: Record<ClaimStatus, { color: BadgeColor; label: string }> = {
pending: { color: 'gray', label: 'در انتظار ارسال' },
submitted: { color: 'blue', label: 'ارسال شده' },
approved: { color: 'amber', label: 'تأیید شده' },
rejected: { color: 'red', label: 'رد شده' },
paid: { color: 'green', label: 'پرداخت شده' },
mixed: { color: 'violet', label: 'وضعیت‌های مختلف' },
};
const invoiceMap: Record<InvoiceListStatus, { color: BadgeColor; label: string }> = {
paid: { color: 'green', label: 'پرداخت شده' },
partial: { color: 'blue', label: 'پرداخت ناقص' },
unsettled: { color: 'amber', label: 'تسویه نشده' },
};
const treatmentSessionMap: Record<string, { color: BadgeColor; label: string }> = {
planned: { color: 'gray', label: 'برنامه‌ریزی شده' },
booked: { color: 'blue', label: 'زمان‌بندی شده' },
in_progress: { color: 'amber', label: 'در حال انجام' },
done: { color: 'green', label: 'انجام شد' },
cancelled: { color: 'red', label: 'لغو شده' },
no_show: { color: 'gray', label: 'غیبت' },
};
const treatmentAreaMap: Record<string, { color: BadgeColor; label: string }> = {
pending: { color: 'gray', label: 'در انتظار' },
in_progress: { color: 'amber', label: 'در حال انجام' },
completed: { color: 'green', label: 'تکمیل شده' },
skipped: { color: 'violet', label: 'صرف‌نظر شده' },
};
interface Props {
type: 'appointment' | 'payment' | 'sms' | 'settlement' | 'active' | 'claim' | 'invoice'
| 'treatment-session' | 'treatment-area';
value: string;
}
export default function StatusBadge({ type, value }: Props) {
let color: BadgeColor = 'gray';
let label = value;
if (type === 'appointment') {
const m = appointmentMap[value as AppointmentStatus];
if (m) { color = m.color; label = m.label; }
} else if (type === 'payment') {
const m = paymentMap[value as PaymentStatus];
if (m) { color = m.color; label = m.label; }
} else if (type === 'sms') {
const m = smsMap[value as SmsTemplateStatus];
if (m) { color = m.color; label = m.label; }
} else if (type === 'settlement') {
const m = settlementMap[value as SettlementStatus];
if (m) { color = m.color; label = m.label; }
} else if (type === 'claim') {
const m = claimMap[value as ClaimStatus];
if (m) { color = m.color; label = m.label; }
} else if (type === 'invoice') {
const m = invoiceMap[value as InvoiceListStatus];
if (m) { color = m.color; label = m.label; }
} else if (type === 'treatment-session') {
const m = treatmentSessionMap[value];
if (m) { color = m.color; label = m.label; }
} else if (type === 'treatment-area') {
const m = treatmentAreaMap[value];
if (m) { color = m.color; label = m.label; }
} else if (type === 'active') {
color = value === 'true' || value === 'active' ? 'green' : 'gray';
label = value === 'true' || value === 'active' ? 'فعال' : 'غیرفعال';
}
return (
<span className={`badge ${color}`}>
<span className="bdot" />
{label}
</span>
);
}
export function ActiveBadge({ active }: { active: boolean }) {
return <StatusBadge type="active" value={active ? 'active' : 'inactive'} />;
}