Skip to content

Commit

Permalink
Feat: show project progress
Browse files Browse the repository at this point in the history
  • Loading branch information
harshithmullapudi committed Nov 12, 2024
1 parent 09a3fa0 commit 68d6acf
Show file tree
Hide file tree
Showing 16 changed files with 306 additions and 81 deletions.
8 changes: 6 additions & 2 deletions apps/server/src/modules/workspaces/workspaces.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@ export class WorkspacesController {
async createIntialResources(
@SessionDecorator() session: SessionContainer,
@Body() workspaceData: CreateInitialResourcesDto,
): Promise<Workspace> {
@Res() res: Response,
@Req() req: Request,
) {
const userId = session.getUserId();
return await this.workspacesService.createInitialResources(
await this.workspacesService.createInitialResources(
userId,
workspaceData,
res,
req,
);
}

Expand Down
16 changes: 14 additions & 2 deletions apps/server/src/modules/workspaces/workspaces.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
createNewSession,
SessionContainer,
} from 'supertokens-node/recipe/session';
import Session from 'supertokens-node/recipe/session';

import { createMagicLink } from 'common/utils/login';

Expand Down Expand Up @@ -46,7 +47,9 @@ export default class WorkspacesService {
async createInitialResources(
userId: string,
workspaceData: CreateInitialResourcesDto,
): Promise<Workspace> {
res: Response,
req: Request,
) {
const workspace = await this.prisma.usersOnWorkspaces.findFirst({
where: { userId },
});
Expand All @@ -55,7 +58,7 @@ export default class WorkspacesService {
throw new BadRequestException('Already workspace exist');
}

return await this.prisma.$transaction(
await this.prisma.$transaction(
async (prisma) => {
await prisma.user.update({
where: { id: userId },
Expand Down Expand Up @@ -109,6 +112,15 @@ export default class WorkspacesService {
timeout: 60000,
},
);

await Session.createNewSession(
req,
res,
'public',
supertokens.convertToRecipeUserId(userId),
);

res.send({ status: 200, message: 'impersonate' });
}

async createWorkspace(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@tegonhq/ui/components/ui/tooltip';
} from '@tegonhq/ui/components/tooltip';
import { ChevronRight, TeamLine } from '@tegonhq/ui/icons';
import { cn } from '@tegonhq/ui/lib/utils';
import { observer } from 'mobx-react-lite';
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/src/common/user-avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { User } from './types';

import { AvatarText } from '@tegonhq/ui/components/avatar';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@tegonhq/ui/components/tooltip';
import { AvatarText } from '@tegonhq/ui/components/ui/avatar';
import { cn } from '@tegonhq/ui/lib/utils';

interface UserAvatarProps {
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/src/modules/onboarding/onboarding-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function OnboardingForm() {
toast({
variant: 'destructive',
title: 'Error!',
description: e.message,
description: e,
});
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { Editor, EditorExtensions } from '@tegonhq/ui/components/editor/index';
import { ScrollArea } from '@tegonhq/ui/components/scroll-area';
import {
Editor,
EditorExtensions,
} from '@tegonhq/ui/components/ui/editor/index';
import { observer } from 'mobx-react-lite';
import React from 'react';
import { useDebouncedCallback } from 'use-debounce';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from '@tegonhq/ui/components/dropdown-menu';
import { Input } from '@tegonhq/ui/components/ui/input';
import { Input } from '@tegonhq/ui/components/input';
import {
AddLine,
CheckLine,
Expand Down
121 changes: 121 additions & 0 deletions apps/webapp/src/modules/projects/project-view/project-progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { WorkflowCategory } from '@tegonhq/types';
import { Progress } from '@tegonhq/ui/components/progress';
import { observer } from 'mobx-react-lite';
import React from 'react';

import type { IssueType, WorkflowType } from 'common/types';

import { useComputedWorkflows } from 'hooks/workflows';

import { useContextStore } from 'store/global-context-provider';

interface ProjectProgressProps {
id: string;
onlyGraph?: boolean;
}

export const ProjectProgress = observer(
({ id, onlyGraph = false }: ProjectProgressProps) => {
const { issuesStore, projectsStore } = useContextStore();
const project = projectsStore.getProjectWithId(id);
const { workflows } = useComputedWorkflows();
const issues = issuesStore.getIssuesForProject({ projectId: project.id });

const totalCompletedIssues = issues.filter((issue: IssueType) => {
const workflow = workflows.find((workflow: WorkflowType) =>
workflow.ids.includes(issue.stateId),
);

if (
workflow.category === WorkflowCategory.COMPLETED ||
workflow.category === WorkflowCategory.CANCELED
) {
return true;
}

return false;
});

const startedIssues = React.useMemo(() => {
return issues.filter((issue: IssueType) => {
const workflow = workflows.find((workflow: WorkflowType) =>
workflow.ids.includes(issue.stateId),
);

if (workflow.category === WorkflowCategory.STARTED) {
return true;
}

return false;
});
}, [issues, workflows]);

const completedPercentage =
issues.length === 0
? 0
: Math.floor((totalCompletedIssues.length / issues.length) * 100);
const startedPercentage =
issues.length === 0
? 0
: Math.floor((startedIssues.length / issues.length) * 100);

const color = React.useCallback((percentage: number) => {
if (percentage > 80) {
return '#3caf20';
} else if (percentage > 30) {
return '#c28c11';
}
return '#d94b0e';
}, []);

if (onlyGraph) {
return (
<div className="flex items-center gap-2">
<Progress
color={color(completedPercentage)}
segments={[
{
value: completedPercentage + startedPercentage,
},
{ value: completedPercentage },
]}
/>
<div className="font-mono"> {completedPercentage}%</div>
</div>
);
}

return (
<div className="p-6 pb-0 flex w-full gap-10">
<div className="flex flex-col grow gap-1">
<h2>Project Progress</h2>
<Progress
color={color(completedPercentage)}
segments={[
{
value: completedPercentage + startedPercentage,
},
{ value: completedPercentage },
]}
/>
</div>
<div className="flex flex-col">
<h2 className="font-mono">Scope</h2>
<p>{issues.length} issues </p>
</div>
<div className="flex flex-col">
<h2 className="font-mono">Started</h2>
<p>
{startedIssues.length} . {startedPercentage}%
</p>
</div>
<div className="flex flex-col">
<h2 className="font-mono">Done</h2>
<p>
{totalCompletedIssues.length} . {completedPercentage}%
</p>
</div>
</div>
);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useLocalState } from 'hooks/use-local-state';
import { ProjectIssues } from './issues';
import { Overview } from './overview';
import { RightSide } from './overview/right-side';
import { ProjectProgress } from './project-progress';
import { Header } from '../header';

export const ProjectView = withApplicationStore(() => {
Expand All @@ -29,6 +30,7 @@ export const ProjectView = withApplicationStore(() => {
<ContentBox>
<main className="grid grid-cols-5 h-[calc(100vh_-_53px)]">
<div className="col-span-4 flex flex-col h-[calc(100vh_-_55px)]">
<ProjectProgress id={project.id} />
{view === 'overview' && <Overview />}
{view === 'issues' && <ProjectIssues />}
</div>
Expand Down
9 changes: 8 additions & 1 deletion apps/webapp/src/modules/projects/projects-list/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useUpdateProjectMutation } from 'services/projects';
import { ProjectWrapper } from './project-wrapper';
import { ProjectDatePicker } from '../dropdowns/project-date-picker';
import { ProjectStatusDropdown } from '../dropdowns/status';
import { ProjectProgress } from '../project-view/project-progress';

export const useProjectColumns = (): Array<ColumnDef<ProjectType>> => {
const { mutate: updateProject } = useUpdateProjectMutation({});
Expand Down Expand Up @@ -44,7 +45,13 @@ export const useProjectColumns = (): Array<ColumnDef<ProjectType>> => {
);
},
},

{
accessorKey: 'progress',
header: () => {
return <span className="px-2">Progress</span>;
},
cell: ({ row }) => <ProjectProgress id={row.original.id} onlyGraph />,
},
{
accessorKey: 'endDate',
header: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import {
PopoverTrigger,
} from '@tegonhq/ui/components/popover';
import { ScrollArea } from '@tegonhq/ui/components/scroll-area';
import { useToast } from '@tegonhq/ui/components/ui/use-toast';
import { useToast } from '@tegonhq/ui/components/use-toast';
import { observer } from 'mobx-react-lite';
import React from 'react';

import type { UsersOnWorkspaceType } from 'common/types';
import { getUserFromUsersData } from 'common/user-util';

import { useCurrentTeam } from 'hooks/teams';
import { useAllUsers } from 'hooks/users';
import { useUsersData } from 'hooks/users';

import { useAddTeamMemberMutation } from 'services/team';

Expand All @@ -46,7 +46,7 @@ export const ShowMembersDropdown = observer(() => {
},
});

const { users, isLoading } = useAllUsers(false);
const { users, isLoading } = useUsersData();

const getMembersNotInTeam = () => {
return workspaceStore.usersOnWorkspaces.filter(
Expand Down
22 changes: 21 additions & 1 deletion apps/webapp/src/pages/_error.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
import * as Sentry from '@sentry/nextjs';
import { Button } from '@tegonhq/ui/components/button';
import Error from 'next/error';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const CustomErrorComponent = (props: any) => {
return <Error statusCode={props.statusCode} />;
const reload = () => {
localStorage.clear();
window.location.reload();
};

return (
<div className="h-[100vh] w-[100vw] flex justify-center items-center flex-col">
<h1>
{props.statusCode ? `Error ${props.statusCode}` : 'An error occurred'}
</h1>
<p>
{props.statusCode === 404
? 'Page not found'
: 'Something went wrong on our end. Please try again later.'}
</p>
<Button variant="secondary" className="mt-2" onClick={reload}>
Reload page
</Button>
</div>
);
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ export function useCreateInitialResourcesMutation({

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onMutationError = (errorResponse: any) => {
const errorText = errorResponse?.errors?.message || 'Error occured';
let errorText = 'Error occured';
if (errorResponse.errors.statusCode === 409) {
errorText = 'Workspace with the name already exist';
}

onError && onError(errorText);
};
Expand Down
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
Expand Down
Loading

0 comments on commit 68d6acf

Please sign in to comment.