From ec9e3075381c5311c93e76603382bf2d937be604 Mon Sep 17 00:00:00 2001 From: jlightner Date: Mon, 30 Mar 2026 12:00:58 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Added=20AppFooter=20component=20display?= =?UTF-8?q?ing=20app=20version,=20build=20date,=20com=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "frontend/src/components/AppFooter.tsx" - "frontend/vite.config.ts" - "frontend/src/App.tsx" - "frontend/src/App.css" - "frontend/src/vite-env.d.ts" GSD-Task: S06/T01 --- frontend/src/App.css | 36 ++++++++++++++++++++ frontend/src/App.tsx | 3 ++ frontend/src/components/AppFooter.tsx | 47 +++++++++++++++++++++++++++ frontend/src/vite-env.d.ts | 4 +++ frontend/tsconfig.app.tsbuildinfo | 2 +- frontend/vite.config.ts | 32 ++++++++++++++++++ 6 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/AppFooter.tsx diff --git a/frontend/src/App.css b/frontend/src/App.css index 520b1cc..720eea7 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -159,6 +159,12 @@ body { /* ── App shell ────────────────────────────────────────────────────────────── */ +.app { + display: flex; + flex-direction: column; + min-height: 100vh; +} + .app-header { display: flex; align-items: center; @@ -191,11 +197,41 @@ body { } .app-main { + flex: 1; max-width: 72rem; margin: 1.5rem auto; padding: 0 1.5rem; } +/* ── App footer ───────────────────────────────────────────────────────────── */ + +.app-footer { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 1rem 1.5rem; + font-size: 0.6875rem; + color: var(--color-text-muted); + border-top: 1px solid var(--color-border); +} + +.app-footer__sep { + opacity: 0.4; +} + +.app-footer__commit, +.app-footer__repo { + color: var(--color-text-muted); + text-decoration: none; + transition: color 0.15s; +} + +a.app-footer__commit:hover, +a.app-footer__repo:hover { + color: var(--color-accent); +} + /* ── Queue header ─────────────────────────────────────────────────────────── */ .queue-header { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7cf0c8a..ca81290 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -10,6 +10,7 @@ import MomentDetail from "./pages/MomentDetail"; import AdminReports from "./pages/AdminReports"; import AdminPipeline from "./pages/AdminPipeline"; import AdminDropdown from "./components/AdminDropdown"; +import AppFooter from "./components/AppFooter"; export default function App() { return ( @@ -50,6 +51,8 @@ export default function App() { } /> + + ); } diff --git a/frontend/src/components/AppFooter.tsx b/frontend/src/components/AppFooter.tsx new file mode 100644 index 0000000..98752be --- /dev/null +++ b/frontend/src/components/AppFooter.tsx @@ -0,0 +1,47 @@ +const REPO_URL = "https://github.com/xpltdco/chrysopedia"; + +export default function AppFooter() { + const commitUrl = + __GIT_COMMIT__ !== "dev" + ? `${REPO_URL}/commit/${__GIT_COMMIT__}` + : undefined; + + return ( +
+ + v{__APP_VERSION__} + + · + + Built {__BUILD_DATE__.slice(0, 10)} + + {commitUrl ? ( + <> + · + + {__GIT_COMMIT__} + + + ) : ( + <> + · + {__GIT_COMMIT__} + + )} + · + + GitHub + +
+ ); +} diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts index 11f02fe..b77c6f5 100644 --- a/frontend/src/vite-env.d.ts +++ b/frontend/src/vite-env.d.ts @@ -1 +1,5 @@ /// + +declare const __APP_VERSION__: string; +declare const __BUILD_DATE__: string; +declare const __GIT_COMMIT__: string; diff --git a/frontend/tsconfig.app.tsbuildinfo b/frontend/tsconfig.app.tsbuildinfo index c9f1734..f4a3120 100644 --- a/frontend/tsconfig.app.tsbuildinfo +++ b/frontend/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/client.ts","./src/api/public-client.ts","./src/components/AdminDropdown.tsx","./src/components/ModeToggle.tsx","./src/components/ReportIssueModal.tsx","./src/components/StatusBadge.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/Home.tsx","./src/pages/MomentDetail.tsx","./src/pages/ReviewQueue.tsx","./src/pages/SearchResults.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx"],"version":"5.6.3"} \ No newline at end of file +{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/client.ts","./src/api/public-client.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/ModeToggle.tsx","./src/components/ReportIssueModal.tsx","./src/components/StatusBadge.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/Home.tsx","./src/pages/MomentDetail.tsx","./src/pages/ReviewQueue.tsx","./src/pages/SearchResults.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx"],"version":"5.6.3"} \ No newline at end of file diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index c72472f..78070d1 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,8 +1,40 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import { execSync } from "child_process"; +import { readFileSync } from "fs"; +import { resolve } from "path"; + +function getGitCommit(): string { + // In Docker builds, VITE_GIT_COMMIT is set via ENV from the build ARG + if (process.env.VITE_GIT_COMMIT) { + return process.env.VITE_GIT_COMMIT; + } + // Local dev: try git + try { + return execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim(); + } catch { + return "dev"; + } +} + +function getAppVersion(): string { + try { + const pkg = JSON.parse( + readFileSync(resolve(__dirname, "package.json"), "utf-8"), + ); + return pkg.version ?? "0.0.0"; + } catch { + return "0.0.0"; + } +} export default defineConfig({ plugins: [react()], + define: { + __APP_VERSION__: JSON.stringify(getAppVersion()), + __BUILD_DATE__: JSON.stringify(new Date().toISOString()), + __GIT_COMMIT__: JSON.stringify(getGitCommit()), + }, server: { proxy: { "/api": {