Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@types/node": "^14.14.35",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.2",
"@types/reactour": "^1.18.1",
"body-parser": "^1.19.0",
"cloudinary": "^1.25.1",
"clsx": "^1.1.1",
Expand All @@ -27,8 +28,10 @@
"react-dropzone": "^11.3.2",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.0",
"reactour": "^1.18.4",
"socket.io-client": "^4.0.1",
"streamifier": "^0.1.1",
"styled-components": "^4.0.0",
"typescript": "^4.2.3",
"use-debounce": "^6.0.0",
"uuid": "^8.3.2",
Expand Down
38 changes: 21 additions & 17 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { theme } from './themes/theme';
import Login from './pages/Login/Login';
import Signup from './pages/SignUp/SignUp';
import Dashboard from './pages/Dashboard/Dashboard';
import ProtectedRoute from '../src/components/ProtectedRoute';
import ProtectedRoute from './components/ProtectedRoute';
import { steps } from './components/TourGuide/steps';
import {
AuthProvider,
SocketProvider,
SnackBarProvider,
KanbanProvider,
DialogProvider,
TourProvider,
UserProvider,
} from './context/';

Expand All @@ -19,22 +21,24 @@ const App = (): JSX.Element => (
<BrowserRouter>
<SnackBarProvider>
<AuthProvider>
<UserProvider>
<SocketProvider>
<KanbanProvider>
<DialogProvider>
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<ProtectedRoute exact path="/dashboard" component={Dashboard} />
<Route path="*">
<Redirect to="/login" />
</Route>
</Switch>
</DialogProvider>
</KanbanProvider>
</SocketProvider>
</UserProvider>
<SocketProvider>
<UserProvider>
<TourProvider steps={steps}>
<KanbanProvider>
<DialogProvider>
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<ProtectedRoute exact path="/dashboard" component={Dashboard} />
<Route path="*">
<Redirect to="/login" />
</Route>
</Switch>
</DialogProvider>
</KanbanProvider>
</TourProvider>
</UserProvider>
</SocketProvider>
</AuthProvider>
</SnackBarProvider>
</BrowserRouter>
Expand Down
1 change: 1 addition & 0 deletions client/src/components/AddBoardDialog/AddBoardDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const AddBoardDialog = (): JSX.Element => {
startIcon={<AddOutlinedIcon />}
onClick={handleClickOpen}
disableElevation
data-tour="add-board-step"
>
Create Board
</Button>
Expand Down
23 changes: 12 additions & 11 deletions client/src/components/Kanban/Board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { DragDropContext, Droppable, DroppableProvided } from 'react-beautiful-d
import { FocusCard } from './FocusCard/FocusCard';
import Column from './Column/Column';
import { IBoard } from '../../interface/';
import { useKanban } from '../../context/';
import { useKanban, useTour } from '../../context/';
import { TourGuide } from '../TourGuide/TourGuide';

const Board = ({ activeBoard }: { activeBoard: IBoard }): JSX.Element => {
const { focusedCard, handleDragEnd } = useKanban();
const { isTourMode } = useTour();

return (
<>
Expand All @@ -16,22 +18,21 @@ const Board = ({ activeBoard }: { activeBoard: IBoard }): JSX.Element => {
{(provided: DroppableProvided) => (
<Grid ref={provided.innerRef} container spacing={2} {...provided.droppableProps}>
{activeBoard.columns.map((column, index) => (
<>
<Column
key={column._id}
index={index}
_id={column._id}
name={column.name}
cards={column.cards}
createdAt={column.createdAt}
/>
</>
<Column
key={column._id}
index={index}
_id={column._id}
name={column.name}
cards={column.cards}
createdAt={column.createdAt}
/>
))}
{provided.placeholder}
</Grid>
)}
</Droppable>
</DragDropContext>
{isTourMode && <TourGuide />}
</>
);
};
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Kanban/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const Card = ({ id, name, tag = 'white', columnId, index }: CardProps): JSX.Elem
<Draggable draggableId={id} index={index}>
{(provided, snapshot) => {
return (
<div ref={provided.innerRef} {...provided.dragHandleProps} {...provided.draggableProps}>
<div ref={provided.innerRef} {...provided.dragHandleProps} {...provided.draggableProps} data-tour="card-step">
<Box
onClick={() => {
setOpenCard({
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Kanban/CardForm/CardForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const CardForm = ({ columnId }: CardFormProps): JSX.Element => {
color="secondary"
disableElevation
className={`${!formIsOpen ? classes.button : ''}`}
data-tour="card-form-step"
>
Add a card...
</Button>
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Kanban/Column/Column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const Column = ({ _id, name, cards, index }: ColumnProps): JSX.Element => {
className={classes.columnGridItem}
ref={provided.innerRef}
{...provided.draggableProps}
data-tour="column-step"
>
<Box className={classes.columnWrapper} {...provided.dragHandleProps}>
<Box className={classes.typographyWrapper}>
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/NavBar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import AddBoardDialog from '../AddBoardDialog/AddBoardDialog';
import NotificationCenter from './Notifications/NotificationCenter/NotificationCenter';
import { testNotifications } from './Notifications/sampleNotificationData';
import logo from '../../Images/logo.png';
import { IUser, IBoard } from '../../interface/';
import { IUser } from '../../interface/';
import { useKanban } from '../../context';

interface Props {
Expand Down Expand Up @@ -68,7 +68,7 @@ const NavBar = ({ loggedInUser, handleDrawerToggle }: Props): JSX.Element => {
color="inherit"
aria-label="menu"
>
<MenuIcon />
<MenuIcon data-tour="show-boards-step" />
</IconButton>
</Toolbar>
</AppBar>
Expand Down
26 changes: 26 additions & 0 deletions client/src/components/TourGuide/TourGuide.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Button, useTheme } from '@material-ui/core';
import Tour from 'reactour';
import { useTour } from '../../context';

export const TourGuide = (): JSX.Element => {
const theme = useTheme();
const { isTourMode, tourSteps, tourModeEndHandler } = useTour();
return (
<Tour
accentColor={theme.palette.primary.main}
isOpen={isTourMode}
steps={tourSteps}
onRequestClose={tourModeEndHandler}
onAfterOpen={() => (document.body.style.overflowY = 'hidden')}
rounded={5}
maskSpace={12}
disableInteraction
closeWithMask={false}
lastStepNextButton={
<Button variant="contained" color="primary">
Get Started
</Button>
}
/>
);
};
7 changes: 7 additions & 0 deletions client/src/components/TourGuide/stepStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { makeStyles } from '@material-ui/core/styles';

export const stepStyles = makeStyles((theme) => ({
divider: {
marginBottom: theme.spacing(2),
},
}));
54 changes: 54 additions & 0 deletions client/src/components/TourGuide/steps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Box, Divider, Typography } from '@material-ui/core';
import { ReactourStep } from 'reactour';
import { stepStyles } from './stepStyles';

interface CardStepProps {
heading: string;
description: string;
}

const CardStep = ({ heading, description }: CardStepProps): JSX.Element => {
const classes = stepStyles();

return (
<Box>
<Typography variant="h4" color="primary" gutterBottom>
{heading}
</Typography>
<Divider light className={classes.divider} />
<Typography variant="body1">{description}</Typography>
</Box>
);
};

export const steps: ReactourStep[] = [

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no step for adding a column, I will add that in as it's a pretty hidden feature and probably best to highlight it.

{
selector: '[data-tour="column-step"]',
content: (
<CardStep
heading="Column"
description="Columns help organize your cards to keep track of what needs to be done, what's happening and what's finished."
/>
),
},
{
selector: '[data-tour="card-step"]',
content: <CardStep heading="Card" description="These are tasks that you can assign yourself to do." />,
},
{
selector: '[data-tour="card-form-step"]',
content: <CardStep heading="Adding a card" description="Click here to add a new card to the column." />,
},
{
selector: '[data-tour="add-board-step"]',
content: (
<CardStep heading="Creating a board" description="Creating a new board allows for further organization." />
),
},
{
selector: '[data-tour="show-boards-step"]',
content: (
<CardStep heading="View all boards" description="View all your created boards and navigate through them here" />
),
},
];
1 change: 1 addition & 0 deletions client/src/context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export { useKanban, KanbanContext, KanbanProvider } from './useKanbanContext';
export { useSocket, SocketContext, SocketProvider } from './useSocketContext';
export { useSnackBar, SnackBarContext, SnackBarProvider } from './useSnackbarContext';
export { useDialog, DialogContext, DialogProvider } from './useDialogContext';
export { useTour, TourContext, TourProvider } from './useTour';
export { useUser, UserContext, UserProvider } from './useUserContext';
2 changes: 1 addition & 1 deletion client/src/context/useKanbanContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export const KanbanProvider: FunctionComponent = ({ children }): JSX.Element =>

const addCard = (card: ICard): boolean => {
if (card.name === '') {
updateSnackBarMessage('Please enter a card name');
updateSnackBarMessage('Please enter a card name', 'error');

return false;
}
Expand Down
47 changes: 47 additions & 0 deletions client/src/context/useTour.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useContext, useState, createContext, PropsWithChildren, useEffect } from 'react';
import { ReactourStep } from 'reactour';
import ITourContext from '../interface/TourContext';

export const TourContext = createContext<ITourContext>({} as ITourContext);

interface TourProviderProps {
steps: ReactourStep[];
}

export const TourProvider = ({ steps, children }: PropsWithChildren<TourProviderProps>): JSX.Element => {
const [isTourMode, setIsTourMode] = useState<boolean>(false);
const [hasSeenTour, setHasSeenTour] = useState<boolean>(false);
const [tourSteps] = useState<ReactourStep[]>(steps || []);

useEffect(() => {
const hasSeenTour = localStorage.getItem('hasSeenTour');

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this project, just utilizing localStorage works however how it works currently is pretty static to only the tour of the board. For extending this and working with showcasing other features (i.e., teams UI), the implementation would have to be touched up. However, the TourProvider is flexible with updating the steps so just some tweaking for a better tour system.

if (!hasSeenTour) {
setIsTourMode(true);
}
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to add in the empty dependency array so it runs on just render as opposed to right now which is running on render then on every update.


const tourModeEndHandler = (): void => {
localStorage.setItem('hasSeenTour', 'true');
setIsTourMode(false);
setHasSeenTour(true);
};

return (
<TourContext.Provider
value={{
isTourMode,
hasSeenTour,
tourSteps,
tourModeEndHandler,
}}
>
{children}
</TourContext.Provider>
);
};

export function useTour(): ITourContext {
const ctx = useContext(TourContext);
if (!ctx) throw new Error('useTour must be used within TourProvider');
return ctx;
}
8 changes: 8 additions & 0 deletions client/src/interface/TourContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ReactourStep } from 'reactour';

export default interface ITourContext {
isTourMode: boolean;
hasSeenTour: boolean;
tourSteps: ReactourStep[];
tourModeEndHandler: () => void;
}
19 changes: 0 additions & 19 deletions client/src/mocks/mockUseAuthProvider.tsx

This file was deleted.

Loading