Skip to content

Commit d42044c

Browse files
committed
update
1 parent 902f114 commit d42044c

File tree

1 file changed

+154
-119
lines changed

1 file changed

+154
-119
lines changed

app/pages/rides/index.vue

Lines changed: 154 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,35 @@
1313
const { data: clients } = await useFetch('/api/get/clients')
1414
1515
const search = ref('')
16-
const activeFilters = ref<{ label: string; value: string }[]>([])
17-
const excludedFilters = ref<{ label: string; value: string }[]>([])
16+
17+
// Persisted State
18+
const savedActiveFilters = useCookie<{ label: string; value: string }[]>('ride-active-filters', {
19+
default: () => [],
20+
})
21+
const savedExcludedFilters = useCookie<{ label: string; value: string }[]>(
22+
'ride-excluded-filters',
23+
{ default: () => [] }
24+
)
25+
26+
const activeFilters = ref<{ label: string; value: string }[]>(savedActiveFilters.value)
27+
const excludedFilters = ref<{ label: string; value: string }[]>(savedExcludedFilters.value)
28+
29+
// Sync state back to cookies
30+
watch(
31+
activeFilters,
32+
(newVal) => {
33+
savedActiveFilters.value = newVal
34+
},
35+
{ deep: true }
36+
)
37+
watch(
38+
excludedFilters,
39+
(newVal) => {
40+
savedExcludedFilters.value = newVal
41+
},
42+
{ deep: true }
43+
)
44+
1845
const startDate = ref('')
1946
const endDate = ref('')
2047
const isCreateModalOpen = ref(false)
@@ -23,7 +50,7 @@
2350
const options = [
2451
{ label: 'Created', value: 'status:CREATED' },
2552
{ label: 'Assigned', value: 'status:ASSIGNED' },
26-
{ label: 'Completed', value: 'status:COMPLETED' }
53+
{ label: 'Completed', value: 'status:COMPLETED' },
2754
]
2855
if (!isAdmin.value) {
2956
options.push({ label: 'Assigned to Me', value: 'assign:ME' })
@@ -41,19 +68,19 @@
4168
// Consolidated Filter Logic (OR Condition for Inclusion)
4269
if (activeFilters.value.length > 0) {
4370
result = result.filter((ride: any) => {
44-
return activeFilters.value.some(filter => {
71+
return activeFilters.value.some((filter) => {
4572
const val = filter.value
46-
73+
4774
if (val.startsWith('status:')) {
4875
const status = val.replace('status:', '')
4976
return ride.status === status
5077
}
51-
78+
5279
if (val === 'assign:ME') {
5380
const myId = session.value?.user?.id
5481
return !!(myId && ride.volunteer?.userId === myId)
5582
}
56-
83+
5784
return false
5885
})
5986
})
@@ -63,27 +90,29 @@
6390
if (excludedFilters.value.length > 0) {
6491
result = result.filter((ride: any) => {
6592
// Must NOT match ANY of the excluded filters
66-
return !excludedFilters.value.some(filter => {
93+
return !excludedFilters.value.some((filter) => {
6794
const val = filter.value
68-
95+
6996
if (val.startsWith('status:')) {
7097
const status = val.replace('status:', '')
7198
return ride.status === status
7299
}
73-
100+
74101
if (val === 'assign:ME') {
75102
const myId = session.value?.user?.id
76103
return !!(myId && ride.volunteer?.userId === myId)
77104
}
78-
105+
79106
return false
80107
})
81108
})
82109
}
83110
84111
// Date Range Filter
85112
if (startDate.value) {
86-
result = result.filter((ride: any) => new Date(ride.scheduledTime) >= new Date(startDate.value))
113+
result = result.filter(
114+
(ride: any) => new Date(ride.scheduledTime) >= new Date(startDate.value)
115+
)
87116
}
88117
if (endDate.value) {
89118
const end = new Date(endDate.value)
@@ -129,7 +158,10 @@
129158
id: 'volunteer',
130159
header: 'Volunteer',
131160
cell: ({ row }) => {
132-
return row.original.volunteer?.user?.name || h('span', { class: 'text-gray-400 italic' }, 'Unassigned')
161+
return (
162+
row.original.volunteer?.user?.name ||
163+
h('span', { class: 'text-gray-400 italic' }, 'Unassigned')
164+
)
133165
},
134166
},
135167
{
@@ -210,7 +242,7 @@
210242
multiple
211243
:searchable="false"
212244
:ui="{ input: 'hidden' }"
213-
placeholder="Include Status / Volunteer"
245+
placeholder="Include Status"
214246
class="w-full sm:w-64"
215247
/>
216248
<USelectMenu
@@ -219,23 +251,13 @@
219251
multiple
220252
:searchable="false"
221253
:ui="{ input: 'hidden' }"
222-
placeholder="Exclude Status / Volunteer"
254+
placeholder="Exclude Status"
223255
class="w-full sm:w-64"
224256
/>
225257
<div class="flex items-center gap-2">
226-
<UInput
227-
v-model="startDate"
228-
type="date"
229-
placeholder="Start"
230-
class="w-full sm:w-auto"
231-
/>
258+
<UInput v-model="startDate" type="date" placeholder="Start" class="w-full sm:w-auto" />
232259
<span class="text-gray-400">-</span>
233-
<UInput
234-
v-model="endDate"
235-
type="date"
236-
placeholder="End"
237-
class="w-full sm:w-auto"
238-
/>
260+
<UInput v-model="endDate" type="date" placeholder="End" class="w-full sm:w-auto" />
239261
</div>
240262
</div>
241263

@@ -247,104 +269,117 @@
247269
@select="onSelect"
248270
/>
249271

250-
<!-- Create Ride Modal -->
251-
<UModal v-model:open="isCreateModalOpen" title="Create New Ride">
252-
<template #content>
253-
<div class="max-h-[70vh] overflow-y-auto p-4">
254-
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
255-
<UFormField label="Client" name="clientId">
256-
<USelect
257-
v-model="state.clientId"
258-
:items="clients?.map((c) => ({ label: c.user.name, value: c.id })) || []"
259-
placeholder="Select a client"
260-
class="w-full"
261-
/>
262-
</UFormField>
263-
264-
<div class="rounded-lg border p-4 space-y-2 dark:border-gray-700">
265-
<h3 class="font-bold text-sm text-gray-700 dark:text-gray-300">Pickup Address</h3>
266-
267-
<!-- Custom Autocomplete -->
268-
<div class="relative mb-2">
269-
<UInput
270-
v-model="pickupSearch"
271-
placeholder="Type to find existing address (e.g. Street)..."
272-
icon="i-lucide-search"
273-
autocomplete="off"
274-
/>
275-
<div
276-
v-if="pickupOptions?.length > 0 && pickupSearch"
277-
class="absolute z-10 mt-1 w-full max-h-60 overflow-auto rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800"
278-
>
279-
<button
280-
v-for="opt in pickupOptions"
281-
:key="opt.id"
282-
type="button"
283-
class="block w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700"
284-
@click="onPickupSelect(opt)"
285-
>
286-
{{ opt.label }}
287-
</button>
288-
</div>
289-
</div>
290-
291-
<UFormField label="Street" name="pickup.street">
292-
<UInput v-model="state.pickup.street" placeholder="Street Address" />
293-
</UFormField> <div class="grid grid-cols-3 gap-2">
294-
<UFormField label="City" name="pickup.city"><UInput v-model="state.pickup.city" placeholder="City" /></UFormField>
295-
<UFormField label="State" name="pickup.state"><UInput v-model="state.pickup.state" placeholder="State" /></UFormField>
296-
<UFormField label="Zip" name="pickup.zip"><UInput v-model="state.pickup.zip" placeholder="Zip" /></UFormField>
297-
</div>
298-
</div>
299-
300-
<div class="rounded-lg border p-4 space-y-2 dark:border-gray-700">
301-
<h3 class="font-bold text-sm text-gray-700 dark:text-gray-300">Dropoff Address</h3>
302-
303-
<!-- Custom Autocomplete -->
304-
<div class="relative mb-2">
305-
<UInput
306-
v-model="dropoffSearch"
307-
placeholder="Type to find existing address (e.g. Street)..."
308-
icon="i-lucide-search"
309-
autocomplete="off"
272+
<!-- Create Ride Modal -->
273+
<UModal v-model:open="isCreateModalOpen" title="Create New Ride">
274+
<template #content>
275+
<div class="max-h-[70vh] overflow-y-auto p-4">
276+
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
277+
<UFormField label="Client" name="clientId">
278+
<USelect
279+
v-model="state.clientId"
280+
:items="clients?.map((c) => ({ label: c.user.name, value: c.id })) || []"
281+
placeholder="Select a client"
282+
class="w-full"
310283
/>
311-
<div
312-
v-if="dropoffOptions?.length > 0 && dropoffSearch"
313-
class="absolute z-10 mt-1 w-full max-h-60 overflow-auto rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800"
314-
>
315-
<button
316-
v-for="opt in dropoffOptions"
317-
:key="opt.id"
318-
type="button"
319-
class="block w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700"
320-
@click="onDropoffSelect(opt)"
284+
</UFormField>
285+
286+
<div class="space-y-2 rounded-lg border p-4 dark:border-gray-700">
287+
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300">Pickup Address</h3>
288+
289+
<!-- Custom Autocomplete -->
290+
<div class="relative mb-2">
291+
<UInput
292+
v-model="pickupSearch"
293+
placeholder="Type to find existing address (e.g. Street)..."
294+
icon="i-lucide-search"
295+
autocomplete="off"
296+
/>
297+
<div
298+
v-if="pickupOptions?.length > 0 && pickupSearch"
299+
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800"
321300
>
322-
{{ opt.label }}
323-
</button>
301+
<button
302+
v-for="opt in pickupOptions"
303+
:key="opt.id"
304+
type="button"
305+
class="block w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700"
306+
@click="onPickupSelect(opt)"
307+
>
308+
{{ opt.label }}
309+
</button>
310+
</div>
311+
</div>
312+
313+
<UFormField label="Street" name="pickup.street">
314+
<UInput v-model="state.pickup.street" placeholder="Street Address" />
315+
</UFormField>
316+
<div class="grid grid-cols-3 gap-2">
317+
<UFormField label="City" name="pickup.city"
318+
><UInput v-model="state.pickup.city" placeholder="City"
319+
/></UFormField>
320+
<UFormField label="State" name="pickup.state"
321+
><UInput v-model="state.pickup.state" placeholder="State"
322+
/></UFormField>
323+
<UFormField label="Zip" name="pickup.zip"
324+
><UInput v-model="state.pickup.zip" placeholder="Zip"
325+
/></UFormField>
324326
</div>
325327
</div>
326328

327-
<UFormField label="Street" name="dropoff.street">
328-
<UInput v-model="state.dropoff.street" placeholder="Street Address" />
329-
</UFormField>
330-
<div class="grid grid-cols-3 gap-2">
331-
<UFormField label="City" name="dropoff.city"><UInput v-model="state.dropoff.city" placeholder="City" /></UFormField>
332-
<UFormField label="State" name="dropoff.state"><UInput v-model="state.dropoff.state" placeholder="State" /></UFormField>
333-
<UFormField label="Zip" name="dropoff.zip"><UInput v-model="state.dropoff.zip" placeholder="Zip" /></UFormField>
329+
<div class="space-y-2 rounded-lg border p-4 dark:border-gray-700">
330+
<h3 class="text-sm font-bold text-gray-700 dark:text-gray-300">Dropoff Address</h3>
331+
332+
<!-- Custom Autocomplete -->
333+
<div class="relative mb-2">
334+
<UInput
335+
v-model="dropoffSearch"
336+
placeholder="Type to find existing address (e.g. Street)..."
337+
icon="i-lucide-search"
338+
autocomplete="off"
339+
/>
340+
<div
341+
v-if="dropoffOptions?.length > 0 && dropoffSearch"
342+
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800"
343+
>
344+
<button
345+
v-for="opt in dropoffOptions"
346+
:key="opt.id"
347+
type="button"
348+
class="block w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700"
349+
@click="onDropoffSelect(opt)"
350+
>
351+
{{ opt.label }}
352+
</button>
353+
</div>
354+
</div>
355+
356+
<UFormField label="Street" name="dropoff.street">
357+
<UInput v-model="state.dropoff.street" placeholder="Street Address" />
358+
</UFormField>
359+
<div class="grid grid-cols-3 gap-2">
360+
<UFormField label="City" name="dropoff.city"
361+
><UInput v-model="state.dropoff.city" placeholder="City"
362+
/></UFormField>
363+
<UFormField label="State" name="dropoff.state"
364+
><UInput v-model="state.dropoff.state" placeholder="State"
365+
/></UFormField>
366+
<UFormField label="Zip" name="dropoff.zip"
367+
><UInput v-model="state.dropoff.zip" placeholder="Zip"
368+
/></UFormField>
369+
</div>
334370
</div>
335-
</div>
336-
337-
<UFormField label="Scheduled Time" name="scheduledTime">
338-
<UInput v-model="state.scheduledTime" type="datetime-local" class="w-full" />
339-
</UFormField>
340-
341-
<UFormField label="Notes" name="notes">
342-
<UTextarea
343-
v-model="state.notes"
344-
placeholder="Additional instructions..."
345-
class="w-full"
346-
/>
347-
</UFormField>
371+
372+
<UFormField label="Scheduled Time" name="scheduledTime">
373+
<UInput v-model="state.scheduledTime" type="datetime-local" class="w-full" />
374+
</UFormField>
375+
376+
<UFormField label="Notes" name="notes">
377+
<UTextarea
378+
v-model="state.notes"
379+
placeholder="Additional instructions..."
380+
class="w-full"
381+
/>
382+
</UFormField>
348383

349384
<div class="flex justify-end gap-2 pt-4">
350385
<UButton

0 commit comments

Comments
 (0)