Skip to content

Commit 37d5774

Browse files
committed
feat: rename actionpage to actionsection
1 parent db84505 commit 37d5774

2 files changed

Lines changed: 42 additions & 10 deletions

File tree

src/components/PageBlockRenderer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { StandaloneCardSection } from "./sections/StandaloneCardSection"
2020
import { VideoSection } from "./sections/VideoSection"
2121
import { WideHeroSection } from "./sections/WideHeroSection"
2222
import { StatsSection } from "./sections/StatsSection"
23-
import { ActionsPageClient } from "./ActionsPageClient"
23+
import { ActionSection } from "./sections/ActionSection"
2424
import type { ActionItem } from "@/lib/cms"
2525

2626
type PageBlock = NonNullable<Page["layout"]>[number]
@@ -56,7 +56,7 @@ const pageBlockRenderers: Partial<Record<PageBlock["blockType"], BlockRenderer>>
5656
widehero: (block) => <WideHeroSection content={block as Extract<PageBlock, { blockType: "widehero" }>} />,
5757
listFeature: (block) => <ListFeatureSection content={block as Extract<PageBlock, { blockType: "listFeature" }>} />,
5858
stats: (block) => <StatsSection content={block as Extract<PageBlock, { blockType: "stats" }>} />,
59-
actions: (block, options) => <ActionsPageClient actions={options.actions ?? []} locale={options.locale ?? "en"} content={block as Extract<PageBlock, { blockType: "actions" }>} />,
59+
actions: (block, options) => <ActionSection actions={options.actions ?? []} locale={options.locale ?? "en"} content={block as Extract<PageBlock, { blockType: "actions" }>} />,
6060
}
6161

6262
function renderPageBlock(block: PageBlock, options: PageBlockRenderOptions) {
Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,61 @@
22

33
import { ActionCard } from "@/components/cards/ActionCard"
44
import type { ActionItem } from "@/lib/cms"
5+
import { cn } from "@/lib/utils"
56
import { TextInput } from "@code0-tech/pictor"
67
import { IconSearch } from "@tabler/icons-react"
78
import type { ChangeEvent } from "react"
89
import { useMemo, useState } from "react"
910

10-
interface ActionsPageContent {
11+
interface ActionSectionContent {
1112
heading: string
1213
description: string
1314
searchPlaceholder: string
1415
noActionsFoundLabel: string
1516
referencesLabel: string
1617
}
1718

18-
interface ActionsPageClientProps {
19+
interface ActionSectionProps {
1920
actions: ActionItem[]
2021
locale: string
21-
content?: Partial<ActionsPageContent> | null
22+
content?: Partial<ActionSectionContent> | null
2223
}
2324

24-
const defaultContent: ActionsPageContent = {
25+
const defaultContent: ActionSectionContent = {
2526
heading: "Actions",
2627
description: "Browse available actions and integrations.",
2728
searchPlaceholder: "Search actions",
2829
noActionsFoundLabel: "No actions found for your search.",
2930
referencesLabel: "References",
3031
}
3132

32-
export function ActionsPageClient({ actions, locale, content }: ActionsPageClientProps) {
33+
export function ActionSection({ actions, locale, content }: ActionSectionProps) {
3334
const labels = { ...defaultContent, ...content }
3435
const [search, setSearch] = useState("")
36+
const [selectedTags, setSelectedTags] = useState<string[]>([])
37+
38+
const tags = useMemo(() => {
39+
return Array.from(new Set(actions.flatMap((action) => action.tags?.filter((tag): tag is string => Boolean(tag?.trim())).map((tag) => tag.trim()) ?? []))).sort((a, b) => a.localeCompare(b))
40+
}, [actions])
3541

3642
const filteredActions = useMemo(() => {
3743
const searchTerm = search.trim().toLowerCase()
38-
if (!searchTerm) return actions
3944

4045
return actions.filter((action) => {
4146
const item = [action.title, action.shortDescription, action.description, ...(action.tags ?? [])]
4247
.filter((value) => Boolean(value))
4348
.join(" ")
4449
.toLowerCase()
50+
const matchesSearch = !searchTerm || item.includes(searchTerm)
51+
const matchesTags = selectedTags.length === 0 || selectedTags.some((selectedTag) => action.tags?.includes(selectedTag))
4552

46-
return item.includes(searchTerm)
53+
return matchesSearch && matchesTags
4754
})
48-
}, [actions, search])
55+
}, [actions, search, selectedTags])
56+
57+
const toggleTag = (tag: string) => {
58+
setSelectedTags((currentTags) => (currentTags.includes(tag) ? currentTags.filter((currentTag) => currentTag !== tag) : [...currentTags, tag]))
59+
}
4960

5061
return (
5162
<div className="mx-auto flex w-full max-w-4xl flex-col gap-8">
@@ -61,6 +72,27 @@ export function ActionsPageClient({ actions, locale, content }: ActionsPageClien
6172
clearable
6273
className="text-white!"
6374
/>
75+
{tags.length > 0 && (
76+
<div className="flex flex-wrap gap-2">
77+
{tags.map((tag) => {
78+
const selected = selectedTags.includes(tag)
79+
80+
return (
81+
<button
82+
key={tag}
83+
type="button"
84+
onClick={() => toggleTag(tag)}
85+
className={cn(
86+
"rounded-full border px-3 py-1 text-xs font-medium transition-colors",
87+
selected ? "border-brand/40 bg-brand/15 text-brand" : "border-white/10 bg-white/3 text-tertiary hover:border-white/20 hover:text-white"
88+
)}
89+
>
90+
{tag}
91+
</button>
92+
)
93+
})}
94+
</div>
95+
)}
6496
<div className="flex flex-col gap-4">
6597
{filteredActions.map((action) => (
6698
<ActionCard key={action.id} action={action} locale={locale} />

0 commit comments

Comments
 (0)