diff --git a/src/components/routes/create-plan/CreatePlan.test.tsx b/src/components/routes/create-plan/CreatePlan.test.tsx index 5d4807a2..83cf990c 100644 --- a/src/components/routes/create-plan/CreatePlan.test.tsx +++ b/src/components/routes/create-plan/CreatePlan.test.tsx @@ -81,6 +81,16 @@ vi.mock( describe("CreatePlan Component", () => { beforeEach(() => { vi.clearAllMocks(); + // `clearAllMocks` resets call history but keeps implementations set via + // `mockReturnValue`. The navigation tests force `useBlocker` into a + // "blocked" state, which opens the navigation dialog; reset it here so + // that leaked dialog doesn't `aria-hidden` the form in later tests. + vi.mocked(useBlocker).mockReturnValue({ + state: "unblocked", + proceed: undefined, + reset: undefined, + location: undefined, + }); vi.mocked(useParams).mockReturnValue({ groupId: "test-group-id" }); vi.mocked(useLocation).mockReturnValue({ pathname: "/groups/test-group-id/plan/new", @@ -428,13 +438,35 @@ describe("CreatePlan Component", () => { screen.getByRole("button", { name: 'Create "Brand New Tag"' }), ); + // Clicking the create option opens a multi-language dialog with the + // typed name pre-filled for the default (EN) language. + const createTagButton = await screen.findByRole("button", { + name: "Create Tag", + }); + fireEvent.click(createTagButton); + await waitFor(() => { expect(axiosInstance.post).toHaveBeenCalledWith( "/api/v1/cms/tags", - expect.objectContaining({ name: "Brand New Tag" }), + expect.objectContaining({ + metadata: expect.arrayContaining([ + expect.objectContaining({ + language: "EN", + name: "Brand New Tag", + }), + ]), + }), expect.any(Object), ); }); + + // Successful creation closes the dialog; wait for it so the Radix + // scroll-lock is torn down and does not leak into later tests. + await waitFor(() => { + expect( + screen.queryByRole("button", { name: "Create Tag" }), + ).not.toBeInTheDocument(); + }); }); it("shows no image preview initially", () => { diff --git a/src/components/routes/create-plan/CreatePlan.tsx b/src/components/routes/create-plan/CreatePlan.tsx index d9386802..9f57b516 100644 --- a/src/components/routes/create-plan/CreatePlan.tsx +++ b/src/components/routes/create-plan/CreatePlan.tsx @@ -21,6 +21,8 @@ import { planSchema } from "@/schema/PlanSchema"; import { z } from "zod"; import { useTranslate } from "@tolgee/react"; import PlanTagSearchInput from "./PlanTagSearchInput"; +import PlanVideosSection from "./PlanVideosSection"; +import type { PlanVideoSummary } from "@/components/routes/task/api/planApi"; import { planTagsToIds, type PlanTagSummary, @@ -259,6 +261,11 @@ const Createplan = () => { } }, [planId, planData]); + const planVideos = useMemo( + () => (Array.isArray(planData?.videos) ? planData.videos : []), + [planData], + ); + const canUpdate = form.formState.isDirty; const hasUnsavedChanges = canUpdate && !form.formState.isSubmitSuccessful; @@ -933,6 +940,11 @@ const Createplan = () => { )} /> + + {planId && ( + + )} +
{isCreateMode ? ( [tag.id, tag.name])), ); const [showCreateDialog, setShowCreateDialog] = useState(false); - const [activeLanguages, setActiveLanguages] = useState(["EN"]); - const [languageData, setLanguageData] = useState>({ + const [activeLanguages, setActiveLanguages] = useState([ + "EN", + ]); + const [languageData, setLanguageData] = useState< + Record + >({ EN: { name: "", description: "" }, BO: { name: "", description: "" }, ZH: { name: "", description: "" }, @@ -155,7 +159,9 @@ const PlanTagSearchInput = ({ for (const lang of activeLanguages) { const data = languageData[lang]; if (!data.name.trim()) { - toast.error(`Name is required for ${PLAN_LANGUAGE.find(l => l.value === lang)?.label}`); + toast.error( + `Name is required for ${PLAN_LANGUAGE.find((l) => l.value === lang)?.label}`, + ); return; } metadata.push({ @@ -182,7 +188,7 @@ const PlanTagSearchInput = ({ const updateLanguageField = ( lang: LanguageCode, field: keyof LanguageData, - value: string + value: string, ) => { setLanguageData((prev) => ({ ...prev, @@ -331,7 +337,10 @@ const PlanTagSearchInput = ({
- {PLAN_LANGUAGE.filter((lang) => !activeLanguages.includes(lang.value as LanguageCode)).length > 0 && ( + {PLAN_LANGUAGE.filter( + (lang) => + !activeLanguages.includes(lang.value as LanguageCode), + ).length > 0 && (
{activeLanguages.map((lang) => { - const langLabel = PLAN_LANGUAGE.find((l) => l.value === lang)?.label || lang; + const langLabel = + PLAN_LANGUAGE.find((l) => l.value === lang)?.label || lang; return ( -
+
{activeLanguages.length > 1 && ( )} -
{langLabel}
+
+ {langLabel} +
updateLanguageField(lang, "name", e.target.value)} + onChange={(e) => + updateLanguageField(lang, "name", e.target.value) + } placeholder="Tag name" required className="bg-white dark:bg-[#181818]" @@ -386,7 +408,13 @@ const PlanTagSearchInput = ({