2026-06-15 11:59:05 +05:30

344 lines
19 KiB
JavaScript

import React from 'react';
import { Head, Link, useForm } from '@inertiajs/react';
import EmployerLayout from '../../../Layouts/EmployerLayout';
import { useTranslation } from '../../../lib/LanguageContext';
import {
ArrowLeft,
Clock,
CheckCircle,
AlertCircle,
CornerDownRight,
Send,
Terminal,
UserCheck,
Lock,
UploadCloud,
X,
Volume2
} from 'lucide-react';
export default function Show({ ticket, replies }) {
const { t } = useTranslation();
const { data, setData, post, processing, reset, errors } = useForm({
message: '',
voice_note: null,
});
const handleSubmit = (e) => {
e.preventDefault();
post(`/employer/support/${ticket.id}/reply`, {
onSuccess: () => reset('message', 'voice_note'),
});
};
const getStatusBadge = (status) => {
switch (status) {
case 'open':
return (
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-black bg-blue-50 text-blue-700 border border-blue-200">
<Clock className="w-3.5 h-3.5 mr-1" />
{t('ticket_status_open', 'Open')}
</span>
);
case 'in_progress':
return (
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-black bg-amber-50 text-amber-700 border border-amber-200">
<Clock className="w-3.5 h-3.5 mr-1" />
{t('ticket_status_in_progress', 'In Progress')}
</span>
);
case 'resolved':
return (
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-black bg-emerald-50 text-emerald-700 border border-emerald-200">
<CheckCircle className="w-3.5 h-3.5 mr-1" />
{t('ticket_status_resolved', 'Resolved')}
</span>
);
case 'closed':
return (
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-black bg-slate-100 text-slate-600 border border-slate-200">
<AlertCircle className="w-3.5 h-3.5 mr-1" />
{t('ticket_status_closed', 'Closed')}
</span>
);
default:
return (
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-black bg-slate-50 text-slate-600">
{status}
</span>
);
}
};
const getPriorityBadge = (priority) => {
switch (priority) {
case 'high':
return (
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-wider bg-rose-50 text-rose-700 border border-rose-100">
{t('priority_high', 'High')}
</span>
);
case 'medium':
return (
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-wider bg-blue-50 text-blue-700 border border-blue-100">
{t('priority_medium', 'Medium')}
</span>
);
case 'low':
return (
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200">
{t('priority_low', 'Low')}
</span>
);
default:
return <span className="text-slate-600">{priority}</span>;
}
};
return (
<EmployerLayout title={t('ticket_details', 'Ticket Details')}>
<Head title={`${ticket.ticket_number} - Support Ticket - Employer Portal`} />
<div className="max-w-4xl select-none w-full pb-24 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>
{/* Ticket main card */}
<div className="bg-white rounded-3xl border border-slate-200 shadow-sm overflow-hidden">
<div className="p-6 md:p-8 border-b border-slate-100 bg-slate-50/40 space-y-4">
<div className="flex flex-wrap items-center justify-between gap-4">
<div className="flex items-center space-x-3">
<span className="text-xs font-mono font-bold text-slate-400 bg-white px-2.5 py-1 rounded-lg border border-slate-200 shadow-xs">
{ticket.ticket_number}
</span>
{getPriorityBadge(ticket.priority)}
{getStatusBadge(ticket.status)}
</div>
<span className="text-[10px] text-slate-400 font-bold">
{t('raised_on', 'Raised on:')} {ticket.created_at}
</span>
</div>
{ticket.reason && (
<div className="inline-flex items-center space-x-1.5 px-3 py-1 bg-indigo-50/80 border border-indigo-100/50 rounded-xl text-indigo-700 font-bold text-[10px] select-none">
<span>Support Category:</span>
<span className="font-extrabold">{ticket.reason}</span>
</div>
)}
<h2 className="text-base md:text-lg font-black text-slate-900 tracking-tight leading-snug">
{ticket.subject}
</h2>
</div>
<div className="p-6 md:p-8 space-y-4">
<div className="space-y-1">
<label className="block text-slate-400 uppercase tracking-widest text-[9px] font-black mb-2">{t('original_message', 'Original Message')}</label>
<div className="text-xs text-slate-600 font-medium leading-relaxed whitespace-pre-wrap bg-slate-50/50 p-4 rounded-2xl border border-slate-100">
{ticket.description}
</div>
</div>
{ticket.voice_note_url && (
<div className="pt-2">
<label className="block text-slate-400 uppercase tracking-widest text-[9px] font-black mb-2">{t('voice_note_attachment', 'Voice Note Attachment')}</label>
<div className="flex items-center space-x-3 bg-slate-50 border border-slate-200 p-3 rounded-2xl max-w-md">
<audio
src={ticket.voice_note_url}
controls
className="w-full h-8 outline-none"
/>
</div>
</div>
)}
</div>
</div>
{/* Replies header */}
<div className="space-y-4 pt-4">
<h3 className="font-black text-slate-800 text-sm flex items-center space-x-2">
<CornerDownRight className="w-4 h-4 text-[#185FA5]" />
<span>{t('ticket_replies', 'Discussion History')} ({replies.length})</span>
</h3>
{/* Replies list */}
<div className="space-y-4">
{replies.map((reply) => {
const isSystemDev = reply.is_developer_response;
const isAdmin = reply.is_admin;
return (
<div
key={reply.id}
className={`p-5 rounded-3xl border transition-all ${
isSystemDev
? 'bg-slate-900 text-slate-100 border-slate-800 shadow-md font-mono'
: isAdmin
? 'bg-blue-50/60 text-slate-700 border-blue-100'
: 'bg-white text-slate-700 border-slate-200'
}`}
>
<div className="flex items-center justify-between border-b pb-2 mb-3.5 select-none text-[10px] font-bold border-current/10">
<div className="flex items-center space-x-2">
{isSystemDev ? (
<div className="w-5 h-5 rounded bg-emerald-500 text-slate-950 flex items-center justify-center">
<Terminal className="w-3.5 h-3.5" />
</div>
) : (
<div className={`w-5 h-5 rounded-full flex items-center justify-center text-[9px] font-black ${
isAdmin ? 'bg-[#185FA5] text-white' : 'bg-slate-100 text-slate-600'
}`}>
{reply.sender_name.charAt(0)}
</div>
)}
<span className={isSystemDev ? 'text-emerald-400 font-mono font-bold' : 'text-slate-800'}>
{reply.sender_name}
</span>
</div>
<span className="opacity-60">{reply.created_at}</span>
</div>
{reply.message && (
<div className={`text-xs font-semibold leading-relaxed whitespace-pre-wrap ${
isSystemDev ? 'text-slate-300 font-mono font-semibold' : 'text-slate-600'
}`}>
{reply.message}
</div>
)}
{reply.voice_note_url && (
<div className="mt-2.5">
<div className="flex items-center space-x-3 bg-slate-50 border border-slate-100 p-2.5 rounded-2xl max-w-xs">
<audio
src={reply.voice_note_url}
controls
className="w-full h-8 outline-none"
/>
</div>
</div>
)}
</div>
);
})}
{replies.length === 0 && (
<div className="p-8 text-center text-xs text-slate-400 font-bold bg-white rounded-2xl border border-slate-100">
{t('no_replies_yet', 'No replies posted yet. Our team is investigating.')}
</div>
)}
</div>
</div>
{/* Reply Form */}
{ticket.status !== 'closed' ? (
<div className="bg-white p-6 rounded-3xl border border-slate-200 shadow-sm space-y-4 pt-6">
<div className="space-y-1">
<h4 className="font-extrabold text-sm text-slate-900">{t('post_a_reply', 'Post a Reply')}</h4>
<p className="text-[10px] text-slate-400 font-bold">{t('post_a_reply_desc', 'Need to add info or follow up? Type your response below.')}</p>
</div>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="text-xs font-bold text-slate-700">
<textarea
rows="4"
placeholder={t('reply_placeholder', 'Type your reply message here...')}
value={data.message}
onChange={e => setData('message', 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.message ? 'border-red-400 bg-red-50/20' : 'border-slate-200'
}`}
required={!data.voice_note}
/>
{errors.message && (
<p className="text-[10px] text-red-500 font-semibold mt-1">{errors.message}</p>
)}
</div>
{/* Voice Note Attachment */}
<div className="space-y-1.5">
<label className="block text-slate-400 uppercase tracking-widest text-[9px] font-black">{t('voice_note_reply', 'Voice Note Attachment (Optional)')}</label>
{!data.voice_note ? (
<div className="border-2 border-dashed border-slate-200 rounded-2xl p-4 text-center space-y-1 relative hover:border-[#185FA5] transition-all bg-slate-50/50">
<UploadCloud className="w-6 h-6 text-slate-400 mx-auto" />
<div className="text-[11px] font-bold text-slate-600">{t('upload_voice_note', 'Upload a voice note or audio file')}</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-3 bg-slate-50/70 space-y-2">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2.5 min-w-0">
<Volume2 className="w-4 h-4 text-[#185FA5] flex-shrink-0" />
<div className="min-w-0">
<p className="text-[11px] font-extrabold text-slate-800 truncate">{data.voice_note.name}</p>
</div>
</div>
<button
type="button"
onClick={() => setData('voice_note', null)}
className="p-1 bg-slate-100 hover:bg-slate-200 rounded-full text-slate-500 transition-colors"
>
<X className="w-3.5 h-3.5" />
</button>
</div>
<audio
src={URL.createObjectURL(data.voice_note)}
controls
className="w-full h-8 outline-none"
/>
</div>
)}
{errors.voice_note && (
<p className="text-[10px] text-red-500 font-semibold mt-1">{errors.voice_note}</p>
)}
</div>
<div className="flex justify-end">
<button
type="submit"
disabled={processing}
className="px-5 py-2.5 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 text-xs font-bold"
>
{processing ? (
<>
<span className="w-3 h-3 border-2 border-white/30 border-t-white rounded-full animate-spin" />
<span>{t('submitting', 'Posting...')}</span>
</>
) : (
<>
<span>{t('send_reply', 'Post Reply')}</span>
<Send className="w-3.5 h-3.5" />
</>
)}
</button>
</div>
</form>
</div>
) : (
<div className="bg-slate-100 border border-slate-200 rounded-3xl p-6 flex items-center justify-between gap-4">
<div className="flex items-center space-x-3.5">
<div className="w-10 h-10 rounded-xl bg-slate-200 text-slate-600 flex items-center justify-center flex-shrink-0 border border-slate-300">
<Lock className="w-5 h-5" />
</div>
<div>
<h4 className="font-extrabold text-xs text-slate-800">{t('ticket_closed_title', 'This support ticket has been closed')}</h4>
<p className="text-[10px] text-slate-400 font-bold">{t('ticket_closed_desc', 'This discussion is locked. If the problem persists, please create a new support ticket.')}</p>
</div>
</div>
</div>
)}
</div>
</EmployerLayout>
);
}