Skip to content

Commit 6e76d20

Browse files
committed
remove ChakraUI dependency; refactor components and pages to use pure html tags; add tailwindcss;
1 parent 377b767 commit 6e76d20

23 files changed

+3185
-2086
lines changed

client/package-lock.json

Lines changed: 2977 additions & 1805 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,19 @@
1616
"prepare": "cd .. && husky client/.husky"
1717
},
1818
"dependencies": {
19-
"@chakra-ui/react": "^2.8.2",
20-
"@emotion/react": "^11.13.3",
21-
"@emotion/styled": "^11.13.0",
2219
"@fontsource/roboto-condensed": "^5.1.0",
23-
"@remix-run/router": "^1.20.0",
2420
"base-64": "^1.0.0",
25-
"framer-motion": "^11.11.17",
2621
"i18n": "^0.15.1",
2722
"i18next": "^24.1.2",
2823
"i18next-browser-languagedetector": "^8.0.0",
2924
"i18next-http-backend": "^2.6.2",
3025
"jwt-decode": "^4.0.0",
31-
"moment": "^2.30.1",
3226
"react": "^19.0.0",
3327
"react-dom": "^19.0.0",
3428
"react-error-boundary": "^5.0.0",
3529
"react-i18next": "^15.1.2",
3630
"react-icons": "^5.3.0",
37-
"react-router-dom": "^7.1.1",
31+
"react-router": "^7.1.1",
3832
"zustand": "^5.0.1"
3933
},
4034
"devDependencies": {
@@ -46,6 +40,7 @@
4640
"@types/react": "^19.0.7",
4741
"@types/react-dom": "^19.0.3",
4842
"@vitejs/plugin-react-swc": "^3.5.0",
43+
"autoprefixer": "^10.4.20",
4944
"eslint": "^9.14.0",
5045
"eslint-config-prettier": "^9.1.0",
5146
"eslint-plugin-prettier": "^5.2.1",
@@ -56,8 +51,10 @@
5651
"husky": "^9.1.7",
5752
"jsdom": "^25.0.1",
5853
"msw": "^2.3.1",
54+
"postcss": "^8.5.1",
5955
"prettier": "^3.3.3",
6056
"prettier-plugin-organize-imports": "^4.1.0",
57+
"tailwindcss": "^3.4.17",
6158
"typescript": "~5.6.2",
6259
"typescript-eslint": "^8.19.0",
6360
"vite": "^5.4.10",

client/postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
};
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { Text } from '@chakra-ui/react';
2-
import { FunctionComponent, ReactElement } from 'react';
1+
import { ReactElement } from 'react';
32

4-
export const NotFound: FunctionComponent = (): ReactElement => {
3+
export const NotFound = (): ReactElement => {
54
return (
65
<>
7-
<Text fontSize={'2xl'}>404</Text>
8-
<Text>Page not found.</Text>
6+
<p className="text-2xl">404</p>
7+
<p>Page not found.</p>
98
</>
109
);
1110
};
Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
11
import Login from '@/app/pages/login';
22
import { useAuthStore } from '@/features/auth/stores/auth-store';
3-
import {
4-
AlertDialog,
5-
AlertDialogBody,
6-
AlertDialogCloseButton,
7-
AlertDialogContent,
8-
AlertDialogFooter,
9-
AlertDialogHeader,
10-
Button,
11-
} from '@chakra-ui/react';
12-
import { useRef, useState } from 'react';
3+
4+
import { useState } from 'react';
135
import { useTranslation } from 'react-i18next';
14-
import {
15-
isRouteErrorResponse,
16-
useNavigate,
17-
useRouteError,
18-
} from 'react-router-dom';
6+
import { isRouteErrorResponse, useNavigate, useRouteError } from 'react-router';
197

208
interface IError {
219
error?: Error;
@@ -30,7 +18,6 @@ interface RouterErrorProps {
3018
export const RouterError = ({ error }: RouterErrorProps) => {
3119
const { t } = useTranslation();
3220
const nav = useNavigate();
33-
const cancelRef = useRef(null);
3421
const authStore = useAuthStore();
3522
const displayedError: unknown = useRouteError() ?? error;
3623
const [isOpened, setIsOpened] = useState(true);
@@ -65,25 +52,33 @@ export const RouterError = ({ error }: RouterErrorProps) => {
6552
)
6653
errorMessage = 'ForbidError';
6754
return (
68-
<AlertDialog
69-
isOpen={isOpened}
70-
onClose={onClose}
71-
leastDestructiveRef={cancelRef}
55+
<div
56+
className={`fixed inset-0 flex items-center justify-center ${isOpened ? 'block' : 'hidden'}`}
7257
>
73-
<AlertDialogContent>
74-
<AlertDialogHeader>
75-
{t('Message.RouterError')}
76-
</AlertDialogHeader>
77-
<AlertDialogCloseButton />
78-
<AlertDialogBody>
79-
{t('Message.' + errorMessage)}
80-
</AlertDialogBody>
81-
<AlertDialogFooter>
82-
<Button colorScheme="green" ml={3} onClick={() => nav('/')}>
58+
<div className="bg-white rounded-lg shadow-lg p-6 w-full max-w-md">
59+
<div className="flex justify-between items-center border-b pb-3">
60+
<h2 className="text-lg font-semibold">
61+
{t('Message.RouterError')}
62+
</h2>
63+
<button
64+
className="text-gray-500 hover:text-gray-700"
65+
onClick={onClose}
66+
>
67+
&times;
68+
</button>
69+
</div>
70+
<div className="py-4">
71+
<p>{t('Message.' + errorMessage)}</p>
72+
</div>
73+
<div className="flex justify-end pt-3 border-t">
74+
<button
75+
className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600"
76+
onClick={() => nav('/')}
77+
>
8378
{t('OK')}
84-
</Button>
85-
</AlertDialogFooter>
86-
</AlertDialogContent>
87-
</AlertDialog>
79+
</button>
80+
</div>
81+
</div>
82+
</div>
8883
);
8984
};

client/src/app/pages/login.test.tsx

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
import Login from '@/app/pages/login';
22
import { useAuthStore } from '@/features/auth/stores/auth-store';
33
import { mockedUseNavigate } from '@/tests/setup';
4-
import { ChakraProvider } from '@chakra-ui/react';
54
import {
65
act,
76
fireEvent,
87
render,
98
screen,
109
waitFor,
1110
} from '@testing-library/react';
12-
import { MemoryRouter } from 'react-router-dom';
11+
import { MemoryRouter } from 'react-router';
1312
import { describe, it } from 'vitest';
1413

1514
describe('<Login />', () => {
1615
it('renders Login page', async () => {
1716
const { container } = render(
18-
<ChakraProvider>
19-
<MemoryRouter>
20-
<Login />
21-
</MemoryRouter>
22-
</ChakraProvider>
17+
<MemoryRouter>
18+
<Login />
19+
</MemoryRouter>
2320
);
2421
const loginInput = container.querySelector('#login');
2522
const passwordInput = container.querySelector('#password');
@@ -31,11 +28,9 @@ describe('<Login />', () => {
3128

3229
it('shows a login error on other than 401 error', async () => {
3330
render(
34-
<ChakraProvider>
35-
<MemoryRouter>
36-
<Login />
37-
</MemoryRouter>
38-
</ChakraProvider>
31+
<MemoryRouter>
32+
<Login />
33+
</MemoryRouter>
3934
);
4035
const button = await screen.getByRole('button');
4136

@@ -48,11 +43,9 @@ describe('<Login />', () => {
4843

4944
it('shows a credentials error on wrong credentials', async () => {
5045
const { container } = render(
51-
<ChakraProvider>
52-
<MemoryRouter>
53-
<Login />
54-
</MemoryRouter>
55-
</ChakraProvider>
46+
<MemoryRouter>
47+
<Login />
48+
</MemoryRouter>
5649
);
5750
const loginInput = container.querySelector('#login');
5851
const passwordInput = container.querySelector('#password');
@@ -73,11 +66,9 @@ describe('<Login />', () => {
7366

7467
it('logins user on correct credentials', async () => {
7568
const { container } = render(
76-
<ChakraProvider>
77-
<MemoryRouter>
78-
<Login />
79-
</MemoryRouter>
80-
</ChakraProvider>
69+
<MemoryRouter>
70+
<Login />
71+
</MemoryRouter>
8172
);
8273
const loginInput = container.querySelector('#login');
8374
const passwordInput = container.querySelector('#password');

client/src/app/pages/login.tsx

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,10 @@ import { FunctionComponent, KeyboardEvent, useState } from 'react';
22

33
import { login as apiLogin } from '@/features/auth/api/login';
44
import { useAuthStore } from '@/features/auth/stores/auth-store';
5-
import {
6-
Button,
7-
Center,
8-
FormControl,
9-
FormLabel,
10-
Input,
11-
InputGroup,
12-
InputLeftElement,
13-
Stack,
14-
Text,
15-
} from '@chakra-ui/react';
5+
166
import { useTranslation } from 'react-i18next';
177
import { MdEmail, MdPassword } from 'react-icons/md';
18-
import { useNavigate } from 'react-router-dom';
8+
import { useNavigate } from 'react-router';
199

2010
const Login: FunctionComponent = () => {
2111
const [username, setUsername] = useState('');
@@ -60,58 +50,54 @@ const Login: FunctionComponent = () => {
6050
};
6151

6252
return (
63-
<Center mt={10}>
64-
<FormControl
65-
as="fieldset"
66-
maxWidth="400px"
67-
padding={4}
68-
border="1px solid #ccc"
69-
borderRadius={4}
70-
>
71-
<Stack spacing={4}>
72-
<FormLabel>{t('Login.Title')}</FormLabel>
73-
<InputGroup>
74-
<InputLeftElement pointerEvents="none">
53+
<div className="flex justify-center mt-10">
54+
<fieldset className="max-w-md p-4 border border-gray-300 rounded">
55+
<div className="space-y-4">
56+
<label className="block text-lg font-medium">
57+
{t('Login.Title')}
58+
</label>
59+
<div className="relative">
60+
<span className="absolute inset-y-0 left-0 flex items-center pl-3">
7561
<MdEmail />
76-
</InputLeftElement>
77-
<Input
62+
</span>
63+
<input
64+
className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded"
7865
placeholder={t('Login.Username')}
7966
id="login"
8067
value={username}
8168
onChange={(e) => setUsername(e.target.value)}
8269
onKeyDown={keyDownHandler}
8370
/>
84-
</InputGroup>
85-
<InputGroup>
86-
<InputLeftElement pointerEvents="none">
71+
</div>
72+
<div className="relative">
73+
<span className="absolute inset-y-0 left-0 flex items-center pl-3">
8774
<MdPassword />
88-
</InputLeftElement>
89-
<Input
75+
</span>
76+
<input
9077
type="password"
78+
className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded"
9179
placeholder={t('Login.Password')}
9280
id="password"
9381
value={password}
9482
onChange={(e) => setPassword(e.target.value)}
9583
onKeyDown={keyDownHandler}
9684
/>
97-
</InputGroup>
98-
<Text
99-
hidden={errorMessage == null || errorMessage === ''}
100-
color="red"
85+
</div>
86+
<p
87+
className={`text-red-500 ${errorMessage ? 'block' : 'hidden'}`}
10188
>
10289
{errorMessage}
103-
</Text>
104-
<Button
90+
</p>
91+
<button
10592
onClick={handleLogin}
106-
background="green"
107-
color="white"
93+
className="w-full py-2 bg-green-500 text-white rounded disabled:opacity-50"
10894
disabled={isLoading}
10995
>
11096
{t('Login.Button')}
111-
</Button>
112-
</Stack>
113-
</FormControl>
114-
</Center>
97+
</button>
98+
</div>
99+
</fieldset>
100+
</div>
115101
);
116102
};
117103

client/src/app/pages/logout.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useAuthStore } from '@/features/auth/stores/auth-store';
2-
import { Text } from '@chakra-ui/react';
32
import { FunctionComponent, useEffect } from 'react';
43
import { useTranslation } from 'react-i18next';
54

@@ -11,5 +10,5 @@ export const Logout: FunctionComponent = () => {
1110
logout();
1211
});
1312

14-
return <Text>{t('Chao!')}</Text>;
13+
return <p>{t('Chao!')}</p>;
1514
};

client/src/app/pages/users.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { User } from '@/features/users/types/user';
2-
import { Heading, Text, VStack } from '@chakra-ui/react';
32
import { ReactElement, useEffect, useState } from 'react';
43
import { useTranslation } from 'react-i18next';
5-
import { useLoaderData } from 'react-router-dom';
4+
import { useLoaderData } from 'react-router';
65

76
export const UsersPage = (): ReactElement => {
87
const data = useLoaderData() as User[];
@@ -14,15 +13,15 @@ export const UsersPage = (): ReactElement => {
1413
}, [data]);
1514

1615
return (
17-
<VStack align={'left'} spacing={5}>
18-
<Heading as="h2" size="md">
19-
{t('Users.Title')}
20-
</Heading>
21-
{users?.map((user) => (
22-
<Text key={user.Username}>
23-
{user.Username} ({user.Status})
24-
</Text>
25-
))}
26-
</VStack>
16+
<div className="text-left space-y-5">
17+
<h2>{t('Users.Title')}</h2>
18+
<ul>
19+
{users?.map((user) => (
20+
<li key={user.Username}>
21+
{user.Username} ({user.Status})
22+
</li>
23+
))}
24+
</ul>
25+
</div>
2726
);
2827
};

0 commit comments

Comments
 (0)