Skip to content

Commit 364d3de

Browse files
authored
feat/FE: change theme (#48)
* update ui * update color in component * integrate with API
1 parent 3034a36 commit 364d3de

28 files changed

+459
-597
lines changed

backend/conf/evolutions/default/7.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# --- !Ups
2+
13
CREATE TABLE user_profiles (
24
id SERIAL PRIMARY KEY,
35
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@@ -7,3 +9,6 @@ CREATE TABLE user_profiles (
79
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
810
CONSTRAINT user_profile_user_id_idx UNIQUE (user_id)
911
);
12+
13+
# --- !Downs
14+
DROP TABLE IF EXISTS user_profiles;

frontend/src/App.tsx

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Provider } from 'react-redux';
1+
import { Provider, useSelector } from 'react-redux';
22
import { PersistGate } from 'redux-persist/integration/react';
3-
import { store, persistor } from './store';
3+
import { store, persistor, type RootState } from './store';
44
import LoadingFallback from './components/ui/LoadingFallback';
55
import { AppRouter } from './router/AppRouter';
66
import { ToastContainer } from "react-toastify";
@@ -14,23 +14,32 @@ function App() {
1414
<Provider store={store}>
1515
<QueryClientProvider client={queryClient}>
1616
<PersistGate loading={<LoadingFallback />} persistor={persistor}>
17-
<AppRouter />
18-
<ToastContainer
19-
position="top-right"
20-
autoClose={3000}
21-
hideProgressBar={false}
22-
newestOnTop={false}
23-
closeOnClick
24-
rtl={false}
25-
pauseOnFocusLoss
26-
draggable
27-
pauseOnHover
28-
theme="dark" // can switch to "dark"
29-
/>
17+
<MainApp />
3018
</PersistGate>
3119
</QueryClientProvider>
3220
</Provider>
3321
);
3422
}
3523

24+
function MainApp() {
25+
const theme = useSelector((state: RootState) => state.theme.mode);
26+
27+
return (
28+
<>
29+
<AppRouter />
30+
<ToastContainer
31+
theme={theme}
32+
position="top-right"
33+
autoClose={3000}
34+
hideProgressBar={false}
35+
newestOnTop={false}
36+
closeOnClick
37+
pauseOnFocusLoss
38+
draggable
39+
pauseOnHover
40+
/>
41+
</>
42+
);
43+
}
44+
3645
export default App;

frontend/src/components/auth/LoginForm.tsx

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import React, { useState, useEffect } from 'react';
22
import { useNavigate, useLocation, Link } from 'react-router-dom';
33
import { useAuth } from '@/hooks/useAuth';
4+
import { useDispatch, useSelector } from 'react-redux';
5+
import { userProfileService } from '@/services/userProfileService';
6+
import { setTheme } from '@/store/slices/themeSlice';
47

58
const LoginForm: React.FC = () => {
69
const [email, setEmail] = useState('');
@@ -10,13 +13,31 @@ const LoginForm: React.FC = () => {
1013
useAuth();
1114
const navigate = useNavigate();
1215
const location = useLocation();
16+
const dispatch = useDispatch();
1317

1418
// Redirect if already authenticated
1519
useEffect(() => {
16-
if (isAuthenticated) {
17-
const from = (location.state as any)?.from || '/user/boards';
18-
navigate(from, { replace: true });
19-
}
20+
const fetchUserProfile = async () => {
21+
if (isAuthenticated) {
22+
const from = (location.state as any)?.from || '/user/boards';
23+
try {
24+
const userProfile = await userProfileService.getUserProfile();
25+
dispatch(setTheme(userProfile.themeMode));
26+
} catch (e) {
27+
console.error('Error setting theme on login:', e);
28+
await userProfileService.createUserProfile({
29+
userLanguage: 'en',
30+
themeMode: 'dark',
31+
});
32+
dispatch(setTheme('dark'));
33+
}
34+
finally {
35+
navigate(from, { replace: true });
36+
}
37+
}
38+
};
39+
40+
fetchUserProfile();
2041
}, [isAuthenticated, navigate, location]);
2142

2243
const handleSubmit = async (e: React.FormEvent) => {
@@ -81,7 +102,7 @@ const LoginForm: React.FC = () => {
81102
required
82103
value={email}
83104
onChange={e => setEmail(e.target.value)}
84-
className='w-full px-3 py-3 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white'
105+
className='w-full px-3 py-3 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-black'
85106
placeholder='Enter your email'
86107
/>
87108
</div>
@@ -100,7 +121,7 @@ const LoginForm: React.FC = () => {
100121
required
101122
value={password}
102123
onChange={e => setPassword(e.target.value)}
103-
className='w-full px-3 py-3 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white'
124+
className='w-full px-3 py-3 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-black'
104125
placeholder='Enter your password'
105126
/>
106127
</div>

frontend/src/components/auth/RegisterForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const RegisterForm: React.FC = () => {
6969
</div>
7070

7171
{/* Registration Form */}
72-
<form onSubmit={handleSubmit} className='space-y-4'>
72+
<form onSubmit={handleSubmit} className='space-y-4 text-black'>
7373
{error && (
7474
<div className='bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm'>
7575
{error}

frontend/src/components/board/AddTask.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ const AddTask: React.FC<AddTaskProps> = ({ columnId }) => {
104104
onChange={(e) => setTaskTitle(e.target.value)}
105105
onKeyDown={handleKeyDown}
106106
placeholder="Enter a title or paste a link"
107-
className="w-full p-2 text-sm bg-[#22272B] text-[#B6C2CF] border border-[#394B59] rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
107+
className="w-full p-2 text-sm bg-[var(--background)] text-[var(--foreground)] border border-[#394B59] rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
108108
rows={3}
109109
/>
110110
<div className="flex items-center gap-2">
@@ -126,7 +126,7 @@ const AddTask: React.FC<AddTaskProps> = ({ columnId }) => {
126126
) : (
127127
<button
128128
onClick={handleAddTaskClick}
129-
className="w-full p-2 text-[#B6C2CF] hover:text-[#B6C2CF] hover:bg-[#222f44] rounded-lg transition-colors flex items-center gap-2"
129+
className="w-full p-2 text-[var(--task-title)] hover:text-[var(--task-title)] hover:bg-[var(--task-bg)] rounded-lg transition-colors flex items-center gap-2"
130130
>
131131
<Plus size={16} />
132132
Add a task

frontend/src/components/board/AssignMembers.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ const AssignMembers: React.FC<AssignMembersProps> = (props) => {
9696
return (
9797

9898
<div className="pr-2 pb-4">
99-
<h3 className="text-sm font-medium text-white mb-2">Members</h3>
99+
<h3 className="text-sm font-medium text-[var(--foreground)] mb-2">Members</h3>
100100
<div className="flex items-center gap-1 mb-1">
101101
{
102102
assignedMembers?.map((assignedMember) => (
@@ -130,14 +130,14 @@ const AssignMembers: React.FC<AssignMembersProps> = (props) => {
130130
left: coords.left,
131131
zIndex: 9999,
132132
}}
133-
className="border border-gray-600 bg-[#282e3e] w-64 rounded-lg shadow-2xl p-4 mt-2"
133+
className="border border-gray-600 bg-[var(--background)] w-64 rounded-lg shadow-2xl p-4 mt-2"
134134
>
135135
{/* Header */}
136136
<div className="flex justify-between items-center border-b pb-2">
137-
<h2 className="text-lg text-white font-semibold">Assign Members</h2>
137+
<h2 className="text-lg text-[var(--foreground)] font-semibold">Assign Members</h2>
138138
<button
139139
onClick={() => setIsOpen(false)}
140-
className="text-gray-400 hover:text-white"
140+
className="text-gray-400 hover:text-[var(--foreground)]"
141141
>
142142
143143
</button>
@@ -149,7 +149,7 @@ const AssignMembers: React.FC<AssignMembersProps> = (props) => {
149149
placeholder="Search members..."
150150
value={search}
151151
onChange={(e) => setSearch(e.target.value)}
152-
className="w-full text-gray-400 border rounded px-2 py-1 mt-3 mb-3 bg-gray-700 border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
152+
className="w-full text-gray-400 border rounded px-2 py-1 mt-3 mb-3 bg-[var(--background)] border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
153153
/>
154154

155155
{/* List members */}
@@ -164,9 +164,9 @@ const AssignMembers: React.FC<AssignMembersProps> = (props) => {
164164
<li
165165
key={m.id}
166166
onClick={() => toggleAssign(m)}
167-
className="flex items-center justify-between px-2 py-1 rounded bg-gray-800 cursor-pointer hover:bg-gray-700"
167+
className="flex items-center justify-between px-2 py-1 rounded bg-[var(--background)] cursor-pointer hover:bg-[var(--hover-bg)]"
168168
>
169-
<span className="text-white">{m.name}</span>
169+
<span className="text-[var(--foreground)]">{m.name}</span>
170170
<span className="text-green-400 text-xs"></span>
171171
</li>
172172
))}
@@ -183,9 +183,9 @@ const AssignMembers: React.FC<AssignMembersProps> = (props) => {
183183
<li
184184
key={m.id}
185185
onClick={() => toggleAssign(m)}
186-
className="flex items-center justify-between px-2 py-1 rounded hover:bg-gray-700 cursor-pointer"
186+
className="flex items-center justify-between px-2 py-1 rounded hover:bg-[var(--hover-bg)] cursor-pointer"
187187
>
188-
<span className="text-gray-300">{m.name}</span>
188+
<span className="text-[var(--foreground)]">{m.name}</span>
189189
</li>
190190
))}
191191
</>

frontend/src/components/board/BoardContent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ const WorkspaceBoard = () => {
233233

234234
// ---------- Render ----------
235235
return (
236-
<div className="bg-[#283449] w-full h-full flex flex-col">
236+
<div className="bg-[var(--board-bg)] w-full h-full flex flex-col">
237237
{isLoading ? (
238238
<div className="mt-6">
239239
<LoadingContent />
@@ -269,7 +269,7 @@ const WorkspaceBoard = () => {
269269
{!isBoardClosed && (
270270
<button
271271
onClick={addColumn}
272-
className={`bg-[#ffffff3d] hover:bg-[#ffffff33] rounded-lg p-4 w-80 flex-shrink-0 transition-colors flex items-center justify-center gap-2 text-white ${isBoardClosed ? 'cursor-not-allowed' : ''}`}
272+
className={`bg-[#ffffff3d] hover:bg-[#ffffff33] rounded-lg p-4 w-80 flex-shrink-0 transition-colors flex items-center justify-center gap-2 text-[var(--foreground)] ${isBoardClosed ? 'cursor-not-allowed' : ''}`}
273273
disabled={isBoardClosed}
274274
>
275275
<Plus size={20} />

frontend/src/components/board/BoardNavbar.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ const BoardNavbar: React.FC<BoardNavbarProps> = ({
176176
};
177177

178178
return (
179-
<div className={`${isBoardClosed ? 'pointer-events-none opacity-60' : ''} h-[50px] flex items-center justify-between bg-[#28303E] p-4`}>
180-
<h1 className='text-xl font-bold text-white mb-2'>
179+
<div className={`${isBoardClosed ? 'pointer-events-none opacity-60' : ''} h-[50px] flex items-center justify-between bg-[var(--board-navbar-bg)] p-4`}>
180+
<h1 className='text-xl font-bold text-[var(--foreground)] mb-2'>
181181
{name}
182182
</h1>
183183
{/* Folder Menu Button */}
@@ -209,7 +209,7 @@ const BoardNavbar: React.FC<BoardNavbarProps> = ({
209209

210210
<button
211211
onClick={() => setShowMenu(!showMenu)}
212-
className="flex items-center justify-center w-8 h-8 text-white hover:bg-[#3A4150] rounded transition-colors"
212+
className="flex items-center justify-center w-8 h-8 text-[var(--foreground)] hover:bg-[var(--board-navbar-menu-hover)] rounded transition-colors"
213213
>
214214
<Menu className="w-5 h-5" />
215215
</button>
@@ -226,10 +226,10 @@ const BoardNavbar: React.FC<BoardNavbarProps> = ({
226226
{/* Menu */}
227227
{
228228
!showArchivedItems ?
229-
<div className="absolute right-0 top-10 w-80 bg-[#2c3e50] rounded-lg shadow-xl z-50 border border-gray-600">
229+
<div className="absolute right-0 top-10 w-80 bg-[var(--background)] rounded-lg shadow-xl z-50 border border-gray-600">
230230
{/* Menu Header */}
231231
<div className="flex items-center justify-between p-3 border-b border-gray-600">
232-
<span className="text-white font-medium text-sm">Menu</span>
232+
<span className="text-[var(--menu-text)] font-medium text-sm">Menu</span>
233233
<button
234234
onClick={() => setShowMenu(false)}
235235
className="text-gray-400 hover:text-white transition-colors"
@@ -260,23 +260,23 @@ const BoardNavbar: React.FC<BoardNavbarProps> = ({
260260
{/* Power-Ups Section */}
261261
<button
262262
onClick={handleArchivedItemsClick}
263-
className="w-full flex items-center px-3 py-2 text-sm hover:bg-[#34495e] transition-colors text-left"
263+
className="w-full flex items-center px-3 py-2 text-sm hover:bg-[var(--hover-bg)] transition-colors text-left"
264264
>
265-
<span className="mr-3 text-base text-white"><Folder /></span>
265+
<span className="mr-3 text-base text-[var(--menu-text)]"><Folder /></span>
266266
<div className="flex-1">
267-
<div className='text-gray-300'>
267+
<div className='text-[var(--menu-text)]'>
268268
Archived items
269269
</div>
270270
</div>
271271
</button>
272272

273273
<button
274274
onClick={handleExportBoard}
275-
className="w-full flex items-center px-3 py-2 text-sm hover:bg-[#34495e] transition-colors text-left"
275+
className="w-full flex items-center px-3 py-2 text-sm hover:bg-[var(--hover-bg)] transition-colors text-left"
276276
>
277-
<span className="mr-3 text-base text-white"><Download /></span>
277+
<span className="mr-3 text-base text-[var(--menu-text)]"><Download /></span>
278278
<div className="flex-1">
279-
<div className='text-gray-300'>
279+
<div className='text-[var(--menu-text)]'>
280280
Export as JSON
281281
</div>
282282
</div>
@@ -303,9 +303,9 @@ const BoardNavbar: React.FC<BoardNavbarProps> = ({
303303
<div className="relative">
304304
<button
305305
onClick={() => setShowCloseConfirm(true)}
306-
className="w-full flex items-center px-3 py-2 text-sm hover:bg-[#34495e] transition-colors text-left"
306+
className="w-full flex items-center px-3 py-2 text-sm hover:bg-[var(--hover-bg)] transition-colors text-left"
307307
>
308-
<span className="mr-3 text-base text-white"><X /></span>
308+
<span className="mr-3 text-base text-[var(--menu-text)]"><X /></span>
309309
<div className="flex-1">
310310
<div className="text-red-400">
311311
Close board

0 commit comments

Comments
 (0)