feat: Add track material column and filtering

This commit is contained in:
gpt-engineer-app[bot]
2025-10-16 19:54:22 +00:00
parent 294019f7bd
commit f176c28df6
8 changed files with 185 additions and 1 deletions

View File

@@ -618,6 +618,26 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>Track Material</Label>
<Select
onValueChange={(value) => setValue('track_material', value === '' ? undefined : value as 'wood' | 'steel' | 'hybrid' | 'aluminum' | 'other')}
defaultValue={initialData?.track_material || ''}
>
<SelectTrigger>
<SelectValue placeholder="Select track material" />
</SelectTrigger>
<SelectContent>
<SelectItem value="">None</SelectItem>
<SelectItem value="wood">Wood</SelectItem>
<SelectItem value="steel">Steel</SelectItem>
<SelectItem value="hybrid">Hybrid (Wood/Steel)</SelectItem>
<SelectItem value="aluminum">Aluminum</SelectItem>
<SelectItem value="other">Other</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">

View File

@@ -42,6 +42,10 @@ export function RideCreditFilters({
const cities = new Set<string>();
const parks = new Map<string, string>();
const manufacturers = new Map<string, string>();
const coasterTypes = new Set<string>();
const seatingTypes = new Set<string>();
const intensityLevels = new Set<string>();
const trackMaterials = new Set<string>();
credits.forEach(credit => {
const location = credit.rides?.parks?.locations;
@@ -56,6 +60,22 @@ export function RideCreditFilters({
if (credit.rides?.manufacturer?.id && credit.rides?.manufacturer?.name) {
manufacturers.set(credit.rides.manufacturer.id, credit.rides.manufacturer.name);
}
if (credit.rides?.coaster_type) {
coasterTypes.add(credit.rides.coaster_type);
}
if (credit.rides?.seating_type) {
seatingTypes.add(credit.rides.seating_type);
}
if (credit.rides?.intensity_level) {
intensityLevels.add(credit.rides.intensity_level);
}
if (credit.rides?.track_material) {
trackMaterials.add(credit.rides.track_material);
}
});
return {
@@ -64,6 +84,10 @@ export function RideCreditFilters({
cities: Array.from(cities).sort(),
parks: Array.from(parks.entries()).map(([id, name]) => ({ id, name })).sort((a, b) => a.name.localeCompare(b.name)),
manufacturers: Array.from(manufacturers.entries()).map(([id, name]) => ({ id, name })).sort((a, b) => a.name.localeCompare(b.name)),
coasterTypes: Array.from(coasterTypes).sort(),
seatingTypes: Array.from(seatingTypes).sort(),
intensityLevels: Array.from(intensityLevels).sort(),
trackMaterials: Array.from(trackMaterials).sort(),
};
}, [credits]);
@@ -295,6 +319,86 @@ export function RideCreditFilters({
</div>
)}
{filterOptions.coasterTypes.length > 0 && (
<div className={sectionSpacing}>
<Label className="text-sm font-medium">Coaster Type</Label>
<MultiSelectCombobox
options={filterOptions.coasterTypes.map(type => ({
label: type.charAt(0).toUpperCase() + type.slice(1),
value: type
}))}
value={filters.coasterTypes || []}
onValueChange={(values) =>
onFilterChange('coasterTypes', values.length > 0 ? values : undefined)
}
placeholder="Select coaster types..."
searchPlaceholder="Search types..."
emptyText="No coaster types found"
className={compact ? "h-9 text-sm" : ""}
/>
</div>
)}
{filterOptions.seatingTypes.length > 0 && (
<div className={sectionSpacing}>
<Label className="text-sm font-medium">Seating Type</Label>
<MultiSelectCombobox
options={filterOptions.seatingTypes.map(type => ({
label: type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()),
value: type
}))}
value={filters.seatingTypes || []}
onValueChange={(values) =>
onFilterChange('seatingTypes', values.length > 0 ? values : undefined)
}
placeholder="Select seating types..."
searchPlaceholder="Search types..."
emptyText="No seating types found"
className={compact ? "h-9 text-sm" : ""}
/>
</div>
)}
{filterOptions.intensityLevels.length > 0 && (
<div className={sectionSpacing}>
<Label className="text-sm font-medium">Intensity Level</Label>
<MultiSelectCombobox
options={filterOptions.intensityLevels.map(level => ({
label: level.charAt(0).toUpperCase() + level.slice(1),
value: level
}))}
value={filters.intensityLevels || []}
onValueChange={(values) =>
onFilterChange('intensityLevels', values.length > 0 ? values : undefined)
}
placeholder="Select intensity levels..."
searchPlaceholder="Search intensity..."
emptyText="No intensity levels found"
className={compact ? "h-9 text-sm" : ""}
/>
</div>
)}
{filterOptions.trackMaterials.length > 0 && (
<div className={sectionSpacing}>
<Label className="text-sm font-medium">Track Material</Label>
<MultiSelectCombobox
options={filterOptions.trackMaterials.map(tm => ({
label: tm.charAt(0).toUpperCase() + tm.slice(1),
value: tm
}))}
value={filters.trackMaterial || []}
onValueChange={(values) =>
onFilterChange('trackMaterial', values.length > 0 ? values : undefined)
}
placeholder="Select track materials..."
searchPlaceholder="Search materials..."
emptyText="No track materials found"
className={compact ? "h-9 text-sm" : ""}
/>
</div>
)}
<div className="space-y-2">
<Label>Inversions</Label>
<div className="flex items-center gap-2">