Skip to content

Commit

Permalink
Merge pull request #9 from tomgasper/project-task-ui
Browse files Browse the repository at this point in the history
Project task UI and some other fixes
  • Loading branch information
tomgasper authored Sep 14, 2024
2 parents f9e9226 + 3ffddbb commit 14c1f54
Show file tree
Hide file tree
Showing 93 changed files with 2,614 additions and 405 deletions.
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

0 comments on commit 14c1f54

Please sign in to comment.