styles all fixed

This commit is contained in:
2025-06-29 14:07:26 -04:00
parent 14b25e7359
commit 64f288d8f7
5 changed files with 465 additions and 81 deletions

View File

@@ -331,27 +331,15 @@ export default function BuildPage() {
{/* Search and Filters */}
<div className="bg-white border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
{/* Search Row */}
<div className="mb-4 flex justify-end">
<div className="w-1/2">
<SearchInput
label="Search Components"
value={searchTerm}
onChange={setSearchTerm}
placeholder="Search components..."
/>
</div>
</div>
{/* Filters Row */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 !bg-white">
{/* Category Dropdown */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Category</label>
<select
value={selectedCategory}
onChange={(e) => setSelectedCategory(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900"
>
{categories.map((category) => (
<option key={category} value={category}>
@@ -364,7 +352,7 @@ export default function BuildPage() {
{/* Status Filter */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Status</label>
<select className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<select className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900">
<option value="all">All Status</option>
<option value="pending">Pending</option>
<option value="in-progress">In Progress</option>
@@ -378,7 +366,7 @@ export default function BuildPage() {
<select
value={sortField}
onChange={(e) => handleSort(e.target.value as SortField)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900"
>
<option value="name">Name</option>
<option value="category">Category</option>
@@ -521,14 +509,11 @@ export default function BuildPage() {
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div className="flex space-x-2">
<Link
href={`/?category=${encodeURIComponent(component.category)}`}
className="bg-blue-600 text-white py-1 px-3 rounded text-xs hover:bg-blue-700 transition-colors"
href={`/parts?category=${encodeURIComponent(component.category)}`}
className="bg-[#4B6516] text-white py-1 px-3 rounded text-xs hover:bg-[#3a4e12] transition-colors"
>
Find Parts
</Link>
<button className="bg-gray-100 text-gray-700 py-1 px-2 rounded text-xs hover:bg-gray-200 transition-colors">
</button>
</div>
</td>
</tr>

View File

@@ -4,7 +4,7 @@ import { useState } from 'react';
import SearchInput from '@/components/SearchInput';
// Sample build data
const sampleBuilds = [
const sampleMyBuilds = [
{
id: '1',
name: 'Budget AR-15 Build',
@@ -121,18 +121,18 @@ const sampleBuilds = [
}
];
type BuildStatus = 'completed' | 'in-progress' | 'planning';
type MyBuildStatus = 'completed' | 'in-progress' | 'planning';
type SortField = 'name' | 'status' | 'totalCost' | 'completedDate';
type SortDirection = 'asc' | 'desc';
export default function BuildsPage() {
export default function MyBuildsPage() {
const [sortField, setSortField] = useState<SortField>('completedDate');
const [sortDirection, setSortDirection] = useState<SortDirection>('desc');
const [selectedStatus, setSelectedStatus] = useState<BuildStatus | 'all'>('all');
const [selectedStatus, setSelectedStatus] = useState<MyBuildStatus | 'all'>('all');
const [searchTerm, setSearchTerm] = useState('');
// Filter builds
const filteredBuilds = sampleBuilds.filter(build => {
const filteredMyBuilds = sampleMyBuilds.filter(build => {
if (selectedStatus !== 'all' && build.status !== selectedStatus) {
return false;
}
@@ -146,7 +146,7 @@ export default function BuildsPage() {
});
// Sort builds
const sortedBuilds = [...filteredBuilds].sort((a, b) => {
const sortedMyBuilds = [...filteredMyBuilds].sort((a, b) => {
let aValue: any, bValue: any;
if (sortField === 'totalCost') {
@@ -170,7 +170,7 @@ export default function BuildsPage() {
}
});
const getStatusColor = (status: BuildStatus) => {
const getStatusColor = (status: MyBuildStatus) => {
switch (status) {
case 'completed':
return 'bg-green-100 text-green-800';
@@ -183,7 +183,7 @@ export default function BuildsPage() {
}
};
const getStatusIcon = (status: BuildStatus) => {
const getStatusIcon = (status: MyBuildStatus) => {
switch (status) {
case 'completed':
return '✓';
@@ -204,14 +204,14 @@ export default function BuildsPage() {
});
};
const getProgressPercentage = (build: typeof sampleBuilds[0]) => {
const getProgressPercentage = (build: typeof sampleMyBuilds[0]) => {
return Math.round((build.components.completed / build.components.total) * 100);
};
const totalBuilds = sampleBuilds.length;
const completedBuilds = sampleBuilds.filter(build => build.status === 'completed').length;
const inProgressBuilds = sampleBuilds.filter(build => build.status === 'in-progress').length;
const totalValue = sampleBuilds.reduce((sum, build) => sum + build.totalCost, 0);
const totalMyBuilds = sampleMyBuilds.length;
const completedMyBuilds = sampleMyBuilds.filter(build => build.status === 'completed').length;
const inProgressMyBuilds = sampleMyBuilds.filter(build => build.status === 'in-progress').length;
const totalValue = sampleMyBuilds.reduce((sum, build) => sum + build.totalCost, 0);
return (
<main className="min-h-screen bg-gray-50">
@@ -228,15 +228,15 @@ export default function BuildsPage() {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="text-center">
<div className="text-2xl font-bold text-gray-900">{totalBuilds}</div>
<div className="text-2xl font-bold text-gray-900">{totalMyBuilds}</div>
<div className="text-sm text-gray-500">Total Builds</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-green-600">{completedBuilds}</div>
<div className="text-2xl font-bold text-green-600">{completedMyBuilds}</div>
<div className="text-sm text-gray-500">Completed</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-blue-600">{inProgressBuilds}</div>
<div className="text-2xl font-bold text-blue-600">{inProgressMyBuilds}</div>
<div className="text-sm text-gray-500">In Progress</div>
</div>
<div className="text-center">
@@ -254,10 +254,10 @@ export default function BuildsPage() {
<div className="mb-4 flex justify-end">
<div className="w-1/2">
<SearchInput
label="Search Builds"
label="Search My Builds"
value={searchTerm}
onChange={setSearchTerm}
placeholder="Search builds..."
placeholder="Search my builds..."
/>
</div>
</div>
@@ -269,7 +269,7 @@ export default function BuildsPage() {
<label className="block text-sm font-medium text-gray-700 mb-1">Status</label>
<select
value={selectedStatus}
onChange={(e) => setSelectedStatus(e.target.value as BuildStatus | 'all')}
onChange={(e) => setSelectedStatus(e.target.value as MyBuildStatus | 'all')}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="all">All Status</option>
@@ -319,9 +319,9 @@ export default function BuildsPage() {
{/* Builds Grid */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{sortedBuilds.length > 0 ? (
{sortedMyBuilds.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{sortedBuilds.map((build) => (
{sortedMyBuilds.map((build) => (
<div key={build.id} className="bg-white rounded-lg shadow-sm border hover:shadow-md transition-shadow overflow-hidden">
{/* Build Image */}
<div className="h-48 bg-gray-200 relative">

417
src/app/my-builds/page.tsx Normal file
View File

@@ -0,0 +1,417 @@
'use client';
import { useState } from 'react';
import SearchInput from '@/components/SearchInput';
// Sample build data
const sampleMyBuilds = [
{
id: '1',
name: 'Budget AR-15 Build',
description: 'A cost-effective AR-15 build using quality budget components',
status: 'completed' as const,
totalCost: 847.50,
completedDate: '2024-01-15',
components: {
total: 18,
completed: 18,
categories: {
'Upper': 8,
'Lower': 7,
'Accessory': 3
}
},
tags: ['Budget', '5.56 NATO', '16" Barrel'],
image: 'https://picsum.photos/400/250?random=1'
},
{
id: '2',
name: 'Precision Long Range',
description: 'High-end precision build optimized for long-range accuracy',
status: 'in-progress' as const,
totalCost: 2847.99,
startedDate: '2024-02-01',
components: {
total: 18,
completed: 12,
categories: {
'Upper': 6,
'Lower': 4,
'Accessory': 2
}
},
tags: ['Precision', '6.5 Creedmoor', '20" Barrel'],
image: 'https://picsum.photos/400/250?random=2'
},
{
id: '3',
name: 'Home Defense Setup',
description: 'Compact AR-15 configured for home defense scenarios',
status: 'planning' as const,
totalCost: 0,
plannedDate: '2024-03-01',
components: {
total: 18,
completed: 0,
categories: {
'Upper': 0,
'Lower': 0,
'Accessory': 0
}
},
tags: ['Home Defense', '5.56 NATO', '10.5" Barrel'],
image: 'https://picsum.photos/400/250?random=3'
},
{
id: '4',
name: 'Competition Rifle',
description: 'Lightweight competition build for 3-gun matches',
status: 'completed' as const,
totalCost: 1650.75,
completedDate: '2023-12-10',
components: {
total: 18,
completed: 18,
categories: {
'Upper': 8,
'Lower': 7,
'Accessory': 3
}
},
tags: ['Competition', '5.56 NATO', '18" Barrel'],
image: 'https://picsum.photos/400/250?random=4'
},
{
id: '5',
name: 'Suppressed SBR',
description: 'Short-barreled rifle build with suppressor integration',
status: 'in-progress' as const,
totalCost: 1895.25,
startedDate: '2024-01-20',
components: {
total: 18,
completed: 8,
categories: {
'Upper': 4,
'Lower': 3,
'Accessory': 1
}
},
tags: ['SBR', 'Suppressed', '300 BLK'],
image: 'https://picsum.photos/400/250?random=5'
},
{
id: '6',
name: 'Retro M16A1 Clone',
description: 'Faithful reproduction of the classic M16A1 rifle',
status: 'planning' as const,
totalCost: 0,
plannedDate: '2024-04-01',
components: {
total: 18,
completed: 0,
categories: {
'Upper': 0,
'Lower': 0,
'Accessory': 0
}
},
tags: ['Retro', '5.56 NATO', '20" Barrel'],
image: 'https://picsum.photos/400/250?random=6'
}
];
type MyBuildStatus = 'completed' | 'in-progress' | 'planning';
type SortField = 'name' | 'status' | 'totalCost' | 'completedDate';
type SortDirection = 'asc' | 'desc';
export default function MyBuildsPage() {
const [sortField, setSortField] = useState<SortField>('completedDate');
const [sortDirection, setSortDirection] = useState<SortDirection>('desc');
const [selectedStatus, setSelectedStatus] = useState<MyBuildStatus | 'all'>('all');
const [searchTerm, setSearchTerm] = useState('');
// Filter builds
const filteredMyBuilds = sampleMyBuilds.filter(build => {
if (selectedStatus !== 'all' && build.status !== selectedStatus) {
return false;
}
if (searchTerm && !build.name.toLowerCase().includes(searchTerm.toLowerCase()) &&
!build.description.toLowerCase().includes(searchTerm.toLowerCase())) {
return false;
}
return true;
});
// Sort builds
const sortedMyBuilds = [...filteredMyBuilds].sort((a, b) => {
let aValue: any, bValue: any;
if (sortField === 'totalCost') {
aValue = a.totalCost;
bValue = b.totalCost;
} else if (sortField === 'status') {
aValue = a.status;
bValue = b.status;
} else if (sortField === 'completedDate') {
aValue = a.completedDate || a.startedDate || a.plannedDate || '';
bValue = b.completedDate || b.startedDate || b.plannedDate || '';
} else {
aValue = a.name.toLowerCase();
bValue = b.name.toLowerCase();
}
if (sortDirection === 'asc') {
return aValue > bValue ? 1 : -1;
} else {
return aValue < bValue ? 1 : -1;
}
});
const getStatusColor = (status: MyBuildStatus) => {
switch (status) {
case 'completed':
return 'bg-green-100 text-green-800';
case 'in-progress':
return 'bg-blue-100 text-blue-800';
case 'planning':
return 'bg-yellow-100 text-yellow-800';
default:
return 'bg-gray-100 text-gray-800';
}
};
const getStatusIcon = (status: MyBuildStatus) => {
switch (status) {
case 'completed':
return '✓';
case 'in-progress':
return '🔄';
case 'planning':
return '📋';
default:
return '❓';
}
};
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
};
const getProgressPercentage = (build: typeof sampleMyBuilds[0]) => {
return Math.round((build.components.completed / build.components.total) * 100);
};
const totalMyBuilds = sampleMyBuilds.length;
const completedMyBuilds = sampleMyBuilds.filter(build => build.status === 'completed').length;
const inProgressMyBuilds = sampleMyBuilds.filter(build => build.status === 'in-progress').length;
const totalValue = sampleMyBuilds.reduce((sum, build) => sum + build.totalCost, 0);
return (
<main className="min-h-screen bg-gray-50">
{/* Page Title */}
<div className="bg-white border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<h1 className="text-3xl font-bold text-gray-900">My Builds</h1>
<p className="text-gray-600 mt-2">Track and manage your firearm builds</p>
</div>
</div>
{/* Build Summary */}
<div className="bg-white border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="text-center">
<div className="text-2xl font-bold text-gray-900">{totalMyBuilds}</div>
<div className="text-sm text-gray-500">Total Builds</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-green-600">{completedMyBuilds}</div>
<div className="text-sm text-gray-500">Completed</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-blue-600">{inProgressMyBuilds}</div>
<div className="text-sm text-gray-500">In Progress</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-purple-600">${totalValue.toFixed(2)}</div>
<div className="text-sm text-gray-500">Total Value</div>
</div>
</div>
</div>
</div>
{/* Search and Filters */}
<div className="bg-white border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
{/* Filters Row */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{/* Status Filter */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Status</label>
<select
value={selectedStatus}
onChange={(e) => setSelectedStatus(e.target.value as MyBuildStatus | 'all')}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="all">All Status</option>
<option value="completed">Completed</option>
<option value="in-progress">In Progress</option>
<option value="planning">Planning</option>
</select>
</div>
{/* Sort by */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Sort By</label>
<select
value={sortField}
onChange={(e) => setSortField(e.target.value as SortField)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="completedDate">Date</option>
<option value="name">Name</option>
<option value="totalCost">Cost</option>
<option value="status">Status</option>
</select>
</div>
{/* Sort Direction */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Order</label>
<select
value={sortDirection}
onChange={(e) => setSortDirection(e.target.value as SortDirection)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="desc">Newest First</option>
<option value="asc">Oldest First</option>
</select>
</div>
{/* New Build Button */}
<div className="flex items-end">
<button className="w-full bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors">
+ New Build
</button>
</div>
</div>
</div>
</div>
{/* Builds Grid */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{sortedMyBuilds.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{sortedMyBuilds.map((build) => (
<div key={build.id} className="bg-white rounded-lg shadow-sm border hover:shadow-md transition-shadow overflow-hidden">
{/* Build Image */}
<div className="h-48 bg-gray-200 relative">
<img
src={build.image}
alt={build.name}
className="w-full h-full object-cover"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.style.display = 'none';
target.nextElementSibling?.classList.remove('hidden');
}}
/>
<div className="hidden w-full h-full flex items-center justify-center bg-gradient-to-br from-gray-300 to-gray-400">
<div className="text-center text-gray-600">
<div className="text-4xl mb-2">🔫</div>
<div className="text-sm font-medium">{build.name}</div>
</div>
</div>
<div className="absolute top-3 right-3">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(build.status)}`}>
{getStatusIcon(build.status)} {build.status}
</span>
</div>
</div>
{/* Build Content */}
<div className="p-6">
{/* Build Title and Date */}
<div className="mb-4">
<h3 className="text-lg font-semibold text-gray-900 mb-1">{build.name}</h3>
<p className="text-sm text-gray-600 mb-2">{build.description}</p>
<div className="text-xs text-gray-500">
{build.status === 'completed' && build.completedDate && `Completed ${formatDate(build.completedDate)}`}
{build.status === 'in-progress' && build.startedDate && `Started ${formatDate(build.startedDate)}`}
{build.status === 'planning' && build.plannedDate && `Planned for ${formatDate(build.plannedDate)}`}
</div>
</div>
{/* Progress Bar */}
<div className="mb-4">
<div className="flex justify-between text-sm text-gray-600 mb-1">
<span>Progress</span>
<span>{build.components.completed}/{build.components.total} components</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${getProgressPercentage(build)}%` }}
></div>
</div>
</div>
{/* Component Categories */}
<div className="mb-4">
<div className="flex space-x-2">
{Object.entries(build.components.categories).map(([category, count]) => (
<span key={category} className="inline-flex items-center px-2 py-1 rounded text-xs bg-gray-100 text-gray-700">
{category}: {count}
</span>
))}
</div>
</div>
{/* Tags */}
<div className="mb-4">
<div className="flex flex-wrap gap-1">
{build.tags.map((tag) => (
<span key={tag} className="inline-flex items-center px-2 py-1 rounded text-xs bg-blue-100 text-blue-800">
{tag}
</span>
))}
</div>
</div>
{/* Cost and Actions */}
<div className="flex justify-between items-center">
<div className="text-lg font-bold text-gray-900">
${build.totalCost.toFixed(2)}
</div>
<div className="flex space-x-2">
<button className="bg-blue-600 text-white px-3 py-1 rounded text-sm hover:bg-blue-700 transition-colors">
View Details
</button>
<button className="bg-gray-100 text-gray-700 px-2 py-1 rounded text-sm hover:bg-gray-200 transition-colors">
Edit
</button>
</div>
</div>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-12">
<div className="text-gray-500">
<div className="text-lg font-medium mb-2">No builds found</div>
<div className="text-sm">Try adjusting your filters or create a new build</div>
</div>
</div>
)}
</div>
</main>
);
}

View File

@@ -3,7 +3,7 @@
import { useState, useEffect } from 'react';
import { useSearchParams } from 'next/navigation';
import { Listbox, Transition } from '@headlessui/react';
import { ChevronUpDownIcon, CheckIcon, XMarkIcon } from '@heroicons/react/20/solid';
import { ChevronUpDownIcon, CheckIcon, XMarkIcon, TableCellsIcon, Squares2X2Icon } from '@heroicons/react/20/solid';
import SearchInput from '@/components/SearchInput';
import ProductCard from '@/components/ProductCard';
import RestrictionAlert from '@/components/RestrictionAlert';
@@ -11,6 +11,7 @@ import Tooltip from '@/components/Tooltip';
import Link from 'next/link';
import { mockProducts } from '@/mock/product';
import type { Product } from '@/mock/product';
import Image from 'next/image';
// Extract unique values for dropdowns
const categories = ['All', ...Array.from(new Set(mockProducts.map(part => part.category.name)))];
@@ -464,14 +465,16 @@ export default function Home() {
<button
className={`btn btn-sm ${viewMode === 'table' ? 'btn-active' : ''}`}
onClick={() => setViewMode('table')}
aria-label="Table view"
>
Table
<TableCellsIcon className="h-5 w-5" />
</button>
<button
className={`btn btn-sm ${viewMode === 'cards' ? 'btn-active' : ''}`}
onClick={() => setViewMode('cards')}
aria-label="Card view"
>
Cards
<Squares2X2Icon className="h-5 w-5" />
</button>
</div>
</div>
@@ -496,24 +499,12 @@ export default function Home() {
<table className="min-w-full divide-y divide-neutral-200 dark:divide-neutral-700">
<thead className="bg-neutral-50 dark:bg-neutral-700">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-neutral-500 dark:text-neutral-300 uppercase tracking-wider">
Product
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-neutral-500 dark:text-neutral-300 uppercase tracking-wider">
Category
</th>
<th
className="px-6 py-3 text-left text-xs font-medium text-neutral-500 dark:text-neutral-300 uppercase tracking-wider cursor-pointer hover:bg-neutral-100 dark:hover:bg-neutral-600"
onClick={() => handleSort('name')}
>
<div className="flex items-center space-x-1">
<span>Name</span>
<span className="text-sm">{getSortIcon('name')}</span>
</div>
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-neutral-500 dark:text-neutral-300 uppercase tracking-wider">
Brand
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-neutral-500 dark:text-neutral-300 uppercase tracking-wider">
Description
</th>
<th
className="px-6 py-3 text-left text-xs font-medium text-neutral-500 dark:text-neutral-300 uppercase tracking-wider cursor-pointer hover:bg-neutral-100 dark:hover:bg-neutral-600"
onClick={() => handleSort('price')}
@@ -531,29 +522,20 @@ export default function Home() {
<tbody className="bg-white dark:bg-neutral-800 divide-y divide-neutral-200 dark:divide-neutral-700">
{sortedParts.map((part) => (
<tr key={part.id} className="hover:bg-neutral-50 dark:hover:bg-neutral-700 transition-colors">
<td className="px-6 py-4 whitespace-nowrap flex items-center gap-3 min-w-[180px]">
<div className="w-12 h-12 flex-shrink-0 rounded bg-neutral-100 dark:bg-neutral-700 overflow-hidden flex items-center justify-center border border-neutral-200 dark:border-neutral-700">
<Image src={Array.isArray(part.images) && (part.images as string[]).length > 0 ? (part.images as string[])[0] : '/window.svg'} alt={part.name} width={48} height={48} className="object-contain w-12 h-12" />
</div>
<div>
<div className="text-sm font-semibold text-neutral-900 dark:text-white">{part.name}</div>
<div className="text-xs text-neutral-500 dark:text-neutral-400">{part.brand.name}</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary-100 dark:bg-primary-900 text-primary-800 dark:text-primary-200">
{part.category.name}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-medium text-neutral-900 dark:text-white">
{part.name}
</div>
<div className="text-sm text-neutral-500 dark:text-neutral-400">
{part.brand.name}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-neutral-900 dark:text-white">
{part.brand.name}
</div>
</td>
<td className="px-6 py-4">
<div className="text-sm text-neutral-500 dark:text-neutral-400 max-w-xs">
{part.description}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-semibold text-neutral-900 dark:text-white">
${Math.min(...part.offers.map(offer => offer.price)).toFixed(2)}
@@ -562,7 +544,7 @@ export default function Home() {
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
<Link href={`/products/${part.id}`} legacyBehavior>
<a className="btn btn-primary btn-sm">
View Details
Add
</a>
</Link>
</td>

View File

@@ -10,8 +10,8 @@ export default function Navbar() {
const navItems = [
{ href: '/parts', label: 'Parts Catalog' },
{ href: '/build', label: 'Build Checklist' },
{ href: '/builds', label: 'My Builds' },
{ href: '/build', label: 'Plan a Build' },
{ href: '/my-builds', label: 'My Builds' },
];
return (