source: .idea/copilotDiffState.xml@ 447c39f

Last change on this file since 447c39f was 42da64d, checked in by Andrej <asumanovski@…>, 8 weeks ago

Fixed auth issue

  • Property mode set to 100644
File size: 18.7 KB
Line 
1<?xml version="1.0" encoding="UTF-8"?>
2<project version="4">
3 <component name="CopilotDiffPersistence">
4 <option name="pendingDiffs">
5 <map>
6 <entry key="$PROJECT_DIR$/frontend/src/App.jsx">
7 <value>
8 <PendingDiffInfo>
9 <option name="filePath" value="$PROJECT_DIR$/frontend/src/App.jsx" />
10 <option name="originalContent" value="import { Navigate, Route, Routes } from &quot;react-router-dom&quot;;&#10;&#10;import LandingPage from &quot;./pages/LandingPage/LandingPage.jsx&quot;;&#10;import Login from &quot;./pages/Login/Login.jsx&quot;;&#10;import Register from &quot;./pages/Register/Register.jsx&quot;;&#10;import DashboardLayout from &quot;./pages/Dashboard/DashboardLayout.jsx&quot;;&#10;import ControlCenter from &quot;./pages/Dashboard/pages/ControlCenter/ControlCenter.jsx&quot;;&#10;import Training from &quot;./pages/Dashboard/pages/Training/Training.jsx&quot;;&#10;import TrainingTracking from &quot;./pages/Dashboard/pages/Training/TrainingTracking.jsx&quot;;&#10;import NewTrainingSession from &quot;./pages/Dashboard/pages/Training/NewTrainingSession.jsx&quot;;&#10;import Weight from &quot;./pages/Dashboard/pages/Weight/Weight.jsx&quot;;&#10;import WeightTracking from &quot;./pages/Dashboard/pages/Weight/WeightTracking.jsx&quot;;&#10;import NewWeightIntake from &quot;./pages/Dashboard/pages/Weight/NewWeightIntake.jsx&quot;;&#10;import Finance from &quot;./pages/Dashboard/pages/Finance/Finance.jsx&quot;;&#10;import FinanceTracking from &quot;./pages/Dashboard/pages/Finance/FinanceTracking.jsx&quot;;&#10;import NewIncome from &quot;./pages/Dashboard/pages/Finance/NewIncome.jsx&quot;;&#10;import Investing from &quot;./pages/Dashboard/pages/Investing/Investing.jsx&quot;;&#10;import InvestingTracking from &quot;./pages/Dashboard/pages/Investing/InvestingTracking.jsx&quot;;&#10;import NewInvestment from &quot;./pages/Dashboard/pages/Investing/NewInvestment.jsx&quot;;&#10;import Discipline from &quot;./pages/Dashboard/pages/Discipline/Discipline.jsx&quot;;&#10;import DisciplineTracking from &quot;./pages/Dashboard/pages/Discipline/DisciplineTracking.jsx&quot;;&#10;import CustomCategoryKanban from &quot;./pages/Dashboard/pages/CustomCategory/CustomCategoryKanban.jsx&quot;;&#10;&#10;function RequireAuth({ children }) {&#10; const token = localStorage.getItem(&quot;authToken&quot;);&#10; if (!token) return &lt;Navigate to=&quot;/login&quot; replace /&gt;;&#10; return children;&#10;}&#10;&#10;function App() {&#10; return (&#10; &lt;div data-theme=&quot;forest&quot; className=&quot;min-h-screen w-full&quot;&gt;&#10; &lt;Routes&gt;&#10; &lt;Route path=&quot;/&quot; element={&lt;LandingPage /&gt;} /&gt;&#10; &lt;Route path=&quot;/login&quot; element={&lt;Login /&gt;} /&gt;&#10; &lt;Route path=&quot;/register&quot; element={&lt;Register /&gt;} /&gt;&#10; &lt;Route&#10; path=&quot;/dashboard&quot;&#10; element={&#10; &lt;RequireAuth&gt;&#10; &lt;DashboardLayout /&gt;&#10; &lt;/RequireAuth&gt;&#10; }&#10; &gt;&#10; &lt;Route index element={&lt;Navigate to=&quot;control-center&quot; replace /&gt;} /&gt;&#10; &lt;Route path=&quot;control-center&quot; element={&lt;ControlCenter /&gt;} /&gt;&#10; &lt;Route path=&quot;training&quot; element={&lt;Training /&gt;} /&gt;&#10; &lt;Route path=&quot;training/tracking&quot; element={&lt;TrainingTracking /&gt;} /&gt;&#10; &lt;Route&#10; path=&quot;training/sessions/new&quot;&#10; element={&lt;NewTrainingSession /&gt;}&#10; /&gt;&#10; &lt;Route path=&quot;weight&quot; element={&lt;Weight /&gt;} /&gt;&#10; &lt;Route path=&quot;weight/tracking&quot; element={&lt;WeightTracking /&gt;} /&gt;&#10; &lt;Route path=&quot;weight/intakes/new&quot; element={&lt;NewWeightIntake /&gt;} /&gt;&#10; &lt;Route path=&quot;finance&quot; element={&lt;Finance /&gt;} /&gt;&#10; &lt;Route path=&quot;finance/tracking&quot; element={&lt;FinanceTracking /&gt;} /&gt;&#10; &lt;Route path=&quot;finance/incomes/new&quot; element={&lt;NewIncome /&gt;} /&gt;&#10; &lt;Route path=&quot;investing&quot; element={&lt;Investing /&gt;} /&gt;&#10; &lt;Route path=&quot;investing/tracking&quot; element={&lt;InvestingTracking /&gt;} /&gt;&#10; &lt;Route path=&quot;investing/assets/new&quot; element={&lt;NewInvestment /&gt;} /&gt;&#10; &lt;Route path=&quot;discipline&quot; element={&lt;Discipline /&gt;} /&gt;&#10; &lt;Route path=&quot;discipline/tracking&quot; element={&lt;DisciplineTracking /&gt;} /&gt;&#10; &lt;Route path=&quot;custom/:customTrackingId&quot; element={&lt;CustomCategoryKanban /&gt;} /&gt;&#10; &lt;/Route&gt;&#10; &lt;Route path=&quot;*&quot; element={&lt;Navigate to=&quot;/&quot; replace /&gt;} /&gt;&#10; &lt;/Routes&gt;&#10; &lt;/div&gt;&#10; );&#10;}&#10;&#10;export default App;&#10;" />
11 <option name="updatedContent" value="import { Navigate, Route, Routes } from &quot;react-router-dom&quot;;&#10;import { clearAuthSession, getAuthToken, isTokenExpired } from &quot;./utils/authSession&quot;;&#10;&#10;import LandingPage from &quot;./pages/LandingPage/LandingPage.jsx&quot;;&#10;import Login from &quot;./pages/Login/Login.jsx&quot;;&#10;import Register from &quot;./pages/Register/Register.jsx&quot;;&#10;import DashboardLayout from &quot;./pages/Dashboard/DashboardLayout.jsx&quot;;&#10;import ControlCenter from &quot;./pages/Dashboard/pages/ControlCenter/ControlCenter.jsx&quot;;&#10;import Training from &quot;./pages/Dashboard/pages/Training/Training.jsx&quot;;&#10;import TrainingTracking from &quot;./pages/Dashboard/pages/Training/TrainingTracking.jsx&quot;;&#10;import NewTrainingSession from &quot;./pages/Dashboard/pages/Training/NewTrainingSession.jsx&quot;;&#10;import Weight from &quot;./pages/Dashboard/pages/Weight/Weight.jsx&quot;;&#10;import WeightTracking from &quot;./pages/Dashboard/pages/Weight/WeightTracking.jsx&quot;;&#10;import NewWeightIntake from &quot;./pages/Dashboard/pages/Weight/NewWeightIntake.jsx&quot;;&#10;import Finance from &quot;./pages/Dashboard/pages/Finance/Finance.jsx&quot;;&#10;import FinanceTracking from &quot;./pages/Dashboard/pages/Finance/FinanceTracking.jsx&quot;;&#10;import NewIncome from &quot;./pages/Dashboard/pages/Finance/NewIncome.jsx&quot;;&#10;import Investing from &quot;./pages/Dashboard/pages/Investing/Investing.jsx&quot;;&#10;import InvestingTracking from &quot;./pages/Dashboard/pages/Investing/InvestingTracking.jsx&quot;;&#10;import NewInvestment from &quot;./pages/Dashboard/pages/Investing/NewInvestment.jsx&quot;;&#10;import Discipline from &quot;./pages/Dashboard/pages/Discipline/Discipline.jsx&quot;;&#10;import DisciplineTracking from &quot;./pages/Dashboard/pages/Discipline/DisciplineTracking.jsx&quot;;&#10;import CustomCategoryKanban from &quot;./pages/Dashboard/pages/CustomCategory/CustomCategoryKanban.jsx&quot;;&#10;&#10;function RequireAuth({ children }) {&#10; const token = getAuthToken();&#10; if (token &amp;&amp; isTokenExpired(token)) {&#10; clearAuthSession();&#10; return &lt;Navigate to=&quot;/login&quot; replace /&gt;;&#10; }&#10; if (!token) return &lt;Navigate to=&quot;/login&quot; replace /&gt;;&#10; return children;&#10;}&#10;&#10;function App() {&#10; return (&#10; &lt;div data-theme=&quot;forest&quot; className=&quot;min-h-screen w-full&quot;&gt;&#10; &lt;Routes&gt;&#10; &lt;Route path=&quot;/&quot; element={&lt;LandingPage /&gt;} /&gt;&#10; &lt;Route path=&quot;/login&quot; element={&lt;Login /&gt;} /&gt;&#10; &lt;Route path=&quot;/register&quot; element={&lt;Register /&gt;} /&gt;&#10; &lt;Route&#10; path=&quot;/dashboard&quot;&#10; element={&#10; &lt;RequireAuth&gt;&#10; &lt;DashboardLayout /&gt;&#10; &lt;/RequireAuth&gt;&#10; }&#10; &gt;&#10; &lt;Route index element={&lt;Navigate to=&quot;control-center&quot; replace /&gt;} /&gt;&#10; &lt;Route path=&quot;control-center&quot; element={&lt;ControlCenter /&gt;} /&gt;&#10; &lt;Route path=&quot;training&quot; element={&lt;Training /&gt;} /&gt;&#10; &lt;Route path=&quot;training/tracking&quot; element={&lt;TrainingTracking /&gt;} /&gt;&#10; &lt;Route&#10; path=&quot;training/sessions/new&quot;&#10; element={&lt;NewTrainingSession /&gt;}&#10; /&gt;&#10; &lt;Route path=&quot;weight&quot; element={&lt;Weight /&gt;} /&gt;&#10; &lt;Route path=&quot;weight/tracking&quot; element={&lt;WeightTracking /&gt;} /&gt;&#10; &lt;Route path=&quot;weight/intakes/new&quot; element={&lt;NewWeightIntake /&gt;} /&gt;&#10; &lt;Route path=&quot;finance&quot; element={&lt;Finance /&gt;} /&gt;&#10; &lt;Route path=&quot;finance/tracking&quot; element={&lt;FinanceTracking /&gt;} /&gt;&#10; &lt;Route path=&quot;finance/incomes/new&quot; element={&lt;NewIncome /&gt;} /&gt;&#10; &lt;Route path=&quot;investing&quot; element={&lt;Investing /&gt;} /&gt;&#10; &lt;Route path=&quot;investing/tracking&quot; element={&lt;InvestingTracking /&gt;} /&gt;&#10; &lt;Route path=&quot;investing/assets/new&quot; element={&lt;NewInvestment /&gt;} /&gt;&#10; &lt;Route path=&quot;discipline&quot; element={&lt;Discipline /&gt;} /&gt;&#10; &lt;Route path=&quot;discipline/tracking&quot; element={&lt;DisciplineTracking /&gt;} /&gt;&#10; &lt;Route path=&quot;custom/:customTrackingId&quot; element={&lt;CustomCategoryKanban /&gt;} /&gt;&#10; &lt;/Route&gt;&#10; &lt;Route path=&quot;*&quot; element={&lt;Navigate to=&quot;/&quot; replace /&gt;} /&gt;&#10; &lt;/Routes&gt;&#10; &lt;/div&gt;&#10; );&#10;}&#10;&#10;export default App;&#10;" />
12 </PendingDiffInfo>
13 </value>
14 </entry>
15 <entry key="$PROJECT_DIR$/frontend/src/api/axios.js">
16 <value>
17 <PendingDiffInfo>
18 <option name="filePath" value="$PROJECT_DIR$/frontend/src/api/axios.js" />
19 <option name="originalContent" value="import axios from &quot;axios&quot;;&#10;&#10;const api = axios.create({&#10; baseURL: import.meta.env.VITE_API_BASE_URL ?? &quot;http://localhost:8080/api&quot;,&#10;});&#10;&#10;api.interceptors.request.use((config) =&gt; {&#10; const token = localStorage.getItem(&quot;authToken&quot;);&#10; if (token) {&#10; config.headers = config.headers ?? {};&#10; config.headers.Authorization = `Bearer ${token}`;&#10; }&#10; return config;&#10;});&#10;&#10;export default api;" />
20 <option name="updatedContent" value="import axios from &quot;axios&quot;;&#10;import { getAuthToken, isTokenExpired, logoutAndRedirect } from &quot;../utils/authSession&quot;;&#10;&#10;const api = axios.create({&#10; baseURL: import.meta.env.VITE_API_BASE_URL ?? &quot;http://localhost:8080/api&quot;,&#10;});&#10;&#10;api.interceptors.request.use((config) =&gt; {&#10; const token = getAuthToken();&#10; if (token) {&#10; if (isTokenExpired(token)) {&#10; logoutAndRedirect(&quot;/dashboard&quot;);&#10; return Promise.reject(new Error(&quot;Authentication session expired&quot;));&#10; }&#10; config.headers = config.headers ?? {};&#10; config.headers.Authorization = `Bearer ${token}`;&#10; }&#10; return config;&#10;});&#10;&#10;api.interceptors.response.use(&#10; (response) =&gt; response,&#10; (error) =&gt; {&#10; const status = error?.response?.status;&#10; if (status === 401 || status === 403) {&#10; logoutAndRedirect(&quot;/dashboard&quot;);&#10; }&#10; return Promise.reject(error);&#10; },&#10;);&#10;&#10;export default api;" />
21 </PendingDiffInfo>
22 </value>
23 </entry>
24 <entry key="$PROJECT_DIR$/frontend/src/pages/Dashboard/DashboardLayout.jsx">
25 <value>
26 <PendingDiffInfo>
27 <option name="filePath" value="$PROJECT_DIR$/frontend/src/pages/Dashboard/DashboardLayout.jsx" />
28 <option name="originalContent" value="import React, { useMemo } from &quot;react&quot;;&#10;import { Outlet, useNavigate } from &quot;react-router-dom&quot;;&#10;&#10;import { SidebarInset, SidebarProvider } from &quot;@relume_io/relume-ui&quot;;&#10;&#10;import DashboardSidebar from &quot;./components/DashboardSidebar.jsx&quot;;&#10;import DashboardTopbar from &quot;./components/DashboardTopbar.jsx&quot;;&#10;&#10;const DashboardLayout = () =&gt; {&#10; const navigate = useNavigate();&#10;&#10; const user = useMemo(() =&gt; {&#10; try {&#10; const raw = localStorage.getItem(&quot;authUser&quot;);&#10; return raw ? JSON.parse(raw) : null;&#10; } catch {&#10; return null;&#10; }&#10; }, []);&#10;&#10; const username = user?.username ?? &quot;&quot;;&#10;&#10; const onLogout = () =&gt; {&#10; localStorage.removeItem(&quot;authToken&quot;);&#10; localStorage.removeItem(&quot;authUser&quot;);&#10; navigate(&quot;/&quot;, { replace: true });&#10; };&#10;&#10; return (&#10; &lt;SidebarProvider&gt;&#10; {/* App shell background */}&#10; &lt;div className=&quot;pointer-events-none fixed inset-0 -z-10&quot;&gt;&#10; &lt;div className=&quot;absolute inset-0 bg-base-300/10&quot; /&gt;&#10; &lt;div className=&quot;absolute inset-0 bg-radial-[circle_at_20%_20%] from-green-400/10 via-transparent to-transparent&quot; /&gt;&#10; &lt;div className=&quot;absolute inset-0 bg-radial-[circle_at_80%_30%] from-blue-400/10 via-transparent to-transparent&quot; /&gt;&#10; &lt;div className=&quot;absolute inset-0 bg-linear-to-b from-black/10 via-transparent to-black/25&quot; /&gt;&#10; &lt;/div&gt;&#10;&#10; &lt;DashboardSidebar /&gt;&#10;&#10; &lt;SidebarInset className=&quot;pt-16 lg:pt-0&quot;&gt;&#10; &lt;DashboardTopbar username={username} onLogout={onLogout} /&gt;&#10;&#10; &lt;div className=&quot;h-[calc(100vh-4rem)] overflow-auto&quot;&gt;&#10; &lt;div className=&quot;mx-auto w-full max-w-7xl px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-10&quot;&gt;&#10; &lt;div className=&quot;rounded-3xl border border-white/10 bg-base-100/60 shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_24px_90px_rgba(0,0,0,0.35)] backdrop-blur-xl&quot;&gt;&#10; &lt;div className=&quot;p-4 sm:p-6 lg:p-8&quot;&gt;&#10; &lt;Outlet /&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/SidebarInset&gt;&#10; &lt;/SidebarProvider&gt;&#10; );&#10;};&#10;&#10;export default DashboardLayout;&#10;" />
29 <option name="updatedContent" value="import React, { useMemo } from &quot;react&quot;;&#10;import { Outlet, useNavigate } from &quot;react-router-dom&quot;;&#10;&#10;import { SidebarInset, SidebarProvider } from &quot;@relume_io/relume-ui&quot;;&#10;&#10;import DashboardSidebar from &quot;./components/DashboardSidebar.jsx&quot;;&#10;import DashboardTopbar from &quot;./components/DashboardTopbar.jsx&quot;;&#10;import { clearAuthSession } from &quot;../../utils/authSession&quot;;&#10;&#10;const DashboardLayout = () =&gt; {&#10; const navigate = useNavigate();&#10;&#10; const user = useMemo(() =&gt; {&#10; try {&#10; const raw = localStorage.getItem(&quot;authUser&quot;);&#10; return raw ? JSON.parse(raw) : null;&#10; } catch {&#10; return null;&#10; }&#10; }, []);&#10;&#10; const username = user?.username ?? &quot;&quot;;&#10;&#10; const onLogout = () =&gt; {&#10; clearAuthSession();&#10; navigate(&quot;/&quot;, { replace: true });&#10; };&#10;&#10; return (&#10; &lt;SidebarProvider&gt;&#10; {/* App shell background */}&#10; &lt;div className=&quot;pointer-events-none fixed inset-0 -z-10&quot;&gt;&#10; &lt;div className=&quot;absolute inset-0 bg-base-300/10&quot; /&gt;&#10; &lt;div className=&quot;absolute inset-0 bg-radial-[circle_at_20%_20%] from-green-400/10 via-transparent to-transparent&quot; /&gt;&#10; &lt;div className=&quot;absolute inset-0 bg-radial-[circle_at_80%_30%] from-blue-400/10 via-transparent to-transparent&quot; /&gt;&#10; &lt;div className=&quot;absolute inset-0 bg-linear-to-b from-black/10 via-transparent to-black/25&quot; /&gt;&#10; &lt;/div&gt;&#10;&#10; &lt;DashboardSidebar /&gt;&#10;&#10; &lt;SidebarInset className=&quot;pt-16 lg:pt-0&quot;&gt;&#10; &lt;DashboardTopbar username={username} onLogout={onLogout} /&gt;&#10;&#10; &lt;div className=&quot;h-[calc(100vh-4rem)] overflow-auto&quot;&gt;&#10; &lt;div className=&quot;mx-auto w-full max-w-7xl px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-10&quot;&gt;&#10; &lt;div className=&quot;rounded-3xl border border-white/10 bg-base-100/60 shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_24px_90px_rgba(0,0,0,0.35)] backdrop-blur-xl&quot;&gt;&#10; &lt;div className=&quot;p-4 sm:p-6 lg:p-8&quot;&gt;&#10; &lt;Outlet /&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/SidebarInset&gt;&#10; &lt;/SidebarProvider&gt;&#10; );&#10;};&#10;&#10;export default DashboardLayout;&#10;" />
30 </PendingDiffInfo>
31 </value>
32 </entry>
33 <entry key="$PROJECT_DIR$/frontend/src/utils/authSession.js">
34 <value>
35 <PendingDiffInfo>
36 <option name="filePath" value="$PROJECT_DIR$/frontend/src/utils/authSession.js" />
37 <option name="updatedContent" value="export const AUTH_TOKEN_KEY = &quot;authToken&quot;;&#10;export const AUTH_USER_KEY = &quot;authUser&quot;;&#10;&#10;function decodeJwtPayload(token) {&#10; try {&#10; const parts = token.split(&quot;.&quot;);&#10; if (parts.length &lt; 2) return null;&#10; const base64 = parts[1].replace(/-/g, &quot;+&quot;).replace(/_/g, &quot;/&quot;);&#10; const normalized = base64.padEnd(Math.ceil(base64.length / 4) * 4, &quot;=&quot;);&#10; const json = atob(normalized);&#10; return JSON.parse(json);&#10; } catch {&#10; return null;&#10; }&#10;}&#10;&#10;export function isTokenExpired(token) {&#10; if (!token) return true;&#10; const payload = decodeJwtPayload(token);&#10; if (!payload || typeof payload.exp !== &quot;number&quot;) return true;&#10; return payload.exp * 1000 &lt;= Date.now();&#10;}&#10;&#10;export function getAuthToken() {&#10; return localStorage.getItem(AUTH_TOKEN_KEY);&#10;}&#10;&#10;export function clearAuthSession() {&#10; localStorage.removeItem(AUTH_TOKEN_KEY);&#10; localStorage.removeItem(AUTH_USER_KEY);&#10;}&#10;&#10;export function logoutAndRedirect(path = &quot;/dashboard&quot;) {&#10; clearAuthSession();&#10; window.location.assign(path);&#10;}&#10;" />
38 </PendingDiffInfo>
39 </value>
40 </entry>
41 </map>
42 </option>
43 </component>
44</project>
Note: See TracBrowser for help on using the repository browser.