feat(web): make vehicle info card a collapsible accordion
Collapse the Araç Bilgileri card by default. The always-visible header now shows an at-a-glance summary (Model, Model yılı, Motor kodu); expanding reveals the full attribute grid, which also gains the decoded drive type (Çekiş / "Önden çekiş") from rawData. The loading skeleton now matches the collapsed header so there's no layout jump when data lands. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,16 @@ import { CategoryViewToggle } from "@/components/categories/category-view-toggle
|
||||
import { CarBrandLogo } from "@/components/ui/car-brand-logo";
|
||||
import { ApiError, api } from "@/lib/api-client";
|
||||
import { getUserSettings, setUserSetting } from "@/lib/user-settings";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@sase/ui";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@sase/ui";
|
||||
import { Skeleton } from "@sase/ui";
|
||||
import { Button } from "@sase/ui";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
@@ -13,7 +22,7 @@ import { createFileRoute, useCanGoBack, useNavigate, useRouter } from "@tanstack
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { KEYS_6, KEYS_8 } from "@/lib/keys";
|
||||
import { KEYS_8 } from "@/lib/keys";
|
||||
export const Route = createFileRoute("/dashboard/vehicles_/$id/")({
|
||||
component: VehicleDetailPage,
|
||||
});
|
||||
@@ -74,17 +83,10 @@ function VehicleDetailPage() {
|
||||
<Skeleton className="h-4 w-40" />
|
||||
</div>
|
||||
</div>
|
||||
{/* Vehicle info card skeleton: 4-cell attribute grid */}
|
||||
<div className="rounded-lg border border-border p-6">
|
||||
<Skeleton className="mb-4 h-5 w-32" />
|
||||
<div className="grid grid-cols-2 gap-x-6 gap-y-4 sm:grid-cols-3">
|
||||
{KEYS_6.map((__k) => (
|
||||
<div key={__k} className="flex flex-col gap-1.5">
|
||||
<Skeleton className="h-3 w-20" />
|
||||
<Skeleton className="h-4 w-28" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Vehicle info card skeleton: collapsed accordion header */}
|
||||
<div className="rounded-xl border border-border px-6 py-4">
|
||||
<Skeleton className="mb-2 h-5 w-32" />
|
||||
<Skeleton className="h-3 w-64" />
|
||||
</div>
|
||||
{/* Categories skeleton: 8 rows */}
|
||||
<div className="rounded-lg border border-border p-6">
|
||||
@@ -179,14 +181,22 @@ function VehicleDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Vehicle Info */}
|
||||
{/* Vehicle Info — collapsed by default; the trigger shows an at-a-glance
|
||||
summary, expanding reveals the full attribute + equipment detail. */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Araç Bilgileri</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<VehicleAttributes vehicle={vehicle} />
|
||||
</CardContent>
|
||||
<Accordion type="single" collapsible>
|
||||
<AccordionItem value="info" className="border-b-0">
|
||||
<AccordionTrigger className="px-6 hover:no-underline">
|
||||
<div className="flex flex-1 flex-col items-start gap-1.5 pr-3 text-left">
|
||||
<span className="text-base font-semibold">Araç Bilgileri</span>
|
||||
<VehicleSummary vehicle={vehicle} />
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="px-6 pb-6">
|
||||
<VehicleAttributes vehicle={vehicle} />
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</Card>
|
||||
|
||||
{/* Categories */}
|
||||
@@ -263,6 +273,7 @@ function VehicleAttributes({ vehicle }: { vehicle: any }) {
|
||||
// decoded codes follow — instead of the codes hiding the readable data.
|
||||
const readableExtras = [
|
||||
{ label: "Motor", value: vehicle?.engine },
|
||||
{ label: "Çekiş", value: vehicle?.rawData?.driveType },
|
||||
{ label: "Kasa", value: vehicle?.bodyType },
|
||||
].filter((a): a is { label: string; value: string } => Boolean(a.value));
|
||||
|
||||
@@ -304,3 +315,24 @@ function VehicleAttributes({ vehicle }: { vehicle: any }) {
|
||||
</dl>
|
||||
);
|
||||
}
|
||||
|
||||
/** Compact at-a-glance fields shown on the collapsed Araç Bilgileri header. */
|
||||
function VehicleSummary({ vehicle }: { vehicle: any }) {
|
||||
const fields = [
|
||||
{ label: "Model", value: vehicle?.model },
|
||||
{ label: "Model yılı", value: vehicle?.year },
|
||||
{ label: "Motor kodu", value: vehicle?.rawData?.engineCode },
|
||||
].filter((f) => f.value != null && f.value !== "");
|
||||
|
||||
if (fields.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs font-normal">
|
||||
{fields.map((f) => (
|
||||
<span key={f.label} className="text-muted-foreground">
|
||||
{f.label}: <span className="font-medium tabular-nums text-foreground">{f.value}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user