#!/usr/bin/env bash
echo "🚀 Generating Admin UI (Next.js App Router, JS, Tailwind)..."
### Root paths
APP_DIR="src/app/admin"
COMP_DIR="src/components/admin"
LIB_DIR="lib"
# Create directories
mkdir -p "$APP_DIR"
mkdir -p "$APP_DIR/models/new"
mkdir -p "$APP_DIR/models/[id]"
mkdir -p "$APP_DIR/parts/new"
mkdir -p "$APP_DIR/parts/[id]"
mkdir -p "$COMP_DIR"
mkdir -p "$LIB_DIR"
############################################
# 1. ADMIN LAYOUT
############################################
cat <<'EOF' > "$APP_DIR/layout.js"
import React from "react";
import Link from "next/link";
import "../globals.css";
export default function AdminLayout({ children }) {
return (
{children}
);
}
EOF
############################################
# 2. ADMIN HOME PAGE
############################################
cat <<'EOF' > "$APP_DIR/page.js"
import { supabaseServer } from "@/lib/supabaseServer";
export default async function AdminHome() {
const { data: models } = await supabaseServer.from("models").select("*");
const { data: parts } = await supabaseServer.from("parts").select("*");
const { data: compat } = await supabaseServer.from("parts_compatible_models").select("*");
return (
Admin Dashboard
Models
{models?.length ?? 0}
Parts
{parts?.length ?? 0}
Compatibility
{compat?.length ?? 0}
);
}
EOF
############################################
# 3. MODELS LIST PAGE
############################################
cat <<'EOF' > "$APP_DIR/models/page.js"
import Link from "next/link";
import { supabaseServer } from "@/lib/supabaseServer";
export default async function ModelsPage() {
const { data: models } = await supabaseServer
.from("models")
.select("*")
.order("name");
return (
Models
+ Add Model
{models?.map((m) => (
{m.name}
{m.brand}
))}
);
}
EOF
############################################
# 4. NEW MODEL PAGE
############################################
cat <<'EOF' > "$APP_DIR/models/new/page.js"
"use client";
import { useState } from "react";
import { supabaseBrowser } from "@/lib/supabaseClient";
import { useRouter } from "next/navigation";
export default function NewModelPage() {
const [form, setForm] = useState({
name: "",
brand: "",
scale: "",
chassis_type: "",
notes: "",
});
const router = useRouter();
async function save() {
const { error } = await supabaseBrowser.from("models").insert(form);
if (!error) router.push("/admin/models");
}
return (
Add New Model
{Object.entries(form).map(([key, value]) => (
setForm({ ...form, [key]: e.target.value })}
/>
))}
);
}
EOF
############################################
# 5. MODEL DETAIL PAGE
############################################
cat <<'EOF' > "$APP_DIR/models/[id]/page.js"
import Link from "next/link";
import { supabaseServer } from "@/lib/supabaseServer";
export default async function ModelDetail({ params }) {
const { id } = params;
const { data: model } = await supabaseServer
.from("models")
.select("*")
.eq("id", id)
.single();
const { data: compat } = await supabaseServer
.from("parts_compatible_models")
.select("*, parts(*)")
.eq("model_id", id);
return (
{model?.name}
Brand: {model?.brand}
Scale: {model?.scale}
Chassis: {model?.chassis_type}
Compatible Parts
+ Add Compatible Part
{compat?.map((c) => (
{c.parts.name}
{c.fit_type}
View Part
))}
);
}
EOF
############################################
# PARTS PAGES (STUBS)
############################################
cat <<'EOF' > "$APP_DIR/parts/page.js"
export default function PartsPage() {
return (
Parts (UI Coming Next)
List, edit, and link parts here.
);
}
EOF
cat <<'EOF' > "$APP_DIR/parts/new/page.js"
export default function NewPartPage() {
return (
Add Part (UI Coming Next)
);
}
EOF
############################################
# SUPABASE CLIENTS
############################################
cat <<'EOF' > "$LIB_DIR/supabaseClient.js"
import { createClient } from "@supabase/supabase-js";
export const supabaseBrowser = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
EOF
cat <<'EOF' > "$LIB_DIR/supabaseServer.js"
import { createClient } from "@supabase/supabase-js";
export const supabaseServer = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY
);
EOF
echo "🎉 Admin UI scaffolding complete!"