Skip to content

Commit

Permalink
refactor: pillar rows w/ selected items on left
Browse files Browse the repository at this point in the history
need to find a different solution. RSC always run logic on search-param change. need to handle the append logic in the client side while keeping track of initial server-rendered items
  • Loading branch information
johnshift committed Jan 21, 2025
1 parent 498780b commit 7aac258
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 10 deletions.
4 changes: 2 additions & 2 deletions src/app/projectsx/[pillar]/[item]/create-labeled-items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface Options {
* Can safely assume main-pillar-item is included in fetchedLabels - page already has logic if main-pillar-item is 404.
*/
export const createLabeledItems = ({
nav,
nav: _nav,
params,
searchParams,
fetchedLabels,
Expand All @@ -42,7 +42,7 @@ export const createLabeledItems = ({
continue;
}

const prefixUrl = `/${nav}/${params.pillar}/${params.item}`;
const prefixUrl = `/projectsx/${params.pillar}/${params.item}`;
const newSearchParams = new URLSearchParams(searchParams);
const currentItems = newSearchParams.get(pillar)?.split(',') ?? [];
const newItems = currentItems.filter((item) => item !== slug);
Expand Down
33 changes: 25 additions & 8 deletions src/app/projectsx/[pillar]/[item]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { AppHeader } from '@/shared/components/app-header';

import { getPillarInfo } from '@/search/data/get-pillar-info';
import { getPillarLabels } from '@/search/data/get-pillar-labels';

import { createLabeledItems } from './create-labeled-items';
import { PillarRows } from './pillar-rows';
import { PillarSearch } from './pillar-search';

interface Props {
Expand All @@ -18,11 +20,18 @@ const Page = async (props: Props) => {
const activePillars = [params.pillar, ...Object.keys(searchParams)].join(',');
const activeSlugs = [params.item, ...Object.values(searchParams)].join(',');

const fetchedLabels = await getPillarLabels({
nav,
pillars: activePillars,
slugs: activeSlugs,
});
const [fetchedLabels, pillarInfo] = await Promise.all([
getPillarLabels({
nav,
pillars: activePillars,
slugs: activeSlugs,
}),
getPillarInfo({
nav,
pillar: params.pillar,
item: params.item,
}),
]);

const labeledItems = createLabeledItems({
nav,
Expand All @@ -31,17 +40,25 @@ const Page = async (props: Props) => {
fetchedLabels,
});

const pathPrefix = `/projectsx/${params.pillar}/${params.item}`;

return (
<div className="relative min-h-screen space-y-4 overflow-hidden">
<AppHeader
input={<PillarSearch labeledItems={labeledItems} />}
searchResults={<p>{'<PillarPageSearchResults />'}</p>}
mainPillar={<p>{'<MainPillarContent />'}</p>}
/>
<p>{'<PillarRows />'}</p>
<p>{'<Content />'}</p>

<pre>{JSON.stringify({ fetchedLabels }, undefined, '\t')}</pre>
<PillarRows
pathPrefix={pathPrefix}
searchParams={searchParams}
itemParam={params.item}
labeledItems={labeledItems}
pillars={pillarInfo.altPillars}
/>

<p>{'<Content />'}</p>
</div>
);
};
Expand Down
49 changes: 49 additions & 0 deletions src/app/projectsx/[pillar]/[item]/pillar-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use client';

import Link from 'next/link';
import { useRouter } from 'next/navigation';

import { Button } from '@heroui/button';

import { cn } from '@/shared/utils/cn';

import { usePillarRoutesContext } from '@/search/state/contexts/pillar-routes-context';

interface Props {
isActive: boolean;
label: string;
href: string;
}

export const PillarItem = ({ isActive, label, href }: Props) => {
const router = useRouter();
const { isPendingPillarRoute: isLoading, startTransition } =
usePillarRoutesContext();

const onClick = () => {
startTransition(() => {
router.push(href);
});
};

const className = cn('border', {
'border-white/60': isActive,
'pointer-events-none text-accent2 border-accent2': !href,
});

const variant = isActive || !href ? 'bordered' : 'faded';

return (
<Button
as={Link}
href={href}
radius="md"
className={className}
variant={variant}
isDisabled={isLoading}
onClick={onClick}
>
{label}
</Button>
);
};
48 changes: 48 additions & 0 deletions src/app/projectsx/[pillar]/[item]/pillar-row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { normalizeString } from '@/shared/utils/normalize-string';

import { PillarItem } from './pillar-item';

interface Props {
itemParam: string;
title: string | null;
selectedItems: { label: string; href: string }[];
items: { label: string; href: string }[];
}

export const PillarRow = (props: Props) => {
const { itemParam, title, selectedItems, items } = props;

return (
<div className="flex flex-col gap-1">
{title && (
<div className="pl-2 text-13 uppercase text-accent2/90">
<span>{title}</span>
</div>
)}
<div className="relative flex h-14 gap-4 overflow-hidden p-1">
<div className="flex max-w-fit flex-wrap gap-4">
{selectedItems.map(({ label, href }) => (
<PillarItem
isActive
key={label}
label={label}
href={normalizeString(label) === itemParam ? '' : href}
/>
))}
{items.map(({ label, href }) => (
<PillarItem
key={label}
isActive={false}
label={label}
href={href}
/>
))}
</div>

<div className="shrink-0 grow">
<p>{'<More />'}</p>
</div>
</div>
</div>
);
};
69 changes: 69 additions & 0 deletions src/app/projectsx/[pillar]/[item]/pillar-rows.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { capitalize } from '@/shared/utils/capitalize';
import { normalizeString } from '@/shared/utils/normalize-string';

import { PillarDto } from '@/search/core/schemas';

import { PillarRow } from './pillar-row';
import { LabeledItem } from './types';

interface Props {
pathPrefix: string;
searchParams: Record<string, string>;
itemParam: string;
labeledItems: LabeledItem[];
pillars: PillarDto[];
}

export const PillarRows = ({
pathPrefix,
searchParams,
itemParam,
labeledItems,
pillars,
}: Props) => {
const pillarRows = pillars.flatMap(({ slug: pillar, items }) => {
const selectedLabels = new Set<string>();
const selectedItems = labeledItems
.filter(
(labeledItem) =>
labeledItem.pillar === pillar && labeledItem.label !== undefined,
)
.map(({ label, href }) => {
selectedLabels.add(label as string);
return { label, href };
}) as {
label: string;
href: string;
}[];

const mappedItems = items
.map((label) => {
const newSearchParams = new URLSearchParams(searchParams);
const paramValues = newSearchParams.get(pillar)?.split(',') ?? [];
paramValues.push(normalizeString(label));
newSearchParams.set(pillar, paramValues.join(','));
return { label, href: `${pathPrefix}?${newSearchParams.toString()}` };
})
.filter(({ label }) => !selectedLabels.has(label));

return {
title: capitalize(pillar),
selectedItems,
items: mappedItems,
};
});

return (
<>
{pillarRows.map(({ title, selectedItems, items }) => (
<PillarRow
key={title}
itemParam={itemParam}
title={title}
selectedItems={selectedItems}
items={items}
/>
))}
</>
);
};

0 comments on commit 7aac258

Please sign in to comment.