231 lines
13 KiB
JavaScript
231 lines
13 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { Head, Link } from '@inertiajs/react';
|
|
import EmployerLayout from '../../../Layouts/EmployerLayout';
|
|
import { useTranslation } from '../../../lib/LanguageContext';
|
|
import {
|
|
LifeBuoy,
|
|
Plus,
|
|
Search,
|
|
Clock,
|
|
CheckCircle,
|
|
AlertCircle,
|
|
HelpCircle,
|
|
ArrowRight,
|
|
MessageSquare
|
|
} from 'lucide-react';
|
|
|
|
export default function Index({ tickets, faqs = [] }) {
|
|
const { t } = useTranslation();
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [statusFilter, setStatusFilter] = useState('all');
|
|
|
|
const filteredTickets = tickets.filter(ticket => {
|
|
const matchesSearch = ticket.subject.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
ticket.ticket_number.toLowerCase().includes(searchQuery.toLowerCase());
|
|
const matchesStatus = statusFilter === 'all' || ticket.status === statusFilter;
|
|
return matchesSearch && matchesStatus;
|
|
});
|
|
|
|
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 animate-pulse" />
|
|
{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('help_support', 'Help & Support')}>
|
|
<Head title={t('support_tickets_title', 'Help & Support Tickets - Employer Portal')} />
|
|
|
|
<div className="space-y-8 select-none w-full pb-16">
|
|
|
|
{/* Support Intro Banner */}
|
|
<div className="bg-gradient-to-r from-blue-600 to-[#185FA5] rounded-3xl p-6 md:p-8 text-white shadow-md flex flex-col md:flex-row items-center justify-between gap-6">
|
|
<div className="space-y-2 max-w-xl text-center md:text-left">
|
|
<div className="inline-flex items-center space-x-2 bg-white/10 px-3 py-1 rounded-full text-xs font-bold backdrop-blur-sm">
|
|
<LifeBuoy className="w-4 h-4 text-amber-300 animate-spin-slow" />
|
|
<span>{t('customer_care_center', 'Customer Care & Tech Support')}</span>
|
|
</div>
|
|
<h2 className="text-xl md:text-2xl font-black tracking-tight">{t('how_can_we_help', 'How can we help you today?')}</h2>
|
|
<p className="text-xs text-blue-100 font-semibold leading-relaxed">
|
|
{t('support_description', 'Raise a ticket for billing queries, technical glitches, or safety concerns. Our senior engineering and operations teams respond promptly.')}
|
|
</p>
|
|
</div>
|
|
<Link
|
|
href="/employer/support/create"
|
|
className="bg-white hover:bg-slate-50 text-[#185FA5] hover:scale-[1.02] active:scale-[0.98] px-5 py-3 rounded-2xl font-black text-xs transition-all shadow-md flex items-center space-x-2 shrink-0 cursor-pointer"
|
|
>
|
|
<Plus className="w-4 h-4" />
|
|
<span>{t('create_new_ticket', 'Create New Ticket')}</span>
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Filters & search */}
|
|
<div className="bg-white p-4 rounded-2xl border border-slate-200 shadow-sm flex flex-col md:flex-row gap-4 items-center justify-between">
|
|
<div className="relative w-full md:w-80">
|
|
<Search className="absolute left-3.5 top-3 w-4 h-4 text-slate-400" />
|
|
<input
|
|
type="text"
|
|
placeholder={t('search_tickets_placeholder', 'Search ticket # or subject...')}
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
className="w-full pl-10 pr-4 py-2 text-xs font-semibold border border-slate-200 rounded-xl bg-slate-50/50 outline-none focus:border-[#185FA5] transition-colors"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2 w-full md:w-auto overflow-x-auto pb-1 md:pb-0">
|
|
{['all', 'open', 'in_progress', 'resolved', 'closed'].map((status) => (
|
|
<button
|
|
key={status}
|
|
onClick={() => setStatusFilter(status)}
|
|
className={`px-4 py-2 rounded-xl text-xs font-black transition-all shrink-0 ${
|
|
statusFilter === status
|
|
? 'bg-[#185FA5] text-white shadow-xs'
|
|
: 'bg-slate-50 hover:bg-slate-100 text-slate-600 border border-slate-200/40'
|
|
}`}
|
|
>
|
|
{t(`filter_status_${status}`, status.charAt(0).toUpperCase() + status.slice(1).replace('_', ' '))}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tickets list */}
|
|
<div className="bg-white rounded-3xl border border-slate-200 shadow-sm overflow-hidden">
|
|
{filteredTickets.length > 0 ? (
|
|
<div className="divide-y divide-slate-100">
|
|
{filteredTickets.map((ticket) => (
|
|
<Link
|
|
key={ticket.id}
|
|
href={`/employer/support/${ticket.id}`}
|
|
className="flex items-center justify-between p-6 hover:bg-slate-50/70 transition-colors group cursor-pointer"
|
|
>
|
|
<div className="space-y-2 pr-4 min-w-0">
|
|
<div className="flex items-center space-x-2">
|
|
<span className="text-[10px] font-mono font-bold text-slate-400 bg-slate-50 px-2 py-0.5 rounded border border-slate-100">
|
|
{ticket.ticket_number}
|
|
</span>
|
|
{ticket.reason && (
|
|
<span className="text-[9px] font-bold text-indigo-700 bg-indigo-50 px-2 py-0.5 rounded border border-indigo-100">
|
|
{ticket.reason}
|
|
</span>
|
|
)}
|
|
{getPriorityBadge(ticket.priority)}
|
|
</div>
|
|
<h3 className="font-extrabold text-sm text-slate-900 group-hover:text-[#185FA5] transition-colors truncate">
|
|
{ticket.subject}
|
|
</h3>
|
|
<div className="flex items-center space-x-3 text-[10px] text-slate-400 font-bold">
|
|
<span>{t('created_on', 'Created:')} {ticket.created_at}</span>
|
|
<span className="w-1.5 h-1.5 rounded-full bg-slate-200" />
|
|
<span>{t('updated_on', 'Updated:')} {ticket.updated_at}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-4 shrink-0">
|
|
{getStatusBadge(ticket.status)}
|
|
<div className="w-8 h-8 rounded-xl bg-slate-50 text-slate-400 group-hover:text-[#185FA5] group-hover:bg-blue-50 border border-slate-100 flex items-center justify-center transition-colors">
|
|
<ArrowRight className="w-4 h-4" />
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="p-12 text-center flex flex-col items-center justify-center space-y-4">
|
|
<div className="w-14 h-14 bg-slate-50 text-slate-400 border border-slate-100 rounded-2xl flex items-center justify-center">
|
|
<HelpCircle className="w-8 h-8" />
|
|
</div>
|
|
<div className="space-y-1">
|
|
<h4 className="font-black text-slate-800 text-sm">{t('no_tickets_found', 'No support tickets found')}</h4>
|
|
<p className="text-xs text-slate-400 font-medium max-w-sm">
|
|
{t('no_tickets_found_desc', 'If you are experiencing any billing or login issues, click the button above to raise a ticket.')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* FAQ section */}
|
|
{faqs && faqs.length > 0 && (
|
|
<div className="bg-slate-50 p-6 rounded-3xl border border-slate-200/80 space-y-4">
|
|
<h3 className="font-black text-slate-800 text-sm flex items-center space-x-2">
|
|
<MessageSquare className="w-4 h-4 text-[#185FA5]" />
|
|
<span>{t('frequently_asked_questions', 'Frequently Asked Questions')}</span>
|
|
</h3>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-xs">
|
|
{faqs.map((faq) => (
|
|
<div key={faq.id} className="bg-white p-4 rounded-2xl border border-slate-200/50 space-y-1">
|
|
<h4 className="font-black text-slate-800">
|
|
{faq.question.trim().endsWith('?') ? faq.question.trim() : faq.question.trim() + '?'}
|
|
</h4>
|
|
<p className="text-slate-500 font-semibold">{faq.answer}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
</div>
|
|
</EmployerLayout>
|
|
);
|
|
}
|