fix: bug and ui fix (#1073)

* fix: cycle and module sidebar scroll

* style: date picker theming

* style: workspace slug spacing

* fix: app sidebar z-index

* fix: favorite cycle mutation

* fix: cycle modal on error close

* feat: cycle view context

* style: active cycle stats scroll

* fix: active cycle favorite mutation

* feat: import export banner

* feat: cycle sidebar date picker logic updated

* fix: NaN in progress percentage fix

* fix: tooltip fix

* style: empty state for active cycle

* style: cycle badge width fix , all cycle empty state fix and cycle icon size fix
This commit is contained in:
Anmol Singh Bhatia 2023-05-18 19:07:01 +05:30 committed by GitHub
parent c3d520aefd
commit 5916d6e749
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 436 additions and 159 deletions

View File

@ -29,7 +29,12 @@ export const SingleProgressStats: React.FC<TSingleProgressStatsProps> = ({
<span className="h-4 w-4 ">
<ProgressBar value={completed} maxValue={total} />
</span>
<span className="w-8 text-right">{Math.floor((completed / total) * 100)}%</span>
<span className="w-8 text-right">
{isNaN(Math.floor((completed / total) * 100))
? "0"
: Math.floor((completed / total) * 100)}
%
</span>
</div>
<span>of</span>
<span>{total}</span>

View File

@ -51,6 +51,7 @@ import {
import {
CYCLE_COMPLETE_LIST,
CYCLE_CURRENT_AND_UPCOMING_LIST,
CYCLE_DETAILS,
CYCLE_DRAFT_LIST,
CYCLE_ISSUES,
} from "constants/fetch-keys";
@ -153,6 +154,15 @@ export const ActiveCycleDetails: React.FC<TSingleStatProps> = ({ cycle, isComple
);
break;
}
mutate(
CYCLE_DETAILS(projectId as string),
(prevData: any) =>
(prevData ?? []).map((c: any) => ({
...c,
is_favorite: c.id === cycle.id ? true : c.is_favorite,
})),
false
);
cyclesService
.addCycleToFavorites(workspaceSlug as string, projectId as string, {
@ -213,6 +223,15 @@ export const ActiveCycleDetails: React.FC<TSingleStatProps> = ({ cycle, isComple
);
break;
}
mutate(
CYCLE_DETAILS(projectId as string),
(prevData: any) =>
(prevData ?? []).map((c: any) => ({
...c,
is_favorite: c.id === cycle.id ? false : c.is_favorite,
})),
false
);
cyclesService
.removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id)
@ -251,24 +270,26 @@ export const ActiveCycleDetails: React.FC<TSingleStatProps> = ({ cycle, isComple
<div className="grid-row-2 grid rounded-[10px] shadow divide-y bg-brand-base border border-brand-base">
<div className="grid grid-cols-1 divide-y border-brand-base lg:divide-y-0 lg:divide-x lg:grid-cols-3">
<div className="flex flex-col text-xs">
<a className="w-full">
<div className="flex h-full flex-col gap-5 rounded-b-[10px] p-4">
<a className="h-full w-full">
<div className="flex h-60 flex-col gap-5 justify-between rounded-b-[10px] p-4">
<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"
: ""
}`}
/>
<span className="h-5 w-5">
<ContrastIcon
className="h-5 w-5"
color={`${
cycleStatus === "current"
? "#09A953"
: cycleStatus === "upcoming"
? "#F7AE59"
: cycleStatus === "completed"
? "#3F76FF"
: cycleStatus === "draft"
? "#858E96"
: ""
}`}
/>
</span>
<Tooltip tooltipContent={cycle.name} position="top-left">
<h3 className="break-all text-lg font-semibold">
{truncateText(cycle.name, 70)}
@ -291,17 +312,17 @@ export const ActiveCycleDetails: React.FC<TSingleStatProps> = ({ cycle, isComple
}`}
>
{cycleStatus === "current" ? (
<span className="flex gap-1">
<span className="flex gap-1 whitespace-nowrap">
<PersonRunningIcon className="h-4 w-4" />
{findHowManyDaysLeft(cycle.end_date ?? new Date())} Days Left
</span>
) : cycleStatus === "upcoming" ? (
<span className="flex gap-1">
<span className="flex gap-1 whitespace-nowrap">
<AlarmClockIcon className="h-4 w-4" />
{findHowManyDaysLeft(cycle.start_date ?? new Date())} Days Left
</span>
) : cycleStatus === "completed" ? (
<span className="flex gap-1">
<span className="flex gap-1 whitespace-nowrap">
{cycle.total_issues - cycle.completed_issues > 0 && (
<Tooltip
tooltipContent={`${
@ -400,7 +421,7 @@ export const ActiveCycleDetails: React.FC<TSingleStatProps> = ({ cycle, isComple
</a>
</div>
<div className="grid col-span-2 grid-cols-1 divide-y border-brand-base md:divide-y-0 md:divide-x md:grid-cols-2">
<div className="flex h-full flex-col border-brand-base">
<div className="flex h-60 flex-col border-brand-base">
<div className="flex h-full w-full flex-col text-brand-secondary p-4">
<div className="flex w-full items-center gap-2 py-1">
<span>Progress</span>
@ -428,7 +449,7 @@ export const ActiveCycleDetails: React.FC<TSingleStatProps> = ({ cycle, isComple
</div>
</div>
</div>
<div className="border-brand-base p-4">
<div className="border-brand-base h-60 overflow-y-auto p-4">
<ActiveCycleProgressStats issues={issues ?? []} />
</div>
</div>

View File

@ -61,7 +61,7 @@ export const AllCyclesBoard: React.FC<TCycleStatsViewProps> = ({
</div>
) : type === "current" ? (
<div className="flex w-full items-center justify-start rounded-[10px] bg-brand-surface-2 px-6 py-4">
<h3 className="text-base font-medium text-brand-base ">No current cycle is present.</h3>
<h3 className="text-base font-medium text-brand-base ">No cycle is present.</h3>
</div>
) : (
<EmptyState

View File

@ -65,7 +65,7 @@ export const AllCyclesList: React.FC<TCycleStatsViewProps> = ({
</div>
) : type === "current" ? (
<div className="flex w-full items-center justify-start rounded-[10px] bg-brand-surface-2 px-6 py-4">
<h3 className="text-base font-medium text-brand-base ">No current cycle is present.</h3>
<h3 className="text-base font-medium text-brand-base ">No cycle is present.</h3>
</div>
) : (
<EmptyState

View File

@ -13,9 +13,10 @@ import {
CompletedCycles,
} from "components/cycles";
// ui
import { Loader } from "components/ui";
import { EmptyState, Loader } from "components/ui";
// icons
import { ListBulletIcon, Squares2X2Icon } from "@heroicons/react/24/outline";
import emptyCycle from "public/empty-state/empty-cycle.svg";
// types
import {
SelectCycleType,
@ -25,8 +26,6 @@ import {
} from "types";
type Props = {
cycleView: string;
setCycleView: React.Dispatch<React.SetStateAction<string>>;
setSelectedCycle: React.Dispatch<React.SetStateAction<SelectCycleType>>;
setCreateUpdateCycleModal: React.Dispatch<React.SetStateAction<boolean>>;
cyclesCompleteList: ICycle[] | undefined;
@ -35,8 +34,6 @@ type Props = {
};
export const CyclesView: React.FC<Props> = ({
cycleView,
setCycleView,
setSelectedCycle,
setCreateUpdateCycleModal,
cyclesCompleteList,
@ -44,6 +41,7 @@ export const CyclesView: React.FC<Props> = ({
draftCycles,
}) => {
const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage("cycleTab", "All");
const { storedValue: cycleView, setValue: setCycleView } = useLocalStorage("cycleView", "list");
const currentTabValue = (tab: string | null) => {
switch (tab) {
@ -153,8 +151,15 @@ export const CyclesView: React.FC<Props> = ({
)}
</Tab.Panel>
<Tab.Panel as="div" className="mt-7 space-y-5">
{currentAndUpcomingCycles?.current_cycle?.[0] && (
{currentAndUpcomingCycles?.current_cycle?.[0] ? (
<ActiveCycleDetails cycle={currentAndUpcomingCycles?.current_cycle?.[0]} />
) : (
<EmptyState
type="cycle"
title="Create New Cycle"
description="Sprint more effectively with Cycles by confining your project to a fixed amount of time. Create new cycle now."
imgURL={emptyCycle}
/>
)}
</Tab.Panel>
<Tab.Panel as="div" className="mt-7 space-y-5">
@ -177,7 +182,7 @@ export const CyclesView: React.FC<Props> = ({
</Tab.Panel>
<Tab.Panel as="div" className="mt-7 space-y-5">
<CompletedCycles
cycleView={cycleView}
cycleView={cycleView ?? "list"}
setCreateUpdateCycleModal={setCreateUpdateCycleModal}
setSelectedCycle={setSelectedCycle}
/>

View File

@ -160,14 +160,10 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
title: "Error!",
message: "Unable to create cycle in past date. Please enter a valid date.",
});
handleClose();
return;
}
const isDateValid = await dateChecker({
start_date: payload.start_date,
end_date: payload.end_date,
});
if (data?.start_date && data?.end_date) {
const isDateValidForExistingCycle = await dateChecker({
start_date: payload.start_date,
@ -185,10 +181,16 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
message:
"You have a cycle already on the given dates, if you want to create your draft cycle you can do that by removing dates",
});
handleClose();
return;
}
}
const isDateValid = await dateChecker({
start_date: payload.start_date,
end_date: payload.end_date,
});
if (isDateValid) {
if (data) {
await updateCycle(data.id, payload);
@ -202,6 +204,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
message:
"You have a cycle already on the given dates, if you want to create your draft cycle you can do that by removing dates",
});
handleClose();
}
} else {
if (data) {

View File

@ -8,7 +8,6 @@ import useSWR, { mutate } from "swr";
// react-hook-form
import { useForm } from "react-hook-form";
import { Disclosure, Popover, Transition } from "@headlessui/react";
import DatePicker from "react-datepicker";
// icons
import {
CalendarDaysIcon,
@ -21,7 +20,7 @@ import {
LinkIcon,
} from "@heroicons/react/24/outline";
// ui
import { CustomMenu, Loader, ProgressBar } from "components/ui";
import { CustomMenu, CustomRangeDatePicker, Loader, ProgressBar } from "components/ui";
// hooks
import useToast from "hooks/use-toast";
// services
@ -34,7 +33,11 @@ import { DeleteCycleModal } from "components/cycles";
import { ExclamationIcon } from "components/icons";
// helpers
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
import { isDateRangeValid, renderDateFormat, renderShortDate } from "helpers/date-time.helper";
import {
isDateGreaterThanToday,
renderDateFormat,
renderShortDate,
} from "helpers/date-time.helper";
// types
import { ICycle, IIssue } from "types";
// fetch-keys
@ -77,7 +80,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
: null
);
const { reset, watch } = useForm({
const { setValue, reset, watch } = useForm({
defaultValues,
});
@ -122,6 +125,166 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
});
}, [cycle, reset]);
const dateChecker = async (payload: any) => {
try {
const res = await cyclesService.cycleDateCheck(
workspaceSlug as string,
projectId as string,
payload
);
return res.status;
} catch (err) {
return false;
}
};
const handleStartDateChange = async (date: string) => {
setValue("start_date", date);
if (
watch("start_date") &&
watch("end_date") &&
watch("start_date") !== "" &&
watch("end_date") &&
watch("start_date") !== ""
) {
if (!isDateGreaterThanToday(`${watch("end_date")}`)) {
setToastAlert({
type: "error",
title: "Error!",
message: "Unable to create cycle in past date. Please enter a valid date.",
});
return;
}
if (cycle?.start_date && cycle?.end_date) {
const isDateValidForExistingCycle = await dateChecker({
start_date: `${watch("start_date")}`,
end_date: `${watch("end_date")}`,
cycle_id: cycle.id,
});
if (isDateValidForExistingCycle) {
await submitChanges({
start_date: renderDateFormat(`${watch("start_date")}`),
end_date: renderDateFormat(`${watch("end_date")}`),
});
setToastAlert({
type: "success",
title: "Success!",
message: "Cycle updated successfully.",
});
return;
} else {
setToastAlert({
type: "error",
title: "Error!",
message:
"You have a cycle already on the given dates, if you want to create your draft cycle you can do that by removing dates",
});
return;
}
}
const isDateValid = await dateChecker({
start_date: `${watch("start_date")}`,
end_date: `${watch("end_date")}`,
});
if (isDateValid) {
submitChanges({
start_date: renderDateFormat(`${watch("start_date")}`),
end_date: renderDateFormat(`${watch("end_date")}`),
});
setToastAlert({
type: "success",
title: "Success!",
message: "Cycle updated successfully.",
});
} else {
setToastAlert({
type: "error",
title: "Error!",
message:
"You have a cycle already on the given dates, if you want to create your draft cycle you can do that by removing dates",
});
}
}
};
const handleEndDateChange = async (date: string) => {
setValue("end_date", date);
if (
watch("start_date") &&
watch("end_date") &&
watch("start_date") !== "" &&
watch("end_date") &&
watch("start_date") !== ""
) {
if (!isDateGreaterThanToday(`${watch("end_date")}`)) {
setToastAlert({
type: "error",
title: "Error!",
message: "Unable to create cycle in past date. Please enter a valid date.",
});
return;
}
if (cycle?.start_date && cycle?.end_date) {
const isDateValidForExistingCycle = await dateChecker({
start_date: `${watch("start_date")}`,
end_date: `${watch("end_date")}`,
cycle_id: cycle.id,
});
if (isDateValidForExistingCycle) {
await submitChanges({
start_date: renderDateFormat(`${watch("start_date")}`),
end_date: renderDateFormat(`${watch("end_date")}`),
});
setToastAlert({
type: "success",
title: "Success!",
message: "Cycle updated successfully.",
});
return;
} else {
setToastAlert({
type: "error",
title: "Error!",
message:
"You have a cycle already on the given dates, if you want to create your draft cycle you can do that by removing dates",
});
return;
}
}
const isDateValid = await dateChecker({
start_date: `${watch("start_date")}`,
end_date: `${watch("end_date")}`,
});
if (isDateValid) {
submitChanges({
start_date: renderDateFormat(`${watch("start_date")}`),
end_date: renderDateFormat(`${watch("end_date")}`),
});
setToastAlert({
type: "success",
title: "Success!",
message: "Cycle updated successfully.",
});
} else {
setToastAlert({
type: "error",
title: "Error!",
message:
"You have a cycle already on the given dates, if you want to create your draft cycle you can do that by removing dates",
});
}
}
};
const isStartValid = new Date(`${cycle?.start_date}`) <= new Date();
const isEndValid = new Date(`${cycle?.end_date}`) >= new Date(`${cycle?.start_date}`);
@ -135,7 +298,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div
className={`fixed top-[66px] ${
isOpen ? "right-0" : "-right-[24rem]"
} h-full w-[24rem] overflow-y-auto border-l border-brand-base bg-brand-sidebar py-5 duration-300`}
} h-full w-[24rem] overflow-y-auto border-l border-brand-base bg-brand-sidebar pt-5 pb-10 duration-300`}
>
{cycle ? (
<>
@ -158,7 +321,12 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
>
<CalendarDaysIcon className="h-3 w-3" />
<span>
{renderShortDate(new Date(`${cycle?.start_date}`), "Start date")}
{renderShortDate(
new Date(
`${watch("start_date") ? watch("start_date") : cycle?.start_date}`
),
"Start date"
)}
</span>
</Popover.Button>
@ -172,36 +340,17 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
<DatePicker
selected={
watch("start_date")
? new Date(`${watch("start_date")}`)
: new Date()
}
onChange={(date) => {
if (date && watch("end_date")) {
if (
isDateRangeValid(renderDateFormat(date), `${watch("end_date")}`)
) {
submitChanges({
start_date: renderDateFormat(date),
});
} else {
setToastAlert({
type: "error",
title: "Error!",
message:
"The date you have entered is invalid. Please check and enter a valid date.",
});
}
<CustomRangeDatePicker
value={watch("start_date") ? watch("start_date") : cycle?.start_date}
onChange={(val) => {
if (val) {
handleStartDateChange(val);
}
}}
selectsStart
startDate={new Date(`${watch("start_date")}`)}
endDate={new Date(`${watch("end_date")}`)}
startDate={watch("start_date") ? `${watch("start_date")}` : null}
endDate={watch("end_date") ? `${watch("end_date")}` : null}
maxDate={new Date(`${watch("end_date")}`)}
shouldCloseOnSelect
inline
selectsStart
/>
</Popover.Panel>
</Transition>
@ -222,7 +371,14 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
>
<CalendarDaysIcon className="h-3 w-3" />
<span>{renderShortDate(new Date(`${cycle?.end_date}`), "End date")}</span>
<span>
{renderShortDate(
new Date(
`${watch("end_date") ? watch("end_date") : cycle?.end_date}`
),
"End date"
)}
</span>
</Popover.Button>
<Transition
@ -235,37 +391,17 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
<DatePicker
selected={
watch("end_date") ? new Date(`${watch("end_date")}`) : new Date()
}
onChange={(date) => {
if (watch("start_date") && date) {
if (
isDateRangeValid(
`${watch("start_date")}`,
renderDateFormat(date)
)
) {
submitChanges({
end_date: renderDateFormat(date),
});
} else {
setToastAlert({
type: "error",
title: "Error!",
message:
"The date you have entered is invalid. Please check and enter a valid date.",
});
}
<CustomRangeDatePicker
value={watch("end_date") ? watch("end_date") : cycle?.end_date}
onChange={(val) => {
if (val) {
handleEndDateChange(val);
}
}}
selectsEnd
startDate={new Date(`${watch("start_date")}`)}
endDate={new Date(`${watch("end_date")}`)}
startDate={watch("start_date") ? `${watch("start_date")}` : null}
endDate={watch("end_date") ? `${watch("end_date")}` : null}
minDate={new Date(`${watch("start_date")}`)}
shouldCloseOnSelect
inline
selectsEnd
/>
</Popover.Panel>
</Transition>

View File

@ -51,6 +51,7 @@ import {
import {
CYCLE_COMPLETE_LIST,
CYCLE_CURRENT_AND_UPCOMING_LIST,
CYCLE_DETAILS,
CYCLE_DRAFT_LIST,
} from "constants/fetch-keys";
@ -150,6 +151,15 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
);
break;
}
mutate(
CYCLE_DETAILS(projectId as string),
(prevData: any) =>
(prevData ?? []).map((c: any) => ({
...c,
is_favorite: c.id === cycle.id ? true : c.is_favorite,
})),
false
);
cyclesService
.addCycleToFavorites(workspaceSlug as string, projectId as string, {
@ -210,6 +220,15 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
);
break;
}
mutate(
CYCLE_DETAILS(projectId as string),
(prevData: any) =>
(prevData ?? []).map((c: any) => ({
...c,
is_favorite: c.id === cycle.id ? false : c.is_favorite,
})),
false
);
cyclesService
.removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id)
@ -263,20 +282,22 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
<div className="flex h-full flex-col gap-4 rounded-b-[10px] p-4">
<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"
: ""
}`}
/>
<span className="h-5 w-5">
<ContrastIcon
className="h-5 w-5"
color={`${
cycleStatus === "current"
? "#09A953"
: cycleStatus === "upcoming"
? "#F7AE59"
: cycleStatus === "completed"
? "#3F76FF"
: cycleStatus === "draft"
? "#858E96"
: ""
}`}
/>
</span>
<Tooltip tooltipContent={cycle.name} className="break-all" position="top-left">
<h3 className="break-all text-lg font-semibold">
{truncateText(cycle.name, 15)}
@ -299,17 +320,17 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
}`}
>
{cycleStatus === "current" ? (
<span className="flex gap-1">
<span className="flex gap-1 whitespace-nowrap">
<PersonRunningIcon className="h-4 w-4" />
{findHowManyDaysLeft(cycle.end_date ?? new Date())} Days Left
</span>
) : cycleStatus === "upcoming" ? (
<span className="flex gap-1">
<span className="flex gap-1 whitespace-nowrap">
<AlarmClockIcon className="h-4 w-4" />
{findHowManyDaysLeft(cycle.start_date ?? new Date())} Days Left
</span>
) : cycleStatus === "completed" ? (
<span className="flex gap-1">
<span className="flex gap-1 whitespace-nowrap">
{cycle.total_issues - cycle.completed_issues > 0 && (
<Tooltip
tooltipContent={`${

View File

@ -42,6 +42,7 @@ import {
import {
CYCLE_COMPLETE_LIST,
CYCLE_CURRENT_AND_UPCOMING_LIST,
CYCLE_DETAILS,
CYCLE_DRAFT_LIST,
} from "constants/fetch-keys";
import { type } from "os";
@ -184,6 +185,15 @@ export const SingleCycleList: React.FC<TSingleStatProps> = ({
);
break;
}
mutate(
CYCLE_DETAILS(projectId as string),
(prevData: any) =>
(prevData ?? []).map((c: any) => ({
...c,
is_favorite: c.id === cycle.id ? true : c.is_favorite,
})),
false
);
cyclesService
.addCycleToFavorites(workspaceSlug as string, projectId as string, {
@ -244,6 +254,15 @@ export const SingleCycleList: React.FC<TSingleStatProps> = ({
);
break;
}
mutate(
CYCLE_DETAILS(projectId as string),
(prevData: any) =>
(prevData ?? []).map((c: any) => ({
...c,
is_favorite: c.id === cycle.id ? false : c.is_favorite,
})),
false
);
cyclesService
.removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id)

View File

@ -185,7 +185,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({ issues, module, isOpen,
<div
className={`fixed top-[66px] ${
isOpen ? "right-0" : "-right-[24rem]"
} h-full w-[24rem] overflow-y-auto border-l border-brand-base bg-brand-sidebar py-5 duration-300`}
} h-full w-[24rem] overflow-y-auto border-l border-brand-base bg-brand-sidebar pt-5 pb-10 duration-300`}
>
{module ? (
<>

View File

@ -23,3 +23,5 @@ export * from "./tooltip";
export * from "./toggle-switch";
export * from "./markdown-to-component";
export * from "./product-updates-modal";
export * from "./integration-and-import-export-banner";
export * from "./range-datepicker";

View File

@ -0,0 +1,19 @@
import { ExclamationIcon } from "components/icons";
type Props = {
bannerName: string;
};
export const IntegrationAndImportExportBanner: React.FC<Props> = ({ bannerName }) => (
<div className="flex flex-col items-start gap-3">
<h3 className="text-2xl font-semibold">{bannerName}</h3>
<div className="flex items-center gap-3 rounded-[10px] border border-brand-accent/75 bg-brand-accent/5 p-4 text-sm text-brand-base">
<ExclamationIcon height={24} width={24} className="fill-current text-brand-base" />
<p className="leading-5">
Integrations and importers are only available on the cloud version. We plan to open-source
our SDKs in the near future so that the community can request or contribute integrations as
needed.
</p>
</div>
</div>
);

View File

@ -0,0 +1,65 @@
// react-datepicker
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
// helpers
import { renderDateFormat } from "helpers/date-time.helper";
type Props = {
renderAs?: "input" | "button";
value: Date | string | null | undefined;
onChange: (val: string | null) => void;
error?: boolean;
className?: string;
isClearable?: boolean;
disabled?: boolean;
startDate: string | null;
endDate: string | null;
selectsStart?: boolean;
selectsEnd?: boolean;
minDate?: Date | null | undefined;
maxDate?: Date | null | undefined;
};
export const CustomRangeDatePicker: React.FC<Props> = ({
renderAs = "button",
value,
onChange,
error = false,
className = "",
disabled = false,
startDate,
endDate,
selectsStart = false,
selectsEnd = false,
minDate = null,
maxDate = null,
}) => (
<DatePicker
selected={value ? new Date(value) : null}
onChange={(val) => {
if (!val) onChange(null);
else onChange(renderDateFormat(val));
}}
className={`${
renderAs === "input"
? "block px-3 py-2 text-sm focus:outline-none"
: renderAs === "button"
? `px-3 py-1 text-xs shadow-sm ${
disabled ? "" : "hover:bg-brand-surface-2"
} duration-300 focus:border-brand-accent focus:outline-none focus:ring-1 focus:ring-brand-accent`
: ""
} ${error ? "border-red-500 bg-red-100" : ""} ${
disabled ? "cursor-not-allowed" : "cursor-pointer"
} w-full rounded-md border border-brand-base bg-transparent caret-transparent ${className}`}
dateFormat="dd-MM-yyyy"
disabled={disabled}
selectsStart={selectsStart}
selectsEnd={selectsEnd}
startDate={startDate ? new Date(startDate) : new Date()}
endDate={endDate ? new Date(endDate) : new Date()}
minDate={minDate}
maxDate={maxDate}
shouldCloseOnSelect
inline
/>
);

View File

@ -46,11 +46,6 @@ export const Tooltip: React.FC<Props> = ({
theme === "light" ? "text-brand-muted-1 bg-brand-surface-2" : "bg-black text-white"
}`}
>
<div
className={`absolute inset-0 left-1/2 -top-1 h-3 w-3 rotate-45 bg-brand-surface-2 ${
theme === "light" ? "text-brand-muted-1 bg-brand-surface-2" : "bg-black text-white"
}`}
/>
{tooltipHeading && <h5 className="font-medium">{tooltipHeading}</h5>}
{tooltipContent}
</div>

View File

@ -137,7 +137,7 @@ export const CreateWorkspaceForm: React.FC<Props> = ({
autoComplete="off"
name="slug"
register={register}
className="block w-full rounded-md bg-transparent py-2 px-0 text-sm"
className="block w-full rounded-md bg-transparent py-2 !px-0 text-sm"
validations={{
required: "Workspace URL is required",
}}

View File

@ -19,7 +19,7 @@ const Sidebar: React.FC<SidebarProps> = ({ toggleSidebar, setToggleSidebar }) =>
return (
<div
className={`z-30 h-full flex-shrink-0 border-r border-brand-base ${
className={`z-20 h-full flex-shrink-0 border-r border-brand-base ${
sidebarCollapse ? "" : "w-auto md:w-[17rem]"
} fixed inset-y-0 top-0 ${
toggleSidebar ? "left-0" : "-left-full md:left-0"

View File

@ -30,7 +30,6 @@ import {
const ProjectCycles: NextPage = () => {
const [selectedCycle, setSelectedCycle] = useState<SelectCycleType>();
const [createUpdateCycleModal, setCreateUpdateCycleModal] = useState(false);
const [cycleView, setCycleView] = useState<string>("list");
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -121,8 +120,6 @@ const ProjectCycles: NextPage = () => {
<div className="flex flex-col gap-5">
<h3 className="text-2xl font-semibold text-brand-base">Cycles</h3>
<CyclesView
cycleView={cycleView}
setCycleView={setCycleView}
setSelectedCycle={setSelectedCycle}
setCreateUpdateCycleModal={setCreateUpdateCycleModal}
cyclesCompleteList={cyclesCompleteList}

View File

@ -12,11 +12,15 @@ import projectService from "services/project.service";
// components
import { SettingsHeader, SingleIntegration } from "components/project";
// ui
import { EmptySpace, EmptySpaceItem, Loader } from "components/ui";
import {
EmptySpace,
EmptySpaceItem,
IntegrationAndImportExportBanner,
Loader,
} from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// icons
import { PlusIcon, PuzzlePieceIcon } from "@heroicons/react/24/outline";
import { ExclamationIcon } from "components/icons";
// types
import { IProject } from "types";
import type { NextPage } from "next";
@ -59,21 +63,7 @@ const ProjectIntegrations: NextPage = () => {
{workspaceIntegrations ? (
workspaceIntegrations.length > 0 ? (
<section className="space-y-8">
<div className="flex flex-col items-start gap-3">
<h3 className="text-2xl font-semibold">Integrations</h3>
<div className="flex items-center gap-3 rounded-[10px] border border-brand-accent/75 bg-brand-accent/5 p-4 text-sm text-brand-base">
<ExclamationIcon
height={24}
width={24}
className="fill-current text-brand-base"
/>
<p className="leading-5">
Integrations and importers are only available on the cloud version. We plan to
open-source our SDKs in the near future so that the community can request or
contribute integrations as needed.
</p>
</div>
</div>
<IntegrationAndImportExportBanner bannerName="Integrations" />
<div className="space-y-5">
{workspaceIntegrations.map((integration) => (
<SingleIntegration

View File

@ -9,6 +9,7 @@ import IntegrationGuide from "components/integration/guide";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// types
import type { NextPage } from "next";
import { IntegrationAndImportExportBanner } from "components/ui";
const ImportExport: NextPage = () => {
const router = useRouter();
@ -23,8 +24,9 @@ const ImportExport: NextPage = () => {
</Breadcrumbs>
}
>
<div className="p-8">
<div className="p-8 space-y-4">
<SettingsHeader />
<IntegrationAndImportExportBanner bannerName="Import/ Export" />
<IntegrationGuide />
</div>
</WorkspaceAuthorizationLayout>

View File

@ -13,10 +13,8 @@ import { SettingsHeader } from "components/workspace";
// components
import { SingleIntegrationCard } from "components/integration";
// ui
import { Loader } from "components/ui";
import { IntegrationAndImportExportBanner, Loader } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// icons
import { ExclamationIcon } from "components/icons";
// types
import type { NextPage } from "next";
// fetch-keys
@ -50,17 +48,7 @@ const WorkspaceIntegrations: NextPage = () => {
<div className="p-8">
<SettingsHeader />
<section className="space-y-5">
<div className="flex flex-col items-start gap-3">
<h3 className="text-2xl font-semibold">Integrations</h3>
<div className="flex items-center gap-3 rounded-[10px] border border-brand-accent/75 bg-brand-accent/5 p-4 text-sm text-brand-base">
<ExclamationIcon height={24} width={24} className="fill-current text-brand-base" />
<p className="leading-5">
Integrations and importers are only available on the cloud version. We plan to
open-source our SDKs in the near future so that the community can request or
contribute integrations as needed.
</p>
</div>
</div>
<IntegrationAndImportExportBanner bannerName="Integrations" />
<div className="space-y-5">
{appIntegrations ? (
appIntegrations.map((integration) => (

View File

@ -117,6 +117,15 @@
color: white !important;
}
.react-datepicker__day--in-range {
.react-datepicker__day--in-range-start,
.react-datepicker__day--in-range-end {
background-color: rgba(var(--color-bg-accent)) !important;
color: white !important;
}
.react-datepicker__day--in-range:not(.react-datepicker__day--in-range-start):not(
.react-datepicker__day--in-range-end
) {
background-color: rgba(var(--color-accent)) !important;
color: white !important;
}