Merge asset recurring templates into the main Recurring/Summary pages
Assets.tsx had its own bare-bones template-creation form (title/interval/ next_due only, no description/priority-choice/assignee/schedule_mode/ lead_days/group) with zero edit or delete path — a linked recurring task could only ever be created from here, never managed. The backend already sourced the asset detail's tasks/templates straight from the main tasks and task_templates tables (no separate storage), so the fix is entirely frontend: stop duplicating the editor, link back to the real one. - Assets.tsx: removed the bespoke ServiceTaskForm entirely. Each linked template row now navigates to /recurring?edit=<id> (opens that template's full edit form there, including delete). "+ Add" navigates to /recurring?new_asset=&new_location= for a prefilled New Template form. Added a matching "+ Report fault" action navigating to /summary?new_asset=&new_location= for one-off tasks, since there was previously no way to create a plain task from the Assets page at all. Existing tasks still open in-place via TaskModal, unchanged. - Recurring.tsx / Summary.tsx: read those query params on mount, open the appropriate form/modal prefilled, then strip the params from the URL (replace, no history pollution). - NewTaskModal.tsx + Recurring.tsx template form: asset picker is no longer gated behind picking a location first — it always shows the full register (grouped by location) or the location-filtered list once one's chosen, and selecting an asset now back-fills its location automatically. Makes tagging routine servicing to a specific asset (fixed or floating recurring) the natural way in, for both one-off and recurring tasks. - Asset list/detail counts (open tasks, recurring templates) are unchanged — still sourced from the same main-table queries. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
d9b01a5981
commit
2d2541576d
4 changed files with 136 additions and 84 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { Plus, RefreshCw, Camera, BedDouble, Layers, ChevronDown, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react'
|
||||
import type { Task, Category, Location, AppConfig, TaskStatus, Priority } from '../types'
|
||||
import { STATUS_LABELS, can } from '../types'
|
||||
|
|
@ -47,7 +48,9 @@ export default function Summary() {
|
|||
setSortMode(prev => SORT_CYCLE[(SORT_CYCLE.indexOf(prev) + 1) % SORT_CYCLE.length])
|
||||
}
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const [showNew, setShowNew] = useState(false)
|
||||
const [newTaskPreset, setNewTaskPreset] = useState<{ locationId?: number; assetId?: number }>({})
|
||||
const [openTask, setOpenTask] = useState<number | null>(null)
|
||||
|
||||
const roomsCat = useMemo(() => categories.find(c => c.is_rooms) ?? null, [categories])
|
||||
|
|
@ -80,6 +83,26 @@ export default function Summary() {
|
|||
fetchConfig().then(setConfig).catch(() => {})
|
||||
}, [])
|
||||
|
||||
// Deep link from the Assets page: ?new_asset=&new_location= opens a prefilled
|
||||
// Report fault form instead of the asset page having its own creation form.
|
||||
useEffect(() => {
|
||||
const newAsset = searchParams.get('new_asset')
|
||||
const newLocation = searchParams.get('new_location')
|
||||
if (newAsset || newLocation) {
|
||||
setNewTaskPreset({
|
||||
locationId: newLocation ? parseInt(newLocation) : undefined,
|
||||
assetId: newAsset ? parseInt(newAsset) : undefined,
|
||||
})
|
||||
setShowNew(true)
|
||||
setSearchParams(prev => {
|
||||
const next = new URLSearchParams(prev)
|
||||
next.delete('new_asset'); next.delete('new_location')
|
||||
return next
|
||||
}, { replace: true })
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchParams])
|
||||
|
||||
const counts = useMemo(() => {
|
||||
const c: Partial<Record<TaskStatus, number>> = {}
|
||||
for (const t of tasks) c[t.status] = (c[t.status] || 0) + 1
|
||||
|
|
@ -141,7 +164,7 @@ export default function Summary() {
|
|||
<RefreshCw size={14} strokeWidth={1.75} /> Refresh
|
||||
</button>
|
||||
{can(user, 'report') && (
|
||||
<button className="btn btn-primary" onClick={() => setShowNew(true)}>
|
||||
<button className="btn btn-primary" onClick={() => { setNewTaskPreset({}); setShowNew(true) }}>
|
||||
<Plus size={14} strokeWidth={1.75} /> Report fault
|
||||
</button>
|
||||
)}
|
||||
|
|
@ -287,6 +310,8 @@ export default function Summary() {
|
|||
categories={categories}
|
||||
locations={locations}
|
||||
config={config}
|
||||
initialLocationId={newTaskPreset.locationId}
|
||||
initialAssetId={newTaskPreset.assetId}
|
||||
onClose={() => setShowNew(false)}
|
||||
onCreated={load}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue