Skip to content

Commit fab444b

Browse files
committed
Succeeded in calling the backend to create an inspection
1 parent 1e6ce3c commit fab444b

File tree

7 files changed

+333
-135
lines changed

7 files changed

+333
-135
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useMutation, useQueryClient } from '@tanstack/react-query'
2+
3+
import { inspectionService } from '@/services/api/core'
4+
import type { components } from '@/services/api/core/generated/api-types'
5+
6+
type CreateInspectionRequest = components['schemas']['CreateInspectionRequest']
7+
type DetailedInspection = components['schemas']['DetailedInspection']
8+
9+
interface UseCreateInspectionOptions {
10+
rentalId?: string
11+
}
12+
13+
export const useCreateInspection = ({
14+
rentalId,
15+
}: UseCreateInspectionOptions) => {
16+
const queryClient = useQueryClient()
17+
18+
return useMutation<DetailedInspection, Error, CreateInspectionRequest>({
19+
mutationFn: (body) => inspectionService.createInspection(body),
20+
onSuccess: () => {
21+
queryClient.invalidateQueries({
22+
queryKey: ['inspections', rentalId],
23+
})
24+
},
25+
})
26+
}

apps/property-tree/src/features/inspections/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export { InspectionForm } from './ui/InspectionForm'
66
export { InspectionFormDialog } from './ui/InspectionFormDialog'
77
export { InspectionProtocol } from './ui/InspectionProtocol'
88
export { InspectionProtocolDropdown } from './ui/InspectionProtocolDropdown'
9+
export { CreateInspectionDialog } from './ui/CreateInspectionDialog'
910
export { InspectionsTabContent } from './ui/InspectionsTabContent'
1011
export { InspectionsTable } from './ui/InspectionsTable'
1112
export { InspectorSelectionCard } from './ui/InspectorSelectionCard'
@@ -20,6 +21,7 @@ export { MobileInspectionSheet } from './ui/mobile/MobileInspectionSheet'
2021

2122
// Hooks
2223
export { useComponentInspection } from './hooks/useComponentInspection'
24+
export { useCreateInspection } from './hooks/useCreateInspection'
2325
export { useInspectionFilters } from './hooks/useInspectionFilters'
2426
export { useInspectionForm } from './hooks/useInspectionForm'
2527
export { useInspectionFormState } from './hooks/useInspectionFormState'
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import { useState } from 'react'
2+
3+
import type { components } from '@/services/api/core/generated/api-types'
4+
5+
import { INSPECTION_STATUS } from '../constants/statuses'
6+
import { INSPECTION_TYPE_LABELS } from '../constants/inspectionTypes'
7+
8+
import { Button } from '@/shared/ui/Button'
9+
import { Checkbox } from '@/shared/ui/Checkbox'
10+
import {
11+
Dialog,
12+
DialogContent,
13+
DialogDescription,
14+
DialogHeader,
15+
DialogTitle,
16+
} from '@/shared/ui/Dialog'
17+
import { Input } from '@/shared/ui/Input'
18+
import { Label } from '@/shared/ui/Label'
19+
import {
20+
Select,
21+
SelectContent,
22+
SelectItem,
23+
SelectTrigger,
24+
SelectValue,
25+
} from '@/shared/ui/Select'
26+
import { Textarea } from '@/shared/ui/Textarea'
27+
28+
type CreateInspectionRequest = components['schemas']['CreateInspectionRequest']
29+
30+
const INSPECTION_TYPES = Object.entries(INSPECTION_TYPE_LABELS).map(
31+
([value, label]) => ({ value, label })
32+
)
33+
34+
interface CreateInspectionDialogProps {
35+
isOpen: boolean
36+
onClose: () => void
37+
onSubmit: (data: CreateInspectionRequest) => void
38+
isSubmitting?: boolean
39+
residenceId: string
40+
address: string
41+
apartmentCode: string | null
42+
leaseId: string
43+
roomNames: string[]
44+
}
45+
46+
export function CreateInspectionDialog({
47+
isOpen,
48+
onClose,
49+
onSubmit,
50+
isSubmitting,
51+
residenceId,
52+
address,
53+
apartmentCode,
54+
leaseId,
55+
roomNames,
56+
}: CreateInspectionDialogProps) {
57+
const [inspector, setInspector] = useState('')
58+
const [date, setDate] = useState(() => new Date().toISOString().slice(0, 10))
59+
const [type, setType] = useState('')
60+
const [isFurnished, setIsFurnished] = useState(false)
61+
const [isTenantPresent, setIsTenantPresent] = useState(false)
62+
const [isNewTenantPresent, setIsNewTenantPresent] = useState(false)
63+
const [masterKeyAccess, setMasterKeyAccess] = useState('')
64+
const [notes, setNotes] = useState('')
65+
66+
const canSubmit = inspector.trim() && type && date
67+
68+
const handleSubmit = () => {
69+
if (!canSubmit) return
70+
71+
const body: CreateInspectionRequest = {
72+
status: INSPECTION_STATUS.REGISTERED,
73+
date: new Date(date).toISOString(),
74+
startedAt: null,
75+
endedAt: null,
76+
inspector: inspector.trim(),
77+
type,
78+
residenceId,
79+
address,
80+
apartmentCode,
81+
isFurnished,
82+
leaseId,
83+
isTenantPresent,
84+
isNewTenantPresent,
85+
masterKeyAccess: masterKeyAccess.trim() || null,
86+
hasRemarks: false,
87+
notes: notes.trim() || null,
88+
totalCost: null,
89+
rooms: roomNames.map((name) => ({ room: name, remarks: [] })),
90+
}
91+
92+
onSubmit(body)
93+
}
94+
95+
const resetForm = () => {
96+
setInspector('')
97+
setDate(new Date().toISOString().slice(0, 10))
98+
setType('')
99+
setIsFurnished(false)
100+
setIsTenantPresent(false)
101+
setIsNewTenantPresent(false)
102+
setMasterKeyAccess('')
103+
setNotes('')
104+
}
105+
106+
const handleClose = () => {
107+
resetForm()
108+
onClose()
109+
}
110+
111+
const handleOpenChange = (open: boolean) => {
112+
if (!open) {
113+
handleClose()
114+
}
115+
}
116+
117+
return (
118+
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
119+
<DialogContent className="max-w-lg">
120+
<DialogHeader className="space-y-1">
121+
<DialogTitle>Skapa besiktning</DialogTitle>
122+
<DialogDescription>
123+
Skapa en ny besiktning för {address}
124+
</DialogDescription>
125+
</DialogHeader>
126+
127+
<div className="space-y-4 mt-2">
128+
<div className="grid grid-cols-2 gap-4">
129+
<div className="space-y-2">
130+
<Label htmlFor="inspector">Besiktningsman</Label>
131+
<Input
132+
id="inspector"
133+
value={inspector}
134+
onChange={(e) => setInspector(e.target.value)}
135+
placeholder="Namn"
136+
/>
137+
</div>
138+
139+
<div className="space-y-2">
140+
<Label htmlFor="date">Datum</Label>
141+
<Input
142+
id="date"
143+
type="date"
144+
value={date}
145+
onChange={(e) => setDate(e.target.value)}
146+
/>
147+
</div>
148+
</div>
149+
150+
<div className="space-y-2">
151+
<Label htmlFor="type">Typ av besiktning</Label>
152+
<Select value={type} onValueChange={setType}>
153+
<SelectTrigger id="type">
154+
<SelectValue placeholder="Välj typ" />
155+
</SelectTrigger>
156+
<SelectContent>
157+
{INSPECTION_TYPES.map((t) => (
158+
<SelectItem key={t.value} value={t.value}>
159+
{t.label}
160+
</SelectItem>
161+
))}
162+
</SelectContent>
163+
</Select>
164+
</div>
165+
166+
<div className="space-y-3">
167+
<div className="flex items-center gap-2">
168+
<Checkbox
169+
id="isFurnished"
170+
checked={isFurnished}
171+
onCheckedChange={(checked) => setIsFurnished(checked === true)}
172+
/>
173+
<Label htmlFor="isFurnished">Möblerad</Label>
174+
</div>
175+
176+
<div className="flex items-center gap-2">
177+
<Checkbox
178+
id="isTenantPresent"
179+
checked={isTenantPresent}
180+
onCheckedChange={(checked) =>
181+
setIsTenantPresent(checked === true)
182+
}
183+
/>
184+
<Label htmlFor="isTenantPresent">Hyresgäst närvarande</Label>
185+
</div>
186+
187+
<div className="flex items-center gap-2">
188+
<Checkbox
189+
id="isNewTenantPresent"
190+
checked={isNewTenantPresent}
191+
onCheckedChange={(checked) =>
192+
setIsNewTenantPresent(checked === true)
193+
}
194+
/>
195+
<Label htmlFor="isNewTenantPresent">
196+
Ny hyresgäst närvarande
197+
</Label>
198+
</div>
199+
</div>
200+
201+
<div className="space-y-2">
202+
<Label htmlFor="masterKeyAccess">Huvudnyckelåtkomst</Label>
203+
<Input
204+
id="masterKeyAccess"
205+
value={masterKeyAccess}
206+
onChange={(e) => setMasterKeyAccess(e.target.value)}
207+
placeholder="T.ex. huvudnyckel, låssmed"
208+
/>
209+
</div>
210+
211+
<div className="space-y-2">
212+
<Label htmlFor="notes">Anteckningar</Label>
213+
<Textarea
214+
id="notes"
215+
value={notes}
216+
onChange={(e) => setNotes(e.target.value)}
217+
placeholder="Valfria anteckningar"
218+
rows={3}
219+
/>
220+
</div>
221+
</div>
222+
223+
<div className="flex gap-3 justify-end pt-4 border-t">
224+
<Button variant="outline" onClick={handleClose} disabled={isSubmitting}>
225+
Avbryt
226+
</Button>
227+
<Button onClick={handleSubmit} disabled={!canSubmit || isSubmitting}>
228+
{isSubmitting ? 'Skapar...' : 'Skapa besiktning'}
229+
</Button>
230+
</div>
231+
</DialogContent>
232+
</Dialog>
233+
)
234+
}

0 commit comments

Comments
 (0)