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";
// ── Content type tab definitions ─────────────────────────────────────
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 },
];
// ── Style option definitions ─────────────────────────────────────────
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 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 (
);
}
function WifiForm() {
const { wifiData, setWifiData } = useQrStore();
return (
);
}
function VCardForm() {
const { vcardData, setVcardData } = useQrStore();
const field = (label: string, key: keyof typeof vcardData, placeholder: string) => (
setVcardData({ [key]: e.target.value })}
placeholder={placeholder}
className={INPUT_CLASS}
/>
);
return (
{field("First Name", "firstName", "John")}
{field("Last Name", "lastName", "Doe")}
{field("Phone", "phone", "+1 234 567 890")}
{field("Email", "email", "john@example.com")}
{field("Organization", "organization", "Company")}
{field("Job Title", "title", "Developer")}
{field("Website", "url", "https://example.com")}
);
}
function EmailForm() {
const { emailData, setEmailData } = useQrStore();
return (
);
}
function PhoneForm() {
const { phoneData, setPhoneData } = useQrStore();
return (
setPhoneData(e.target.value)}
placeholder="+1 234 567 890"
className={INPUT_CLASS}
/>
);
}
function SmsForm() {
const { smsData, setSmsData } = useQrStore();
return (
);
}
const FORM_MAP: Record = {
url: UrlForm,
text: TextForm,
wifi: WifiForm,
vcard: VCardForm,
email: EmailForm,
phone: PhoneForm,
sms: SmsForm,
};
// ── Pill button helpers ──────────────────────────────────────────────
function PillButton({
selected,
onClick,
children,
}: {
selected: boolean;
onClick: () => void;
children: React.ReactNode;
}) {
return (
);
}
// ── Main settings component ──────────────────────────────────────────
export function QrGenerateSettings() {
const store = useQrStore();
const logoInputRef = useRef(null);
const hasData = !!encodeQrData(store);
const handleDownload = useCallback(() => {
const data = encodeQrData(store);
if (!data) return;
const qr = new QRCodeStyling({
width: store.size,
height: store.size,
data,
margin: 8,
qrOptions: { errorCorrectionLevel: store.errorCorrection },
dotsOptions: {
type: store.dotType,
...(store.dotGradientEnabled
? {
gradient: {
type: store.dotGradientType,
rotation: store.dotGradientRotation * (Math.PI / 180),
colorStops: [
{ offset: 0, color: store.dotGradientColor1 },
{ offset: 1, color: store.dotGradientColor2 },
],
},
}
: { color: store.dotColor }),
},
cornersSquareOptions: {
type: store.cornerSquareType,
color: store.useCustomCornerColors ? store.cornerSquareColor : undefined,
},
cornersDotOptions: {
type: store.cornerDotType,
color: store.useCustomCornerColors ? store.cornerDotColor : undefined,
},
backgroundOptions: store.bgTransparent ? { color: "transparent" } : { color: store.bgColor },
image: store.logoDataUrl || "",
imageOptions: {
hideBackgroundDots: store.hideBackgroundDots,
imageSize: store.logoSize,
margin: store.logoMargin,
},
} as never);
qr.download({
name: "qrcode",
extension: store.downloadFormat,
});
}, [store]);
const handleLogoUpload = useCallback(
(e: React.ChangeEvent) => {
const file = e.target.files?.[0];
store.setLogoFile(file ?? null);
},
[store],
);
const ContentForm = FORM_MAP[store.contentType];
return (
{/* ── Content type tabs ── */}
{CONTENT_TYPES.map(({ id, label, icon: Icon }) => (
))}
{/* ── Data entry form ── */}
{/* ── Style section ── */}
Dot Pattern
{DOT_TYPES.map(({ value, label }) => (
store.setDotType(value)}
>
{label}
))}
Corner Square
{CORNER_SQUARE_TYPES.map(({ value, label }) => (
store.setCornerSquareType(value)}
>
{label}
))}
Corner Dot
{CORNER_DOT_TYPES.map(({ value, label }) => (
store.setCornerDotType(value)}
>
{label}
))}
{/* ── Colors section ── */}
{/* Gradient toggle */}
{store.dotGradientEnabled && (
store.setDotGradientType("linear")}
>
Linear
store.setDotGradientType("radial")}
>
Radial
{store.dotGradientType === "linear" && (
)}
)}
{/* Custom corner colors */}
{store.useCustomCornerColors && (
)}
{/* ── Logo section ── */}
{/* ── Download section ── */}
Format
{DOWNLOAD_FORMATS.map(({ value, label, desc }) => (
))}
);
}