Skip to content

Commit

Permalink
feat(GoalCriteria): show criteria progess onto goals lists
Browse files Browse the repository at this point in the history
  • Loading branch information
LamaEats committed Jul 18, 2023
1 parent dcbd0ce commit 01396db
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 48 deletions.
2 changes: 2 additions & 0 deletions prisma/migrations/20230717111348_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Goal" ADD COLUMN "completedCriteriaWeight" INTEGER;
71 changes: 36 additions & 35 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -192,41 +192,42 @@ model Priority {
}

model Goal {
id String @id @default(cuid())
scopeId Int @default(1)
title String
description String
kind String?
key Boolean?
personal Boolean?
private Boolean?
archived Boolean? @default(false)
priority String?
priorityExact Priority? @relation(fields: [priorityId], references: [id])
priorityId Int?
estimate EstimateToGoal[]
project Project? @relation("projectGoals", fields: [projectId], references: [id])
projectId String?
teamId String?
state State? @relation("goalState", fields: [stateId], references: [id])
stateId String?
activity Activity? @relation("goalIssuer", fields: [activityId], references: [id])
activityId String?
owner Activity? @relation("goalOwner", fields: [ownerId], references: [id])
ownerId String?
participants Activity[] @relation("goalParticipants")
watchers Activity[] @relation("goalWatchers")
stargizers Activity[] @relation("goalStargizers")
comments Comment[]
reactions Reaction[]
tags Tag[]
dependsOn Goal[] @relation("dependsOn")
blocks Goal[] @relation("dependsOn")
relatedTo Goal[] @relation("connected")
connected Goal[] @relation("connected")
history GoalHistory[]
goalAchiveCriteria GoalAchieveCriteria[] @relation("GoalCriterion")
goalAsCriteria GoalAchieveCriteria? @relation("GoalAsCriteria")
id String @id @default(cuid())
scopeId Int @default(1)
title String
description String
kind String?
key Boolean?
personal Boolean?
private Boolean?
archived Boolean? @default(false)
priority String?
priorityExact Priority? @relation(fields: [priorityId], references: [id])
priorityId Int?
estimate EstimateToGoal[]
project Project? @relation("projectGoals", fields: [projectId], references: [id])
projectId String?
teamId String?
state State? @relation("goalState", fields: [stateId], references: [id])
stateId String?
activity Activity? @relation("goalIssuer", fields: [activityId], references: [id])
activityId String?
owner Activity? @relation("goalOwner", fields: [ownerId], references: [id])
ownerId String?
participants Activity[] @relation("goalParticipants")
watchers Activity[] @relation("goalWatchers")
stargizers Activity[] @relation("goalStargizers")
comments Comment[]
reactions Reaction[]
tags Tag[]
dependsOn Goal[] @relation("dependsOn")
blocks Goal[] @relation("dependsOn")
relatedTo Goal[] @relation("connected")
connected Goal[] @relation("connected")
history GoalHistory[]
goalAchiveCriteria GoalAchieveCriteria[] @relation("GoalCriterion")
goalAsCriteria GoalAchieveCriteria? @relation("GoalAsCriteria")
completedCriteriaWeight Int?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
Expand Down
18 changes: 16 additions & 2 deletions src/components/GoalListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import React, { FC, MouseEventHandler, useMemo } from 'react';
import styled from 'styled-components';
import NextLink from 'next/link';
import { textColor, gapS, gapXs, gray9 } from '@taskany/colors';
import { MessageIcon, Text, Tag as TagItem, nullable, StarFilledIcon, EyeIcon } from '@taskany/bricks';
import {
MessageIcon,
Text,
Tag as TagItem,
nullable,
StarFilledIcon,
EyeIcon,
CircleProgressBar,
} from '@taskany/bricks';
import type { Estimate, State as StateType, Tag } from '@prisma/client';

import { routes } from '../hooks/router';
Expand Down Expand Up @@ -36,6 +44,7 @@ interface GoalListItemProps {
starred?: boolean;
watching?: boolean;
className?: string;
achivedCriteriaWeight?: number | null;
onClick?: MouseEventHandler<HTMLAnchorElement>;
onTagClick?: (tag: Tag) => MouseEventHandler<HTMLDivElement>;
}
Expand Down Expand Up @@ -115,7 +124,7 @@ export const GoalsListContainer: FC<{ children: React.ReactNode; offset?: number
offset = 0,
className,
}) => (
<Table columns={12} offset={offset} className={className}>
<Table columns={13} offset={offset} className={className}>
{children}
</Table>
);
Expand All @@ -138,6 +147,7 @@ export const GoalListItem: React.FC<GoalListItemProps> = React.memo(
priority,
starred,
watching,
achivedCriteriaWeight,
onClick,
className,
onTagClick,
Expand Down Expand Up @@ -182,6 +192,10 @@ export const GoalListItem: React.FC<GoalListItemProps> = React.memo(
<GoalTextItem>{nullable(estimate, (e) => estimateToString(e))}</GoalTextItem>
</GoalContentItem>

<GoalContentItem>
{achivedCriteriaWeight != null && <CircleProgressBar value={achivedCriteriaWeight} />}
</GoalContentItem>

<GoalContentItem>
{tags?.map((tag) =>
nullable(tag, (t) => (
Expand Down
4 changes: 1 addition & 3 deletions src/components/GoalPreview/GoalPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,7 @@ const GoalPreview: React.FC<GoalPreviewProps> = ({ preview, onClose, onDelete })
owner={goal?.owner}
estimate={goal?._lastEstimate}
priority={goal?.priority}
achivedCriteriaWeight={
goal?._hasAchievementCriteria ? goal?._achivedCriteriaWeight : undefined
}
achivedCriteriaWeight={goal?._achivedCriteriaWeight}
comments={goal?._count?.comments ?? 0}
onCommentsClick={onCommentsClick}
updatedAt={updatedAt}
Expand Down
1 change: 1 addition & 0 deletions src/components/GoalsGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const GoalsGroup: React.FC<GoalGroupProps> = React.memo(
participants={g.participants}
starred={g._isStarred}
watching={g._isWatching}
achivedCriteriaWeight={g._achivedCriteriaWeight}
key={g.id}
focused={selectedResolver(g.id)}
onClick={onClickProvider(g)}
Expand Down
1 change: 1 addition & 0 deletions src/components/GoalsPage/GoalsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export const GoalsPage = ({ user, ssrTime }: ExternalPageProps) => {
participants={g.participants}
starred={g._isStarred}
watching={g._isWatching}
achivedCriteriaWeight={g._achivedCriteriaWeight}
key={g.id}
focused={selectedGoalResolver(g.id)}
onClick={onGoalPrewiewShow(g as GoalByIdReturnType)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/IssueStats/IssueStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface IssueStatsProps {
issuer?: ActivityByIdReturnType | null;
estimate?: { date: string; q?: string; y: string };
priority?: string | null;
achivedCriteriaWeight?: number;
achivedCriteriaWeight?: number | null;
mode?: 'compact' | 'default';
onCommentsClick?: () => void;
}
Expand Down Expand Up @@ -89,7 +89,7 @@ export const IssueStats: React.FC<IssueStatsProps> = ({
{nullable(priority, (p) => (
<DotSep>{getPriorityText(p)}</DotSep>
))}
{achivedCriteriaWeight !== undefined && (
{achivedCriteriaWeight != null && (
<DotSep>
<StyledCircleProgressBar value={achivedCriteriaWeight} />
</DotSep>
Expand Down
1 change: 1 addition & 0 deletions src/components/ProjectListItemConnected.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export const ProjectListItemConnected: FC<{
participants={g.participants}
starred={g._isStarred}
watching={g._isWatching}
achivedCriteriaWeight={g._achivedCriteriaWeight}
key={g.id}
focused={selectedResolver?.(g.id)}
onClick={onClickProvider?.(g as NonNullable<GoalByIdReturnType>)}
Expand Down
26 changes: 25 additions & 1 deletion src/utils/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { nanoid } from 'nanoid';
import { GoalHistory, Comment, Activity, User, Goal } from '@prisma/client';

import { GoalCommon, GoalUpdate, dependencyKind } from '../schema/goal';
import { addCalclulatedGoalsFields } from '../../trpc/queries/goals';
import { addCalclulatedGoalsFields, calcAchievedWeight } from '../../trpc/queries/goals';
import { HistoryRecordWithActivity, HistoryRecordMeta, HistoryRecordSubject, HistoryAction } from '../types/history';

import { prisma } from './prisma';
Expand Down Expand Up @@ -267,3 +267,27 @@ export const makeGoalRelationMap = <T extends Goal>(
): Array<{ kind: dependencyKind; goals: T[] }> => {
return (Object.entries(values) as [dependencyKind, T[]][]).map(([kind, goals]) => ({ kind, goals }));
};

export const updateGoalWithCalculatedWeight = async (goalId: string) => {
const criteriaList = await prisma.goalAchieveCriteria.findMany({
where: { goalId },
include: {
goalAsCriteria: {
include: {
state: true,
},
},
},
});

if (!criteriaList) {
return;
}

await prisma.goal.update({
where: { id: goalId },
data: {
completedCriteriaWeight: calcAchievedWeight(criteriaList),
},
});
};
11 changes: 8 additions & 3 deletions trpc/queries/goals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,9 @@ export const goalDeepQuery = {

const maxPossibleCriteriaWeight = 100;

const calcAchievedWeight = (list: (GoalAchieveCriteria & { goalAsCriteria: Goal & { state: State } })[]): number => {
export const calcAchievedWeight = (
list: (GoalAchieveCriteria & { goalAsCriteria: (Goal & { state: State | null }) | null })[],
): number => {
const { achivedWithWeight, comletedWithoutWeight, anyWithoutWeight, allWeight } = list.reduce(
(acc, value) => {
acc.allWeight += value.weight;
Expand Down Expand Up @@ -473,8 +475,11 @@ export const addCalclulatedGoalsFields = (goal: any, activityId: string) => {
const _isIssuer = goal.activityId === activityId;
const _lastEstimate = goal.estimate?.length ? goal.estimate[goal.estimate.length - 1].estimate : undefined;
const _shortId = `${goal.projectId}-${goal.scopeId}`;
const _hasAchievementCriteria = goal.goalAchiveCriteria?.length;
const _achivedCriteriaWeight = calcAchievedWeight(goal.goalAchiveCriteria ?? []);
const _hasAchievementCriteria = !!goal.goalAchiveCriteria?.length;
const _achivedCriteriaWeight: number | null =
goal.completedCriteriaWeight == null && goal.goalAchieveCriteria
? calcAchievedWeight(goal.goalAchieveCriteria)
: goal.completedCriteriaWeight;

let parentOwner = false;
function checkParent(project?: any) {
Expand Down
39 changes: 37 additions & 2 deletions trpc/router/goal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
findOrCreateEstimate,
mixHistoryWithComments,
makeGoalRelationMap,
updateGoalWithCalculatedWeight,
} from '../../src/utils/db';
import { createEmailJob } from '../../src/utils/worker/create';
import { calculateDiffBetweenArrays } from '../../src/utils/calculateDiffBetweenArrays';
Expand Down Expand Up @@ -530,9 +531,14 @@ export const goal = router({
},
include: {
...goalDeepQuery,
goalAsCriteria: true,
},
});

if (goal.goalAsCriteria) {
await updateGoalWithCalculatedWeight(goal.goalAsCriteria.goalId);
}

return {
...goal,
...addCalclulatedGoalsFields(goal, ctx.session.user.activityId),
Expand Down Expand Up @@ -632,7 +638,7 @@ export const goal = router({
}

try {
return await prisma.goal.update({
const updatedGoal = await prisma.goal.update({
where: {
id: input.id,
},
Expand All @@ -656,7 +662,16 @@ export const goal = router({
}
: undefined,
},
include: {
goalAsCriteria: true,
},
});

if (updatedGoal.goalAsCriteria) {
await updateGoalWithCalculatedWeight(updatedGoal.goalAsCriteria.goalId);
}

return updatedGoal;
} catch (error: any) {
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: String(error.message), cause: error });
}
Expand Down Expand Up @@ -687,7 +702,7 @@ export const goal = router({

try {
// We want to see state changes record and comment next in activity feed.
const [, newComment] = await prisma.$transaction([
const [updatedGoal, newComment] = await prisma.$transaction([
// Update goal and push to history first.
prisma.goal.update({
where: { id: input.goalId },
Expand All @@ -714,6 +729,9 @@ export const goal = router({
}
: undefined,
},
include: {
goalAsCriteria: true,
},
}),
// Create comment next.
prisma.comment.create({
Expand All @@ -726,6 +744,10 @@ export const goal = router({
}),
]);

if (updatedGoal.goalAsCriteria) {
await updateGoalWithCalculatedWeight(updatedGoal.goalAsCriteria.goalId);
}

let toEmails = actualGoal.participants;

if (commentAuthor.user?.email === actualGoal.activity?.user?.email) {
Expand Down Expand Up @@ -835,6 +857,8 @@ export const goal = router({
// }),
]);

await updateGoalWithCalculatedWeight(criteria.goalId);

return criteria;
} catch (error: any) {
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: String(error.message), cause: error });
Expand Down Expand Up @@ -871,15 +895,26 @@ export const goal = router({
// },
// }),
]);

// update goal criteria weight
await updateGoalWithCalculatedWeight(currentCriteria.goalId);
} catch (error: any) {
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: String(error.message), cause: error });
}
}),
removeCriteria: protectedProcedure.input(removeCriteria).mutation(async ({ input }) => {
const current = await prisma.goalAchieveCriteria.findUnique({
where: { id: input.id },
});

try {
await prisma.goalAchieveCriteria.delete({
where: { id: input.id },
});

if (current) {
await updateGoalWithCalculatedWeight(current.goalId);
}
} catch (error: any) {
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: String(error.message), cause: error });
}
Expand Down

0 comments on commit 01396db

Please sign in to comment.