226 lines
14 KiB
JavaScript
226 lines
14 KiB
JavaScript
import React from 'react';
|
|
import { Head, Link, useForm } from '@inertiajs/react';
|
|
import EmployerLayout from '../../../Layouts/EmployerLayout';
|
|
import { useTranslation } from '../../../lib/LanguageContext';
|
|
import {
|
|
LifeBuoy,
|
|
ArrowLeft,
|
|
AlertTriangle,
|
|
CheckCircle,
|
|
UploadCloud,
|
|
X,
|
|
Volume2
|
|
} from 'lucide-react';
|
|
|
|
export default function Create({ reasons = [] }) {
|
|
const { t } = useTranslation();
|
|
|
|
const { data, setData, post, processing, errors } = useForm({
|
|
reason_id: '',
|
|
subject: '',
|
|
description: '',
|
|
priority: 'medium',
|
|
voice_note: null,
|
|
});
|
|
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
post('/employer/support/create');
|
|
};
|
|
|
|
return (
|
|
<EmployerLayout title={t('create_new_ticket', 'Create New Ticket')}>
|
|
<Head title={t('create_support_ticket_title', 'Create Support Ticket - Employer Portal')} />
|
|
|
|
<div className="max-w-2xl select-none w-full pb-16 space-y-6">
|
|
|
|
{/* Back button */}
|
|
<Link
|
|
href="/employer/support"
|
|
className="inline-flex items-center space-x-2 text-xs font-black text-slate-500 hover:text-slate-800 transition-colors"
|
|
>
|
|
<ArrowLeft className="w-4 h-4" />
|
|
<span>{t('back_to_support', 'Back to Help & Support')}</span>
|
|
</Link>
|
|
|
|
{/* Form Card */}
|
|
<div className="bg-white p-6 md:p-8 rounded-3xl border border-slate-200 shadow-sm space-y-6">
|
|
<div className="flex items-center space-x-3.5 border-b border-slate-100 pb-4">
|
|
<div className="w-11 h-11 rounded-xl bg-blue-50 text-[#185FA5] flex items-center justify-center border border-blue-100 flex-shrink-0">
|
|
<LifeBuoy className="w-5 h-5 animate-spin-slow" />
|
|
</div>
|
|
<div>
|
|
<h3 className="font-extrabold text-sm text-slate-900">{t('open_support_case', 'Open a Support Case')}</h3>
|
|
<p className="text-[11px] text-slate-400 font-semibold">{t('open_support_case_desc', 'Please provide detailed info to help our engineering team solve it fast.')}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-5 text-xs font-bold text-slate-700">
|
|
{/* Support Reason */}
|
|
<div className="space-y-1.5">
|
|
<label className="block text-slate-400 uppercase tracking-widest text-[9px]">{t('ticket_reason', 'Support Category / Reason')}</label>
|
|
<select
|
|
value={data.reason_id}
|
|
onChange={e => setData('reason_id', e.target.value)}
|
|
className={`w-full p-3 border rounded-xl bg-slate-50/50 text-slate-800 outline-none focus:bg-white focus:border-[#185FA5] transition-all ${
|
|
errors.reason_id ? 'border-red-400 bg-red-50/20' : 'border-slate-200'
|
|
}`}
|
|
>
|
|
<option value="">{t('select_reason_placeholder', 'Select support reason (optional)')}</option>
|
|
{reasons.map(r => (
|
|
<option key={r.id} value={r.id}>
|
|
{r.reason}
|
|
</option>
|
|
))}
|
|
</select>
|
|
{errors.reason_id && (
|
|
<p className="text-[10px] text-red-500 font-semibold mt-1">{errors.reason_id}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Subject */}
|
|
<div className="space-y-1.5">
|
|
<label className="block text-slate-400 uppercase tracking-widest text-[9px]">{t('ticket_subject', 'Subject / Summary')}</label>
|
|
<input
|
|
type="text"
|
|
placeholder={t('ticket_subject_placeholder', 'e.g. Stripe checkout throws 500 error')}
|
|
value={data.subject}
|
|
onChange={e => setData('subject', e.target.value)}
|
|
className={`w-full p-3 border rounded-xl bg-slate-50/50 text-slate-800 outline-none focus:bg-white focus:border-[#185FA5] transition-all ${
|
|
errors.subject ? 'border-red-400 bg-red-50/20' : 'border-slate-200'
|
|
}`}
|
|
required
|
|
/>
|
|
{errors.subject && (
|
|
<p className="text-[10px] text-red-500 font-semibold mt-1">{errors.subject}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Priority */}
|
|
<div className="space-y-1.5">
|
|
<label className="block text-slate-400 uppercase tracking-widest text-[9px]">{t('ticket_priority', 'Severity / Priority')}</label>
|
|
<select
|
|
value={data.priority}
|
|
onChange={e => setData('priority', e.target.value)}
|
|
className="w-full p-3 border border-slate-200 rounded-xl bg-slate-50/50 text-slate-800 outline-none focus:bg-white focus:border-[#185FA5] transition-all"
|
|
>
|
|
<option value="low">{t('priority_low', 'Low (General queries, feedback)')}</option>
|
|
<option value="medium">{t('priority_medium', 'Medium (Functionality bugs, minor errors)')}</option>
|
|
<option value="high">{t('priority_high', 'High (Payment failures, account lockouts)')}</option>
|
|
</select>
|
|
</div>
|
|
|
|
{/* Description */}
|
|
<div className="space-y-1.5">
|
|
<label className="block text-slate-400 uppercase tracking-widest text-[9px]">{t('ticket_description', 'Detailed Description')}</label>
|
|
<textarea
|
|
rows="6"
|
|
placeholder={t('ticket_description_placeholder', 'Detail the steps to reproduce the error or query. Paste exact log outputs if possible.')}
|
|
value={data.description}
|
|
onChange={e => setData('description', e.target.value)}
|
|
className={`w-full p-3 border rounded-xl bg-slate-50/50 text-slate-800 outline-none focus:bg-white focus:border-[#185FA5] transition-all leading-relaxed ${
|
|
errors.description ? 'border-red-400 bg-red-50/20' : 'border-slate-200'
|
|
}`}
|
|
required
|
|
/>
|
|
{errors.description && (
|
|
<p className="text-[10px] text-red-500 font-semibold mt-1">{errors.description}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Voice Note Attachment */}
|
|
<div className="space-y-1.5">
|
|
<label className="block text-slate-400 uppercase tracking-widest text-[9px]">{t('voice_note_attachment', 'Voice Note Attachment (Optional)')}</label>
|
|
|
|
{!data.voice_note ? (
|
|
<div className="border-2 border-dashed border-slate-200 rounded-2xl p-6 text-center space-y-2 relative hover:border-[#185FA5] transition-all bg-slate-50/50">
|
|
<UploadCloud className="w-8 h-8 text-slate-400 mx-auto" />
|
|
<div className="text-xs font-bold text-slate-600">{t('upload_voice_note', 'Upload a voice note or audio file')}</div>
|
|
<div className="text-[9px] text-slate-400">MP3, WAV, M4A, WEBM (Max 10MB)</div>
|
|
<input
|
|
type="file"
|
|
accept="audio/*"
|
|
onChange={e => setData('voice_note', e.target.files[0])}
|
|
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
/>
|
|
</div>
|
|
) : (
|
|
<div className="border border-slate-200 rounded-2xl p-4 bg-slate-50/70 space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center space-x-3 min-w-0">
|
|
<div className="w-8 h-8 rounded-lg bg-blue-50 text-[#185FA5] flex items-center justify-center border border-blue-100 flex-shrink-0">
|
|
<Volume2 className="w-4 h-4 animate-pulse" />
|
|
</div>
|
|
<div className="min-w-0">
|
|
<p className="text-xs font-extrabold text-slate-800 truncate">{data.voice_note.name}</p>
|
|
<p className="text-[10px] text-slate-400 font-bold">{(data.voice_note.size / 1024 / 1024).toFixed(2)} MB</p>
|
|
</div>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={() => setData('voice_note', null)}
|
|
className="p-1.5 bg-slate-105 bg-slate-100 hover:bg-slate-200 rounded-full text-slate-500 hover:text-slate-700 transition-colors"
|
|
>
|
|
<X className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Audio preview player */}
|
|
<div className="pt-1 border-t border-slate-100">
|
|
<audio
|
|
src={URL.createObjectURL(data.voice_note)}
|
|
controls
|
|
className="w-full h-8 outline-none"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
{errors.voice_note && (
|
|
<p className="text-[10px] text-red-500 font-semibold mt-1">{errors.voice_note}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Support notice */}
|
|
{data.priority === 'high' && (
|
|
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-4 flex items-start space-x-3 text-amber-800 animate-pulse">
|
|
<AlertTriangle className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" />
|
|
<div>
|
|
<h5 className="font-extrabold text-[11px]">{t('high_priority_alert', 'Priority High Selected')}</h5>
|
|
<p className="text-[10px] font-semibold text-amber-700 mt-0.5 leading-relaxed">
|
|
{t('high_priority_desc', 'Tickets marked high route directly to our standby on-call senior engineering queue. Response is initiated under 2 hours.')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Buttons */}
|
|
<div className="flex items-center justify-end space-x-3 pt-4 border-t border-slate-100">
|
|
<Link
|
|
href="/employer/support"
|
|
className="px-5 py-3 rounded-xl border border-slate-200 hover:bg-slate-50 text-slate-600 transition-colors"
|
|
>
|
|
{t('cancel', 'Cancel')}
|
|
</Link>
|
|
<button
|
|
type="submit"
|
|
disabled={processing}
|
|
className="px-6 py-3 rounded-xl bg-[#185FA5] hover:bg-[#144f8a] text-white transition-all shadow-md disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-2"
|
|
>
|
|
{processing ? (
|
|
<>
|
|
<span className="w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
<span>{t('submitting', 'Submitting...')}</span>
|
|
</>
|
|
) : (
|
|
<span>{t('submit_ticket', 'Submit Support Ticket')}</span>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
</div>
|
|
</EmployerLayout>
|
|
);
|
|
}
|