diff --git a/apps/web/src/components/tools/qr-generate-settings.tsx b/apps/web/src/components/tools/qr-generate-settings.tsx index 4316e465..7a835f48 100644 --- a/apps/web/src/components/tools/qr-generate-settings.tsx +++ b/apps/web/src/components/tools/qr-generate-settings.tsx @@ -1,158 +1,774 @@ -import { Download, Loader2 } from "lucide-react"; -import { useState } from "react"; -import { formatHeaders } from "@/lib/api"; -export function QrGenerateSettings() { - const [text, setText] = useState(""); - const [size, setSize] = useState(400); - const [errorCorrection, setErrorCorrection] = useState<"L" | "M" | "Q" | "H">("M"); - const [foreground, setForeground] = useState("#000000"); - const [background, setBackground] = useState("#FFFFFF"); - const [processing, setProcessing] = useState(false); - const [error, setError] = useState(null); - const [downloadUrl, setDownloadUrl] = useState(null); - const [previewUrl, setPreviewUrl] = useState(null); +import { + Contact, + Download, + Globe, + ImagePlus, + Mail, + MessageSquare, + Phone, + Type, + Wifi, + X, +} from "lucide-react"; +import QRCodeStyling from "qr-code-styling"; +import { useCallback, useRef } from "react"; +import { CollapsibleSection } from "@/components/common/collapsible-section"; +import { + type ContentType, + type CornerDotType, + type CornerSquareType, + type DotType, + type DownloadFormat, + encodeQrData, + useQrStore, +} from "@/stores/qr-store"; - const handleGenerate = async () => { - if (!text) return; +// ── Content type tab definitions ───────────────────────────────────── - setProcessing(true); - setError(null); - setDownloadUrl(null); - setPreviewUrl(null); +const CONTENT_TYPES: { + id: ContentType; + label: string; + icon: React.ComponentType<{ className?: string }>; +}[] = [ + { id: "url", label: "URL", icon: Globe }, + { id: "text", label: "Text", icon: Type }, + { id: "wifi", label: "WiFi", icon: Wifi }, + { id: "vcard", label: "vCard", icon: Contact }, + { id: "email", label: "Email", icon: Mail }, + { id: "phone", label: "Phone", icon: Phone }, + { id: "sms", label: "SMS", icon: MessageSquare }, +]; - try { - const res = await fetch("/api/v1/tools/qr-generate", { - method: "POST", - headers: formatHeaders({ "Content-Type": "application/json" }), - body: JSON.stringify({ text, size, errorCorrection, foreground, background }), - }); +// ── Style option definitions ───────────────────────────────────────── - if (!res.ok) { - const body = await res.json().catch(() => ({})); - throw new Error(body.error || `Generation failed: ${res.status}`); - } +const DOT_TYPES: { value: DotType; label: string }[] = [ + { value: "square", label: "Square" }, + { value: "rounded", label: "Rounded" }, + { value: "dots", label: "Dots" }, + { value: "classy", label: "Classy" }, + { value: "classy-rounded", label: "Classy Rnd" }, + { value: "extra-rounded", label: "Extra Rnd" }, +]; - const result = await res.json(); - setDownloadUrl(result.downloadUrl); - setPreviewUrl(result.downloadUrl); - } catch (err) { - setError(err instanceof Error ? err.message : "Generation failed"); - } finally { - setProcessing(false); - } - }; +const CORNER_SQUARE_TYPES: { value: CornerSquareType; label: string }[] = [ + { value: "square", label: "Square" }, + { value: "rounded", label: "Rounded" }, + { value: "dot", label: "Dot" }, + { value: "extra-rounded", label: "Extra Rnd" }, +]; +const CORNER_DOT_TYPES: { value: CornerDotType; label: string }[] = [ + { value: "square", label: "Square" }, + { value: "rounded", label: "Rounded" }, + { value: "dot", label: "Dot" }, +]; + +const DOWNLOAD_FORMATS: { value: DownloadFormat; label: string; desc: string }[] = [ + { value: "png", label: "PNG", desc: "Best for digital" }, + { value: "svg", label: "SVG", desc: "Best for print" }, + { value: "jpeg", label: "JPEG", desc: "Smaller file" }, + { value: "webp", label: "WebP", desc: "Modern format" }, +]; + +// ── Data entry forms per content type ──────────────────────────────── + +const INPUT_CLASS = + "w-full mt-0.5 px-2 py-1.5 rounded border border-border bg-background text-sm text-foreground"; +const TEXTAREA_CLASS = `${INPUT_CLASS} resize-none`; + +function UrlForm() { + const { textData, setTextData } = useQrStore(); return ( -
+
+ + setTextData(e.target.value)} + placeholder="https://example.com" + className={INPUT_CLASS} + data-testid="qr-input-url" + /> +
+ ); +} + +function TextForm() { + const { textData, setTextData } = useQrStore(); + return ( +
+ +