feat: response in mobile and accouting
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
interface ConfirmDialogProps {
|
||||
open: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
tone?: 'danger' | 'primary';
|
||||
isLoading?: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
// Styled replacement for the native confirm() dialog so destructive actions
|
||||
// match the rest of the panel.
|
||||
export default function ConfirmDialog({
|
||||
open,
|
||||
title,
|
||||
message,
|
||||
confirmLabel = 'تأیید',
|
||||
cancelLabel = 'انصراف',
|
||||
tone = 'danger',
|
||||
isLoading = false,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: ConfirmDialogProps) {
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape' && !isLoading) onCancel();
|
||||
};
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}, [open, isLoading, onCancel]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
const confirmClass =
|
||||
tone === 'danger'
|
||||
? 'bg-red-600 hover:bg-red-700 focus:ring-red-500/30 shadow-red-500/20'
|
||||
: 'bg-indigo-600 hover:bg-indigo-700 focus:ring-indigo-500/30 shadow-indigo-500/20';
|
||||
const iconWrapClass =
|
||||
tone === 'danger'
|
||||
? 'bg-red-50 text-red-600 dark:bg-red-500/10 dark:text-red-400'
|
||||
: 'bg-indigo-50 text-indigo-600 dark:bg-indigo-500/10 dark:text-indigo-400';
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 dark:bg-black/60 p-4"
|
||||
onClick={() => {
|
||||
if (!isLoading) onCancel();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="bg-white dark:bg-gray-900 rounded-2xl shadow-xl ring-1 ring-gray-200 dark:ring-gray-800 w-full max-w-md"
|
||||
dir="rtl"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
role="alertdialog"
|
||||
aria-modal="true"
|
||||
aria-label={title}
|
||||
>
|
||||
<div className="px-6 py-5 flex items-start gap-4">
|
||||
<div className={`flex-shrink-0 h-11 w-11 rounded-xl flex items-center justify-center ${iconWrapClass}`}>
|
||||
<ExclamationTriangleIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h3 className="text-base font-semibold text-gray-900 dark:text-gray-100">{title}</h3>
|
||||
<p className="mt-1.5 text-sm text-gray-500 dark:text-gray-400 leading-6">{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-3 px-6 py-4 border-t border-gray-100 dark:border-gray-800">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
disabled={isLoading}
|
||||
className="px-5 py-2.5 rounded-xl border border-gray-200 dark:border-gray-700 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{cancelLabel}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onConfirm}
|
||||
disabled={isLoading}
|
||||
className={`inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-white text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-gray-900 disabled:opacity-50 transition-colors shadow-sm ${confirmClass}`}
|
||||
>
|
||||
{isLoading && (
|
||||
<span className="h-4 w-4 border-2 border-white/40 border-t-white rounded-full animate-spin" />
|
||||
)}
|
||||
{isLoading ? 'در حال انجام...' : confirmLabel}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -139,7 +139,7 @@ export default function PersianDatePicker({
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div className="absolute right-0 z-50 mt-2 bg-white dark:bg-gray-900 ring-1 ring-gray-200 dark:ring-gray-800 rounded-2xl shadow-xl p-2">
|
||||
<div className="absolute right-0 z-50 mt-2 bg-white dark:bg-gray-900 ring-1 ring-gray-200 dark:ring-gray-800 rounded-2xl shadow-xl p-2 max-w-[calc(100vw-3rem)] overflow-x-auto">
|
||||
<DayPicker
|
||||
mode="single"
|
||||
selected={selectedDate ?? undefined}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function StepIndicator({ steps }: StepIndicatorProps) {
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className={`mt-1.5 text-xs font-medium whitespace-nowrap ${
|
||||
className={`mt-1.5 text-[10px] sm:text-xs font-medium whitespace-nowrap ${
|
||||
step.isCurrent
|
||||
? 'text-indigo-700 dark:text-indigo-400'
|
||||
: step.isCompleted
|
||||
|
||||
Reference in New Issue
Block a user