Skip to content

Commit

Permalink
Implemented kanban board page with lists of issues
Browse files Browse the repository at this point in the history
  • Loading branch information
oldboyxx committed Dec 12, 2019
1 parent 3143f66 commit 73b4ff9
Show file tree
Hide file tree
Showing 73 changed files with 1,334 additions and 552 deletions.
7 changes: 2 additions & 5 deletions api/src/controllers/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import express from 'express';

import { User } from 'entities';
import { catchErrors } from 'errors';
import { createEntity } from 'utils/typeorm';
import { signToken } from 'utils/authToken';
import seedGuestUserEntities from 'database/seeds/guestUser';

const router = express.Router();

router.post(
'/authentication/guest',
catchErrors(async (req, res) => {
const user = await createEntity(User, req.body);
await seedGuestUserEntities(user);
catchErrors(async (_req, res) => {
const user = await seedGuestUserEntities();
res.respond({
authToken: signToken({ sub: user.id }),
});
Expand Down
30 changes: 3 additions & 27 deletions api/src/controllers/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,20 @@ import express from 'express';

import { Project } from 'entities';
import { catchErrors } from 'errors';
import { findEntityOrThrow, updateEntity, deleteEntity, createEntity } from 'utils/typeorm';
import { findEntityOrThrow, updateEntity } from 'utils/typeorm';

const router = express.Router();

router.get(
'/projects',
catchErrors(async (_req, res) => {
const projects = await Project.find();
res.respond({ projects });
}),
);

router.get(
'/projects/:projectId',
'/project',
catchErrors(async (req, res) => {
const project = await findEntityOrThrow(Project, req.params.projectId, {
const project = await findEntityOrThrow(Project, req.currentUser.projectId, {
relations: ['users', 'issues', 'issues.comments'],
});
res.respond({ project });
}),
);

router.post(
'/projects',
catchErrors(async (req, res) => {
const project = await createEntity(Project, req.body);
res.respond({ project });
}),
);

router.put(
'/projects/:projectId',
catchErrors(async (req, res) => {
Expand All @@ -40,12 +24,4 @@ router.put(
}),
);

router.delete(
'/projects/:projectId',
catchErrors(async (req, res) => {
const project = await deleteEntity(Project, req.params.projectId);
res.respond({ project });
}),
);

export default router;
14 changes: 14 additions & 0 deletions api/src/controllers/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import express from 'express';

import { catchErrors } from 'errors';

const router = express.Router();

router.get(
'/currentUser',
catchErrors((req, res) => {
res.respond({ currentUser: req.currentUser });
}),
);

export default router;
1 change: 1 addition & 0 deletions api/src/database/seeds/development/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import User from 'entities/User';

const generateUser = (data: Partial<User> = {}): Partial<User> => ({
name: faker.company.companyName(),
avatarUrl: faker.image.avatar(),
email: faker.internet.email(),
...data,
});
Expand Down
55 changes: 38 additions & 17 deletions api/src/database/seeds/guestUser.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,49 @@
import faker from 'faker';
import { sample } from 'lodash';

import { Comment, Issue, Project, User } from 'entities';
import { ProjectCategory } from 'constants/projects';
import { IssueType, IssueStatus, IssuePriority } from 'constants/issues';
import { createEntity } from 'utils/typeorm';

const seedProject = (user: User): Promise<Project> =>
const seedUsers = (): Promise<User[]> => {
const users = [
createEntity(User, {
email: '[email protected]',
name: 'Greg the Egg',
avatarUrl: faker.image.avatar(),
}),
createEntity(User, {
email: '[email protected]',
name: 'Baby Yoda',
avatarUrl: faker.image.avatar(),
}),
];
return Promise.all(users);
};

const seedProject = (users: User[]): Promise<Project> =>
createEntity(Project, {
name: 'Project: Hello World',
name: 'singularity 1.0',
url: 'https://www.atlassian.com/software/jira',
description:
'Plan, track, and manage your agile and software development projects in Jira. Customize your workflow, collaborate, and release great software.',
category: ProjectCategory.SOFTWARE,
users: [user],
users,
});

const seedIssues = (project: Project): Promise<Issue[]> => {
const user = project.users[0];
const getRandomUser = (): User => sample(project.users) as User;
const issues = [
createEntity(Issue, {
title: 'This is an issue of type: Task.',
type: IssueType.TASK,
status: IssueStatus.BACKLOG,
priority: IssuePriority.LOWEST,
estimate: 8,
reporterId: user.id,
reporterId: getRandomUser().id,
project,
users: [user],
users: [getRandomUser()],
}),
createEntity(Issue, {
title: "Click on an issue to see what's behind it.",
Expand All @@ -33,7 +52,7 @@ const seedIssues = (project: Project): Promise<Issue[]> => {
priority: IssuePriority.LOW,
description: 'Nothing in particular.',
estimate: 40,
reporterId: user.id,
reporterId: getRandomUser().id,
project,
}),
createEntity(Issue, {
Expand All @@ -42,9 +61,9 @@ const seedIssues = (project: Project): Promise<Issue[]> => {
status: IssueStatus.BACKLOG,
priority: IssuePriority.MEDIUM,
estimate: 15,
reporterId: user.id,
reporterId: getRandomUser().id,
project,
users: [user],
users: [getRandomUser()],
}),
createEntity(Issue, {
title: 'You can use markdown for issue descriptions.',
Expand All @@ -54,17 +73,17 @@ const seedIssues = (project: Project): Promise<Issue[]> => {
description:
"#### Colons can be used to align columns.\n\n| Tables | Are | Cool |\n| ------------- |:-------------:| -----:|\n| col 3 is | right-aligned | |\n| col 2 is | centered | |\n| zebra stripes | are neat | |\n\nThe outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown.\n\nMarkdown | Less | Pretty\n--- | --- | ---\n*Still* | `renders` | **nicely**\n1 | 2 | 3",
estimate: 4,
reporterId: user.id,
reporterId: getRandomUser().id,
project,
users: [user],
users: [getRandomUser()],
}),
createEntity(Issue, {
title: 'You must assign priority from lowest to highest to all issues.',
type: IssueType.TASK,
status: IssueStatus.SELECTED,
priority: IssuePriority.HIGHEST,
estimate: 15,
reporterId: user.id,
reporterId: getRandomUser().id,
project,
}),
createEntity(Issue, {
Expand All @@ -73,17 +92,17 @@ const seedIssues = (project: Project): Promise<Issue[]> => {
status: IssueStatus.SELECTED,
priority: IssuePriority.MEDIUM,
estimate: 55,
reporterId: user.id,
reporterId: getRandomUser().id,
project,
users: [user],
users: [getRandomUser()],
}),
createEntity(Issue, {
title: 'Try leaving a comment on this issue.',
type: IssueType.TASK,
status: IssueStatus.SELECTED,
priority: IssuePriority.MEDIUM,
estimate: 12,
reporterId: user.id,
reporterId: getRandomUser().id,
project,
}),
];
Expand All @@ -97,10 +116,12 @@ const seedComments = (issue: Issue, user: User): Promise<Comment> =>
user,
});

const seedGuestUserEntities = async (user: User): Promise<void> => {
const project = await seedProject(user);
const seedGuestUserEntities = async (): Promise<User> => {
const users = await seedUsers();
const project = await seedProject(users);
const issues = await seedIssues(project);
await seedComments(issues[issues.length - 1], project.users[0]);
return users[0];
};

export default seedGuestUserEntities;
8 changes: 4 additions & 4 deletions api/src/entities/Issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ class Issue extends BaseEntity {
@Column('varchar')
priority: IssuePriority;

@Column({ type: 'text', nullable: true })
@Column('text', { nullable: true })
description: string | null;

@Column({ type: 'integer', nullable: true })
@Column('integer', { nullable: true })
estimate: number | null;

@Column({ type: 'integer', nullable: true })
@Column('integer', { nullable: true })
timeSpent: number | null;

@Column({ type: 'integer', nullable: true })
@Column('integer', { nullable: true })
timeRemaining: number | null;

@CreateDateColumn({ type: 'timestamp' })
Expand Down
9 changes: 3 additions & 6 deletions api/src/entities/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import {
CreateDateColumn,
UpdateDateColumn,
OneToMany,
ManyToMany,
JoinTable,
} from 'typeorm';

import is from 'utils/validation';
Expand All @@ -32,7 +30,7 @@ class Project extends BaseEntity {
@Column('varchar', { nullable: true })
url: string | null;

@Column({ type: 'text', nullable: true })
@Column('text', { nullable: true })
description: string | null;

@Column('varchar')
Expand All @@ -50,11 +48,10 @@ class Project extends BaseEntity {
)
issues: Issue[];

@ManyToMany(
@OneToMany(
() => User,
user => user.projects,
user => user.project,
)
@JoinTable()
users: User[];
}

Expand Down
12 changes: 10 additions & 2 deletions api/src/entities/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
UpdateDateColumn,
OneToMany,
ManyToMany,
ManyToOne,
RelationId,
} from 'typeorm';

import is from 'utils/validation';
Expand All @@ -28,6 +30,9 @@ class User extends BaseEntity {
@Column('varchar')
email: string;

@Column('varchar', { length: 2000 })
avatarUrl: string;

@CreateDateColumn({ type: 'timestamp' })
createdAt: Date;

Expand All @@ -46,11 +51,14 @@ class User extends BaseEntity {
)
issues: Issue[];

@ManyToMany(
@ManyToOne(
() => Project,
project => project.users,
)
projects: Project[];
project: Project;

@RelationId((user: User) => user.project)
projectId: number;
}

export default User;
2 changes: 1 addition & 1 deletion api/src/errors/errorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ export const errorHandler: ErrorRequestHandler = (error, _req, res, _next) => {
data: {},
};

res.status(errorData.status).send({ errors: [errorData] });
res.status(errorData.status).send({ error: errorData });
};
4 changes: 3 additions & 1 deletion api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { authenticateUser } from 'middleware/authentication';
import authenticationRoutes from 'controllers/authentication';
import projectsRoutes from 'controllers/projects';
import issuesRoutes from 'controllers/issues';
import usersRoutes from 'controllers/users';
import { RouteNotFoundError } from 'errors';
import { errorHandler } from 'errors/errorHandler';

Expand All @@ -30,7 +31,7 @@ const initializeExpress = (): void => {

app.use((_req, res, next) => {
res.respond = (data): void => {
res.status(200).send({ data });
res.status(200).send(data);
};
next();
});
Expand All @@ -41,6 +42,7 @@ const initializeExpress = (): void => {

app.use('/', projectsRoutes);
app.use('/', issuesRoutes);
app.use('/', usersRoutes);

app.use((req, _res, next) => next(new RouteNotFoundError(req.originalUrl)));
app.use(errorHandler);
Expand Down
7 changes: 5 additions & 2 deletions api/src/middleware/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Request } from 'express';

import { verifyToken } from 'utils/authToken';
import { findEntityOrThrow } from 'utils/typeorm';
import { catchErrors, InvalidTokenError } from 'errors';
import { User } from 'entities';

Expand All @@ -20,6 +19,10 @@ export const authenticateUser = catchErrors(async (req, _res, next) => {
if (!userId) {
throw new InvalidTokenError('Authentication token is invalid.');
}
req.currentUser = await findEntityOrThrow(User, userId);
const user = await User.findOne(userId);
if (!user) {
throw new InvalidTokenError('Authentication token is invalid: User not found.');
}
req.currentUser = user;
next();
});
2 changes: 1 addition & 1 deletion api/src/utils/authToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { InvalidTokenError } from 'errors';

export const signToken = (payload: object, options?: SignOptions): string =>
jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '7 days',
expiresIn: '180 days',
...options,
});

Expand Down
4 changes: 2 additions & 2 deletions client/src/components/App/App.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react';

import Toast from './Toast';
import Routes from './Routes';
import NormalizeStyles from './NormalizeStyles';
import FontStyles from './FontStyles';
import BaseStyles from './BaseStyles';
import Toast from './Toast';
import Routes from './Routes';

const App = () => (
<>
Expand Down
Loading

0 comments on commit 73b4ff9

Please sign in to comment.