import { SERTIFICATE_PRICE } from '@/shared/lib/metadata'; import { useTranslations } from 'next-intl'; import React, { useEffect } from 'react'; // ─── FieldWrapper ──────────────────────────────────────────────────────────── interface FieldWrapperProps { label: string; htmlFor?: string; error?: string; required?: boolean; children: React.ReactNode; } export function FieldWrapper({ label, htmlFor, error, required, children, }: FieldWrapperProps) { return (
{children} {error && (

{error}

)}
); } // ─── TextInput ─────────────────────────────────────────────────────────────── interface TextInputProps extends React.InputHTMLAttributes { hasError?: boolean; } export function TextInput({ hasError, className = '', ...props }: TextInputProps) { return ( ); } // ─── ReadonlyField ─────────────────────────────────────────────────────────── interface ReadonlyFieldProps { value: string; icon?: React.ReactNode; autoFilledText?: string; } export function ReadonlyField({ value, icon, autoFilledText = 'Auto-filled', }: ReadonlyFieldProps) { return (
{icon && {icon}} {value} {autoFilledText}
); } // ─── FileUploadField ───────────────────────────────────────────────────────── interface FileUploadFieldProps { file: File | null; onFileChange: (file: File | null) => void; hasError?: boolean; accept?: string; clickToUploadText?: string; fileTypesText?: string; removeFileAriaLabel?: string; } export function FileUploadField({ file, onFileChange, hasError, accept = '.pdf,.doc,.docx,.txt', clickToUploadText = 'Click to upload document', fileTypesText = 'PDF, DOC, DOCX, TXT · Max 20 MB', removeFileAriaLabel = 'Remove file', }: FileUploadFieldProps) { const inputRef = React.useRef(null); const handleChange = (e: React.ChangeEvent) => { const selected = e.target.files?.[0] ?? null; onFileChange(selected); // Reset so the same file can be re-selected after removal e.target.value = ''; }; const handleRemove = () => { onFileChange(null); }; const formatBytes = (bytes: number) => { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; }; return (
{!file ? ( ) : (

{file.name}

{formatBytes(file.size)}

)}
); } // ─── CertificateCheckbox ───────────────────────────────────────────────────── interface CertificateCheckboxProps { checked: boolean; onChange: () => void; title?: string; description?: string; } export function CertificateCheckbox({ checked, onChange, title = 'Return result with certificate', description = 'An official certificate will be attached to your originality report.', }: CertificateCheckboxProps) { const t = useTranslations('PlagiarismCheck'); return ( ); } // ─── SubmitButton ──────────────────────────────────────────────────────────── interface SubmitButtonProps { isLoading: boolean; submittingText?: string; submitText?: string; } export function SubmitButton({ isLoading, submittingText = 'Submitting…', submitText = 'Submit for Originality Check', }: SubmitButtonProps) { return ( ); } // ─── StatusBanner ──────────────────────────────────────────────────────────── interface StatusBannerProps { status: 'success' | 'error'; message: string; onDismiss: () => void; dismissText?: string; } export function StatusBanner({ status, message, onDismiss, dismissText = 'Dismiss', }: StatusBannerProps) { const isSuccess = status === 'success'; useEffect(() => { setTimeout(onDismiss, 3000); }, []); return (
{isSuccess ? '✓' : '✕'}

{message}

); } // ─── Inline SVG Icons ──────────────────────────────────────────────────────── function UploadIcon({ className = '' }: { className?: string }) { return ( ); } function DocumentIcon({ className = '' }: { className?: string }) { return ( ); } function XIcon() { return ( ); } function ShieldIcon() { return ( ); } function SpinnerIcon() { return ( ); }