Skip to content

Commit b0a5fdb

Browse files
committed
feat: adapt action pages to new json structure
1 parent 1ceac69 commit b0a5fdb

19 files changed

Lines changed: 26897 additions & 456 deletions

src/app/(landing)/[locale]/actions/[slug]/page.tsx

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
import { ActionTriggerView } from "@/components/ActionTriggerView"
22
import { ActionCard } from "@/components/cards/ActionCard"
33
import { Aurora } from "@/components/ui/Aurora"
4-
import { HapticButtonLink } from "@/components/ui/HapticButtonLink"
54
import { LandingContainer } from "@/components/ui/LandingContainer"
65
import { LinkButton } from "@/components/ui/LinkButton"
76
import { getPageLocaleAndSlug, type LocaleSlugPageParams } from "@/lib/appRoute"
8-
import { extractFunctionDefsFromJson, extractTriggersFromJson, fetchMediaJson } from "@/lib/actionTriggerExtraction"
7+
import { extractFlowTypesFromJson, extractFunctionDefinitionsFromJson, fetchMediaJson } from "@/lib/actionExtraction"
98
import { getActionBySlug, getLandingPage } from "@/lib/cms"
10-
import { getMediaUrl } from "@/lib/media"
119
import { findPageBlock } from "@/lib/pageBlocks"
1210
import { createMetadata } from "@/lib/siteConfig"
1311
import { getIcon } from "@/components/IconRenderer"
1412
import type { Media } from "@/payload-types"
15-
import { IconArrowLeft, IconExternalLink } from "@tabler/icons-react"
13+
import { IconArrowLeft } from "@tabler/icons-react"
1614
import type { Metadata } from "next"
17-
import Image from "next/image"
1815
import { notFound } from "next/navigation"
1916

2017
export default async function ActionDetailPage({ params }: { params: LocaleSlugPageParams }) {
@@ -23,25 +20,27 @@ export default async function ActionDetailPage({ params }: { params: LocaleSlugP
2320
if (!action) notFound()
2421
const actionsPage = await getLandingPage("actions", locale)
2522

26-
const icon = action.icon as Media | undefined
27-
const iconUrl = getMediaUrl(icon?.url)
28-
const triggers = action.trigger as Media | undefined
29-
const functionDefs = action.functiondefinitions as Media | undefined
30-
const [extractedTriggers, extractedFunctionDefs] = await Promise.all([fetchMediaJson(triggers), fetchMediaJson(functionDefs)])
31-
.then(([triggerJson, functionDefJson]) => [extractTriggersFromJson(triggerJson), extractFunctionDefsFromJson(functionDefJson)] as const)
32-
.catch(() => [[], []] as const)
33-
const triggerItems = extractedTriggers.map((item) => ({
23+
const module = action.module as Media | undefined
24+
const moduleJson = await fetchMediaJson(module).catch(() => null)
25+
const extractedFlowTypes = extractFlowTypesFromJson(moduleJson)
26+
const extractedFunctionDefinitions = extractFunctionDefinitionsFromJson(moduleJson)
27+
const flowTypeItems = extractedFlowTypes.map((item) => ({
3428
item,
3529
icon: getIcon(item.displayIcon, 32),
3630
}))
37-
const functionDefItems = extractedFunctionDefs.map((item) => ({
31+
const functionDefinitionItems = extractedFunctionDefinitions.map((item) => ({
3832
item,
39-
icon: getIcon("function", 32),
33+
icon: getIcon(item.displayIcon ?? "function", 32),
4034
}))
4135
const references = (action.references ?? []).filter((reference): reference is Exclude<typeof reference, number> => typeof reference !== "number")
4236
const tags = (action.tags ?? []).filter((tag): tag is string => Boolean(tag))
4337
const actionsBlock = findPageBlock(actionsPage, "actions")
4438
const referencesLabel = actionsBlock?.referencesLabel ?? (locale === "de" ? "Referenzen" : "References")
39+
const emptyDefinitionLabels = {
40+
flowTypes: actionsBlock?.noFlowTypesFoundLabel ?? (locale === "de" ? "Keine FlowTypes gefunden." : "No flow types found."),
41+
functionDefinitions: actionsBlock?.noFunctionDefinitionsFoundLabel ?? (locale === "de" ? "Keine FunctionDefinitions gefunden." : "No function definitions found."),
42+
both: actionsBlock?.noActionDefinitionsFoundLabel ?? (locale === "de" ? "Keine FlowTypes oder FunctionDefinitions gefunden." : "No flow types or function definitions found."),
43+
}
4544

4645
return (
4746
<>
@@ -57,9 +56,9 @@ export default async function ActionDetailPage({ params }: { params: LocaleSlugP
5756
<div className="relative z-10 flex flex-col gap-8">
5857
<div className="flex flex-col gap-4 sm:flex-row sm:justify-between">
5958
<div className="flex flex-col gap-6 sm:flex-row sm:items-start">
60-
{iconUrl && (
61-
<div className="relative size-20 shrink-0 overflow-hidden rounded-3xl border border-white/10 bg-light">
62-
<Image src={iconUrl} alt={icon?.alt ?? action.title} fill sizes="80px" className="object-contain p-2" />
59+
{action.icon && (
60+
<div className="flex size-20 shrink-0 items-center justify-center overflow-hidden rounded-3xl border border-white/10 bg-light text-white">
61+
{getIcon(action.icon, 40)}
6362
</div>
6463
)}
6564

@@ -76,17 +75,10 @@ export default async function ActionDetailPage({ params }: { params: LocaleSlugP
7675
)}
7776
</div>
7877
</div>
79-
80-
{action.documentation?.label && action.documentation?.url && (
81-
<HapticButtonLink href={action.documentation.url} variant="normal" className="text-sm!">
82-
<IconExternalLink size={16} />
83-
{action.documentation.label}
84-
</HapticButtonLink>
85-
)}
8678
</div>
8779
{action.description && <div className="max-w-3xl whitespace-pre-line text-sm leading-6 text-secondary">{action.description}</div>}
8880

89-
<ActionTriggerView locale={locale} triggers={triggerItems} functionDefs={functionDefItems} />
81+
<ActionTriggerView locale={locale} flowTypes={flowTypeItems} functionDefinitions={functionDefinitionItems} emptyLabels={emptyDefinitionLabels} />
9082

9183
{references.length > 0 && (
9284
<div className="space-y-3">

src/blocks/ActionBlock.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,26 @@ export const ActionBlock: Block = {
4242
localized: true,
4343
defaultValue: "References",
4444
},
45+
{
46+
name: "noFlowTypesFoundLabel",
47+
type: "text",
48+
required: true,
49+
localized: true,
50+
defaultValue: "No flow types found.",
51+
},
52+
{
53+
name: "noFunctionDefinitionsFoundLabel",
54+
type: "text",
55+
required: true,
56+
localized: true,
57+
defaultValue: "No function definitions found.",
58+
},
59+
{
60+
name: "noActionDefinitionsFoundLabel",
61+
type: "text",
62+
required: true,
63+
localized: true,
64+
defaultValue: "No flow types or function definitions found.",
65+
},
4566
],
4667
}

src/collections/actions.ts

Lines changed: 23 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,33 @@
11
import type { CollectionConfig } from "payload"
22

33
export const Actions: CollectionConfig = {
4-
slug: "actions",
5-
admin: {
6-
useAsTitle: "title",
7-
defaultColumns: ["title", "updatedAt"],
8-
},
9-
access: {
10-
read: () => true,
11-
create: ({ req }) => Boolean(req.user),
12-
update: ({ req }) => Boolean(req.user),
13-
delete: ({ req }) => Boolean(req.user),
14-
},
15-
fields: [
16-
{
17-
name: "title",
18-
type: "text",
19-
required: true,
20-
localized: true,
4+
slug: "actions",
5+
admin: {
6+
defaultColumns: ["module", "tags", "updatedAt"],
217
},
22-
{
23-
name: "slug",
24-
type: "text",
25-
required: true,
26-
unique: true,
27-
index: true,
28-
admin: {
29-
description: "URL slug for the action subpage, e.g. 'slack-sync'.",
30-
},
8+
access: {
9+
read: () => true,
10+
create: ({ req }) => Boolean(req.user),
11+
update: ({ req }) => Boolean(req.user),
12+
delete: ({ req }) => Boolean(req.user),
3113
},
32-
{
33-
name: "shortDescription",
34-
type: "textarea",
35-
localized: true,
36-
},
37-
{
38-
name: "description",
39-
type: "textarea",
40-
localized: true,
41-
},
42-
{
43-
name: "icon",
44-
type: "upload",
45-
relationTo: "media",
46-
},
47-
{
48-
name: "trigger",
49-
type: "upload",
50-
relationTo: "media",
51-
},
52-
{
53-
name: "functiondefinitions",
54-
type: "upload",
55-
relationTo: "media",
56-
},
57-
{
58-
name: "tags",
59-
type: "text",
60-
hasMany: true,
61-
},
62-
{
63-
name: "documentation",
64-
type: "group",
65-
fields: [
14+
fields: [
6615
{
67-
name: "label",
68-
type: "text",
69-
localized: true,
16+
name: "module",
17+
type: "upload",
18+
relationTo: "media",
19+
required: true,
7020
},
7121
{
72-
name: "url",
73-
type: "text",
22+
name: "tags",
23+
type: "text",
24+
hasMany: true,
7425
},
75-
],
76-
},
77-
{
78-
name: "references",
79-
type: "relationship",
80-
relationTo: "actions",
81-
hasMany: true,
82-
},
83-
],
26+
{
27+
name: "references",
28+
type: "relationship",
29+
relationTo: "actions",
30+
hasMany: true,
31+
},
32+
],
8433
}

src/components/ActionTriggerCard.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
"use client"
22

33
import { BaseAccordionItem } from "@/components/ui/Accordion"
4-
import type { ExtractedActionTriggerItem } from "@/lib/actionTriggerExtraction"
4+
import type { ExtractedFlowType, ExtractedFunctionDefinition } from "@/lib/actionExtraction"
55
import { type ReactNode, useCallback, useMemo, useState } from "react"
66
import { Card } from "./ui/Card"
77

88
interface ActionTriggerCardProps {
9-
type: "trigger" | "functionDef"
10-
item: ExtractedActionTriggerItem
9+
type: "flowType" | "functionDefinition"
10+
item: ExtractedFlowType | ExtractedFunctionDefinition
1111
icon: ReactNode
1212
}
1313

1414
export function ActionTriggerCard({ type, item, icon }: ActionTriggerCardProps) {
1515
const [openItem, setOpenItem] = useState<number | null>(null)
16-
const parameters = item.kind === "functionDef" ? item.parameters : item.settings
17-
const label = type === "trigger" ? "Trigger" : "FunctionDefinition"
18-
const parameterLabel = type === "trigger" ? "Settings" : "Parameters"
16+
const parameters = item.kind === "functionDefinition" ? item.parameters : item.kind === "flowType" ? item.settings : []
17+
const label = type === "flowType" ? "FlowType" : "FunctionDefinition"
18+
const parameterLabel = type === "flowType" ? "Settings" : "Parameters"
1919
const toggleItem = useCallback((index: number) => {
2020
setOpenItem((previousItem) => (previousItem === index ? null : index))
2121
}, [])
2222
const parameterList = useMemo(
2323
() => (
2424
<div className="flex flex-col gap-2">
2525
{parameters.map((parameter) => (
26-
<div key={parameter.id} className="rounded-md bg-white/2 p-2">
27-
<div className="text-xs font-medium text-secondary">{parameter.name || parameter.identifier}</div>
26+
<div key={parameter.id} className="rounded-md bg-primary/50 p-2">
27+
<div className="text-xs font-medium text-white">{parameter.name || parameter.identifier}</div>
2828
{parameter.description && <div className="mt-1 text-xs leading-5 text-tertiary">{parameter.description}</div>}
2929
</div>
3030
))}
@@ -34,14 +34,14 @@ export function ActionTriggerCard({ type, item, icon }: ActionTriggerCardProps)
3434
)
3535

3636
return (
37-
<Card size="sm" className="bg-primary">
37+
<Card size="sm" className="bg-light">
3838
<div className="relative z-10 flex h-full flex-col gap-2">
3939
<div className="flex items-start justify-between gap-3">
4040
<div className="flex min-w-0 items-center gap-2">
41-
<div className="flex size-12 shrink-0 items-center justify-center rounded-lg border border-white/5 bg-light text-brand">{icon}</div>
41+
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg border border-white/5 bg-light text-brand">{icon}</div>
4242
<div className="min-w-0">
43-
<p className="text-xs font-medium tracking-wider text-tertiary">{label}</p>
44-
<h3 className="mt-1 truncate text-base tracking-wide font-semibold text-white">{item.name || item.identifier}</h3>
43+
<p className="text-xs tracking-wide text-tertiary">{label}</p>
44+
<h3 className="truncate tracking-wide text-white">{item.name || item.identifier}</h3>
4545
</div>
4646
</div>
4747
<div className="shrink-0 rounded-lg border border-white/5 bg-light px-2 py-1 text-xs text-tertiary">{item.identifier}</div>
@@ -57,7 +57,8 @@ export function ActionTriggerCard({ type, item, icon }: ActionTriggerCardProps)
5757
isOpen={openItem === 0}
5858
onToggle={toggleItem}
5959
className="rounded-lg before:bg-none"
60-
questionClassname="pl-5 pr-2 py-1 text-sm lg:text-sm"
60+
questionClassname="pl-3 pr-1 py-0 text-sm lg:text-sm"
61+
answerClassname="px-2 pb-2 pt-0"
6162
answer={parameterList}
6263
/>
6364
</div>

src/components/ActionTriggerView.tsx

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,54 @@
33
import { type ReactNode, useState } from "react"
44
import { ActionTriggerCard } from "@/components/ActionTriggerCard"
55
import { Switch, type SwitchOption } from "@/components/ui/Switch"
6-
import type { ExtractedActionTriggerItem, ExtractedFunctionDef, ExtractedTrigger } from "@/lib/actionTriggerExtraction"
6+
import type { ExtractedFlowType, ExtractedFunctionDefinition } from "@/lib/actionExtraction"
77

88
interface ActionTriggerViewProps {
99
locale: string
10-
triggers: Array<{ item: ExtractedTrigger; icon: ReactNode }>
11-
functionDefs: Array<{ item: ExtractedFunctionDef; icon: ReactNode }>
10+
flowTypes: Array<{ item: ExtractedFlowType; icon: ReactNode }>
11+
functionDefinitions: Array<{ item: ExtractedFunctionDefinition; icon: ReactNode }>
12+
emptyLabels: {
13+
flowTypes: string
14+
functionDefinitions: string
15+
both: string
16+
}
1217
}
1318

14-
type ViewMode = "both" | "triggers" | "functionDefs"
19+
type ViewMode = "both" | "flowTypes" | "functionDefinitions"
1520

1621
interface DisplayItem {
17-
type: "trigger" | "functionDef"
18-
item: ExtractedActionTriggerItem
22+
type: "flowType" | "functionDefinition"
23+
item: ExtractedFlowType | ExtractedFunctionDefinition
1924
icon: ReactNode
2025
}
2126

22-
export function ActionTriggerView({ locale, triggers, functionDefs }: ActionTriggerViewProps) {
27+
export function ActionTriggerView({ locale, flowTypes, functionDefinitions, emptyLabels }: ActionTriggerViewProps) {
2328
const [viewMode, setViewMode] = useState<ViewMode>("both")
2429
const viewModeOptions: SwitchOption<ViewMode>[] = [
30+
{ value: "flowTypes", label: "FlowTypes" },
31+
{ value: "functionDefinitions", label: "FunctionDefinitions" },
2532
{ value: "both", label: locale === "en" ? "Both" : "Beide" },
26-
{ value: "triggers", label: "Triggers" },
27-
{ value: "functionDefs", label: "FunctionDefinitions" },
2833
]
2934

3035
const visibleItems: DisplayItem[] = [
31-
...(viewMode === "both" || viewMode === "triggers" ? triggers.map(({ item, icon }) => ({ type: "trigger" as const, item, icon })) : []),
32-
...(viewMode === "both" || viewMode === "functionDefs" ? functionDefs.map(({ item, icon }) => ({ type: "functionDef" as const, item, icon })) : []),
36+
...(viewMode === "both" || viewMode === "flowTypes" ? flowTypes.map(({ item, icon }) => ({ type: "flowType" as const, item, icon })) : []),
37+
...(viewMode === "both" || viewMode === "functionDefinitions" ? functionDefinitions.map(({ item, icon }) => ({ type: "functionDefinition" as const, item, icon })) : []),
3338
]
39+
const emptyLabel = viewMode === "both" ? emptyLabels.both : viewMode === "flowTypes" ? emptyLabels.flowTypes : emptyLabels.functionDefinitions
3440

3541
return (
3642
<div className="space-y-6">
37-
<Switch value={viewMode} options={viewModeOptions} onChange={setViewMode} className="w-max" />
38-
39-
<div className="flex flex-col gap-4 md:hidden">
40-
{visibleItems.map(({ type, item, icon }) => (
41-
<ActionTriggerCard key={item.id} type={type} item={item} icon={icon} />
42-
))}
43-
</div>
43+
<Switch value={viewMode} options={viewModeOptions} onChange={setViewMode} className="w-max max-w-full overflow-x-auto" fitContent />
44+
45+
{visibleItems.length > 0 ? (
46+
<div className="flex flex-col gap-4 md:hidden">
47+
{visibleItems.map(({ type, item, icon }) => (
48+
<ActionTriggerCard key={item.id} type={type} item={item} icon={icon} />
49+
))}
50+
</div>
51+
) : (
52+
<p className="px-2 text-sm text-tertiary">{emptyLabel}</p>
53+
)}
4454
</div>
4555
)
4656
}

src/components/ActionsPageClient.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function ActionsPageClient({ actions, locale, content }: ActionsPageClien
6161
clearable
6262
className="text-white!"
6363
/>
64-
<div className="grid gap-4 md:grid-cols-2">
64+
<div className="flex flex-col gap-4">
6565
{filteredActions.map((action) => (
6666
<ActionCard key={action.id} action={action} locale={locale} />
6767
))}

src/components/IconRenderer.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import "server-only"
2-
31
import { IconRenderer as PayloadIconRenderer } from "@mvriu5/payload-icon-picker/client"
42
import type { CSSProperties, Key, ReactNode } from "react"
53
import { simpleIconsAdapter } from "@mvriu5/payload-icon-picker/adapters/simple-icons"

0 commit comments

Comments
 (0)