mirror of
https://github.com/sakaljurgis/best-choice.git
synced 2026-07-08 21:47:40 +00:00
Add active-only filter for project items
This commit is contained in:
@@ -1,27 +1,35 @@
|
||||
interface ProjectItemsHeaderProps {
|
||||
itemCount: number;
|
||||
totalItemCount: number;
|
||||
isLoading: boolean;
|
||||
showDifferencesOnly: boolean;
|
||||
hasAttributeDifferences: boolean;
|
||||
onToggleDifferences: (value: boolean) => void;
|
||||
showLargeImages: boolean;
|
||||
onToggleLargeImages: (value: boolean) => void;
|
||||
showActiveOnly: boolean;
|
||||
onToggleActiveOnly: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export function ProjectItemsHeader({
|
||||
itemCount,
|
||||
totalItemCount,
|
||||
isLoading,
|
||||
showDifferencesOnly,
|
||||
hasAttributeDifferences,
|
||||
onToggleDifferences,
|
||||
showLargeImages,
|
||||
onToggleLargeImages
|
||||
onToggleLargeImages,
|
||||
showActiveOnly,
|
||||
onToggleActiveOnly
|
||||
}: ProjectItemsHeaderProps) {
|
||||
const subtitle = isLoading
|
||||
? 'Loading items…'
|
||||
: itemCount
|
||||
? `${itemCount} item${itemCount === 1 ? '' : 's'} tracked.`
|
||||
: 'No items yet – add your first one below.';
|
||||
: totalItemCount === 0
|
||||
? 'No items yet – add your first one below.'
|
||||
: showActiveOnly && itemCount !== totalItemCount
|
||||
? `${itemCount} of ${totalItemCount} active item${itemCount === 1 ? '' : 's'} shown.`
|
||||
: `${totalItemCount} item${totalItemCount === 1 ? '' : 's'} tracked.`;
|
||||
|
||||
return (
|
||||
<header className="mb-6 flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||
@@ -30,6 +38,15 @@ export function ProjectItemsHeader({
|
||||
<p className="text-sm text-slate-500">{subtitle}</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 text-xs font-medium uppercase tracking-wide text-slate-500 md:flex-row md:items-center md:gap-6">
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500"
|
||||
checked={showActiveOnly}
|
||||
onChange={(event) => onToggleActiveOnly(event.target.checked)}
|
||||
/>
|
||||
Active only
|
||||
</label>
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
||||
@@ -42,11 +42,19 @@ export function ProjectItemsSection({
|
||||
const [showDifferencesOnly, setShowDifferencesOnly] = useState(false);
|
||||
const [expandedItemId, setExpandedItemId] = useState<string | null>(null);
|
||||
const [showLargeImages, setShowLargeImages] = useState(false);
|
||||
const [showActiveOnly, setShowActiveOnly] = useState(false);
|
||||
|
||||
const displayItems = useMemo(() => {
|
||||
if (!showActiveOnly) {
|
||||
return items;
|
||||
}
|
||||
return items.filter((item) => item.status === 'active');
|
||||
}, [items, showActiveOnly]);
|
||||
|
||||
const mismatchedAttributes = useMemo(() => {
|
||||
const result = new Set<string>();
|
||||
|
||||
if (items.length <= 1 || projectAttributes.length === 0) {
|
||||
if (displayItems.length <= 1 || projectAttributes.length === 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -59,7 +67,7 @@ export function ProjectItemsSection({
|
||||
let referenceValue: unknown = null;
|
||||
let hasReference = false;
|
||||
|
||||
for (const item of items) {
|
||||
for (const item of displayItems) {
|
||||
const attributes = (item.attributes ?? {}) as Record<string, unknown>;
|
||||
const currentValue = attributes[attribute];
|
||||
|
||||
@@ -77,7 +85,7 @@ export function ProjectItemsSection({
|
||||
});
|
||||
|
||||
return result;
|
||||
}, [items, projectAttributes]);
|
||||
}, [displayItems, projectAttributes]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showDifferencesOnly && mismatchedAttributes.size === 0) {
|
||||
@@ -89,11 +97,11 @@ export function ProjectItemsSection({
|
||||
if (!expandedItemId) {
|
||||
return;
|
||||
}
|
||||
const itemStillPresent = items.some((item) => item.id === expandedItemId);
|
||||
const itemStillPresent = displayItems.some((item) => item.id === expandedItemId);
|
||||
if (!itemStillPresent) {
|
||||
setExpandedItemId(null);
|
||||
}
|
||||
}, [items, expandedItemId]);
|
||||
}, [displayItems, expandedItemId]);
|
||||
|
||||
const visibleAttributes = useMemo(() => {
|
||||
if (!showDifferencesOnly) {
|
||||
@@ -105,7 +113,7 @@ export function ProjectItemsSection({
|
||||
|
||||
const hasAttributeDifferences = mismatchedAttributes.size > 0;
|
||||
const showNoDifferencesMessage =
|
||||
!hasAttributeDifferences && projectAttributes.length > 0 && items.length > 1;
|
||||
!hasAttributeDifferences && projectAttributes.length > 0 && displayItems.length > 1;
|
||||
|
||||
const handleTogglePrices = (itemId: string) => {
|
||||
setExpandedItemId((current) => (current === itemId ? null : itemId));
|
||||
@@ -115,13 +123,29 @@ export function ProjectItemsSection({
|
||||
<>
|
||||
<section className="rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
|
||||
<ProjectItemsHeader
|
||||
itemCount={items.length}
|
||||
itemCount={displayItems.length}
|
||||
totalItemCount={items.length}
|
||||
isLoading={isLoading}
|
||||
showDifferencesOnly={showDifferencesOnly}
|
||||
hasAttributeDifferences={hasAttributeDifferences}
|
||||
onToggleDifferences={(value) => setShowDifferencesOnly(value)}
|
||||
showLargeImages={showLargeImages}
|
||||
onToggleLargeImages={(value) => setShowLargeImages(value)}
|
||||
showActiveOnly={showActiveOnly}
|
||||
onToggleActiveOnly={(value) => {
|
||||
setShowActiveOnly(value);
|
||||
if (value) {
|
||||
setExpandedItemId((current) => {
|
||||
if (!current) {
|
||||
return current;
|
||||
}
|
||||
const stillVisible = items.some(
|
||||
(item) => item.id === current && item.status === 'active'
|
||||
);
|
||||
return stillVisible ? current : null;
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{error ? (
|
||||
@@ -135,7 +159,7 @@ export function ProjectItemsSection({
|
||||
) : null}
|
||||
|
||||
<ProjectItemsDesktopTable
|
||||
items={items}
|
||||
items={displayItems}
|
||||
visibleAttributes={visibleAttributes}
|
||||
expandedItemId={expandedItemId}
|
||||
onTogglePrices={handleTogglePrices}
|
||||
@@ -147,7 +171,7 @@ export function ProjectItemsSection({
|
||||
/>
|
||||
|
||||
<ProjectItemsMobileList
|
||||
items={items}
|
||||
items={displayItems}
|
||||
visibleAttributes={visibleAttributes}
|
||||
expandedItemId={expandedItemId}
|
||||
onTogglePrices={handleTogglePrices}
|
||||
|
||||
Reference in New Issue
Block a user