8 Commits

Author SHA1 Message Date
6de7839107 revert(build): drop ineffective per-commit cache-bust (#219)
SOURCE_COMMIT isn't substituted into docker-compose build args by Coolify
(resolved to a constant "unknown"), so the cache-bust never fired. Removing the
dead ARG/RUN + compose arg. Web-only deploys use a Coolify force-rebuild instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 08:13:44 +03:00
edcd168d48 Merge pull request 'fix(build): per-commit cache-bust for web-only deploys' (#219) from fix/build-cache-web into dev 2026-07-14 07:43:07 +03:00
c20fc5462a fix(build): per-commit cache-bust so web-only changes actually deploy
Auto-deploys (docker build with cache) were reusing a stale build layer, so
web-only commits shipped an old bundle — the switcher removal (#216) and the
canonical fix (#217) only appeared after a manual --no-cache/force rebuild.
Reference SOURCE_COMMIT (substituted by Coolify) right before `COPY . .`, so the
build layer's cache key changes every commit and COPY + `pnpm build` re-run. The
deps stage stays cached on the lockfile, so installs aren't repeated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 07:43:05 +03:00
2bf7525fe2 Merge pull request 'fix(catalog): Fiat Birleşik = canonical view (not raw pcat)' (#217) from feat/fiat-birlesik-canonical into dev
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
2026-07-14 07:11:53 +03:00
bf49900b57 fix(catalog): Fiat "Birleşik" stays the canonical view, not the raw pcat tree
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Correction to #216: "Birleşik" must fold the catalog into OUR canonical 20-bucket
structure (pcat + eper), not show parts-catalogs' raw tree. Only "Orijinal
Katalog" swaps to the native ePER tree for Fiat.

- treeSource → eper-pekidi for Fiat (Orijinal); canonical tab renders CanonicalView
- reverts #216's "canonical tab → pcat source tree"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 07:11:50 +03:00
ff96dcac1c Merge pull request 'feat(catalog): Fiat tabs → eper/pcat, drop extra switcher' (#216) from feat/fiat-eper-simplify into dev 2026-07-14 06:56:14 +03:00
5a3e1a2299 feat(catalog): map the two Fiat catalog tabs onto eper/pcat, drop the extra switcher
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Simplify the previous change: instead of a third source-switcher pill, reuse the
two tabs that are already there. For an eper (Fiat) vehicle:
  "Orijinal Katalog" → the native ePER tree (default)
  "Birleşik"         → parts-catalogs
Non-eper vehicles keep the original source/canonical tabs untouched.

- web: derive treeSource from browseMode when the vehicle has eper; canonical
  tab renders the pcat source tree (not CanonicalView) for eper vehicles
- removes the ePER/Diğer switcher, SOURCE_LABELS, and the activeSource state
Net −27 lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 06:56:12 +03:00
24495d8970 Merge pull request 'feat(catalog): eper as default Fiat catalog source (pcat switchable)' (#215) from feat/fiat-eper-default into dev 2026-07-14 06:42:06 +03:00

View File

@@ -46,15 +46,6 @@ export const Route = createFileRoute("/dashboard/vehicles_/$id/")({
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
// Short labels for the per-vehicle source switcher (eper-pekidi = the native ePER
// catalog; parts-catalogs = the broader fallback).
const SOURCE_LABELS: Record<string, string> = {
"eper-pekidi": "ePER",
"parts-catalogs": "Diğer",
emex: "Emex",
pl24: "PL24",
};
function VehicleDetailPage() { function VehicleDetailPage() {
const { t } = useTranslation(); const { t } = useTranslation();
const { id } = Route.useParams(); const { id } = Route.useParams();
@@ -73,10 +64,6 @@ function VehicleDetailPage() {
// drill fills them ("Aydınlatma — 0 kategori") — stays source-default until // drill fills them ("Aydınlatma — 0 kategori") — stays source-default until
// per-bucket empty-state/drill-through UX is solved. // per-bucket empty-state/drill-through UX is solved.
const [browseMode, setBrowseMode] = useState<"source" | "canonical">("source"); const [browseMode, setBrowseMode] = useState<"source" | "canonical">("source");
// Which per-source catalog "source" mode shows. Defaults to the vehicle's
// primary source — eper-pekidi first, the native ePER tree Fiat dealers know
// (fully populated + diagrams) over parts-catalogs' broader-but-lazy folders.
const [activeSource, setActiveSource] = useState<string | null>(null);
const changeViewMode = (mode: "grid" | "tree" | "columns") => { const changeViewMode = (mode: "grid" | "tree" | "columns") => {
setViewMode(mode); setViewMode(mode);
@@ -111,24 +98,23 @@ function VehicleDetailPage() {
retry: (count, err) => !(err instanceof ApiError && err.status === 404) && count < 2, retry: (count, err) => !(err instanceof ApiError && err.status === 404) && count < 2,
}); });
// Fiat (any eper) vehicle: "Orijinal Katalog" shows the native ePER tree
// dealers know; "Birleşik" stays the canonical 20-bucket view (which folds the
// catalog into OUR structure), NOT the raw pcat tree. Non-eper vehicles keep
// the default source tree (treeSource undefined).
const { data: catalogSources } = useQuery({ const { data: catalogSources } = useQuery({
queryKey: ["catalog-sources", id], queryKey: ["catalog-sources", id],
queryFn: () => api.get<string[]>(`/categories/tree/${id}/sources`), queryFn: () => api.get<string[]>(`/categories/tree/${id}/sources`),
enabled: !!id && idValid, enabled: !!id && idValid,
}); });
// Once the source list is known, default the active source to the first const hasEper = catalogSources?.includes("eper-pekidi") ?? false;
// (eper-pekidi when present). Never override a source the user picked. const treeSource = hasEper ? "eper-pekidi" : undefined;
useEffect(() => {
if (catalogSources && catalogSources.length > 0 && activeSource === null) {
setActiveSource(catalogSources[0]);
}
}, [catalogSources, activeSource]);
const { data: categoryTree, isLoading: categoriesLoading } = useQuery({ const { data: categoryTree, isLoading: categoriesLoading } = useQuery({
queryKey: ["category-tree", id, activeSource], queryKey: ["category-tree", id, treeSource],
queryFn: () => queryFn: () =>
api.get<CategoryNode[]>( api.get<CategoryNode[]>(
`/categories/tree/${id}${activeSource ? `?source=${encodeURIComponent(activeSource)}` : ""}`, `/categories/tree/${id}${treeSource ? `?source=${encodeURIComponent(treeSource)}` : ""}`,
), ),
enabled: !!id && idValid, enabled: !!id && idValid,
}); });
@@ -368,23 +354,6 @@ function VehicleDetailPage() {
Orijinal Katalog Orijinal Katalog
</button> </button>
</div> </div>
{browseMode === "source" && (catalogSources?.length ?? 0) > 1 && (
<div className="inline-flex rounded-md border border-border p-0.5">
{catalogSources?.map((src) => (
<button
key={src}
type="button"
onClick={() => {
setActiveSource(src);
capture("catalog_source_changed", { vehicle_id: id, source: src });
}}
className={`rounded px-2.5 py-1 text-xs font-medium transition-colors ${activeSource === src ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"}`}
>
{SOURCE_LABELS[src] ?? src}
</button>
))}
</div>
)}
<CategoryViewToggle value={viewMode} onChange={changeViewMode} /> <CategoryViewToggle value={viewMode} onChange={changeViewMode} />
</div> </div>
)} )}