Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Update frontend/src/pages/SupportEngineerPanel.jsx
Browse files
frontend/src/pages/SupportEngineerPanel.jsx
CHANGED
|
@@ -219,6 +219,23 @@ export const SupportEngineerPanel = () => {
|
|
| 219 |
const [userRoleFilter, setUserRoleFilter] = useState('all');
|
| 220 |
const [userOrgFilter, setUserOrgFilter] = useState('all');
|
| 221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
// Pagination states
|
| 223 |
const [orgPage, setOrgPage] = useState(1);
|
| 224 |
const [orgPageSize, setOrgPageSize] = useState(25);
|
|
@@ -1182,12 +1199,67 @@ export const SupportEngineerPanel = () => {
|
|
| 1182 |
{/* BOOKINGS TAB */}
|
| 1183 |
{activeTab === 'bookings' && (
|
| 1184 |
<div className="flex flex-col gap-md mb-xl animate-fade-in">
|
| 1185 |
-
<
|
| 1186 |
-
<
|
| 1187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1188 |
<div className="bg-surface border border-outline-variant rounded-2xl overflow-hidden shadow-sm">
|
| 1189 |
-
{
|
| 1190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1191 |
) : (
|
| 1192 |
<>
|
| 1193 |
<div className="overflow-x-auto">
|
|
@@ -1202,7 +1274,7 @@ export const SupportEngineerPanel = () => {
|
|
| 1202 |
</tr>
|
| 1203 |
</thead>
|
| 1204 |
<tbody className="divide-y divide-outline-variant">
|
| 1205 |
-
{
|
| 1206 |
const statusStyle =
|
| 1207 |
b.status === 'completed'
|
| 1208 |
? 'bg-green-500/10 text-green-600 border-green-500/30'
|
|
@@ -1278,7 +1350,7 @@ export const SupportEngineerPanel = () => {
|
|
| 1278 |
</div>
|
| 1279 |
<TablePagination
|
| 1280 |
currentPage={bookingPage}
|
| 1281 |
-
totalEntries={
|
| 1282 |
pageSize={bookingPageSize}
|
| 1283 |
onPageChange={setBookingPage}
|
| 1284 |
onPageSizeChange={setBookingPageSize}
|
|
|
|
| 219 |
const [userRoleFilter, setUserRoleFilter] = useState('all');
|
| 220 |
const [userOrgFilter, setUserOrgFilter] = useState('all');
|
| 221 |
|
| 222 |
+
// Filter states for Bookings
|
| 223 |
+
const [bookingSearch, setBookingSearch] = useState('');
|
| 224 |
+
const [bookingStatusFilter, setBookingStatusFilter] = useState('all');
|
| 225 |
+
|
| 226 |
+
const getFilteredBookings = () => {
|
| 227 |
+
return (demoBookings || []).filter(b => {
|
| 228 |
+
const displayEmail = b.email || b.user_email || '';
|
| 229 |
+
const emailMatch = !bookingSearch ||
|
| 230 |
+
displayEmail.toLowerCase().includes(bookingSearch.toLowerCase()) ||
|
| 231 |
+
(b.company_size || '').toLowerCase().includes(bookingSearch.toLowerCase()) ||
|
| 232 |
+
(b.meeting_date || '').toLowerCase().includes(bookingSearch.toLowerCase());
|
| 233 |
+
|
| 234 |
+
const statusMatch = bookingStatusFilter === 'all' || (b.status || 'pending') === bookingStatusFilter;
|
| 235 |
+
return emailMatch && statusMatch;
|
| 236 |
+
});
|
| 237 |
+
};
|
| 238 |
+
|
| 239 |
// Pagination states
|
| 240 |
const [orgPage, setOrgPage] = useState(1);
|
| 241 |
const [orgPageSize, setOrgPageSize] = useState(25);
|
|
|
|
| 1199 |
{/* BOOKINGS TAB */}
|
| 1200 |
{activeTab === 'bookings' && (
|
| 1201 |
<div className="flex flex-col gap-md mb-xl animate-fade-in">
|
| 1202 |
+
<div className="flex flex-col md:flex-row md:items-center justify-between gap-md mb-xs">
|
| 1203 |
+
<h2 className="font-headline-sm font-bold text-on-surface flex items-center text-[18px]">
|
| 1204 |
+
<Calendar className="w-5 h-5 text-primary mr-2" /> Demo Bookings & Leads
|
| 1205 |
+
</h2>
|
| 1206 |
+
<div className="text-[12.5px] font-bold text-on-surface-variant">
|
| 1207 |
+
Total Leads: <span className="text-primary font-extrabold">{getFilteredBookings().length}</span>
|
| 1208 |
+
</div>
|
| 1209 |
+
</div>
|
| 1210 |
+
|
| 1211 |
<div className="bg-surface border border-outline-variant rounded-2xl overflow-hidden shadow-sm">
|
| 1212 |
+
{/* Filter Controls Bar */}
|
| 1213 |
+
<div className="p-md bg-surface-container-high/40 border-b border-outline-variant/60 flex flex-col md:flex-row justify-between items-start md:items-center gap-md">
|
| 1214 |
+
<div className="flex items-center gap-sm flex-wrap w-full md:w-auto">
|
| 1215 |
+
<div className="relative flex-1 md:w-64">
|
| 1216 |
+
<Search className="w-4 h-4 absolute left-3 top-2.5 text-on-surface-variant" />
|
| 1217 |
+
<input
|
| 1218 |
+
type="text"
|
| 1219 |
+
placeholder="Search email, company size, date..."
|
| 1220 |
+
value={bookingSearch}
|
| 1221 |
+
onChange={(e) => { setBookingSearch(e.target.value); setBookingPage(1); }}
|
| 1222 |
+
className="w-full bg-surface border border-outline-variant/60 rounded-lg pl-9 pr-8 py-1.5 text-[13px] outline-none focus:border-primary text-on-surface font-medium"
|
| 1223 |
+
/>
|
| 1224 |
+
{bookingSearch && (
|
| 1225 |
+
<button
|
| 1226 |
+
onClick={() => { setBookingSearch(''); setBookingPage(1); }}
|
| 1227 |
+
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-on-surface-variant hover:text-on-surface bg-transparent border-0 cursor-pointer p-0.5"
|
| 1228 |
+
>
|
| 1229 |
+
<X className="w-3.5 h-3.5" />
|
| 1230 |
+
</button>
|
| 1231 |
+
)}
|
| 1232 |
+
</div>
|
| 1233 |
+
|
| 1234 |
+
<div className="flex items-center gap-1.5">
|
| 1235 |
+
<span className="text-[11px] font-bold text-on-surface-variant uppercase tracking-wider shrink-0">Status:</span>
|
| 1236 |
+
<select
|
| 1237 |
+
value={bookingStatusFilter}
|
| 1238 |
+
onChange={(e) => { setBookingStatusFilter(e.target.value); setBookingPage(1); }}
|
| 1239 |
+
className="bg-surface border border-outline-variant/60 text-on-surface text-[13px] font-medium rounded-lg px-3 py-1.5 focus:outline-none focus:border-primary cursor-pointer"
|
| 1240 |
+
>
|
| 1241 |
+
<option value="all">All Statuses</option>
|
| 1242 |
+
<option value="pending">Pending</option>
|
| 1243 |
+
<option value="completed">Completed</option>
|
| 1244 |
+
<option value="rescheduled">Rescheduled</option>
|
| 1245 |
+
<option value="cancelled">Cancelled</option>
|
| 1246 |
+
</select>
|
| 1247 |
+
</div>
|
| 1248 |
+
|
| 1249 |
+
{(bookingSearch || bookingStatusFilter !== 'all') && (
|
| 1250 |
+
<button
|
| 1251 |
+
onClick={() => { setBookingSearch(''); setBookingStatusFilter('all'); setBookingPage(1); }}
|
| 1252 |
+
className="text-error hover:underline text-[12.5px] font-bold flex items-center gap-1 cursor-pointer bg-transparent border-0 px-1 ml-auto md:ml-0"
|
| 1253 |
+
>
|
| 1254 |
+
<X className="w-4 h-4" />
|
| 1255 |
+
Reset Filters
|
| 1256 |
+
</button>
|
| 1257 |
+
)}
|
| 1258 |
+
</div>
|
| 1259 |
+
</div>
|
| 1260 |
+
|
| 1261 |
+
{getFilteredBookings().length === 0 ? (
|
| 1262 |
+
<div className="p-xl text-center text-on-surface-variant font-bold">No demo bookings match your filter criteria.</div>
|
| 1263 |
) : (
|
| 1264 |
<>
|
| 1265 |
<div className="overflow-x-auto">
|
|
|
|
| 1274 |
</tr>
|
| 1275 |
</thead>
|
| 1276 |
<tbody className="divide-y divide-outline-variant">
|
| 1277 |
+
{getFilteredBookings().slice((bookingPage - 1) * bookingPageSize, bookingPage * bookingPageSize).map(b => {
|
| 1278 |
const statusStyle =
|
| 1279 |
b.status === 'completed'
|
| 1280 |
? 'bg-green-500/10 text-green-600 border-green-500/30'
|
|
|
|
| 1350 |
</div>
|
| 1351 |
<TablePagination
|
| 1352 |
currentPage={bookingPage}
|
| 1353 |
+
totalEntries={getFilteredBookings().length}
|
| 1354 |
pageSize={bookingPageSize}
|
| 1355 |
onPageChange={setBookingPage}
|
| 1356 |
onPageSizeChange={setBookingPageSize}
|