Assign a region manager for the route
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from '@/components/ui/dialog'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { toast } from '@/components/ui/toast'
|
||||
import { regions as regionsApi, users as usersApi, getApiError, getPaginatedItems } from '@/lib/api/index'
|
||||
import type { RegionResponse, RegionManagerResponse, UserResponse } from '@/types/api'
|
||||
|
||||
const MANAGER_ROLE = 'RegionalManager'
|
||||
|
||||
interface RegionManagerDialogProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
region: RegionResponse | null
|
||||
}
|
||||
|
||||
export function RegionManagerDialog({ open, onClose, region }: RegionManagerDialogProps) {
|
||||
const [current, setCurrent] = useState<RegionManagerResponse | null>(null)
|
||||
const [candidates, setCandidates] = useState<UserResponse[]>([])
|
||||
const [selected, setSelected] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !region) return
|
||||
let cancelled = false
|
||||
setLoading(true)
|
||||
setSelected('')
|
||||
setCurrent(null)
|
||||
Promise.all([
|
||||
regionsApi.getManager(region.id).catch(() => null), // 404 = no manager yet
|
||||
usersApi.list({ pageSize: 500 }),
|
||||
])
|
||||
.then(([manager, userRes]) => {
|
||||
if (cancelled) return
|
||||
setCurrent(manager)
|
||||
setSelected(manager?.userId ?? '')
|
||||
setCandidates(getPaginatedItems(userRes).filter((u) => u.isActive && u.roleName === MANAGER_ROLE))
|
||||
})
|
||||
.catch((err) => toast.add({ title: 'Failed to load users', description: getApiError(err), type: 'error' }))
|
||||
.finally(() => !cancelled && setLoading(false))
|
||||
return () => { cancelled = true }
|
||||
}, [open, region])
|
||||
|
||||
async function save() {
|
||||
if (!region || !selected) return
|
||||
setSaving(true)
|
||||
try {
|
||||
await regionsApi.setManager(region.id, selected)
|
||||
toast.add({ title: 'Region manager assigned', type: 'success' })
|
||||
onClose()
|
||||
} catch (err) {
|
||||
toast.add({ title: 'Failed to assign manager', description: getApiError(err), type: 'error' })
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function remove() {
|
||||
if (!region) return
|
||||
setSaving(true)
|
||||
try {
|
||||
await regionsApi.removeManager(region.id)
|
||||
toast.add({ title: 'Region manager removed', type: 'success' })
|
||||
onClose()
|
||||
} catch (err) {
|
||||
toast.add({ title: 'Failed to remove manager', description: getApiError(err), type: 'error' })
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(v) => !v && !saving && onClose()}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Region Manager</DialogTitle>
|
||||
<DialogDescription>
|
||||
{region ? `${region.code} – ${region.name}. ` : ''}
|
||||
Each region has one manager, chosen from users with the RegionalManager role.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Current manager:{' '}
|
||||
<span className="font-medium text-foreground">
|
||||
{loading ? 'Loading…' : current ? `${current.fullName} (${current.username})` : 'None'}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<Select value={selected} onValueChange={(v) => setSelected(v ?? '')} disabled={loading || saving}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder={candidates.length ? 'Select a manager' : 'No RegionalManager users available'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{candidates.map((u) => (
|
||||
<SelectItem key={u.id} value={u.id}>
|
||||
{u.fullName} ({u.username})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
{current && (
|
||||
<Button type="button" variant="destructive" onClick={remove} disabled={saving}>
|
||||
Remove manager
|
||||
</Button>
|
||||
)}
|
||||
<Button type="button" variant="outline" onClick={onClose} disabled={saving}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="button" onClick={save} disabled={saving || loading || !selected || selected === current?.userId}>
|
||||
{saving ? 'Saving…' : 'Assign manager'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { regions as regionsApi, getApiError, getPaginatedItems } from '@/lib/api
|
||||
import type { RegionResponse } from '@/types/api'
|
||||
|
||||
import { RegionSheet } from './region-sheet'
|
||||
import { RegionManagerDialog } from './region-manager-dialog'
|
||||
|
||||
export function RegionsClient() {
|
||||
const [data, setData] = useState<RegionResponse[]>([])
|
||||
@@ -27,6 +28,7 @@ export function RegionsClient() {
|
||||
const [viewOnly, setViewOnly] = useState(false)
|
||||
const [deleting, setDeleting] = useState<RegionResponse | null>(null)
|
||||
const [deleteBusy, setDeleteBusy] = useState(false)
|
||||
const [managerRegion, setManagerRegion] = useState<RegionResponse | null>(null)
|
||||
|
||||
const fetchData = useCallback(async (p: number, ps: number) => {
|
||||
setLoading(true)
|
||||
@@ -124,6 +126,11 @@ export function RegionsClient() {
|
||||
],
|
||||
rowActions: {
|
||||
actions: () => [
|
||||
{
|
||||
action: 'confirm',
|
||||
label: 'Manage region manager',
|
||||
onClick: (region: RegionResponse) => setManagerRegion(region),
|
||||
},
|
||||
{
|
||||
action: 'view',
|
||||
label: 'View region',
|
||||
@@ -176,6 +183,12 @@ export function RegionsClient() {
|
||||
readOnly={viewOnly}
|
||||
onSuccess={() => fetchData(page, pageSize)}
|
||||
/>
|
||||
<RegionManagerDialog
|
||||
open={!!managerRegion}
|
||||
onClose={() => setManagerRegion(null)}
|
||||
region={managerRegion}
|
||||
/>
|
||||
|
||||
<Dialog open={!!deleting} onOpenChange={(v) => !v && !deleteBusy && setDeleting(null)}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
|
||||
Reference in New Issue
Block a user