style: cycle new ui (#1052)

* style: cycles new ui

* chore: added progress bar for the high priority issues

* fix: build fix

* style: active cycle details, theming , padding and layout

* style: cycle list and card styling

* style: cycle card

* fix: tooltip text overflow

* fix: cycle mutation fix

* style: cycle list and card view improvement, chore: code refactor

* feat: view cycle button

* style: cycle list and board view improvement

* style: responsiveness added

* feat: active cycle stats component, chore: code refactor

* fix: active cycle divider fix, style: stats font color

* fix: tooltip fix

---------

Co-authored-by: kunal_17 <kunalvish17360@gmail.com>
This commit is contained in:
Anmol Singh Bhatia 2023-05-17 12:58:01 +05:30 committed by GitHub
parent c49b0d6151
commit 559b0cc9c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1980 additions and 228 deletions

View file

@ -13,9 +13,19 @@ import useToast from "hooks/use-toast";
// ui
import { CustomMenu, LinearProgressIndicator, Tooltip } from "components/ui";
import { Disclosure, Transition } from "@headlessui/react";
import { AssigneesList, Avatar } from "components/ui/avatar";
import { SingleProgressStats } from "components/core";
// icons
import { CalendarDaysIcon } from "@heroicons/react/20/solid";
import { TargetIcon } from "components/icons";
import { CalendarDaysIcon, ExclamationCircleIcon } from "@heroicons/react/20/solid";
import {
TargetIcon,
ContrastIcon,
PersonRunningIcon,
ArrowRightIcon,
TriangleExclamationIcon,
AlarmClockIcon,
} from "components/icons";
import {
ChevronDownIcon,
LinkIcon,
@ -24,7 +34,11 @@ import {
TrashIcon,
} from "@heroicons/react/24/outline";
// helpers
import { getDateRangeStatus, renderShortDateWithYearFormat } from "helpers/date-time.helper";
import {
getDateRangeStatus,
renderShortDateWithYearFormat,
findHowManyDaysLeft,
} from "helpers/date-time.helper";
import { copyTextToClipboard, truncateText } from "helpers/string.helper";
// types
import {
@ -86,14 +100,13 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
const { setToastAlert } = useToast();
const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
const endDate = new Date(cycle.end_date ?? "");
const startDate = new Date(cycle.start_date ?? "");
const handleAddToFavorites = () => {
if (!workspaceSlug || !projectId || !cycle) return;
const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
switch (cycleStatus) {
case "current":
case "upcoming":
@ -154,8 +167,6 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
const handleRemoveFromFavorites = () => {
if (!workspaceSlug || !projectId || !cycle) return;
const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
switch (cycleStatus) {
case "current":
case "upcoming":
@ -236,69 +247,158 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
color: group.color,
}));
const groupedIssues: any = {
backlog: cycle.backlog_issues,
unstarted: cycle.unstarted_issues,
started: cycle.started_issues,
completed: cycle.completed_issues,
cancelled: cycle.cancelled_issues,
};
return (
<div>
<div className="flex flex-col rounded-[10px] bg-brand-base text-xs shadow">
<div className="flex flex-col rounded-[10px] bg-brand-base border border-brand-base text-xs shadow">
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`}>
<a className="w-full">
<div className="flex h-full flex-col gap-4 rounded-b-[10px] p-4">
<div className="flex items-start justify-between gap-1">
<Tooltip tooltipContent={cycle.name} position="top-left">
<h3 className="break-all text-lg font-semibold">
{truncateText(cycle.name, 75)}
</h3>
</Tooltip>
{cycle.is_favorite ? (
<button
onClick={(e) => {
e.preventDefault();
handleRemoveFromFavorites();
}}
<div className="flex items-center justify-between gap-1">
<span className="flex items-center gap-1">
<ContrastIcon
className="h-5 w-5"
color={`${
cycleStatus === "current"
? "#09A953"
: cycleStatus === "upcoming"
? "#F7AE59"
: cycleStatus === "completed"
? "#3F76FF"
: cycleStatus === "draft"
? "#858E96"
: ""
}`}
/>
<Tooltip tooltipContent={cycle.name} className="break-all" position="top-left">
<h3 className="break-all text-lg font-semibold">
{truncateText(cycle.name, 15)}
</h3>
</Tooltip>
</span>
<span className="flex items-center gap-1 capitalize">
<span
className={`rounded-full px-1.5 py-0.5
${
cycleStatus === "current"
? "bg-green-600/5 text-green-600"
: cycleStatus === "upcoming"
? "bg-orange-300/5 text-orange-300"
: cycleStatus === "completed"
? "bg-blue-500/5 text-blue-500"
: cycleStatus === "draft"
? "bg-neutral-400/5 text-neutral-400"
: ""
}`}
>
<StarIcon className="h-4 w-4 text-orange-400" fill="#f6ad55" />
</button>
) : (
<button
onClick={(e) => {
e.preventDefault();
handleAddToFavorites();
}}
>
<StarIcon className="h-4 w-4 " color="#858E96" />
</button>
{cycleStatus === "current" ? (
<span className="flex gap-1">
<PersonRunningIcon className="h-4 w-4" />
{findHowManyDaysLeft(cycle.end_date ?? new Date())} Days Left
</span>
) : cycleStatus === "upcoming" ? (
<span className="flex gap-1">
<AlarmClockIcon className="h-4 w-4" />
{findHowManyDaysLeft(cycle.start_date ?? new Date())} Days Left
</span>
) : cycleStatus === "completed" ? (
<span className="flex gap-1">
{cycle.total_issues - cycle.completed_issues > 0 && (
<Tooltip
tooltipContent={`${
cycle.total_issues - cycle.completed_issues
} more pending ${
cycle.total_issues - cycle.completed_issues === 1 ? "issue" : "issues"
}`}
>
<span>
<TriangleExclamationIcon className="h-3.5 w-3.5 fill-current" />
</span>
</Tooltip>
)}{" "}
Completed
</span>
) : (
cycleStatus
)}
</span>
{cycle.is_favorite ? (
<button
onClick={(e) => {
e.preventDefault();
handleRemoveFromFavorites();
}}
>
<StarIcon className="h-4 w-4 text-orange-400" fill="#f6ad55" />
</button>
) : (
<button
onClick={(e) => {
e.preventDefault();
handleAddToFavorites();
}}
>
<StarIcon className="h-4 w-4 " color="#858E96" />
</button>
)}
</span>
</div>
<div className="flex h-4 items-center justify-start gap-5 text-brand-secondary">
{cycleStatus !== "draft" && (
<>
<div className="flex items-start gap-1">
<CalendarDaysIcon className="h-4 w-4" />
<span>{renderShortDateWithYearFormat(startDate)}</span>
</div>
<ArrowRightIcon className="h-4 w-4" />
<div className="flex items-start gap-1">
<TargetIcon className="h-4 w-4" />
<span>{renderShortDateWithYearFormat(endDate)}</span>
</div>
</>
)}
</div>
<div className="flex items-center justify-start gap-5 text-brand-secondary">
<div className="flex items-start gap-1 ">
<CalendarDaysIcon className="h-4 w-4" />
<span>Start :</span>
<span>{renderShortDateWithYearFormat(startDate)}</span>
<div className="flex justify-between items-end">
<div className="flex flex-col gap-2 text-xs text-brand-secondary">
<div className="flex items-center gap-2">
<div className="w-16">Creator:</div>
<div className="flex items-center gap-2.5 text-brand-secondary">
{cycle.owned_by.avatar && cycle.owned_by.avatar !== "" ? (
<Image
src={cycle.owned_by.avatar}
height={16}
width={16}
className="rounded-full"
alt={cycle.owned_by.first_name}
/>
) : (
<span className="bg-brand-secondary flex h-5 w-5 items-center justify-center rounded-full bg-orange-300 capitalize text-white">
{cycle.owned_by.first_name.charAt(0)}
</span>
)}
<span className="text-brand-secondary">{cycle.owned_by.first_name}</span>
</div>
</div>
<div className="flex h-5 items-center gap-2">
<div className="w-16">Members:</div>
{cycle.assignees.length > 0 ? (
<div className="flex items-center gap-1 text-brand-secondary">
<AssigneesList users={cycle.assignees} length={4} />
</div>
) : (
"No members"
)}
</div>
</div>
<div className="flex items-start gap-1 ">
<TargetIcon className="h-4 w-4" />
<span>End :</span>
<span>{renderShortDateWithYearFormat(endDate)}</span>
</div>
</div>
<div className="mt-4 flex items-center justify-between text-brand-secondary">
<div className="flex items-center gap-2.5">
{cycle.owned_by.avatar && cycle.owned_by.avatar !== "" ? (
<Image
src={cycle.owned_by.avatar}
height={16}
width={16}
className="rounded-full"
alt={cycle.owned_by.first_name}
/>
) : (
<span className="bg-brand-secondary flex h-5 w-5 items-center justify-center rounded-full capitalize">
{cycle.owned_by.first_name.charAt(0)}
</span>
)}
<span>{cycle.owned_by.first_name}</span>
</div>
<div className="flex items-center">
{!isCompleted && (
<button
@ -306,7 +406,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
e.preventDefault();
handleEditCycle();
}}
className="flex cursor-pointer items-center rounded p-1 duration-300 hover:bg-brand-surface-1"
className="flex cursor-pointer items-center rounded p-1 text-brand-secondary duration-300 hover:bg-brand-surface-1"
>
<span>
<PencilIcon className="h-4 w-4" />
@ -356,7 +456,35 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
>
<div className="flex w-full items-center gap-2 px-4 py-1">
<span>Progress</span>
<LinearProgressIndicator data={progressIndicatorData} />
<Tooltip
tooltipContent={
<div className="flex w-56 flex-col">
{Object.keys(groupedIssues).map((group, index) => (
<SingleProgressStats
key={index}
title={
<div className="flex items-center gap-2">
<span
className="block h-3 w-3 rounded-full "
style={{
backgroundColor: stateGroups[index].color,
}}
/>
<span className="text-xs capitalize">{group}</span>
</div>
}
completed={groupedIssues[group]}
total={cycle.total_issues}
/>
))}
</div>
}
position="bottom"
>
<div className="flex w-full items-center">
<LinearProgressIndicator data={progressIndicatorData} noTooltip={true} />
</div>
</Tooltip>
<Disclosure.Button>
<span className="p-1">
<ChevronDownIcon