180 lines
5.7 KiB
TypeScript
180 lines
5.7 KiB
TypeScript
/**
|
|
* @ai-summary Mobile admin screen for community station reviews
|
|
*/
|
|
|
|
import React, { useState } from 'react';
|
|
import { Navigate } from 'react-router-dom';
|
|
import {
|
|
Box,
|
|
Select,
|
|
MenuItem,
|
|
FormControl,
|
|
InputLabel,
|
|
} from '@mui/material';
|
|
import { MobileContainer } from '../../../shared-minimal/components/mobile/MobileContainer';
|
|
import { useAdminAccess } from '../../../core/auth/useAdminAccess';
|
|
import { CommunityStationReviewQueue } from '../components/CommunityStationReviewQueue';
|
|
import { CommunityStationsList } from '../../stations/components/CommunityStationsList';
|
|
import {
|
|
usePendingSubmissions,
|
|
useAllCommunitySubmissions,
|
|
useReviewStation,
|
|
} from '../../stations/hooks/useCommunityStations';
|
|
import toast from 'react-hot-toast';
|
|
|
|
type TabType = 'pending' | 'all';
|
|
|
|
/**
|
|
* Mobile admin screen for reviewing community station submissions
|
|
* Touch-friendly layout with tab navigation
|
|
*/
|
|
export const AdminCommunityStationsMobileScreen: React.FC = () => {
|
|
const { isAdmin, loading } = useAdminAccess();
|
|
const [activeTab, setActiveTab] = useState<TabType>('pending');
|
|
const [statusFilter, setStatusFilter] = useState<string>('');
|
|
const [page, setPage] = useState(0);
|
|
|
|
// Hooks
|
|
const pendingSubmissions = usePendingSubmissions(page, 20);
|
|
const allSubmissions = useAllCommunitySubmissions(statusFilter || undefined, page, 20);
|
|
const reviewMutation = useReviewStation();
|
|
|
|
if (loading) {
|
|
return (
|
|
<MobileContainer>
|
|
<div className="flex items-center justify-center min-h-[400px]">
|
|
<div className="text-center">
|
|
<div className="text-slate-500 mb-2">Loading admin access...</div>
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto"></div>
|
|
</div>
|
|
</div>
|
|
</MobileContainer>
|
|
);
|
|
}
|
|
|
|
if (!isAdmin) {
|
|
return <Navigate to="/garage/settings" replace />;
|
|
}
|
|
|
|
// Handle approval
|
|
const handleApprove = async (id: string) => {
|
|
try {
|
|
await reviewMutation.mutateAsync({
|
|
id,
|
|
decision: { status: 'approved' },
|
|
});
|
|
toast.success('Station approved');
|
|
} catch (error: any) {
|
|
toast.error(error?.response?.data?.message || 'Failed to approve station');
|
|
}
|
|
};
|
|
|
|
// Handle rejection
|
|
const handleReject = async (id: string, reason: string) => {
|
|
try {
|
|
await reviewMutation.mutateAsync({
|
|
id,
|
|
decision: { status: 'rejected', rejectionReason: reason },
|
|
});
|
|
toast.success('Station rejected');
|
|
} catch (error: any) {
|
|
toast.error(error?.response?.data?.message || 'Failed to reject station');
|
|
}
|
|
};
|
|
|
|
const displayData = activeTab === 'pending' ? pendingSubmissions : allSubmissions;
|
|
const stations = displayData.data?.stations || [];
|
|
const totalPages = displayData.data?.total ? Math.ceil(displayData.data.total / 20) : 1;
|
|
|
|
return (
|
|
<MobileContainer>
|
|
<div className="space-y-4 pb-20 p-4">
|
|
{/* Header */}
|
|
<div className="text-center mb-6">
|
|
<h1 className="text-2xl font-bold text-slate-800">Community Station Reviews</h1>
|
|
<p className="text-slate-500 mt-1">Review user-submitted gas stations</p>
|
|
</div>
|
|
|
|
{/* Tab navigation */}
|
|
<Box sx={{ display: 'flex', gap: 1, mb: 2 }}>
|
|
<button
|
|
onClick={() => {
|
|
setActiveTab('pending');
|
|
setPage(0);
|
|
}}
|
|
className={`flex-1 py-2 px-3 rounded-lg font-medium transition ${
|
|
activeTab === 'pending'
|
|
? 'bg-blue-600 text-white'
|
|
: 'bg-slate-100 text-slate-600'
|
|
}`}
|
|
>
|
|
Pending ({pendingSubmissions.data?.total || 0})
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
setActiveTab('all');
|
|
setPage(0);
|
|
}}
|
|
className={`flex-1 py-2 px-3 rounded-lg font-medium transition ${
|
|
activeTab === 'all'
|
|
? 'bg-blue-600 text-white'
|
|
: 'bg-slate-100 text-slate-600'
|
|
}`}
|
|
>
|
|
All
|
|
</button>
|
|
</Box>
|
|
|
|
{/* Status filter for all tab */}
|
|
{activeTab === 'all' && (
|
|
<FormControl fullWidth size="small" sx={{ mb: 2 }}>
|
|
<InputLabel>Filter by Status</InputLabel>
|
|
<Select
|
|
value={statusFilter}
|
|
label="Filter by Status"
|
|
onChange={(e) => {
|
|
setStatusFilter(e.target.value);
|
|
setPage(0);
|
|
}}
|
|
>
|
|
<MenuItem value="">All</MenuItem>
|
|
<MenuItem value="pending">Pending</MenuItem>
|
|
<MenuItem value="approved">Approved</MenuItem>
|
|
<MenuItem value="rejected">Rejected</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
)}
|
|
|
|
{/* Content */}
|
|
{activeTab === 'pending' ? (
|
|
<CommunityStationReviewQueue
|
|
stations={stations}
|
|
loading={displayData.isLoading}
|
|
error={displayData.error ? 'Failed to load submissions' : null}
|
|
onApprove={handleApprove}
|
|
onReject={handleReject}
|
|
page={page}
|
|
onPageChange={setPage}
|
|
totalPages={totalPages}
|
|
isReviewLoading={reviewMutation.isPending}
|
|
/>
|
|
) : (
|
|
<CommunityStationsList
|
|
stations={stations}
|
|
loading={displayData.isLoading}
|
|
error={displayData.error ? 'Failed to load submissions' : null}
|
|
isAdmin={true}
|
|
onApprove={handleApprove}
|
|
onReject={handleReject}
|
|
page={page}
|
|
onPageChange={setPage}
|
|
totalPages={totalPages}
|
|
/>
|
|
)}
|
|
</div>
|
|
</MobileContainer>
|
|
);
|
|
};
|
|
|
|
export default AdminCommunityStationsMobileScreen;
|