Files
community-rule/app/(app)/invite/_components/MakeACopyDialog.tsx
T

123 lines
3.7 KiB
TypeScript

"use client";
import { useState } from "react";
import Button from "../../../components/buttons/Button";
import TextInput from "../../../components/controls/TextInput";
import Dialog from "../../../components/modals/Dialog";
import { useTranslation } from "../../../contexts/MessagesContext";
const COMMUNITY_NAME_MAX = 48;
export function MakeACopyDialog({
isOpen,
onClose,
possessive,
countsSpoken,
viewerEmail,
busy,
error,
onConfirm,
}: {
isOpen: boolean;
onClose: () => void;
possessive: string;
countsSpoken: string;
viewerEmail: string;
busy: boolean;
error: string | null;
onConfirm: (_title: string) => void;
}) {
const t = useTranslation("pages.invite");
const [title, setTitle] = useState("");
const [nameError, setNameError] = useState<string | null>(null);
const trimmed = title.trim();
return (
<Dialog
isOpen={isOpen}
onClose={() => {
if (!busy) onClose();
}}
title={t("dialogTitle")}
description={t("dialogDescription")
.replace("{possessive}", possessive)
.replace("{counts}", countsSpoken)}
footer={
<>
<Button
type="button"
size="medium"
buttonType="outline"
palette="default"
onClick={onClose}
disabled={busy}
>
{t("cancel")}
</Button>
<Button
type="button"
size="medium"
buttonType="filled"
palette="default"
disabled={busy}
onClick={() => {
if (trimmed.length === 0) {
setNameError(t("nameRequired"));
return;
}
onConfirm(trimmed);
}}
>
{t("confirm")}
</Button>
</>
}
>
<div className="flex w-full min-w-0 flex-col gap-[var(--space-500)]">
<TextInput
id="make-copy-community-name"
inputSize="small"
showHelpIcon={false}
label={t("communityNameLabel")}
placeholder={t("communityNamePlaceholder")}
value={title}
maxLength={COMMUNITY_NAME_MAX}
error={Boolean(nameError)}
textHint={
nameError ||
t("characterCountTemplate")
.replace("{current}", String(title.length))
.replace("{max}", String(COMMUNITY_NAME_MAX))
}
onChange={(event) => {
setTitle(event.target.value);
setNameError(null);
}}
/>
<div className="grid w-full grid-cols-1 gap-[var(--space-300)] sm:grid-cols-2">
<div className="rounded-[var(--radius-400)] bg-[var(--color-surface-invert-brand-primary)] p-[var(--space-500)] text-[var(--color-content-invert-primary)]">
<p className="text-small-label">{t("comesWithTitle")}</p>
<p className="mt-[var(--space-200)] text-small-paragraph">
{t("comesWithBody")}
</p>
</div>
<div className="rounded-[var(--radius-400)] bg-[var(--color-surface-invert-brand-primary)] p-[var(--space-500)] text-[var(--color-content-invert-primary)]">
<p className="text-small-label">{t("staysWithTitle")}</p>
<p className="mt-[var(--space-200)] text-small-paragraph">
{t("staysWithBody")}
</p>
</div>
</div>
<p className="text-small-paragraph text-[var(--color-content-default-tertiary)]">
{t("saveTo").replace("{email}", viewerEmail)}
</p>
{error ? (
<p role="alert" className="text-small-paragraph text-[var(--color-border-default-utility-negative)]">
{error}
</p>
) : null}
</div>
</Dialog>
);
}