Skip to content

Commit

Permalink
Feat: added cycle history, UX improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
harshithmullapudi committed Nov 20, 2024
1 parent aa4ba55 commit 9a4123d
Show file tree
Hide file tree
Showing 53 changed files with 652 additions and 172 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
-- AlterTable
ALTER TABLE "IssueHistory" ADD COLUMN "fromCycleId" TEXT,
ADD COLUMN "toCycleId" TEXT;

-- CreateTable
CREATE TABLE "CycleHistory" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deleted" TIMESTAMP(3),
"userId" TEXT,
"cycleId" TEXT NOT NULL,
"issueId" TEXT NOT NULL,
"fromStateId" TEXT,
"toStateId" TEXT,
"fromEstimate" INTEGER,
"toEstimate" INTEGER,
"isRemoved" BOOLEAN NOT NULL,

CONSTRAINT "CycleHistory_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "ProjectHistory" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deleted" TIMESTAMP(3),
"userId" TEXT,
"projectId" TEXT NOT NULL,
"issueId" TEXT NOT NULL,
"fromStateId" TEXT,
"toStateId" TEXT,
"fromEstimate" INTEGER,
"toEstimate" INTEGER,
"isRemoved" BOOLEAN NOT NULL,

CONSTRAINT "ProjectHistory_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "CycleHistory" ADD CONSTRAINT "CycleHistory_cycleId_fkey" FOREIGN KEY ("cycleId") REFERENCES "Cycle"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "ProjectHistory" ADD CONSTRAINT "ProjectHistory_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
Warnings:
- You are about to drop the column `isRemoved` on the `CycleHistory` table. All the data in the column will be lost.
- You are about to drop the column `isRemoved` on the `ProjectHistory` table. All the data in the column will be lost.
- Added the required column `changeType` to the `CycleHistory` table without a default value. This is not possible if the table is not empty.
- Added the required column `changeType` to the `ProjectHistory` table without a default value. This is not possible if the table is not empty.
*/
-- CreateEnum
CREATE TYPE "CycleHistoryChangeType" AS ENUM ('ADDED', 'MOVED', 'UPDATED', 'REMOVED');

-- CreateEnum
CREATE TYPE "ProjectHistoryChangeType" AS ENUM ('ADDED', 'MOVED', 'UPDATED', 'REMOVED');

-- AlterTable
ALTER TABLE "CycleHistory" DROP COLUMN "isRemoved",
ADD COLUMN "changeType" "CycleHistoryChangeType" NOT NULL;

-- AlterTable
ALTER TABLE "ProjectHistory" DROP COLUMN "isRemoved",
ADD COLUMN "changeType" "ProjectHistoryChangeType" NOT NULL;

-- AlterTable
ALTER TABLE "UsersOnWorkspaces" ADD COLUMN "ai" BOOLEAN NOT NULL DEFAULT false;

-- AddForeignKey
ALTER TABLE "CycleHistory" ADD CONSTRAINT "CycleHistory_issueId_fkey" FOREIGN KEY ("issueId") REFERENCES "Issue"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "ProjectHistory" ADD CONSTRAINT "ProjectHistory_issueId_fkey" FOREIGN KEY ("issueId") REFERENCES "Issue"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
66 changes: 66 additions & 0 deletions apps/server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,34 @@ model Cycle {
startDate DateTime
endDate DateTime
history CycleHistory[]
preferences Json @default("{}")
issues Issue[]
}

model CycleHistory {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deleted DateTime?
userId String?
cycle Cycle @relation(fields: [cycleId], references: [id])
cycleId String
issueId String
issue Issue @relation(fields: [issueId], references: [id])
changeType CycleHistoryChangeType
fromStateId String?
toStateId String?
fromEstimate Int?
toEstimate Int?
}

model Emoji {
id String @id @default(uuid())
name String
Expand Down Expand Up @@ -281,6 +304,8 @@ model Issue {
history IssueHistory[]
linkedIssue LinkedIssue[]
issueRelations IssueRelation[]
cycleHistory CycleHistory[]
projectHistory ProjectHistory[]
IssueSuggestion IssueSuggestion? @relation(fields: [issueSuggestionId], references: [id])
issueSuggestionId String? @unique
Expand Down Expand Up @@ -341,6 +366,9 @@ model IssueHistory {
toProjectId String?
fromProjectMilestoneId String?
toProjectMilestoneId String?
fromCycleId String?
toCycleId String?
relationChanges Json?
}

Expand Down Expand Up @@ -484,13 +512,34 @@ model Project {
milestones ProjectMilestone[]
issues Issue[]
history ProjectHistory[]
workspace Workspace @relation(fields: [workspaceId], references: [id])
workspaceId String
@@unique([name, workspaceId])
}

model ProjectHistory {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deleted DateTime?
userId String?
project Project @relation(fields: [projectId], references: [id])
projectId String
issueId String
issue Issue @relation(fields: [issueId], references: [id])
changeType ProjectHistoryChangeType
fromStateId String?
toStateId String?
fromEstimate Int?
toEstimate Int?
}

model ProjectMilestone {
id String @id @default(uuid())
createdAt DateTime @default(now())
Expand Down Expand Up @@ -631,6 +680,8 @@ model UsersOnWorkspaces {
workspaceId String
teamIds String[]
status Status @default(ACTIVE)
ai Boolean @default(false)
externalAccountMappings Json?
role Role @default(ADMIN)
joinedAt DateTime?
Expand Down Expand Up @@ -836,3 +887,18 @@ enum LLMModels {
CLAUDEOPUS
GPT4O
}

enum CycleHistoryChangeType {
ADDED
MOVED
UPDATED
REMOVED
}


enum ProjectHistoryChangeType {
ADDED
MOVED
UPDATED
REMOVED
}
49 changes: 39 additions & 10 deletions apps/webapp/src/common/header-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,44 @@
import { Button } from '@tegonhq/ui/components/button';
import { Separator } from '@tegonhq/ui/components/separator';
import { AI } from '@tegonhq/ui/icons';
import { observer } from 'mobx-react-lite';

import { useCommonStore } from 'hooks/use-common-store';

interface HeaderLayoutProps {
children: React.ReactNode;
actions?: React.ReactNode;
}

export function HeaderLayout({ children, actions }: HeaderLayoutProps) {
return (
<header className="flex px-4 w-full items-center">
<div className="flex justify-between w-full py-2.5 h-[48px] items-center">
<div className="flex gap-1 items-center">{children}</div>
{actions && <div>{actions}</div>}
</div>
</header>
);
}
export const HeaderLayout = observer(
({ children, actions }: HeaderLayoutProps) => {
const commonStore = useCommonStore();

return (
<header className="flex px-4 w-full items-center">
<div className="flex justify-between w-full py-2.5 h-[48px] items-center">
<div className="flex gap-1 items-center">{children}</div>
<div className="flex h-full items-center gap-1">
{actions && (
<>
<div className="flex items-center">{actions}</div>
<Separator orientation="vertical" className="h-full" />
</>
)}
<Button
size="sm"
variant="ghost"
isActive={commonStore.chatOpen}
className="ml-0.5"
onClick={() =>
commonStore.update({ chatOpen: !commonStore.chatOpen })
}
>
<AI />
</Button>
</div>
</div>
</header>
);
},
);
23 changes: 16 additions & 7 deletions apps/webapp/src/common/layouts/app-layout/team-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import {
} from '@tegonhq/ui/components/tooltip';
import {
ChevronRight,
Cycle,
IssuesLine,
StackLine,
TriageLine,
} from '@tegonhq/ui/icons';
import { observer } from 'mobx-react-lite';
import { useRouter } from 'next/router';
import * as React from 'react';

import type { TeamType } from 'common/types';
Expand All @@ -40,6 +42,7 @@ export const TeamList = observer(() => {
teamAccessList.includes(team.id),
);
const workspace = useCurrentWorkspace();
const router = useRouter();

return (
<div ref={containerRef} className="mt-4">
Expand All @@ -50,6 +53,12 @@ export const TeamList = observer(() => {
collapsible
defaultValue={team?.id ?? teams[0].id}
className="w-full flex flex-col gap-4"
onValueChange={(value: string) => {
if (value && value !== team?.id) {
const newTeam = teams.find((team: TeamType) => team.id === value);
router.push(`/${workspace.slug}/team/${newTeam.identifier}/all`);
}
}}
>
{teams.map((team: TeamType) => {
const links = [
Expand All @@ -71,13 +80,13 @@ export const TeamList = observer(() => {
},
];

// if (team.preferences.cyclesEnabled) {
// links.push({
// title: 'Cycles',
// icon: Cycle,
// href: `/${workspace.slug}/team/${team.identifier}/cycles`,
// });
// }
if (team.preferences.cyclesEnabled) {
links.push({
title: 'Cycles',
icon: Cycle,
href: `/${workspace.slug}/team/${team.identifier}/cycles`,
});
}

return (
<AccordionItem
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/src/common/layouts/content-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const ContentBox = observer(
<main
className={cn(
'p-3 pt-0 pl-0 flex flex-col h-[calc(100vh_-_48px)]',
className,
applicationStore.sidebarCollapsed && 'pl-3',
className,
)}
>
<div className="bg-background-2 h-full rounded-lg overflow-hidden shadow flex flex-col">
Expand Down
55 changes: 55 additions & 0 deletions apps/webapp/src/common/layouts/main-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from '@tegonhq/ui/components/resizable';
import { cn } from '@tegonhq/ui/lib/utils';
import { observer } from 'mobx-react-lite';

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

import { ContentBox } from './content-box';
interface MainLayoutProps {
header: React.ReactNode;
children: React.ReactNode;
className?: string;
}

export const MainLayout = observer(
({ header, children, className }: MainLayoutProps) => {
const { commonStore } = useContextStore();

return (
<main className={cn('flex flex-col h-[100vh]', className)}>
{header}
<ResizablePanelGroup direction="horizontal">
<ResizablePanel
collapsible={false}
order={1}
id="app-layout"
className="w-full"
>
{children}
</ResizablePanel>
{commonStore.chatOpen && (
<>
<ResizableHandle className="bg-transparent" />
<ResizablePanel
collapsible={false}
maxSize={50}
minSize={10}
defaultSize={25}
order={2}
id="app-layout-chat"
>
<ContentBox className="pl-0">
<h2></h2>
</ContentBox>
</ResizablePanel>
</>
)}
</ResizablePanelGroup>
</main>
);
},
);
6 changes: 6 additions & 0 deletions apps/webapp/src/hooks/teams/use-teams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ export function useTeams() {
)
: (teamsStore.teams as TeamType[]);
}

export function useAllTeams() {
const { teamsStore } = useContextStore();

return teamsStore.teams as TeamType[];
}
8 changes: 8 additions & 0 deletions apps/webapp/src/hooks/use-common-store.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { CommonStoreType } from 'store/common';
import { useContextStore } from 'store/global-context-provider';

export const useCommonStore = (): CommonStoreType => {
const { commonStore } = useContextStore();

return commonStore;
};
Loading

0 comments on commit 9a4123d

Please sign in to comment.