From f2ee7d773fd2f19c3f11ba9f1cf1c6a91f83d867 Mon Sep 17 00:00:00 2001 From: Jonatan Granqvist Date: Sat, 21 Feb 2026 11:37:19 +0100 Subject: [PATCH] add fonts and some basic routes --- apps/web/CLAUDE.md | 5 + apps/web/package.json | 2 + apps/web/public/logo.png | Bin 0 -> 6974 bytes apps/web/src/components/header.tsx | 1 + apps/web/src/index.css | 4 + apps/web/src/routeTree.gen.ts | 95 ++++++++++++++++++- apps/web/src/routes/index.tsx | 39 ++++---- apps/web/src/routes/timeline.$timelineId.tsx | 9 ++ apps/web/src/routes/timeline.tsx | 14 +++ apps/web/src/routes/timelines.tsx | 9 ++ pnpm-lock.yaml | 16 ++++ 11 files changed, 168 insertions(+), 26 deletions(-) create mode 100644 apps/web/CLAUDE.md create mode 100644 apps/web/public/logo.png create mode 100644 apps/web/src/routes/timeline.$timelineId.tsx create mode 100644 apps/web/src/routes/timeline.tsx create mode 100644 apps/web/src/routes/timelines.tsx diff --git a/apps/web/CLAUDE.md b/apps/web/CLAUDE.md new file mode 100644 index 0000000..d87b4f6 --- /dev/null +++ b/apps/web/CLAUDE.md @@ -0,0 +1,5 @@ +# ZENDEGI WEB APP + +Zendegi is a web app for creating and exploring timelines. They could be personal or professional. + +The timelines are shown horizontally and are interactive. diff --git a/apps/web/package.json b/apps/web/package.json index 7f46f69..553150b 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,6 +11,8 @@ }, "dependencies": { "@base-ui/react": "^1.0.0", + "@fontsource-variable/inter": "^5.2.8", + "@fontsource/instrument-serif": "^5.2.8", "@tailwindcss/vite": "^4.1.8", "@tanstack/react-form": "^1.23.5", "@tanstack/react-query": "^5.80.6", diff --git a/apps/web/public/logo.png b/apps/web/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..60243488d3f62d8aace276095d07d4d36cd79384 GIT binary patch literal 6974 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 rootRouteImport, +} as any) +const TimelineRoute = TimelineRouteImport.update({ + id: '/timeline', + path: '/timeline', + getParentRoute: () => rootRouteImport, +} as any) const LoginRoute = LoginRouteImport.update({ id: '/login', path: '/login', @@ -35,6 +48,11 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) +const TimelineTimelineIdRoute = TimelineTimelineIdRouteImport.update({ + id: '/$timelineId', + path: '/$timelineId', + getParentRoute: () => TimelineRoute, +} as any) const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({ id: '/api/auth/$', path: '/api/auth/$', @@ -46,6 +64,9 @@ export interface FileRoutesByFullPath { '/dashboard': typeof DashboardRoute '/demo': typeof DemoRoute '/login': typeof LoginRoute + '/timeline': typeof TimelineRouteWithChildren + '/timelines': typeof TimelinesRoute + '/timeline/$timelineId': typeof TimelineTimelineIdRoute '/api/auth/$': typeof ApiAuthSplatRoute } export interface FileRoutesByTo { @@ -53,6 +74,9 @@ export interface FileRoutesByTo { '/dashboard': typeof DashboardRoute '/demo': typeof DemoRoute '/login': typeof LoginRoute + '/timeline': typeof TimelineRouteWithChildren + '/timelines': typeof TimelinesRoute + '/timeline/$timelineId': typeof TimelineTimelineIdRoute '/api/auth/$': typeof ApiAuthSplatRoute } export interface FileRoutesById { @@ -61,14 +85,42 @@ export interface FileRoutesById { '/dashboard': typeof DashboardRoute '/demo': typeof DemoRoute '/login': typeof LoginRoute + '/timeline': typeof TimelineRouteWithChildren + '/timelines': typeof TimelinesRoute + '/timeline/$timelineId': typeof TimelineTimelineIdRoute '/api/auth/$': typeof ApiAuthSplatRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/dashboard' | '/demo' | '/login' | '/api/auth/$' + fullPaths: + | '/' + | '/dashboard' + | '/demo' + | '/login' + | '/timeline' + | '/timelines' + | '/timeline/$timelineId' + | '/api/auth/$' fileRoutesByTo: FileRoutesByTo - to: '/' | '/dashboard' | '/demo' | '/login' | '/api/auth/$' - id: '__root__' | '/' | '/dashboard' | '/demo' | '/login' | '/api/auth/$' + to: + | '/' + | '/dashboard' + | '/demo' + | '/login' + | '/timeline' + | '/timelines' + | '/timeline/$timelineId' + | '/api/auth/$' + id: + | '__root__' + | '/' + | '/dashboard' + | '/demo' + | '/login' + | '/timeline' + | '/timelines' + | '/timeline/$timelineId' + | '/api/auth/$' fileRoutesById: FileRoutesById } export interface RootRouteChildren { @@ -76,11 +128,27 @@ export interface RootRouteChildren { DashboardRoute: typeof DashboardRoute DemoRoute: typeof DemoRoute LoginRoute: typeof LoginRoute + TimelineRoute: typeof TimelineRouteWithChildren + TimelinesRoute: typeof TimelinesRoute ApiAuthSplatRoute: typeof ApiAuthSplatRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/timelines': { + id: '/timelines' + path: '/timelines' + fullPath: '/timelines' + preLoaderRoute: typeof TimelinesRouteImport + parentRoute: typeof rootRouteImport + } + '/timeline': { + id: '/timeline' + path: '/timeline' + fullPath: '/timeline' + preLoaderRoute: typeof TimelineRouteImport + parentRoute: typeof rootRouteImport + } '/login': { id: '/login' path: '/login' @@ -109,6 +177,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/timeline/$timelineId': { + id: '/timeline/$timelineId' + path: '/$timelineId' + fullPath: '/timeline/$timelineId' + preLoaderRoute: typeof TimelineTimelineIdRouteImport + parentRoute: typeof TimelineRoute + } '/api/auth/$': { id: '/api/auth/$' path: '/api/auth/$' @@ -119,11 +194,25 @@ declare module '@tanstack/react-router' { } } +interface TimelineRouteChildren { + TimelineTimelineIdRoute: typeof TimelineTimelineIdRoute +} + +const TimelineRouteChildren: TimelineRouteChildren = { + TimelineTimelineIdRoute: TimelineTimelineIdRoute, +} + +const TimelineRouteWithChildren = TimelineRoute._addFileChildren( + TimelineRouteChildren, +) + const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, DashboardRoute: DashboardRoute, DemoRoute: DemoRoute, LoginRoute: LoginRoute, + TimelineRoute: TimelineRouteWithChildren, + TimelinesRoute: TimelinesRoute, ApiAuthSplatRoute: ApiAuthSplatRoute, } export const routeTree = rootRouteImport diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index 4449f24..645db06 100644 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -4,31 +4,24 @@ export const Route = createFileRoute("/")({ component: HomeComponent, }); -const TITLE_TEXT = ` - ██████╗ ███████╗████████╗████████╗███████╗██████╗ - ██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗ - ██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝ - ██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗ - ██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║ - ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ - - ████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗ - ╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝ - ██║ ███████╗ ██║ ███████║██║ █████╔╝ - ██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗ - ██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗ - ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ - `; - function HomeComponent() { return ( -
-
{TITLE_TEXT}
-
-
-

API Status

-
+
+
+
+ Zendegi +
+
+

+ Everything you need to visualize your story +

+

+ With our web app, bring to life stunning timelines of your + milestones, stories, and plans. Unleash creativity, boost + productivity, and transform how you experience your world. +

+
-
+ ); } diff --git a/apps/web/src/routes/timeline.$timelineId.tsx b/apps/web/src/routes/timeline.$timelineId.tsx new file mode 100644 index 0000000..7cdb3d2 --- /dev/null +++ b/apps/web/src/routes/timeline.$timelineId.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/timeline/$timelineId")({ + component: RouteComponent, +}); + +function RouteComponent() { + return
Timeline detail
; +} diff --git a/apps/web/src/routes/timeline.tsx b/apps/web/src/routes/timeline.tsx new file mode 100644 index 0000000..7525046 --- /dev/null +++ b/apps/web/src/routes/timeline.tsx @@ -0,0 +1,14 @@ +import { Outlet, createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/timeline")({ + component: RouteComponent, +}); + +function RouteComponent() { + return ( +
+ Shell of timeline + +
+ ); +} diff --git a/apps/web/src/routes/timelines.tsx b/apps/web/src/routes/timelines.tsx new file mode 100644 index 0000000..6af7fba --- /dev/null +++ b/apps/web/src/routes/timelines.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/timelines")({ + component: RouteComponent, +}); + +function RouteComponent() { + return
List of timelines
; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3adb8e5..69d82e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,6 +60,12 @@ importers: '@base-ui/react': specifier: ^1.0.0 version: 1.1.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@fontsource-variable/inter': + specifier: ^5.2.8 + version: 5.2.8 + '@fontsource/instrument-serif': + specifier: ^5.2.8 + version: 5.2.8 '@tailwindcss/vite': specifier: ^4.1.8 version: 4.1.18(vite@7.3.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) @@ -1050,6 +1056,12 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@fontsource-variable/inter@5.2.8': + resolution: {integrity: sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==} + + '@fontsource/instrument-serif@5.2.8': + resolution: {integrity: sha512-s+bkz+syj2rO00Rmq9g0P+PwuLig33DR1xDR8pTWmovH1pUjwnncrFk++q9mmOex8fUQ7oW80gPpPDaw7V1MMw==} + '@hono/node-server@1.19.9': resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} engines: {node: '>=18.14.1'} @@ -5178,6 +5190,10 @@ snapshots: '@floating-ui/utils@0.2.10': {} + '@fontsource-variable/inter@5.2.8': {} + + '@fontsource/instrument-serif@5.2.8': {} + '@hono/node-server@1.19.9(hono@4.11.9)': dependencies: hono: 4.11.9