From 10d4de5d691135c4bf305ca6d0b930bbfc1d3bc3 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Thu, 5 Feb 2026 17:57:50 -0600 Subject: [PATCH] fix(#338): Disable QuickCaptureWidget in production with Coming Soon - Show Coming Soon placeholder in production for both widget versions - Widget available in development mode only - Added tests verifying environment-based behavior - Use runtime check for testability (isDevelopment function vs constant) Refs #338 Co-Authored-By: Claude Opus 4.5 --- .../dashboard/QuickCaptureWidget.tsx | 37 +++++++- .../__tests__/QuickCaptureWidget.test.tsx | 93 +++++++++++++++++++ .../components/widgets/QuickCaptureWidget.tsx | 54 ++++++++++- .../__tests__/QuickCaptureWidget.test.tsx | 52 ++++++++++- 4 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/components/dashboard/__tests__/QuickCaptureWidget.test.tsx diff --git a/apps/web/src/components/dashboard/QuickCaptureWidget.tsx b/apps/web/src/components/dashboard/QuickCaptureWidget.tsx index 3a763e8..96cac82 100644 --- a/apps/web/src/components/dashboard/QuickCaptureWidget.tsx +++ b/apps/web/src/components/dashboard/QuickCaptureWidget.tsx @@ -3,8 +3,19 @@ import { useState } from "react"; import { Button } from "@mosaic/ui"; import { useRouter } from "next/navigation"; +import { ComingSoon } from "@/components/ui/ComingSoon"; -export function QuickCaptureWidget(): React.JSX.Element { +/** + * Check if we're in development mode (runtime check for testability) + */ +function isDevelopment(): boolean { + return process.env.NODE_ENV === "development"; +} + +/** + * Internal Quick Capture Widget implementation + */ +function QuickCaptureWidgetInternal(): React.JSX.Element { const [idea, setIdea] = useState(""); const router = useRouter(); @@ -48,3 +59,27 @@ export function QuickCaptureWidget(): React.JSX.Element { ); } + +/** + * Quick Capture Widget (Dashboard version) + * + * In production: Shows Coming Soon placeholder + * In development: Full widget functionality + */ +export function QuickCaptureWidget(): React.JSX.Element { + // In production, show Coming Soon placeholder + if (!isDevelopment()) { + return ( +
+ +
+ ); + } + + // In development, show full widget functionality + return ; +} diff --git a/apps/web/src/components/dashboard/__tests__/QuickCaptureWidget.test.tsx b/apps/web/src/components/dashboard/__tests__/QuickCaptureWidget.test.tsx new file mode 100644 index 0000000..91cac92 --- /dev/null +++ b/apps/web/src/components/dashboard/__tests__/QuickCaptureWidget.test.tsx @@ -0,0 +1,93 @@ +/** + * QuickCaptureWidget (Dashboard) Component Tests + * Tests environment-based behavior + */ + +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { QuickCaptureWidget } from "../QuickCaptureWidget"; + +// Mock next/navigation +vi.mock("next/navigation", () => ({ + useRouter: (): { push: () => void } => ({ + push: vi.fn(), + }), +})); + +describe("QuickCaptureWidget (Dashboard)", (): void => { + beforeEach((): void => { + vi.clearAllMocks(); + }); + + afterEach((): void => { + vi.unstubAllEnvs(); + }); + + describe("Development mode", (): void => { + beforeEach((): void => { + vi.stubEnv("NODE_ENV", "development"); + }); + + it("should render the widget form in development", (): void => { + render(); + + // Should show the header + expect(screen.getByText("Quick Capture")).toBeInTheDocument(); + // Should show the textarea + expect(screen.getByRole("textbox")).toBeInTheDocument(); + // Should show the Save Note button + expect(screen.getByRole("button", { name: /save note/i })).toBeInTheDocument(); + // Should show the Create Task button + expect(screen.getByRole("button", { name: /create task/i })).toBeInTheDocument(); + // Should NOT show Coming Soon badge + expect(screen.queryByText("Coming Soon")).not.toBeInTheDocument(); + }); + + it("should have a placeholder for the textarea", (): void => { + render(); + + const textarea = screen.getByRole("textbox"); + expect(textarea).toHaveAttribute("placeholder", "What's on your mind?"); + }); + }); + + describe("Production mode", (): void => { + beforeEach((): void => { + vi.stubEnv("NODE_ENV", "production"); + }); + + it("should show Coming Soon placeholder in production", (): void => { + render(); + + // Should show Coming Soon badge + expect(screen.getByText("Coming Soon")).toBeInTheDocument(); + // Should show feature name + expect(screen.getByText("Quick Capture")).toBeInTheDocument(); + // Should NOT show the textarea + expect(screen.queryByRole("textbox")).not.toBeInTheDocument(); + // Should NOT show the buttons + expect(screen.queryByRole("button", { name: /save note/i })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: /create task/i })).not.toBeInTheDocument(); + }); + + it("should show description in Coming Soon placeholder", (): void => { + render(); + + expect(screen.getByText(/jot down ideas for later organization/i)).toBeInTheDocument(); + }); + }); + + describe("Test mode (non-development)", (): void => { + beforeEach((): void => { + vi.stubEnv("NODE_ENV", "test"); + }); + + it("should show Coming Soon placeholder in test mode", (): void => { + render(); + + // Test mode is not development, so should show Coming Soon + expect(screen.getByText("Coming Soon")).toBeInTheDocument(); + expect(screen.queryByRole("textbox")).not.toBeInTheDocument(); + }); + }); +}); diff --git a/apps/web/src/components/widgets/QuickCaptureWidget.tsx b/apps/web/src/components/widgets/QuickCaptureWidget.tsx index b201f6f..46085a2 100644 --- a/apps/web/src/components/widgets/QuickCaptureWidget.tsx +++ b/apps/web/src/components/widgets/QuickCaptureWidget.tsx @@ -1,12 +1,48 @@ /** * Quick Capture Widget - idea/brain dump input + * + * In production, shows a Coming Soon placeholder since the feature + * is not yet complete. Full functionality available in development mode. */ import { useState } from "react"; import { Send, Lightbulb } from "lucide-react"; import type { WidgetProps } from "@mosaic/shared"; -export function QuickCaptureWidget({ id: _id, config: _config }: WidgetProps): React.JSX.Element { +/** + * Check if we're in development mode (runtime check for testability) + */ +function isDevelopment(): boolean { + return process.env.NODE_ENV === "development"; +} + +/** + * Compact Coming Soon placeholder for widget contexts + */ +function WidgetComingSoon(): React.JSX.Element { + return ( +
+ {/* Lightbulb Icon */} +
+ ); +} + +/** + * Internal Quick Capture Widget implementation + */ +function QuickCaptureWidgetInternal({ id: _id, config: _config }: WidgetProps): React.JSX.Element { const [input, setInput] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [recentCaptures, setRecentCaptures] = useState([]); @@ -92,3 +128,19 @@ export function QuickCaptureWidget({ id: _id, config: _config }: WidgetProps): R ); } + +/** + * Quick Capture Widget + * + * In production: Shows Coming Soon placeholder + * In development: Full widget functionality + */ +export function QuickCaptureWidget(props: WidgetProps): React.JSX.Element { + // In production, show Coming Soon placeholder + if (!isDevelopment()) { + return ; + } + + // In development, show full widget functionality + return ; +} diff --git a/apps/web/src/components/widgets/__tests__/QuickCaptureWidget.test.tsx b/apps/web/src/components/widgets/__tests__/QuickCaptureWidget.test.tsx index 1fd2704..3c4d5e1 100644 --- a/apps/web/src/components/widgets/__tests__/QuickCaptureWidget.test.tsx +++ b/apps/web/src/components/widgets/__tests__/QuickCaptureWidget.test.tsx @@ -3,7 +3,7 @@ * Following TDD principles */ -import { describe, it, expect, vi, beforeEach } from "vitest"; +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { QuickCaptureWidget } from "../QuickCaptureWidget"; @@ -13,6 +13,12 @@ global.fetch = vi.fn() as typeof global.fetch; describe("QuickCaptureWidget", (): void => { beforeEach((): void => { vi.clearAllMocks(); + // Set development mode by default for existing tests + vi.stubEnv("NODE_ENV", "development"); + }); + + afterEach((): void => { + vi.unstubAllEnvs(); }); it("should render input field", (): void => { @@ -147,4 +153,48 @@ describe("QuickCaptureWidget", (): void => { expect(screen.getByText("Test note")).toBeInTheDocument(); }); }); + + describe("Environment-based behavior", (): void => { + it("should show Coming Soon placeholder in production", (): void => { + vi.stubEnv("NODE_ENV", "production"); + + render(); + + // Should show Coming Soon badge + expect(screen.getByText("Coming Soon")).toBeInTheDocument(); + // Should show feature name + expect(screen.getByText("Quick Capture")).toBeInTheDocument(); + // Should show description + expect( + screen.getByText(/Quickly jot down ideas for later organization/i) + ).toBeInTheDocument(); + // Should NOT show the input field + expect(screen.queryByRole("textbox")).not.toBeInTheDocument(); + // Should NOT show the submit button + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + }); + + it("should show full widget in development mode", (): void => { + vi.stubEnv("NODE_ENV", "development"); + + render(); + + // Should show the input field + expect(screen.getByRole("textbox")).toBeInTheDocument(); + // Should show the submit button + expect(screen.getByRole("button", { name: /submit/i })).toBeInTheDocument(); + // Should NOT show Coming Soon badge + expect(screen.queryByText("Coming Soon")).not.toBeInTheDocument(); + }); + + it("should show Coming Soon placeholder in test mode (non-development)", (): void => { + vi.stubEnv("NODE_ENV", "test"); + + render(); + + // Test mode is not development, so should show Coming Soon + expect(screen.getByText("Coming Soon")).toBeInTheDocument(); + expect(screen.queryByRole("textbox")).not.toBeInTheDocument(); + }); + }); });