diff --git a/react_fe/src/components/Navbar.tsx b/react_fe/src/components/Navbar.tsx index c4a5daa..b05cd70 100644 --- a/react_fe/src/components/Navbar.tsx +++ b/react_fe/src/components/Navbar.tsx @@ -2,6 +2,7 @@ import { ReactNode, useEffect, useRef, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; import { PermissionRole } from "../models/dto/PermissionsDto"; +import ThemeToggleButton from "./ThemeToggleButton"; interface NavItem { label: string; @@ -80,6 +81,7 @@ function Navbar({ children }: { children: ReactNode }) { )} + diff --git a/react_fe/src/components/ThemeToggleButton.tsx b/react_fe/src/components/ThemeToggleButton.tsx new file mode 100644 index 0000000..9baa207 --- /dev/null +++ b/react_fe/src/components/ThemeToggleButton.tsx @@ -0,0 +1,39 @@ +import { useEffect, useState } from 'react'; +import { LocalStorageNames } from '../constants/LocalStorageNames'; + +function ThemeToggleButton() { + const [darkMode, setDarkMode] = useState(() => { + return localStorage.getItem(LocalStorageNames.THEME) === 'dark'; + }); + + useEffect(() => { + const theme = darkMode ? 'dark' : 'light'; + if (darkMode) { + localStorage.setItem(LocalStorageNames.THEME, theme); + } else { + localStorage.removeItem(LocalStorageNames.THEME); + } + document.documentElement.setAttribute('data-theme', theme); + }, [darkMode]); + + return ( + + ); +} + +export default ThemeToggleButton; diff --git a/react_fe/src/constants/LocalStorageNames.ts b/react_fe/src/constants/LocalStorageNames.ts index 8b0539e..a347400 100644 --- a/react_fe/src/constants/LocalStorageNames.ts +++ b/react_fe/src/constants/LocalStorageNames.ts @@ -1,4 +1,5 @@ export class LocalStorageNames { static readonly REFRESH_TOKEN = 'refreshToken'; static readonly CHOSEN_TENANT_ID = 'chosenTenantId'; + static readonly THEME = 'theme'; } \ No newline at end of file diff --git a/react_fe/src/index.css b/react_fe/src/index.css index e362404..ad1cb38 100644 --- a/react_fe/src/index.css +++ b/react_fe/src/index.css @@ -17,7 +17,7 @@ font: 18px/145% var(--sans); letter-spacing: 0.18px; - color-scheme: light dark; + color-scheme: light; color: var(--text); background: var(--bg); font-synthesis: none; @@ -30,24 +30,23 @@ } } -@media (prefers-color-scheme: dark) { - :root { - --text: #9ca3af; - --text-h: #f3f4f6; - --bg: #16171d; - --border: #2e303a; - --code-bg: #1f2028; - --accent: #c084fc; - --accent-bg: rgba(192, 132, 252, 0.15); - --accent-border: rgba(192, 132, 252, 0.5); - --social-bg: rgba(47, 48, 58, 0.5); - --shadow: - rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; - } +[data-theme="dark"] { + color-scheme: dark; + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --code-bg: #1f2028; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: + rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; +} - #social .button-icon { - filter: invert(1) brightness(2); - } +[data-theme="dark"] #social .button-icon { + filter: invert(1) brightness(2); } body { @@ -157,6 +156,27 @@ p { border-color: var(--accent-border); } +.navbar-theme-toggle { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + border-radius: 6px; + border: 1px solid var(--border); + background: transparent; + color: var(--text); + cursor: pointer; + transition: background 0.15s, color 0.15s, border-color 0.15s; +} + +.navbar-theme-toggle:hover { + background: var(--accent-bg); + color: var(--accent); + border-color: var(--accent-border); +} + .navbar-right { display: flex; align-items: center; diff --git a/react_fe/src/pages/IssueDetailsPage.tsx b/react_fe/src/pages/IssueDetailsPage.tsx index 892527e..8e7e266 100644 --- a/react_fe/src/pages/IssueDetailsPage.tsx +++ b/react_fe/src/pages/IssueDetailsPage.tsx @@ -1,18 +1,13 @@ import { useNavigate, useParams } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { useAlert } from "../context/AlertContext"; -import { IssueDto } from "../models/dto/IssueDto"; -import { issuesApi } from "../api/issues"; function IssueDetailsPage() { const { tenantId, id } = useParams<{ tenantId: string, id: string }>(); const { permissions } = useAuth(); const {info} = useAlert(); const navigate = useNavigate(); - const [paginationSize, setPaginationSize] = useState(10); - const [currentPage, setCurrentPage] = useState(1); - const [issues, setIssues] = useState(null); useEffect(() => { if (hasFaultyParams() || !permissions) { @@ -24,14 +19,7 @@ function IssueDetailsPage() { info("You do not have permissions for this tenant"); navigate('/'); } - - issuesApi.getIssues(parseInt(tenantId!), { page: currentPage, size: paginationSize }) - .then(response => setIssues(response.content)) - .catch(error => { - info("Failed to fetch issues"); - navigate('/'); - }); - }, [permissions, tenantId, navigate, currentPage, paginationSize]); + }, [permissions, tenantId]); function hasFaultyParams() { return !tenantId || isNaN(parseInt(tenantId)) || !id || isNaN(parseInt(id)); diff --git a/react_fe/src/pages/Login.tsx b/react_fe/src/pages/Login.tsx index ff427ca..3ff04a4 100644 --- a/react_fe/src/pages/Login.tsx +++ b/react_fe/src/pages/Login.tsx @@ -3,6 +3,7 @@ import { Link, useNavigate } from 'react-router-dom' import { useAuth } from '../context/AuthContext' import { AuthRequest } from '../models/dto/AuthRequest' import { useAlert } from '../context/AlertContext'; +import ThemeToggleButton from '../components/ThemeToggleButton'; interface FormErrors { email?: string; @@ -60,7 +61,10 @@ function LoginPage() { return (
-

Welcome back

+
+

Welcome back

+ +

Sign in to your account

diff --git a/react_fe/src/pages/Register.tsx b/react_fe/src/pages/Register.tsx index 8c9472a..22cb1b9 100644 --- a/react_fe/src/pages/Register.tsx +++ b/react_fe/src/pages/Register.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { Link, useNavigate } from 'react-router-dom' import { useAuth } from '../context/AuthContext' import { useAlert } from '../context/AlertContext' +import ThemeToggleButton from '../components/ThemeToggleButton' interface FormErrors { username?: string @@ -77,7 +78,10 @@ function RegisterPage() { return (
-

Create an account

+
+

Create an account

+ +

Get started with Task Planner

diff --git a/task_planner/pom.xml b/task_planner/pom.xml index d4b6890..a721ac6 100644 --- a/task_planner/pom.xml +++ b/task_planner/pom.xml @@ -122,6 +122,24 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + + properties + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -javaagent:${org.mockito:mockito-core:jar} + +