2026-07-14 21:07:05 +03:30
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useCallback, useState } from 'react';
|
|
|
|
|
import { useTranslations } from 'next-intl';
|
|
|
|
|
import { Copy } from 'lucide-react';
|
|
|
|
|
import { DialogCloseButton } from '@/components/ui/shared/DialogCloseButton';
|
|
|
|
|
import {
|
|
|
|
|
ResponsiveDialogOverlay,
|
|
|
|
|
ResponsiveDialogPanel,
|
|
|
|
|
} from '@/components/ui/shared/ResponsiveDialog';
|
|
|
|
|
import { Button } from '@/components/ui/shared/Button';
|
|
|
|
|
import { useToast } from '@/lib/hooks/useToast';
|
|
|
|
|
import { LabCaseShareQrCode } from '@/components/ui/lab/LabCaseShareQrCode';
|
|
|
|
|
|
|
|
|
|
interface LabCaseShareQrDialogProps {
|
|
|
|
|
open: boolean;
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
shareUrl: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function LabCaseShareQrDialog({ open, onClose, shareUrl }: LabCaseShareQrDialogProps) {
|
|
|
|
|
const t = useTranslations('cases');
|
|
|
|
|
const { showSuccess } = useToast();
|
|
|
|
|
const [copying, setCopying] = useState(false);
|
|
|
|
|
|
|
|
|
|
const handleCopy = useCallback(async () => {
|
|
|
|
|
setCopying(true);
|
|
|
|
|
try {
|
|
|
|
|
await navigator.clipboard.writeText(shareUrl);
|
|
|
|
|
showSuccess(t('shareLinkCopied'));
|
|
|
|
|
} catch {
|
|
|
|
|
// ignore clipboard failures
|
|
|
|
|
} finally {
|
|
|
|
|
setCopying(false);
|
|
|
|
|
}
|
|
|
|
|
}, [shareUrl, showSuccess, t]);
|
|
|
|
|
|
|
|
|
|
if (!open) return null;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<ResponsiveDialogOverlay onBackdropClick={onClose}>
|
|
|
|
|
<ResponsiveDialogPanel
|
|
|
|
|
maxWidthClass="sm:max-w-3xl"
|
|
|
|
|
role="dialog"
|
|
|
|
|
aria-modal="true"
|
|
|
|
|
aria-labelledby="case-share-qr-title"
|
|
|
|
|
className="space-y-4"
|
|
|
|
|
>
|
|
|
|
|
<div className="flex items-start justify-between gap-3">
|
|
|
|
|
<div className="min-w-0 flex-1">
|
|
|
|
|
<h2 id="case-share-qr-title" className="text-lg font-semibold text-text-primary">
|
|
|
|
|
{t('shareQrDialogTitle')}
|
|
|
|
|
</h2>
|
|
|
|
|
<p className="text-sm text-text-muted mt-1">{t('shareQrDialogSubtitle')}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<DialogCloseButton onClick={onClose} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex flex-col items-center gap-4 min-w-0">
|
|
|
|
|
<div className="w-full max-w-[17rem] sm:max-w-[16rem] rounded-md border border-border bg-white p-3 sm:p-4">
|
|
|
|
|
<LabCaseShareQrCode value={shareUrl} size={256} className="h-auto w-full" />
|
|
|
|
|
</div>
|
|
|
|
|
<p className="w-full min-w-0 break-all text-center text-sm text-text-secondary px-2">
|
|
|
|
|
{shareUrl}
|
|
|
|
|
</p>
|
|
|
|
|
<Button
|
|
|
|
|
type="button"
|
|
|
|
|
variant="outline"
|
|
|
|
|
disabled={copying}
|
|
|
|
|
className="w-full sm:w-auto"
|
2026-07-17 10:58:04 +03:30
|
|
|
onClick={() => handleCopy()}
|
2026-07-14 21:07:05 +03:30
|
|
|
>
|
|
|
|
|
<Copy className="h-4 w-4 me-1.5" />
|
|
|
|
|
{t('copyShareLink')}
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</ResponsiveDialogPanel>
|
|
|
|
|
</ResponsiveDialogOverlay>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface LabCaseShareQrThumbProps {
|
|
|
|
|
shareUrl: string;
|
|
|
|
|
onClick: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function LabCaseShareQrThumb({ shareUrl, onClick }: LabCaseShareQrThumbProps) {
|
|
|
|
|
const t = useTranslations('cases');
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={onClick}
|
|
|
|
|
className="aspect-square w-24 sm:w-32 cursor-pointer rounded-[var(--radius-md)] border border-border/60 overflow-hidden bg-white p-2 transition-colors hover:border-primary/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary flex items-center justify-center"
|
|
|
|
|
title={t('viewShareQr')}
|
|
|
|
|
aria-label={t('viewShareQr')}
|
|
|
|
|
>
|
|
|
|
|
<LabCaseShareQrCode value={shareUrl} size={96} className="h-full w-full max-h-full max-w-full" />
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
}
|