feat: add Vite + React SPA with Tailwind CSS and routing

This commit is contained in:
Siddharth Kumar Sah
2026-03-22 02:57:49 +08:00
parent 3cb7123070
commit 5fa012e44b
11 changed files with 1667 additions and 2 deletions
+11
View File
@@ -0,0 +1,11 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";
export function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<div className="p-8 text-2xl font-bold text-primary">Stirling Image</div>} />
</Routes>
</BrowserRouter>
);
}
+34
View File
@@ -0,0 +1,34 @@
const API_BASE = "/api";
export async function apiGet<T>(path: string): Promise<T> {
const res = await fetch(`${API_BASE}${path}`, {
headers: { Authorization: `Bearer ${getToken()}` },
});
if (!res.ok) throw new Error(`API error: ${res.status}`);
return res.json();
}
export async function apiPost<T>(path: string, body?: unknown): Promise<T> {
const res = await fetch(`${API_BASE}${path}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${getToken()}`,
},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) throw new Error(`API error: ${res.status}`);
return res.json();
}
function getToken(): string {
return localStorage.getItem("stirling-token") || "";
}
export function setToken(token: string) {
localStorage.setItem("stirling-token", token);
}
export function clearToken() {
localStorage.removeItem("stirling-token");
}
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+10
View File
@@ -0,0 +1,10 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
import "./styles/globals.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
+29
View File
@@ -0,0 +1,29 @@
@import "tailwindcss";
@theme {
--color-primary: #3b82f6;
--color-primary-foreground: #ffffff;
--color-background: #ffffff;
--color-foreground: #0f172a;
--color-muted: #f1f5f9;
--color-muted-foreground: #64748b;
--color-border: #e2e8f0;
--color-card: #ffffff;
--color-card-foreground: #0f172a;
--color-sidebar: #f8fafc;
--color-sidebar-foreground: #334155;
--color-accent: #3b82f6;
--color-destructive: #ef4444;
}
.dark {
--color-background: #0f172a;
--color-foreground: #f8fafc;
--color-muted: #1e293b;
--color-muted-foreground: #94a3b8;
--color-border: #334155;
--color-card: #1e293b;
--color-card-foreground: #f8fafc;
--color-sidebar: #1e293b;
--color-sidebar-foreground: #cbd5e1;
}