From a08b9ee192ca3bf69d2a7d6b4cbcae346c2fb6fe Mon Sep 17 00:00:00 2001 From: RickjanHoornbeeck <51879@hoornbeeck.nl> Date: Mon, 4 May 2026 13:17:42 +0200 Subject: [PATCH 1/2] This fixed the error 500 in issue #226 --- console/src/views/campaign/CreateCampaign.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/console/src/views/campaign/CreateCampaign.tsx b/console/src/views/campaign/CreateCampaign.tsx index e002bd1c..cd6ea6ae 100644 --- a/console/src/views/campaign/CreateCampaign.tsx +++ b/console/src/views/campaign/CreateCampaign.tsx @@ -4,7 +4,7 @@ import { ProjectContext } from "@/contexts" import { useCallback, useContext, useMemo, useState } from "react" import type { ReactNode } from "react" import { useResolver } from "@/hooks" - +import api from "@/api" import { Button } from "@/components/ui/button" import { ArrowRight, Mail, MessageSquareDot, PlusIcon, Smartphone } from "lucide-react" import type { ChannelType } from "@/types" @@ -136,7 +136,9 @@ export function CreateCampaign({ open = false, onBeforeCreate, trigger }: Create data: {}, }) - navigate(`/projects/${project.id}/campaigns/${campaign.data.id}/templates/${template.id}`) + navigate( + `/projects/${project.id}/campaigns/${campaign.data.id}/templates/${template.id}`, + ) } } From 7b29b1671324e5648ea121564b2a8eae2880f2a9 Mon Sep 17 00:00:00 2001 From: RickjanHoornbeeck <51879@hoornbeeck.nl> Date: Mon, 4 May 2026 13:29:22 +0200 Subject: [PATCH 2/2] fix: error fix or something --- console/src/views/campaign/CreateCampaign.tsx | 256 ++++++++++++++++-- 1 file changed, 238 insertions(+), 18 deletions(-) diff --git a/console/src/views/campaign/CreateCampaign.tsx b/console/src/views/campaign/CreateCampaign.tsx index 0642a4d5..cd6ea6ae 100644 --- a/console/src/views/campaign/CreateCampaign.tsx +++ b/console/src/views/campaign/CreateCampaign.tsx @@ -1,13 +1,14 @@ import { useTranslation } from "react-i18next" import { useNavigate } from "react-router" import { ProjectContext } from "@/contexts" -import { useContext, useState } from "react" +import { useCallback, useContext, useMemo, useState } from "react" import type { ReactNode } from "react" import { useResolver } from "@/hooks" import api from "@/api" import { Button } from "@/components/ui/button" import { ArrowRight, Mail, MessageSquareDot, PlusIcon, Smartphone } from "lucide-react" import type { ChannelType } from "@/types" +import type { components } from "@/oapi/management.generated" import { Item, @@ -27,6 +28,18 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { Switch } from "@/components/ui/switch" +import { oapiClient } from "@/oapi/client" + +type Subscription = components["schemas"]["Subscription"] interface Channel { key: ChannelType @@ -47,19 +60,81 @@ export function CreateCampaign({ open = false, onBeforeCreate, trigger }: Create const navigate = useNavigate() const { t } = useTranslation() const [isOpen, setIsOpen] = useState(open) - const [selectingChannel, setSelectingChannel] = useState(null) + const [selectedChannel, setSelectedChannel] = useState(null) + const [transactional, setTransactional] = useState(false) + const [subscriptionId, setSubscriptionId] = useState("") + + const [subscriptions] = useResolver( + useCallback(async (): Promise => { + if (!project?.id) return [] + + const response = await oapiClient.GET("/api/admin/projects/{projectID}/subscriptions", { + params: { + path: { + projectID: project.id, + }, + query: { + limit: 100, + }, + }, + }) - async function selectChannel(channel: ChannelType) { - if (!project?.id || selectingChannel) { + if (response.error || !response.data?.results) { + return [] + } + + return response.data.results + }, [project?.id]), + ) + + const filteredSubscriptions = useMemo(() => { + if (!selectedChannel) return [] + return (subscriptions ?? []).filter( + (subscription) => subscription.channel === selectedChannel, + ) + }, [selectedChannel, subscriptions]) + + const subscriptionsLoading = subscriptions === null + async function create() { + if (!project?.id || !selectedChannel) { return } - setSelectingChannel(channel) + if (onBeforeCreate) { + await onBeforeCreate() + } + + const body: { + name: string + channel: components["schemas"]["Channel"] + transactional?: boolean + subscription_id?: string + } = { + name: generateProjectName(), + channel: selectedChannel, + transactional, + } - try { - if (onBeforeCreate) { - await onBeforeCreate() - } + if (transactional) { + body.subscription_id = undefined + } else if (subscriptionId) { + body.subscription_id = subscriptionId + } + + const campaign = await oapiClient.POST("/api/admin/projects/{projectID}/campaigns", { + params: { + path: { + projectID: project.id, + }, + }, + body, + }) + + if (campaign.data?.id) { + const template = await api.campaigns.templates.create(project.id, campaign.data.id, { + locale: project.locale, + data: {}, + }) navigate( `/projects/${project.id}/campaigns/${campaign.data.id}/templates/${template.id}`, @@ -112,8 +187,10 @@ export function CreateCampaign({ open = false, onBeforeCreate, trigger }: Create ))} + + {selectedChannel && ( +
+
+
+ +

+ {t( + "campaign.transactional.help", + "When enabled, subscription preference is ignored.", + )} +

+
+ { + setTransactional(checked) + if (checked) setSubscriptionId("") + }} + /> +
+ + {!transactional && ( +
+ + +
+ )} + + +
+ )} ) } + +const adjectives = [ + "adaptive", + "bold", + "bright", + "calm", + "clear", + "confident", + "connected", + "consistent", + "conversational", + "curated", + "direct", + "dynamic", + "effective", + "elegant", + "engaging", + "focused", + "friendly", + "impactful", + "informative", + "insightful", + "intentional", + "modern", + "personal", + "polished", + "proactive", + "relevant", + "responsive", + "smart", + "smooth", + "strategic", + "targeted", + "timely", + "trusted", + "unified", + "useful", + "warm", +] + +const names = [ + "announcement", + "beacon", + "broadcast", + "bulletin", + "campaign", + "cascade", + "connect", + "conversation", + "dispatch", + "engagement", + "experience", + "feature", + "followup", + "highlight", + "insight", + "invitation", + "journey", + "launch", + "message", + "moment", + "nudge", + "outreach", + "pulse", + "release", + "reminder", + "signal", + "spotlight", + "story", + "touchpoint", + "update", + "wave", +] + +function generateProjectName() { + const adjective = adjectives[Math.floor(Math.random() * adjectives.length)] + const name = names[Math.floor(Math.random() * names.length)] + return `${adjective} ${name}` +}