From 62114470dee33c36898294f247b82fe2d313c450 Mon Sep 17 00:00:00 2001 From: makearmy Date: Mon, 22 Sep 2025 18:51:12 -0400 Subject: [PATCH] added laser finder --- app/buying-guide/finder/page.tsx | 322 +++++++++++++++++++++++++++++++ lib/laser-finder.ts | 187 ++++++++++++++++++ 2 files changed, 509 insertions(+) create mode 100644 app/buying-guide/finder/page.tsx create mode 100644 lib/laser-finder.ts diff --git a/app/buying-guide/finder/page.tsx b/app/buying-guide/finder/page.tsx new file mode 100644 index 00000000..99d10d2a --- /dev/null +++ b/app/buying-guide/finder/page.tsx @@ -0,0 +1,322 @@ +// app/buying-guide/finder/page.tsx +"use client"; + +import { useMemo, useState } from "react"; +import { useForm } from "react-hook-form"; +import type { Answers, LaserType } from "@/lib/laser-finder"; +import { scoreAnswers, LASER_LABEL, TYPE_INFO } from "@/lib/laser-finder"; +import Link from "next/link"; + +export default function LaserFinderPage() { + const [result, setResult] = useState<{ + top: LaserType[]; + score: Record; + why: Record; + } | null>(null); + + const { register, handleSubmit, reset, watch } = useForm({ + defaultValues: { + materials: [], + operations: [], + part_size: "medium", + detail: "medium", + throughput: "medium", + budget: "mid", + }, + }); + + const onSubmit = (vals: Answers) => { + const { ranked, score, why } = scoreAnswers(vals); + setResult({ top: ranked.slice(0, 2), score, why }); + // (Optional) later: POST vals to Directus for analytics + }; + + const selectedMaterials = watch("materials"); + const selectedOps = watch("operations"); + + return ( +
+

Laser Type Finder

+

+ Answer a few questions and we’ll suggest the best laser types for your work + with clear use-cases, materials, and cautions. No product pitches—just guidance. +

+ + {!result && ( +
+ {/* Materials */} +
+ Materials (select all that apply) +
+ {[ + ["metals_bare", "Bare metals"], + ["metals_coated", "Coated/painted metals"], + ["plastics", "Plastics"], + ["wood_paper_leather", "Wood, paper, leather"], + ["glass_ceramic", "Glass / ceramic"], + ["stone", "Stone"], + ["textiles", "Textiles"], + ].map(([val, label]) => ( + + ))} +
+ {selectedMaterials?.length === 0 && ( +

Tip: choose at least one material for a better match.

+ )} +
+ + {/* Operations */} +
+ Typical operations (select all that apply) +
+ {[ + ["deep_mark_metal", "Deep mark on metal"], + ["color_mark_stainless", "Color mark stainless"], + ["fine_engraving", "Fine engraving (small features)"], + ["photo_engrave", "Photo engraving"], + ["cut_nonmetals_thick", "Cut thick non-metals (e.g., 6+ mm acrylic/wood)"], + ["cut_nonmetals_thin", "Cut thin non-metals"], + ["mark_coated", "Mark coated items"], + ].map(([val, label]) => ( + + ))} +
+ {selectedOps?.length === 0 && ( +

Tip: pick one or more to sharpen the recommendation.

+ )} +
+ + {/* Size / Detail / Speed / Budget */} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + Back to Buying Guide + +
+
+ )} + + {!!result && ( +
+ + + {result.top[1] && ( + + )} + + + +
+ + + + Back to Buying Guide + +
+
+ )} +
+ ); +} + +function ResultCard({ + title, + type, + why, + secondary, +}: { + title: string; + type: LaserType; + why?: string[]; + secondary?: boolean; +}) { + const info = TYPE_INFO[type]; + + return ( +
+
+

{title}

+ {!secondary && Best match} +
+
{LASER_LABEL[type]}
+

{info.summary}

+ + {!!why?.length && ( +
+
Why this fits
+
    + {why.slice(0, 5).map((w, i) =>
  • {w}
  • )} +
+
+ )} + +
+ + + +
+ +
+ + See community settings + + + Suggest new settings + +
+
+ ); +} + +function TagList({ + label, + items, + tone, +}: { + label: string; + items: string[]; + tone?: "warn"; +}) { + return ( +
+
{label}
+
+ {items.map((t, i) => ( + + {t} + + ))} +
+
+ ); +} + +function CompareMatrix() { + const rows: Array<{ + k: LaserType; + label: string; + best: string[]; + ok: string[]; + avoid: string[]; + }> = [ + { + k: "fiber", + label: LASER_LABEL.fiber, + best: ["Bare metals", "Deep metal engrave", "Color marking stainless"], + ok: ["Some coated items", "Some plastics w/ additives"], + avoid: ["Thick organics cutting"], + }, + { + k: "co2_gantry", + label: LASER_LABEL.co2_gantry, + best: ["Acrylic cutting", "Wood cutting/engraving", "Large panels"], + ok: ["Leathers, textiles, rubber"], + avoid: ["Bare metals (no coat)"], + }, + { + k: "co2_galvo", + label: LASER_LABEL.co2_galvo, + best: ["Fast marking organics", "Photo engraving organics"], + ok: ["Coated metals/non-metals"], + avoid: ["Thick sheet cutting", "Large panels"], + }, + { + k: "uv", + label: LASER_LABEL.uv, + best: ["Micro features", "Glass/ceramic/plastics marking"], + ok: ["Fine logos on coated metals"], + avoid: ["Thick cutting"], + }, + ]; + + return ( +
+
Compare laser types
+
+ + + + + + + + + + + {rows.map((r) => ( + + + + + + + ))} + +
TypeBest forOkay forNot ideal
{r.label}{r.best.join(", ")}{r.ok.join(", ")}{r.avoid.join(", ")}
+
+
+ ); +} diff --git a/lib/laser-finder.ts b/lib/laser-finder.ts new file mode 100644 index 00000000..8b594702 --- /dev/null +++ b/lib/laser-finder.ts @@ -0,0 +1,187 @@ +// lib/laser-finder.ts +export type LaserType = "fiber" | "co2_gantry" | "co2_galvo" | "uv"; + +export const LASER_LABEL: Record = { + fiber: "Fiber (MOPA/QR)", + co2_gantry: "CO₂ Gantry", + co2_galvo: "CO₂ Galvo", + uv: "UV (355 nm)", +}; + +export const TYPE_INFO: Record< +LaserType, +{ + summary: string; + bestFor: string[]; // show as tags + materials: string[]; // show as tags + cautions: string[]; // negatives / limits + learnLink: string; // link to your settings pages +} +> = { + fiber: { + summary: + "Best for marking/engraving bare metals, color marking on stainless (MOPA), and high-throughput galvo jobs.", + bestFor: [ + "Bare metal marking", + "Deep engraving on metals", + "Color marking stainless", + "Serials/QR/codes on parts", + ], + materials: ["Steel", "Stainless", "Aluminum", "Brass", "Titanium"], + cautions: [ + "Not for cutting wood/acrylic", + "Limited on organics/plastics (unless additives/coatings)", + ], + learnLink: "/fiber-settings", + }, + co2_gantry: { + summary: + "Large work area; best for cutting & engraving non-metals (wood, acrylic, leather, textiles).", + bestFor: ["Thick acrylic cuts", "Wood cutting/engraving", "Signage", "Textiles"], + materials: ["Wood", "Acrylic", "Leather", "Paper", "Textiles", "Rubber"], + cautions: [ + "Poor on bare metals without coatings", + "Slower for fine micro-engraving", + ], + learnLink: "/co2-gantry-settings", + }, + co2_galvo: { + summary: + "High-speed CO₂ marking/engraving on organics/non-metals with small scan fields.", + bestFor: ["Fast marking on organics", "Photo engraving on wood/leather", "High throughput"], + materials: ["Wood", "Leather", "Paper/Card", "Anodized/painted items"], + cautions: [ + "Small scan field vs gantry", + "Not for cutting thick sheets", + "Poor on bare metals", + ], + learnLink: "/co2-galvo-settings", + }, + uv: { + summary: + "Ultra-fine marking/engraving on plastics, glass, ceramics; low heat-affected zone for micro features.", + bestFor: ["Micro text/logos", "Fine plastic marking", "Glass/ceramic marking"], + materials: ["Plastics", "Glass", "Ceramics", "PCB/silicon (marking)"], + cautions: [ + "Typically lower power; not for thick cutting", + "Higher $/W, smaller working areas", + ], + learnLink: "/uv-settings", + }, +}; + +export type Answers = { + materials: Array< + | "metals_bare" + | "metals_coated" + | "plastics" + | "wood_paper_leather" + | "glass_ceramic" + | "stone" + | "textiles" + >; + operations: Array< + | "deep_mark_metal" + | "color_mark_stainless" + | "fine_engraving" + | "photo_engrave" + | "cut_nonmetals_thick" + | "cut_nonmetals_thin" + | "mark_coated" + >; + part_size: "small" | "medium" | "large"; // ~ scan field or bed + detail: "low" | "medium" | "high" | "micro"; + throughput: "low" | "medium" | "high"; + budget: "low" | "mid" | "high"; +}; + +type Score = Record; +const bump = (s: Score, k: LaserType, n: number) => (s[k] += n); + +export function scoreAnswers(a: Answers): { + score: Score; + ranked: LaserType[]; + why: Record; +} { + const s: Score = { fiber: 0, co2_gantry: 0, co2_galvo: 0, uv: 0 }; + const why: Record = { + fiber: [], + co2_gantry: [], + co2_galvo: [], + uv: [], + }; + + // Materials + if (a.materials.includes("metals_bare")) { + bump(s, "fiber", 6); why.fiber.push("Bare metals benefit from fiber."); + bump(s, "uv", 2); why.uv.push("UV can mark some metals with fine detail."); + bump(s, "co2_gantry", -3); + bump(s, "co2_galvo", -3); + } + if (a.materials.includes("metals_coated")) { + bump(s, "fiber", 3); why.fiber.push("Coated metals are fiber-friendly."); + bump(s, "uv", 2); why.uv.push("UV works well on coatings and labels."); + bump(s, "co2_galvo", 1); why.co2_galvo.push("CO₂ galvo can mark coated items quickly."); + } + if (a.materials.includes("plastics") || a.materials.includes("wood_paper_leather")) { + bump(s, "co2_gantry", 3); why.co2_gantry.push("Organics & plastics suit CO₂ gantry cutting/engraving."); + bump(s, "co2_galvo", 3); why.co2_galvo.push("CO₂ galvo is fast for organic marking."); + bump(s, "uv", 1); why.uv.push("UV excels at fine marking plastics."); + } + if (a.materials.includes("glass_ceramic")) { + bump(s, "uv", 4); why.uv.push("Glass/ceramic: UV has low HAZ for crisp marks."); + bump(s, "co2_galvo", 1); + } + if (a.materials.includes("textiles")) { + bump(s, "co2_gantry", 3); why.co2_gantry.push("Textiles: CO₂ gantry handles larger panels."); + } + if (a.materials.includes("stone")) { + bump(s, "co2_gantry", 1); bump(s, "co2_galvo", 1); + } + + // Operations + if (a.operations.includes("deep_mark_metal")) { bump(s, "fiber", 5); why.fiber.push("Deep metal marking favors fiber."); } + if (a.operations.includes("color_mark_stainless")) { bump(s, "fiber", 5); why.fiber.push("Color marking stainless = MOPA fiber."); } + if (a.operations.includes("fine_engraving")) { + bump(s, "uv", 4); why.uv.push("Micro features need UV’s small spot."); + bump(s, "fiber", 2); why.fiber.push("Fiber can achieve fine detail on metals."); + bump(s, "co2_galvo", 2); + } + if (a.operations.includes("photo_engrave")) { + bump(s, "uv", 3); why.uv.push("UV gives clean dithers on many materials."); + bump(s, "co2_galvo", 2); why.co2_galvo.push("CO₂ galvo is common for photo engraving organics."); + } + if (a.operations.includes("cut_nonmetals_thick")) { bump(s, "co2_gantry", 6); why.co2_gantry.push("Thick cutting needs gantry CO₂."); } + if (a.operations.includes("cut_nonmetals_thin")) { + bump(s, "co2_gantry", 3); why.co2_gantry.push("Thin cutting works well on gantry CO₂."); + bump(s, "co2_galvo", 2); + } + if (a.operations.includes("mark_coated")) { + bump(s, "fiber", 2); why.fiber.push("Coated marks are straightforward on fiber."); + bump(s, "uv", 2); why.uv.push("UV marks coatings with low HAZ."); + bump(s, "co2_galvo", 1); + } + + // Part size & throughput + if (a.part_size === "large") { bump(s, "co2_gantry", 5); why.co2_gantry.push("Large work area points to gantry CO₂."); } + if (a.part_size === "small") { bump(s, "co2_galvo", 2); } + if (a.throughput === "high") { + bump(s, "co2_galvo", 3); why.co2_galvo.push("High throughput favors galvo systems."); + bump(s, "fiber", 2); why.fiber.push("Fiber galvos are fast on metals."); + } + + // Detail requirement + if (a.detail === "micro") { + bump(s, "uv", 5); why.uv.push("Micro detail → UV’s spot and short wavelength."); + bump(s, "fiber", 2); + } else if (a.detail === "high") { + bump(s, "uv", 3); bump(s, "fiber", 2); bump(s, "co2_galvo", 1); + } + + // Budget (very soft tie-breaker) + if (a.budget === "low") bump(s, "co2_gantry", 1); + if (a.budget === "high") { bump(s, "fiber", 1); bump(s, "uv", 1); } + + const ranked = (Object.keys(s) as LaserType[]).sort((x, y) => s[y] - s[x]); + return { score: s, ranked, why }; +}