split items section

This commit is contained in:
Jurgis Sakalauskas
2025-10-14 14:05:57 +03:00
parent 5cd864318f
commit e30e116187
7 changed files with 601 additions and 490 deletions
@@ -0,0 +1,106 @@
import { ExternalLink, Pencil } from 'lucide-react';
import type { Item } from '@shared/models/item';
import { ItemPricesPanel } from './item-prices-panel';
import { formatAttributeValue, getPriceSummaryDisplay } from './project-items-utils';
interface ProjectItemsMobileListProps {
items: Item[];
visibleAttributes: string[];
expandedItemId: string | null;
onTogglePrices: (itemId: string) => void;
onEditItem: (item: Item) => void;
projectId: string | undefined;
}
export function ProjectItemsMobileList({
items,
visibleAttributes,
expandedItemId,
onTogglePrices,
onEditItem,
projectId
}: ProjectItemsMobileListProps) {
return (
<div className="space-y-4 md:hidden">
{items.map((item) => {
const attributes = (item.attributes ?? {}) as Record<string, unknown>;
const priceDisplay = getPriceSummaryDisplay(item.priceSummary);
const isExpanded = expandedItemId === item.id;
const panelId = `item-${item.id}-prices-mobile`;
return (
<div key={item.id} className="rounded-xl border border-slate-200 p-4 shadow-sm">
<div className="flex items-start gap-3">
<button
type="button"
onClick={() => onEditItem(item)}
className="group flex flex-1 items-center gap-2 text-left text-slate-900 transition hover:text-blue-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
title="Edit item details"
>
<span
aria-hidden
className={`inline-block h-2.5 w-2.5 rounded-full ${
item.status === 'active' ? 'bg-emerald-500' : 'bg-slate-300'
}`}
/>
<span className="font-semibold group-hover:underline">
{item.manufacturer ? `${item.manufacturer} ${item.model}` : item.model}
</span>
</button>
{item.sourceUrl ? (
<a
href={item.sourceUrl}
target="_blank"
rel="noreferrer"
className="inline-flex h-9 w-9 items-center justify-center rounded-md text-blue-600 transition focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
title="Open source link"
>
<ExternalLink aria-hidden className="h-5 w-5" />
<span className="sr-only">Open source link</span>
</a>
) : null}
</div>
{item.note ? <p className="mt-1 text-sm text-slate-600">Note: {item.note}</p> : null}
<div className="mt-3 space-y-2">
{visibleAttributes.map((attribute) => (
<div key={attribute} className="flex justify-between text-xs text-slate-600">
<span className="font-semibold uppercase tracking-wide text-slate-500">
{attribute}
</span>
<span className="text-right">{formatAttributeValue(attributes[attribute])}</span>
</div>
))}
</div>
<div className="mt-3 flex flex-col gap-0.5 text-xs">
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-slate-900">{priceDisplay.primary}</span>
<button
type="button"
onClick={() => onTogglePrices(item.id)}
className="inline-flex h-9 w-9 items-center justify-center rounded-md text-slate-500 transition hover:text-slate-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
aria-label="Edit Prices"
title="Edit Prices"
aria-expanded={isExpanded}
aria-controls={panelId}
>
<Pencil aria-hidden className="h-4 w-4" />
<span className="sr-only">Edit Prices</span>
</button>
</div>
{priceDisplay.secondary ? (
<span className="text-slate-500">{priceDisplay.secondary}</span>
) : null}
</div>
{isExpanded ? (
<div id={panelId} className="mt-4">
<ItemPricesPanel itemId={item.id} projectId={projectId} />
</div>
) : null}
</div>
);
})}
</div>
);
}