"use client"; import { useState, useCallback } from "react"; import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; /** Maps channel IDs to the instructions for finding the user ID. */ const CHANNEL_ID_HELP: Record = { telegram: "telegramIdHelp", discord: "discordIdHelp", email: "emailIdHelp", }; interface ChannelUsersProps { tenantName: string; /** Currently enabled channel packages (e.g. ["telegram", "discord"]) */ enabledChannels: string[]; /** Current channelUsers from the PiecedTenant spec */ initialChannelUsers: Record; } export function ChannelUsers({ tenantName, enabledChannels, initialChannelUsers, }: ChannelUsersProps) { const t = useTranslations("channelUsers"); const router = useRouter(); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const [inputValues, setInputValues] = useState>({}); const [channelUsers, setChannelUsers] = useState>(initialChannelUsers); const updateChannelUsers = useCallback( async (updated: Record) => { setSaving(true); setError(""); try { const res = await fetch(`/api/tenants/${tenantName}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ channelUsers: updated }), }); if (!res.ok) { const data = await res.json(); throw new Error(data.error || "Update failed"); } setChannelUsers(updated); router.refresh(); } catch (e: any) { setError(e.message); } finally { setSaving(false); } }, [tenantName, router] ); const handleAdd = useCallback( (channel: string) => { const userId = inputValues[channel]?.trim(); if (!userId) return; const current = channelUsers[channel] || []; if (current.includes(userId)) { setError(t("alreadyAdded")); return; } const updated = { ...channelUsers, [channel]: [...current, userId], }; setInputValues((prev) => ({ ...prev, [channel]: "" })); updateChannelUsers(updated); }, [channelUsers, inputValues, updateChannelUsers, t] ); const handleRemove = useCallback( (channel: string, userId: string) => { const current = channelUsers[channel] || []; const updated = { ...channelUsers, [channel]: current.filter((id) => id !== userId), }; updateChannelUsers(updated); }, [channelUsers, updateChannelUsers] ); if (enabledChannels.length === 0) return null; return (

{t("title")}

{t("description")}

{error && (
{error}
)} {enabledChannels.map((channel) => { const users = channelUsers[channel] || []; const helpKey = CHANNEL_ID_HELP[channel]; return (

{channel}

{users.length} {t("users")}
{helpKey && (

{t(helpKey)}

)} {/* Current users */} {users.length > 0 && (
{users.map((userId) => ( {userId} ))}
)} {/* Add user */}
setInputValues((prev) => ({ ...prev, [channel]: e.target.value, })) } onKeyDown={(e) => { if (e.key === "Enter") handleAdd(channel); }} placeholder={t("placeholder")} className="flex-1 px-3 py-2 bg-surface-1 border border-border rounded-lg text-sm text-text-primary font-mono placeholder:text-text-muted focus:outline-none focus:ring-1 focus:ring-accent focus:border-accent transition-colors" />
); })}
); }