feat(29-02): update detail pages and LinkToGlobalItem to use GearImage

Replace object-cover on item detail, global item detail, candidate
detail, global items index, and LinkToGlobalItem. Detail pages use
dominant color backgrounds. LinkToGlobalItem uses cover mode for
32px thumbnails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-12 20:02:12 +02:00
parent febc43a074
commit 66d9c4157b
5 changed files with 61 additions and 20 deletions

View File

@@ -1,5 +1,6 @@
import { Link } from "@tanstack/react-router";
import { useEffect, useState } from "react";
import { GearImage } from "./GearImage";
import {
useGlobalItem,
useGlobalItems,
@@ -177,11 +178,13 @@ export function LinkToGlobalItem({
className="w-full text-left px-3 py-2 hover:bg-gray-50 transition-colors flex items-center gap-2"
>
{item.imageUrl ? (
<img
src={item.imageUrl}
alt=""
className="w-8 h-8 rounded object-cover shrink-0"
/>
<div className="w-8 h-8 rounded overflow-hidden shrink-0">
<GearImage
src={item.imageUrl}
alt=""
cover
/>
</div>
) : (
<div className="w-8 h-8 rounded bg-gray-100 shrink-0" />
)}

View File

@@ -1,4 +1,5 @@
import { createFileRoute, Link } from "@tanstack/react-router";
import { GearImage, imageContainerBg } from "../../components/GearImage";
import { useAuth } from "../../hooks/useAuth";
import { useFormatters } from "../../hooks/useFormatters";
import { useGlobalItem } from "../../hooks/useGlobalItems";
@@ -62,15 +63,25 @@ function GlobalItemDetail() {
</div>
{/* Image */}
<div className="aspect-[16/9] bg-gray-50 rounded-xl overflow-hidden">
<div
className="aspect-[16/9] rounded-xl overflow-hidden"
style={{
backgroundColor: item.imageUrl
? imageContainerBg(item.dominantColor)
: undefined,
}}
>
{item.imageUrl ? (
<img
<GearImage
src={item.imageUrl}
alt={`${item.brand} ${item.model}`}
className="w-full h-full object-cover"
dominantColor={item.dominantColor}
cropZoom={item.cropZoom}
cropX={item.cropX}
cropY={item.cropY}
/>
) : (
<div className="w-full h-full flex flex-col items-center justify-center">
<div className="w-full h-full bg-gray-50 flex flex-col items-center justify-center">
<svg
className="w-16 h-16 text-gray-300"
fill="none"

View File

@@ -2,6 +2,7 @@ import { createFileRoute, Link } from "@tanstack/react-router";
import { ArrowLeft, LayoutGrid, LayoutList, X } from "lucide-react";
import { useEffect, useMemo, useRef, useState } from "react";
import { z } from "zod";
import { GearImage } from "../../components/GearImage";
import { GlobalItemCard } from "../../components/GlobalItemCard";
import { useFormatters } from "../../hooks/useFormatters";
import { useGlobalItems } from "../../hooks/useGlobalItems";
@@ -537,15 +538,21 @@ function GlobalItemListRow({ item, weight, price }: ListRowProps) {
className="bg-white rounded-xl border border-gray-100 flex items-center gap-4 px-4 py-3 hover:border-gray-200 hover:shadow-sm transition-all block"
>
{/* Thumbnail */}
<div className="w-12 h-12 rounded-lg bg-gray-50 shrink-0 overflow-hidden">
<div
className="w-12 h-12 rounded-lg shrink-0 overflow-hidden"
style={{
backgroundColor: item.imageUrl
? (item.dominantColor as string) || "#f3f4f6"
: undefined,
}}
>
{item.imageUrl ? (
<img
<GearImage
src={item.imageUrl}
alt={`${item.brand} ${item.model}`}
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full flex items-center justify-center">
<div className="w-full h-full bg-gray-50 flex items-center justify-center">
<svg
className="w-5 h-5 text-gray-300"
fill="none"

View File

@@ -1,6 +1,7 @@
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { useState } from "react";
import { CategoryPicker } from "../../components/CategoryPicker";
import { GearImage, imageContainerBg } from "../../components/GearImage";
import { ImageUpload } from "../../components/ImageUpload";
import { useFormatters } from "../../hooks/useFormatters";
import { useDuplicateItem, useItem, useUpdateItem } from "../../hooks/useItems";
@@ -242,15 +243,25 @@ function ItemDetail() {
/>
</div>
) : (
<div className="aspect-[4/3] bg-gray-50 rounded-xl overflow-hidden mb-6">
<div
className="aspect-[4/3] rounded-xl overflow-hidden mb-6"
style={{
backgroundColor: imageUrl
? imageContainerBg(item.dominantColor)
: undefined,
}}
>
{imageUrl ? (
<img
<GearImage
src={imageUrl}
alt={item.name}
className="w-full h-full object-cover"
dominantColor={item.dominantColor}
cropZoom={item.cropZoom}
cropX={item.cropX}
cropY={item.cropY}
/>
) : (
<div className="w-full h-full flex flex-col items-center justify-center">
<div className="w-full h-full bg-gray-50 flex flex-col items-center justify-center">
<LucideIcon
name={item.categoryIcon}
size={64}

View File

@@ -1,6 +1,7 @@
import { createFileRoute, Link } from "@tanstack/react-router";
import { useState } from "react";
import { CategoryPicker } from "../../../../components/CategoryPicker";
import { GearImage, imageContainerBg } from "../../../../components/GearImage";
import { ImageUpload } from "../../../../components/ImageUpload";
import { StatusBadge } from "../../../../components/StatusBadge";
import { useUpdateCandidate } from "../../../../hooks/useCandidates";
@@ -204,11 +205,19 @@ function CandidateDetailPage() {
/>
</div>
) : imageUrl ? (
<div className="aspect-[16/9] bg-gray-50 rounded-xl overflow-hidden mb-6">
<img
<div
className="aspect-[16/9] rounded-xl overflow-hidden mb-6"
style={{
backgroundColor: imageContainerBg(candidate.dominantColor),
}}
>
<GearImage
src={imageUrl}
alt={candidate.name}
className="w-full h-full object-cover"
dominantColor={candidate.dominantColor}
cropZoom={candidate.cropZoom}
cropX={candidate.cropX}
cropY={candidate.cropY}
/>
</div>
) : null}