#!/bin/zsh
echo "🎨 Setting up UI integration (Next.js App Router)..."
BASE="src/app"
COMP="src/app/components"
UTIL="src/lib/ui"
mkdir -p "$COMP"
mkdir -p "$UTIL"
#############################################
# API CLIENT (simple fetch helpers)
#############################################
cat > "$UTIL/api.js" << 'EOF'
export async function fetchJSON(url) {
const res = await fetch(url, { cache: "no-store" });
if (!res.ok) return null;
return res.json();
}
export function getModel(id) {
return fetchJSON(`/api/models/${id}`);
}
export function getPart(id) {
return fetchJSON(`/api/parts/${id}`);
}
export function getCompatibleParts(modelId) {
return fetchJSON(`/api/compatibility/model/${modelId}`);
}
export function getCompatibleModels(partId) {
return fetchJSON(`/api/compatibility/part/${partId}`);
}
EOF
#############################################
# COMPONENTS
#############################################
# ModelCard.js
cat > "$COMP/ModelCard.js" << 'EOF'
export default function ModelCard({ model }) {
return (
{model.name}
{model.brand} • {model.scale}
);
}
EOF
# PartCard.js
cat > "$COMP/PartCard.js" << 'EOF'
export default function PartCard({ part }) {
return (
{part.name}
ID: {part.id}
);
}
EOF
# SearchBar.js
cat > "$COMP/SearchBar.js" << 'EOF'
"use client";
import { useState } from "react";
export default function SearchBar({ onSearch }) {
const [text, setText] = useState("");
return (
setText(e.target.value)}
/>
);
}
EOF
#############################################
# UI: Model Page Integration
#############################################
cat > "$BASE/model/[modelId]/page.js" << 'EOF'
import { getModel, getCompatibleParts } from "@/src/lib/ui/api";
import PartCard from "@/src/app/components/PartCard";
export default async function ModelPage({ params }) {
const model = await getModel(params.modelId);
const parts = await getCompatibleParts(params.modelId);
if (!model) return Model not found
;
return (
{model.name}
{model.brand} • {model.scale}
Compatible Parts
{Array.isArray(parts) && parts.length > 0 ? (
parts.map(item =>
)
) : (
No compatible parts.
)}
);
}
EOF
#############################################
# UI: Part Page Integration
#############################################
cat > "$BASE/part/[partId]/page.js" << 'EOF'
import { getPart, getCompatibleModels } from "@/src/lib/ui/api";
import ModelCard from "@/src/app/components/ModelCard";
export default async function PartDetailPage({ params }) {
const part = await getPart(params.partId);
const models = await getCompatibleModels(params.partId);
if (!part) return Part not found
;
return (
{part.name}
ID: {part.id}
Compatible Models
{Array.isArray(models) && models.length > 0 ? (
models.map(item => (
))
) : (
No compatible models.
)}
);
}
EOF
#############################################
# UI: Category Page Integration
#############################################
cat > "$BASE/category/[modelId]/[categoryName]/page.js" << 'EOF'
import { getModel, getCompatibleParts } from "@/src/lib/ui/api";
import PartCard from "@/src/app/components/PartCard";
export default async function CategoryPage({ params }) {
const model = await getModel(params.modelId);
const allParts = await getCompatibleParts(params.modelId);
if (!model) return Model not found
;
const parts = allParts
? allParts.filter(p => p.part.category === params.categoryName)
: [];
return (
{params.categoryName} for {model.name}
{parts.length ? (
parts.map(p => (
))
) : (
No compatible parts in this category.
)}
);
}
EOF
#############################################
# UI: Compatibility Explorer Page
#############################################
cat > "$BASE/compatibility/page.js" << 'EOF'
"use client";
import { useState } from "react";
import { fetchJSON } from "@/src/lib/ui/api";
import ModelCard from "../components/ModelCard";
import PartCard from "../components/PartCard";
export default function CompatibilityPage() {
const [query, setQuery] = useState("");
const [result, setResult] = useState([]);
async function compare() {
const modelData = await fetchJSON(`/api/compatibility/model/${query}`);
const partData = await fetchJSON(`/api/compatibility/part/${query}`);
setResult(modelData || partData || []);
}
return (
Compatibility Explorer
setQuery(e.target.value)}
/>
{result.map(item =>
item.model ? (
) : (
)
)}
);
}
EOF
#############################################
# UI: Home Page Search Integration
#############################################
cat > "$BASE/page.js" << 'EOF'
"use client";
import { useState } from "react";
import SearchBar from "./components/SearchBar";
import { searchModels, searchParts } from "@/src/lib/search/search";
export default function HomePage() {
const [results, setResults] = useState([]);
async function handleSearch(query) {
const models = searchModels(query);
const parts = searchParts(query);
setResults([...models, ...parts]);
}
return (
RC Compatibility Explorer
{results.map(item =>
item.categories ? (
{item.name} — Model
) : (
{item.name} — Part
)
)}
);
}
EOF
echo "🎉 UI integration created successfully!"