Community 93 Premium feature complete
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* @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;
|
||||
Reference in New Issue
Block a user