diff --git a/app/materials-coatings/[id]/page.tsx b/app/materials/materials-coatings/[id]/page.tsx similarity index 100% rename from app/materials-coatings/[id]/page.tsx rename to app/materials/materials-coatings/[id]/page.tsx diff --git a/app/materials-coatings/page.tsx b/app/materials/materials-coatings/page.tsx similarity index 100% rename from app/materials-coatings/page.tsx rename to app/materials/materials-coatings/page.tsx diff --git a/app/materials/[id]/page.tsx b/app/materials/materials/[id]/page.tsx similarity index 100% rename from app/materials/[id]/page.tsx rename to app/materials/materials/[id]/page.tsx diff --git a/app/materials/page.tsx b/app/materials/materials/page.tsx similarity index 100% rename from app/materials/page.tsx rename to app/materials/materials/page.tsx diff --git a/app/portal/laser-sources/page.tsx b/app/portal/laser-sources/page.tsx index 483158a7..93325b95 100644 --- a/app/portal/laser-sources/page.tsx +++ b/app/portal/laser-sources/page.tsx @@ -1,9 +1,18 @@ // app/portal/laser-sources/page.tsx -export default function LaserSourcesPage() { +import dynamic from "next/dynamic"; + +export const metadata = { title: "MakerDash • Laser Sources" }; + +// Mount the existing canonical page (adjust path if needed) +const LasersView = dynamic(() => import("@/app/lasers/page"), { ssr: false }); + +export default function LaserSourcesPortalPage() { return (
-

Laser Sources

-

WIP: list & manage sources here.

+

Laser Sources

+
+ +
); } diff --git a/app/portal/materials/page.tsx b/app/portal/materials/page.tsx index 2037c162..2402f64c 100644 --- a/app/portal/materials/page.tsx +++ b/app/portal/materials/page.tsx @@ -1,9 +1,13 @@ // app/portal/materials/page.tsx -export default function MaterialsPage() { +import MaterialsSwitcher from "@/components/portal/MaterialsSwitcher"; + +export const metadata = { title: "MakerDash • Materials" }; + +export default function MaterialsPortalPage() { return (
-

Materials

-

WIP: materials library management.

+

Materials

+
); } diff --git a/app/portal/projects/page.tsx b/app/portal/projects/page.tsx index 09cf11f8..de7d4d4e 100644 --- a/app/portal/projects/page.tsx +++ b/app/portal/projects/page.tsx @@ -1,9 +1,18 @@ // app/portal/projects/page.tsx -export default function ProjectsPage() { +import dynamic from "next/dynamic"; + +export const metadata = { title: "MakerDash • Projects" }; + +// Mount the existing canonical page (adjust path if needed) +const ProjectsView = dynamic(() => import("@/app/projects/page"), { ssr: false }); + +export default function ProjectsPortalPage() { return (
-

Projects

-

WIP: authenticated project list & details.

+

Projects

+
+ +
); } diff --git a/app/projects/page.tsx.bak b/app/projects/page.tsx.bak deleted file mode 100644 index 7011024c..00000000 --- a/app/projects/page.tsx.bak +++ /dev/null @@ -1,273 +0,0 @@ -"use client"; - -import { useEffect, useState, useMemo } from "react"; -import { useSearchParams } from "next/navigation"; -import Link from "next/link"; -import Image from "next/image"; - -export default function ProjectsPage() { - const searchParams = useSearchParams(); - const initialQuery = searchParams.get("query") || ""; - - const [query, setQuery] = useState(initialQuery); - const [debouncedQuery, setDebouncedQuery] = useState(initialQuery); - const [projects, setProjects] = useState([]); - const [loading, setLoading] = useState(true); - const [categories] = useState([ - "assets", - "documents", - "fixtures", - "projects", - "templates", - "test files", - "tools" - ]); - - useEffect(() => { - const timer = setTimeout(() => setDebouncedQuery(query), 300); - return () => clearTimeout(timer); - }, [query]); - - useEffect(() => { - fetch( - `${process.env.NEXT_PUBLIC_API_BASE_URL}/items/projects?fields=submission_id,title,uploader,category,tags,p_image.filename_disk,p_image.title&limit=-1fields=*.*` - ) - .then((res) => res.json()) - .then((data) => { - setProjects(data.data || []); - setLoading(false); - }) - .catch(() => setLoading(false)); - }, []); - - const highlight = (text: string) => { - if (!debouncedQuery) return text; - const regex = new RegExp(`(${debouncedQuery})`, "gi"); - return text?.replace(regex, '$1'); - }; - - const normalize = (str: string) => str?.toLowerCase().replace(/[_\s]/g, ""); - - const filtered = useMemo(() => { - const q = normalize(debouncedQuery); - return projects.filter((entry) => { - const fieldsToSearch = [ - entry.title ?? "", - entry.uploader ?? "", - entry.category ?? "", - Array.isArray(entry.tags) ? entry.tags.join(" ") : "", - ]; - return fieldsToSearch.filter(Boolean).some((field) => - normalize(field).includes(q) - ); - }); - }, [projects, debouncedQuery]); - - const tagCounts = useMemo(() => { - const counts: Record = {}; - projects.forEach((p) => { - if (Array.isArray(p.tags)) { - p.tags.forEach((tag) => { - counts[tag] = (counts[tag] || 0) + 1; - }); - } - }); - return counts; - }, [projects]); - - const popularTags = Object.entries(tagCounts) - .sort((a, b) => (Number(b[1]) || 0) - (Number(a[1]) || 0)) - .slice(0, 10) - .map(([tag]) => tag); - - const recentTags = [...projects] - .sort((a, b) => b.submission_id - a.submission_id) - .slice(0, 10) - .flatMap((p) => p.tags || []) - .filter((tag, i, self) => self.indexOf(tag) === i) - .slice(0, 10); - - const uniqueUploaders = new Set(projects.map((p) => p.uploader).filter(Boolean)).size; - const totalTags = Object.keys(tagCounts).length; - - return ( -
- - -
-
-

Community Projects

- setQuery(e.target.value)} - placeholder="Search projects..." - className="w-full dark:bg-background border border-border rounded-md p-2" - /> - - ← Back to Main Menu - -
-
-

Popular Tags

- {popularTags.length > 0 ? ( -
- {popularTags.map((tag, i) => ( - setQuery(tag)} title={tag}> - {tag} - - ))} -
- ) : ( -

No tags yet.

- )} -
-
-

Recent Tags

- {recentTags.length > 0 ? ( -
- {recentTags.map((tag, i) => ( - setQuery(tag)} title={tag}> - {tag} - - ))} -
- ) : ( -

No recent tags.

- )} -
-
-

Project Stats

-

Total Projects: {projects.length}

-

Unique Uploaders: {uniqueUploaders}

-

Total Tags: {totalTags}

-
-
-

Browse by Category

-
- {categories.map((cat, i) => ( - setQuery(cat)}> - {cat === "test_files" ? "test files" : cat} - - ))} -
-
-
-
-

Submit a Project

-

- Have a cool design, tool, or jig to share? Submit it to the community database. -

-
- -
-
- -
- - {loading ? ( -

Loading projects...

- ) : filtered.length === 0 ? ( -

No projects found.

- ) : ( -
- {filtered.map((project) => ( -
- {project.p_image?.title -
-
- - {project.title || "Untitled"} - -

Uploaded by: {project.uploader || "—"}

-

Category: {project.category || "—"}

-
-
- {Array.isArray(project.tags) && project.tags.length > 0 - ? project.tags.map((tag, i) => ( - setQuery(tag)} - title={tag} - > - {tag} - - )) - : ""} -
-
-
- ))} -
- )} -
- ); -} - diff --git a/components/portal/MaterialsSwitcher.tsx b/components/portal/MaterialsSwitcher.tsx new file mode 100644 index 00000000..8a8b0a3c --- /dev/null +++ b/components/portal/MaterialsSwitcher.tsx @@ -0,0 +1,59 @@ +// components/portal/MaterialsSwitcher.tsx +"use client"; + +import { useRouter, useSearchParams } from "next/navigation"; +import dynamic from "next/dynamic"; +import { cn } from "@/lib/utils"; + +// Reuse the *existing* canonical pages. +// Adjust paths if your folders differ. +const MaterialsPanel = dynamic(() => import("@/app/materials/materials/page"), { ssr: false }); +const CoatingsPanel = dynamic(() => import("@/app/materials/materials-coatings/page"), { ssr: false }); + +const TABS = [ + { key: "materials", label: "Materials" }, +{ key: "materials-coatings", label: "Coatings" }, +]; + +function Panel({ tab }: { tab: string }) { + switch (tab) { + case "materials": return ; + case "materials-coatings": return ; + default: return ; + } +} + +export default function MaterialsSwitcher() { + const router = useRouter(); + const sp = useSearchParams(); + const active = sp.get("t") || "materials"; + + function setTab(nextKey: string) { + const q = new URLSearchParams(sp.toString()); + q.set("t", nextKey); + router.replace(`/portal/materials?${q.toString()}`, { scroll: false }); + } + + return ( +
+
+ {TABS.map(({ key, label }) => ( + + ))} +
+ +
+ +
+
+ ); +}