From d4d104cd604741ac0c308efe5050a0eec69bb389 Mon Sep 17 00:00:00 2001 From: yyamashita Date: Fri, 8 May 2026 23:37:51 +0900 Subject: Initial scaffold: React Router v7 + SQLite + Tailwind CSS v4 --- app/app.css | 15 +++++++++++ app/lib/db.server.ts | 45 +++++++++++++++++++++++++++++++ app/root.tsx | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ app/routes.ts | 5 ++++ app/routes/home.tsx | 8 ++++++ 5 files changed, 149 insertions(+) create mode 100644 app/app.css create mode 100644 app/lib/db.server.ts create mode 100644 app/root.tsx create mode 100644 app/routes.ts create mode 100644 app/routes/home.tsx (limited to 'app') diff --git a/app/app.css b/app/app.css new file mode 100644 index 0000000..99345d8 --- /dev/null +++ b/app/app.css @@ -0,0 +1,15 @@ +@import "tailwindcss"; + +@theme { + --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +html, +body { + @apply bg-white dark:bg-gray-950; + + @media (prefers-color-scheme: dark) { + color-scheme: dark; + } +} diff --git a/app/lib/db.server.ts b/app/lib/db.server.ts new file mode 100644 index 0000000..6aa9313 --- /dev/null +++ b/app/lib/db.server.ts @@ -0,0 +1,45 @@ +import Database from "better-sqlite3"; +import path from "path"; + +let db: Database.Database | null = null; + +export function getDb(): Database.Database { + if (!db) { + const dbPath = path.resolve("whois.db"); + db = new Database(dbPath); + db.pragma("journal_mode = WAL"); + db.pragma("foreign_keys = ON"); + initSchema(db); + } + return db; +} + +function initSchema(db: Database.Database) { + db.exec(` + CREATE TABLE IF NOT EXISTS bands ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + name_kana TEXT, + formed_at TEXT, + area TEXT, + genre TEXT, + url TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE TABLE IF NOT EXISTS members ( + id TEXT PRIMARY KEY, + band_id TEXT NOT NULL REFERENCES bands(id), + name TEXT NOT NULL, + name_kana TEXT, + role TEXT, + joined_at TEXT, + left_at TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE INDEX IF NOT EXISTS idx_members_band_id ON members(band_id); + `); +} diff --git a/app/root.tsx b/app/root.tsx new file mode 100644 index 0000000..2c88ff1 --- /dev/null +++ b/app/root.tsx @@ -0,0 +1,76 @@ +import { + isRouteErrorResponse, + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from "react-router"; + +import type { Route } from "./+types/root"; +import "./app.css"; + +export const links: Route.LinksFunction = () => [ + { rel: "preconnect", href: "https://fonts.googleapis.com" }, + { + rel: "preconnect", + href: "https://fonts.gstatic.com", + crossOrigin: "anonymous", + }, + { + rel: "stylesheet", + href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", + }, +]; + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + whois.band + + + + + {children} + + + + + ); +} + +export default function App() { + return ; +} + +export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + let message = "Oops!"; + let details = "An unexpected error occurred."; + let stack: string | undefined; + + if (isRouteErrorResponse(error)) { + message = error.status === 404 ? "404" : "Error"; + details = + error.status === 404 + ? "The requested page could not be found." + : error.statusText || details; + } else if (import.meta.env.DEV && error && error instanceof Error) { + details = error.message; + stack = error.stack; + } + + return ( +
+

{message}

+

{details}

+ {stack && ( +
+          {stack}
+        
+ )} +
+ ); +} diff --git a/app/routes.ts b/app/routes.ts new file mode 100644 index 0000000..935792d --- /dev/null +++ b/app/routes.ts @@ -0,0 +1,5 @@ +import { type RouteConfig, index } from "@react-router/dev/routes"; + +export default [ + index("routes/home.tsx"), +] satisfies RouteConfig; diff --git a/app/routes/home.tsx b/app/routes/home.tsx new file mode 100644 index 0000000..03ae39a --- /dev/null +++ b/app/routes/home.tsx @@ -0,0 +1,8 @@ +export default function Home() { + return ( +
+

whois.band

+

Band identification service. Coming soon.

+
+ ); +} -- cgit v1.2.3