Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project task UI and some other fixes #9

Merged
merged 12 commits into from
Sep 14, 2024
Merged
5 changes: 5 additions & 0 deletions Requests/User/GetUser.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@host=https://localhost:5234
@userId=e0d91303-b5c9-4530-9914-d27c7a054415

GET {{host}}/api/users/{{userId}}
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlMGQ5MTMwMy1iNWM5LTQ1MzAtOTkxNC1kMjdjN2EwNTQ0MTUiLCJnaXZlbl9uYW1lIjoiSmFjdcWbIiwiZmFtaWx5X25hbWUiOiJCb3NhayIsImp0aSI6IjhjZDRjOWQxLThlNjUtNDNlMy05NTIxLWVkNGI2N2UzYjYxYyIsImV4cCI6MTcyNjEzNjA0MSwiaXNzIjoiUGxhbkl0IiwiYXVkIjoiUGxhbkl0In0.XyhpGaYaQzxXS2i7O70KxHdJLKjnH0bDhZk3q-bRB24
23 changes: 17 additions & 6 deletions clients/plan-it-web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import './App.css';
import { MainWindow } from './components/MainWindow/MainWindow';
import { useGetUserWorkspacesQuery } from './services/planit-api';

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { useAppDispatch} from './hooks/reduxHooks';
import { useEffect } from 'react';
import { BrowserRouter, Route, Routes, useNavigate } from 'react-router-dom';
import { useAppDispatch, useAppSelector} from './hooks/reduxHooks';
import { useEffect, useState } from 'react';
import { setWorkspaces } from './redux/workspacesSlice';
import { WorkspaceSettings } from './components/WorkspaceSettings/WorkspaceSettings';
import { Login } from './components/Login/Login';
Expand All @@ -18,20 +18,30 @@ import { Register } from './components/Register/Register';
import { ProfilePage } from './components/Profile/ProfilePage';
import { ProtectedRoute } from './router/ProtectedRoute';
import { Flex, Loader } from '@mantine/core';
import { UserFromJwt } from './types/User';

export default function App() {
const dispatch = useAppDispatch();
const isAuthenticated = useJwtAuth();
const [isAuthChecking, setIsAuthChecking] = useState(true);
const { isAuthenticated } = useJwtAuth();
const user : UserFromJwt | null = useAppSelector(state => state.auth.user);
const { data, isLoading, error } = useGetUserWorkspacesQuery(user?.id ?? '', { skip: user == null});

const { data, isLoading, error } = useGetUserWorkspacesQuery('e0d91303-b5c9-4530-9914-d27c7a054415');
console.log(isAuthenticated);

useEffect(() => {
if (data) {
dispatch(setWorkspaces(data));
}
},[data, dispatch]);

if (isLoading)
useEffect(() => {
if (isAuthenticated !== null) {
setIsAuthChecking(false);
}
}, [isAuthenticated]);

if (isAuthChecking || isLoading)
{
return <Flex style={{width: "100%", height: "100vh"}}justify='center' align='center'><Loader /></Flex>
}
Expand All @@ -43,6 +53,7 @@ export default function App() {
<BrowserRouter>
{isAuthenticated && <Navbar />}
<Routes>
<Route path="/" element={<ProtectedRoute><MainWindow /></ProtectedRoute>} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/profile" element={<ProtectedRoute><ProfilePage /></ProtectedRoute>} />
Expand Down
6 changes: 3 additions & 3 deletions clients/plan-it-web/src/components/Common/ExtendedModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ export function ExtendedModal({
<>
<Modal.Root onMouseDown={e => e.stopPropagation() } opened={opened} onClose={onClose} closeOnClickOutside={true}>
<Modal.Overlay />
<Modal.Content>
<Modal.Header>
<Modal.Content p={0} m={0}>
<Modal.Header >
<Title order={5}>{title}</Title>
<Modal.CloseButton />
</Modal.Header>
<Modal.Body>{children}</Modal.Body>
<Modal.Body >{children}</Modal.Body>
</Modal.Content>
</Modal.Root>
</>
Expand Down
10 changes: 8 additions & 2 deletions clients/plan-it-web/src/components/Login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,20 @@ import { notifications } from '@mantine/notifications';
e.preventDefault();
try {
const userData: AuthResponse = await login({ email, password }).unwrap();
dispatch(setCredentials({user: userData.user, token: userData.token}));
dispatch(setCredentials({user:
{
id: userData.id,
firstName: userData.firstName,
lastName: userData.lastName,
avatarUrl: userData.avatarUrl
}, token: userData.token}));

notifications.show({
title: 'Login successful',
message: 'You have been successfully logged in',
color: 'green'
});
navigate('/workspace');
navigate('/');
} catch (error) {
const err = error as { data?: { title?: string; errors?: Record<string, string[]> } };

Expand Down
21 changes: 15 additions & 6 deletions clients/plan-it-web/src/components/MainWindow/MainWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { Flex, Group, Title } from "@mantine/core";
import { Flex, Group, Loader, Title } from "@mantine/core";
import { MultipleSortableProjects } from '../SortableItems/MultipleSortableProjects';
import classes from './MainWindow.module.css';
import { useCreateProjectMutation, useGetProjectsForWorkspaceQuery, useGetWorkspaceQuery } from "../../services/planit-api";
import { useParams } from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";
import { useEffect } from "react";
import { WorkspaceMenu } from "./WorkspaceMenu";
import { Project } from "../../types/Project";
import { useAppSelector } from "../../hooks/reduxHooks";

export function MainWindow() {
const navigate = useNavigate();
const { workspaces }= useAppSelector(state => state.workspaces);
const { workspaceId } = useParams<{ workspaceId: string }>();

const { data: workspace, error: workspaceFetchError, isLoading: isLoadingWorkspace } = useGetWorkspaceQuery(workspaceId ?? "");
const { data: workspace, error: workspaceFetchError, isLoading: isLoadingWorkspace, refetch : refetchWorkspace } = useGetWorkspaceQuery(workspaceId ?? "");
const { data : projects, error: workspaceProjectsFetchError, isLoading, refetch } = useGetProjectsForWorkspaceQuery(workspaceId ?? "", {
skip: !workspaceId
});
const [ createProject ] = useCreateProjectMutation();

console.log(projects);

useEffect(() => {
if (workspaceId) {
refetchWorkspace().catch(console.error);
refetch().catch(console.error);
}
}, [workspaceId, refetch]);

if (!workspaceId && workspaces && workspaces.length > 0) {
navigate(`/workspaces/${workspaces[0].id}`);
}
}, [workspace, navigate, workspaces, workspaceId, refetch, refetchWorkspace]);

useEffect(() => {
console.log('Refetched projects:', projects);
Expand Down Expand Up @@ -58,7 +67,7 @@ export function MainWindow() {
<>
<Group>
<Group gap={15}>
<Title>{workspace!.name}</Title>
<Title>{workspace!.name} </Title>
<WorkspaceMenu />
</Group>

Expand Down
8 changes: 4 additions & 4 deletions clients/plan-it-web/src/components/Navbar/Navbar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,27 +67,27 @@
pointer-events: none;
}

.collections {
.workspaces {
padding-left: calc(var(--mantine-spacing-md) - rem(6px));
padding-right: calc(var(--mantine-spacing-md) - rem(6px));
padding-bottom: var(--mantine-spacing-md);
}

.collectionsHeader {
.workspacesHeader {
padding-left: calc(var(--mantine-spacing-md) + rem(2px));
padding-right: var(--mantine-spacing-md);
margin-bottom: rem(5px);
}

.collectionLink {
.workspaceLink {
display: block;
padding: rem(8px) var(--mantine-spacing-xs);
text-decoration: none;
border-radius: var(--mantine-radius-sm);
font-size: var(--mantine-font-size-xs);
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
line-height: 1;
font-weight: 500;
font-weight: 700;

&:hover {
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
Expand Down
22 changes: 14 additions & 8 deletions clients/plan-it-web/src/components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,17 @@ import {
Tooltip,
Loader,
} from '@mantine/core';
import { IconBulb, IconUser, IconCheckbox, IconSearch, IconPlus} from '@tabler/icons-react';
import { IconBulb, IconUser, IconCheckbox, IconSearch, IconPlus, IconClipboard, IconLogout} from '@tabler/icons-react';
import { UserButton } from '../UserButton/UserButton';
import classes from './Navbar.module.css';
import { NavLink, useNavigate } from 'react-router-dom';
import { useCreateWorkspaceMutation } from '../../services/planit-api';
import { useAppDispatch, useAppSelector } from '../../hooks/reduxHooks';
import { addWorkspace } from '../../redux/workspacesSlice';
import { logOut } from '../../redux/authSlice';



const links: { icon: any; label: string; notifications?: number }[] = [
{ icon: IconBulb, label: 'Activity', notifications: 3 },
{ icon: IconCheckbox, label: 'Tasks', notifications: 4 },
{ icon: IconUser, label: 'Contacts' },
];



Expand All @@ -51,8 +48,14 @@ import { addWorkspace } from '../../redux/workspacesSlice';
navigate('/profile');
}

const links: { icon: any; label: string; notifications?: number, onClick?: () => void }[] = [
{ icon: IconBulb, label: 'Activity', notifications: 3 },
{ icon: IconCheckbox, label: 'Tasks', notifications: 4 },
{ icon: IconLogout, label: 'Log out', onClick: () => dispatch(logOut()) },
];

const mainLinks = links.map((link) => (
<UnstyledButton key={link.label} className={classes.mainLink}>
<UnstyledButton key={link.label} className={classes.mainLink} onClick={link.onClick}>
<div className={classes.mainLinkInner}>
<link.icon size={20} className={classes.mainLinkIcon} stroke={1.5} />
<span>{link.label}</span>
Expand All @@ -74,7 +77,10 @@ import { addWorkspace } from '../../redux/workspacesSlice';
`${classes.workspaceLink} ${isActive ? classes.workspaceLinkActive : ''}`
}
>
<span>{workspace.name}</span>
<Group>
<IconClipboard size={15} /> <span>{workspace.name}</span>
</Group>

</NavLink>
))
: null;
Expand Down
17 changes: 17 additions & 0 deletions clients/plan-it-web/src/components/Navbar/NavbarMainLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import classes from './Navbar.module.css';

export function NavbarMainLink({icon, label, notifications}: {icon: JSX, label: string, notifications?: number}) {
return (
<div className={classes.mainLink}>
<div className={classes.mainLinkInner}>
<icon size={20} className={classes.mainLinkIcon} stroke={1.5} />
<span>{link.label}</span>
</div>
{link.notifications && (
<Badge size="sm" variant="filled" className={classes.mainLinkBadge}>
{link.notifications}
</Badge>
)}
</div>
);
}
37 changes: 22 additions & 15 deletions clients/plan-it-web/src/components/Profile/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ import { useAppSelector } from '../../hooks/reduxHooks';
interface ProfileFormValues {
firstName: string;
lastName: string;
password: string;
confirmPassword: string;
oldPassword: string;
newPassword: string;
}

export function ProfilePage() {
const authState = useAppSelector(state => state.auth);
const userFromToken = useAppSelector(state => state.auth.user);
const { data: currentUser } = useGetUserQuery(userFromToken?.id ?? "");
console.log(authState);
const { data: currentUser, refetch: refetchUser } = useGetUserQuery(userFromToken?.id ?? "");
const [updateUser, { isLoading: isUpdating }] = useUpdateUserMutation();
const [uploadAvatar, { isLoading: isUploading }] = useUploadAvatarMutation();
const [avatarFile, setAvatarFile] = useState<File | null>(null);
Expand All @@ -36,19 +38,21 @@ export function ProfilePage() {
initialValues: {
firstName: currentUser?.firstName ?? '',
lastName: currentUser?.lastName ?? '',
password: '',
confirmPassword: '',
oldPassword: '',
newPassword: '',
},
validate: {
firstName: (value : string) => (value.length < 2 ? 'First name must have at least 2 characters' : null),
lastName: (value: string) => (value.length < 2 ? 'Last name must have at least 2 characters' : null),
password: (value : string) => (
oldPassword: (value : string) => (
value.length > 0 && value.length < 8
? 'Password must be at least 8 characters long'
: null
),
confirmPassword: (value: string, values: ProfileFormValues) =>
value !== values.password ? 'Passwords do not match' : null,
newPassword: (value: string) =>
value.length > 0 && value.length < 8
? 'Password must be at least 8 characters long'
: null
},
});

Expand All @@ -59,7 +63,7 @@ export function ProfilePage() {
lastName: currentUser.lastName || '',
});
}
}, [currentUser, form]);
}, [currentUser]);

const handleSubmit = async (values: ProfileFormValues) => {
if (!currentUser) {
Expand All @@ -76,7 +80,8 @@ export function ProfilePage() {
userId: currentUser.id,
firstName: values.firstName,
lastName: values.lastName,
...(values.password ? { password: values.password } : {}),
...(values.oldPassword ? { oldPassword: values.oldPassword } : {}),
...(values.newPassword ? { newPassword: values.newPassword } : {}),
}).unwrap();

if (avatarFile) {
Expand All @@ -90,6 +95,8 @@ export function ProfilePage() {
message: 'Profile updated successfully',
color: 'green',
});

await refetchUser();
} catch {
showNotification({
title: 'Error',
Expand Down Expand Up @@ -133,15 +140,15 @@ export function ProfilePage() {
mb="md"
/>
<PasswordInput
label="New Password"
placeholder="Enter new password"
{...form.getInputProps('password')}
label="Old Password"
placeholder="Enter old password"
{...form.getInputProps('oldPassword')}
mb="md"
/>
<PasswordInput
label="Confirm New Password"
label="New Password"
placeholder="Confirm new password"
{...form.getInputProps('confirmPassword')}
{...form.getInputProps('newPassword')}
mb="xl"
/>
<Group justify="right">
Expand Down
Loading
Loading