From a2d8a1e08075467ee6d516f69ab8a3a53fa222be Mon Sep 17 00:00:00 2001
From: lumijiez <59575049+lumijiez@users.noreply.github.com>
Date: Mon, 23 Jun 2025 22:21:48 +0300
Subject: [PATCH] Connect auth to backend and persist theme preference
---
.../PaymentForm.js | 0
.../src/app/components/ClientLayoutEffect.js | 21 ++++++
webui/src/app/components/ImprinkAppBar.js | 66 +++++++++++++++++++
.../src/app/components/theme/ThemeContext.js | 46 ++++++++++++-
.../{ => theme}/ThemeToggleButton.js | 2 +-
webui/src/app/layout.js | 4 ++
webui/src/app/page.js | 16 +----
webui/src/app/token/route.js | 10 +--
webui/src/lib/api.js | 22 -------
webui/src/lib/clientApi.js | 31 +++++++++
webui/src/lib/serverApi.js | 8 +++
11 files changed, 181 insertions(+), 45 deletions(-)
rename webui/{src/app/components => auth0-templates}/PaymentForm.js (100%)
create mode 100644 webui/src/app/components/ClientLayoutEffect.js
create mode 100644 webui/src/app/components/ImprinkAppBar.js
rename webui/src/app/components/{ => theme}/ThemeToggleButton.js (92%)
delete mode 100644 webui/src/lib/api.js
create mode 100644 webui/src/lib/clientApi.js
create mode 100644 webui/src/lib/serverApi.js
diff --git a/webui/src/app/components/PaymentForm.js b/webui/auth0-templates/PaymentForm.js
similarity index 100%
rename from webui/src/app/components/PaymentForm.js
rename to webui/auth0-templates/PaymentForm.js
diff --git a/webui/src/app/components/ClientLayoutEffect.js b/webui/src/app/components/ClientLayoutEffect.js
new file mode 100644
index 0000000..9f52a2a
--- /dev/null
+++ b/webui/src/app/components/ClientLayoutEffect.js
@@ -0,0 +1,21 @@
+'use client';
+
+import { useEffect } from 'react';
+import axios from 'axios';
+
+export default function ClientLayoutEffect() {
+ useEffect(() => {
+ async function fetchData() {
+ try {
+ const res = await axios.get('/token');
+ console.log('Token response:', res.data);
+ } catch (error) {
+ console.error('Token fetch error:', error);
+ }
+ }
+
+ fetchData().then(r => console.log("Ok"));
+ }, []);
+
+ return null;
+}
\ No newline at end of file
diff --git a/webui/src/app/components/ImprinkAppBar.js b/webui/src/app/components/ImprinkAppBar.js
new file mode 100644
index 0000000..45587fd
--- /dev/null
+++ b/webui/src/app/components/ImprinkAppBar.js
@@ -0,0 +1,66 @@
+'use client'
+
+import {AppBar, Button, Toolbar, Typography, Avatar, Box} from "@mui/material";
+import { useUser } from "@auth0/nextjs-auth0";
+import ThemeToggleButton from "@/app/components/theme/ThemeToggleButton";
+
+export default function ImprinkAppBar() {
+ const { user, error, isLoading } = useUser();
+
+ return (
+
+
+
+ Modern App
+
+
+
+ {isLoading ? (
+
+ Loading...
+
+ ) : user ? (
+
+
+
+ {user.name}
+
+
+
+ ) : (
+
+
+
+
+ )}
+
+
+ )
+}
\ No newline at end of file
diff --git a/webui/src/app/components/theme/ThemeContext.js b/webui/src/app/components/theme/ThemeContext.js
index ae25725..55017ed 100644
--- a/webui/src/app/components/theme/ThemeContext.js
+++ b/webui/src/app/components/theme/ThemeContext.js
@@ -1,16 +1,58 @@
'use client';
-import { createContext, useContext, useState } from 'react';
+import { createContext, useContext, useState, useEffect } from 'react';
const ThemeContext = createContext({ isDarkMode: true });
+function getInitialTheme() {
+ if (typeof window === 'undefined') {
+ return null;
+ }
+
+ const savedTheme = localStorage.getItem('theme-preference');
+ if (savedTheme) {
+ return savedTheme === 'dark';
+ }
+
+ return window.matchMedia('(prefers-color-scheme: dark)').matches;
+}
+
export function ThemeContextProvider({ children }) {
const [isDarkMode, setIsDarkMode] = useState(true);
+ const [isInitialized, setIsInitialized] = useState(false);
+
+ useEffect(() => {
+ const initialTheme = getInitialTheme();
+ if (initialTheme !== null) {
+ setIsDarkMode(initialTheme);
+ }
+ setIsInitialized(true);
+ }, []);
const toggleTheme = () => {
- setIsDarkMode(!isDarkMode);
+ const newTheme = !isDarkMode;
+ setIsDarkMode(newTheme);
+
+ if (typeof window !== 'undefined') {
+ localStorage.setItem('theme-preference', newTheme ? 'dark' : 'light');
+ }
};
+ if (!isInitialized) {
+ return (
+
+ {children}
+
+ );
+ }
+
return (
{children}
diff --git a/webui/src/app/components/ThemeToggleButton.js b/webui/src/app/components/theme/ThemeToggleButton.js
similarity index 92%
rename from webui/src/app/components/ThemeToggleButton.js
rename to webui/src/app/components/theme/ThemeToggleButton.js
index 54f0f8d..c228443 100644
--- a/webui/src/app/components/ThemeToggleButton.js
+++ b/webui/src/app/components/theme/ThemeToggleButton.js
@@ -1,7 +1,7 @@
'use client';
import { IconButton } from '@mui/material';
-import { useTheme } from './theme/ThemeContext';
+import { useTheme } from './ThemeContext';
export default function ThemeToggleButton() {
const { isDarkMode, toggleTheme } = useTheme();
diff --git a/webui/src/app/layout.js b/webui/src/app/layout.js
index 3e9fc1e..1ee1f4a 100644
--- a/webui/src/app/layout.js
+++ b/webui/src/app/layout.js
@@ -2,6 +2,8 @@ import { Inter } from 'next/font/google';
import MuiThemeProvider from './components/theme/MuiThemeProvider';
import { ThemeContextProvider } from './components/theme/ThemeContext';
import { AppRouterCacheProvider } from "@mui/material-nextjs/v13-appRouter";
+import ImprinkAppBar from "@/app/components/ImprinkAppBar";
+import ClientLayoutEffect from "@/app/components/ClientLayoutEffect";
const inter = Inter({ subsets: ['latin'] });
@@ -17,6 +19,8 @@ export default function RootLayout({children}) {
+
+
{children}
diff --git a/webui/src/app/page.js b/webui/src/app/page.js
index e3f550b..661b9d1 100644
--- a/webui/src/app/page.js
+++ b/webui/src/app/page.js
@@ -9,25 +9,12 @@ import {
CardContent,
TextField,
Chip,
- AppBar,
- Toolbar,
Grid,
} from '@mui/material';
-import ThemeToggleButton from './components/ThemeToggleButton';
export default function HomePage() {
- return (
- <>
-
-
-
- Modern App
-
-
-
-
-
+ return (
@@ -100,6 +87,5 @@ export default function HomePage() {
- >
);
}
\ No newline at end of file
diff --git a/webui/src/app/token/route.js b/webui/src/app/token/route.js
index a3fad83..9ffd1a3 100644
--- a/webui/src/app/token/route.js
+++ b/webui/src/app/token/route.js
@@ -1,6 +1,6 @@
import { NextResponse } from 'next/server';
import {auth0} from "@/lib/auth0";
-import api from "@/lib/api";
+import serverApi from "@/lib/serverApi";
export async function GET() {
try {
@@ -8,13 +8,13 @@ export async function GET() {
if (!token) { return NextResponse.json({ error: 'No access token found' }, { status: 401 }); }
- await api.post('/users/sync', {}, {
- headers: { Cookie: `access_token=${token}` }
+ await serverApi.post('/users/me/sync', null, {
+ headers: { Authorization: `Bearer ${token}`}
});
- return NextResponse.json({ access_token: token });
+ return NextResponse.json("Ok");
} catch (error) {
- console.error('Error in /api/token:', error);
+ console.error('Error in /serverApi/token:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
\ No newline at end of file
diff --git a/webui/src/lib/api.js b/webui/src/lib/api.js
deleted file mode 100644
index 8cf8595..0000000
--- a/webui/src/lib/api.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import axios from "axios";
-
-const api = axios.create({
- baseURL: process.env.NEXT_PUBLIC_API_URL,
- withCredentials: true,
-});
-
-api.interceptors.request.use((config) => {
- if (typeof window !== 'undefined') {
- const token = localStorage.getItem('access_token');
- if (token) {
- config.headers.Authorization = `Bearer ${token}`;
- } else {
- console.log("Token not found in localStorage, please auth!");
- }
- }
- return config;
-}, (error) => {
- return Promise.reject(error);
-});
-
-export default api;
\ No newline at end of file
diff --git a/webui/src/lib/clientApi.js b/webui/src/lib/clientApi.js
new file mode 100644
index 0000000..de600a8
--- /dev/null
+++ b/webui/src/lib/clientApi.js
@@ -0,0 +1,31 @@
+import axios from "axios";
+
+const clientApi = axios.create({
+ baseURL: process.env.NEXT_PUBLIC_API_URL,
+ withCredentials: true,
+});
+
+clientApi.interceptors.request.use(async (config) => {
+ if (typeof window === 'undefined') return config;
+
+ try {
+ const res = await fetch('/auth/access-token');
+ if (!res.ok)
+ throw new Error('Failed to fetch token');
+ const data = await res.json();
+
+ if (data.token) {
+ config.headers.Authorization = `Bearer ${data.token}`;
+ } else {
+ console.warn('No token received from /auth/access-token');
+ }
+ } catch (err) {
+ console.error('Error fetching token:', err);
+ }
+
+ return config;
+}, error => {
+ return Promise.reject(error);
+});
+
+export default clientApi;
\ No newline at end of file
diff --git a/webui/src/lib/serverApi.js b/webui/src/lib/serverApi.js
new file mode 100644
index 0000000..5371f2f
--- /dev/null
+++ b/webui/src/lib/serverApi.js
@@ -0,0 +1,8 @@
+import axios from "axios";
+
+const serverApi = axios.create({
+ baseURL: process.env.NEXT_PUBLIC_API_URL,
+ withCredentials: true,
+});
+
+export default serverApi;
\ No newline at end of file