Frontend for the Figma عملیات menu on the confirmed-appointments table: - AppointmentActions composite: six-item row menu (ویرایش، ثبت سرویس، مشاهده، جا به جایی، انتقال به لیست رزرو، جایگزینی) plus the four modals it opens. ثبت سرویس and the info modal resolve the patient record via the patient-list search (mobile) to reuse the existing wallet endpoint and NewSessionPage. - Info modal mirrors appointments-info.pdf: start time, duration, phone, بخش/سرویس/پرسنل, wallet balance, status dropdown, مشاهده پرونده. - Move/transfer/replace modals PATCH the new general update endpoint with optimistic-lock version; transfer uses day-level midnight slots. - Status labels/transitions updated to the design set (ثبت شده/قطعی شده/ در حال پیگیری/سالن/ویزیت شده/لغو شده) in AppointmentStatusDropdown and StatusBadge; Appointment type gains the new workflow fields; the table gains سرویس/پرسنل/عملیات columns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
76 lines
3.0 KiB
TypeScript
76 lines
3.0 KiB
TypeScript
import React from 'react';
|
|
import type { AppointmentStatus, PaymentStatus, SmsTemplateStatus, SettlementStatus } 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: 'رد شده' },
|
|
};
|
|
|
|
interface Props {
|
|
type: 'appointment' | 'payment' | 'sms' | 'settlement' | 'active';
|
|
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 === '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'} />;
|
|
}
|