mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: removed minor bugs (#273)
This commit is contained in:
parent
8fb34fe1e3
commit
214e860e67
@ -34,8 +34,8 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
|
|||||||
reValidateMode: "onChange",
|
reValidateMode: "onChange",
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = ({ email }: EmailCodeFormValues) => {
|
const onSubmit = async ({ email }: EmailCodeFormValues) => {
|
||||||
authenticationService
|
await authenticationService
|
||||||
.emailCode({ email })
|
.emailCode({ email })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setValue("key", res.key);
|
setValue("key", res.key);
|
||||||
@ -46,8 +46,8 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSignin = (formData: EmailCodeFormValues) => {
|
const handleSignin = async (formData: EmailCodeFormValues) => {
|
||||||
authenticationService
|
await authenticationService
|
||||||
.magicSignIn(formData)
|
.magicSignIn(formData)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
onSuccess(response);
|
onSuccess(response);
|
||||||
@ -68,10 +68,7 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<form
|
<form className="mt-5 space-y-5">
|
||||||
className="mt-5 space-y-5"
|
|
||||||
onSubmit={codeSent ? handleSubmit(handleSignin) : handleSubmit(onSubmit)}
|
|
||||||
>
|
|
||||||
{codeSent && (
|
{codeSent && (
|
||||||
<div className="rounded-md bg-green-50 p-4">
|
<div className="rounded-md bg-green-50 p-4">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
@ -117,16 +114,37 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
|
|||||||
error={errors.token}
|
error={errors.token}
|
||||||
placeholder="Enter code"
|
placeholder="Enter code"
|
||||||
/>
|
/>
|
||||||
|
{/* <span
|
||||||
|
className="text-xs outline-none hover:text-theme"
|
||||||
|
onClick={() => {
|
||||||
|
console.log("Triggered");
|
||||||
|
handleSubmit(onSubmit);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Resend code
|
||||||
|
</span> */}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<Button
|
{codeSent ? (
|
||||||
disabled={isSubmitting || (!isValid && isDirty)}
|
<Button
|
||||||
className="w-full text-center"
|
type="submit"
|
||||||
type="submit"
|
className="w-full text-center"
|
||||||
>
|
onClick={handleSubmit(handleSignin)}
|
||||||
{isSubmitting ? "Signing in..." : codeSent ? "Sign In" : "Continue with Email ID"}
|
disabled={isSubmitting || (!isValid && isDirty)}
|
||||||
</Button>
|
>
|
||||||
|
{isSubmitting ? "Signing in..." : "Sign in"}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="w-full text-center"
|
||||||
|
onClick={handleSubmit(onSubmit)}
|
||||||
|
disabled={isSubmitting || (!isValid && isDirty)}
|
||||||
|
>
|
||||||
|
{isSubmitting ? "Sending code..." : "Send code"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
|
@ -69,6 +69,8 @@ export const SingleBoard: React.FC<Props> = ({
|
|||||||
? (bgColor = "#22c55e")
|
? (bgColor = "#22c55e")
|
||||||
: (bgColor = "#ff0000");
|
: (bgColor = "#ff0000");
|
||||||
|
|
||||||
|
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`h-full flex-shrink-0 rounded ${!isCollapsed ? "" : "w-80 border bg-gray-50"}`}>
|
<div className={`h-full flex-shrink-0 rounded ${!isCollapsed ? "" : "w-80 border bg-gray-50"}`}>
|
||||||
<div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3 overflow-y-auto"}`}>
|
<div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3 overflow-y-auto"}`}>
|
||||||
@ -95,7 +97,7 @@ export const SingleBoard: React.FC<Props> = ({
|
|||||||
key={issue.id}
|
key={issue.id}
|
||||||
draggableId={issue.id}
|
draggableId={issue.id}
|
||||||
index={index}
|
index={index}
|
||||||
isDragDisabled={selectedGroup === "created_by"}
|
isDragDisabled={isNotAllowed || selectedGroup === "created_by"}
|
||||||
>
|
>
|
||||||
{(provided, snapshot) => (
|
{(provided, snapshot) => (
|
||||||
<SingleBoardIssue
|
<SingleBoardIssue
|
||||||
|
@ -28,11 +28,13 @@ export const ViewStateSelect: React.FC<Props> = ({
|
|||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const { data: stateGroups } = useSWR(
|
const { data: stateGroups } = useSWR(
|
||||||
workspaceSlug && projectId ? STATE_LIST(issue.project) : null,
|
workspaceSlug && issue ? STATE_LIST(issue.project) : null,
|
||||||
workspaceSlug ? () => stateService.getStates(workspaceSlug as string, issue.project) : null
|
workspaceSlug && issue
|
||||||
|
? () => stateService.getStates(workspaceSlug as string, issue.project)
|
||||||
|
: null
|
||||||
);
|
);
|
||||||
const states = getStatesList(stateGroups ?? {});
|
const states = getStatesList(stateGroups ?? {});
|
||||||
|
|
||||||
|
@ -12,13 +12,14 @@ import {
|
|||||||
ClipboardDocumentListIcon,
|
ClipboardDocumentListIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import type { IProject } from "types";
|
|
||||||
// ui
|
// ui
|
||||||
import { Button } from "components/ui";
|
import { Button } from "components/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import useProjectMembers from "hooks/use-project-members";
|
import useProjectMembers from "hooks/use-project-members";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
||||||
|
// types
|
||||||
|
import type { IProject } from "types";
|
||||||
|
|
||||||
export type ProjectCardProps = {
|
export type ProjectCardProps = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -85,6 +86,14 @@ export const ProjectCard: React.FC<ProjectCardProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-3 flex h-full items-end justify-between">
|
<div className="mt-3 flex h-full items-end justify-between">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
theme="secondary"
|
||||||
|
className="flex items-center gap-1"
|
||||||
|
onClick={() => router.push(`/${workspaceSlug}/projects/${project.id}/issues`)}
|
||||||
|
>
|
||||||
|
<ClipboardDocumentListIcon className="h-3 w-3" />
|
||||||
|
Open Project
|
||||||
|
</Button>
|
||||||
{!isMember ? (
|
{!isMember ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -97,19 +106,11 @@ export const ProjectCard: React.FC<ProjectCardProps> = (props) => {
|
|||||||
<span>Select to Join</span>
|
<span>Select to Join</span>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<Button theme="secondary" className="flex items-center gap-1" disabled>
|
<div className="flex items-center gap-1 text-xs">
|
||||||
<CheckIcon className="h-3 w-3" />
|
<CheckIcon className="h-3 w-3" />
|
||||||
Member
|
Member
|
||||||
</Button>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Button
|
|
||||||
theme="secondary"
|
|
||||||
className="flex items-center gap-1"
|
|
||||||
onClick={() => router.push(`/${workspaceSlug}/projects/${project.id}/issues`)}
|
|
||||||
>
|
|
||||||
<ClipboardDocumentListIcon className="h-3 w-3" />
|
|
||||||
Open Project
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-1 flex items-center gap-1 text-xs">
|
<div className="mb-1 flex items-center gap-1 text-xs">
|
||||||
<CalendarDaysIcon className="h-4 w-4" />
|
<CalendarDaysIcon className="h-4 w-4" />
|
||||||
|
@ -36,10 +36,6 @@ const ConfirmCycleDeletion: React.FC<TConfirmCycleDeletionProps> = ({
|
|||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
data && setIsOpen(true);
|
|
||||||
}, [data, setIsOpen]);
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
ChartPieIcon,
|
ChartPieIcon,
|
||||||
LinkIcon,
|
LinkIcon,
|
||||||
Squares2X2Icon,
|
Squares2X2Icon,
|
||||||
|
TrashIcon,
|
||||||
UserIcon,
|
UserIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
// ui
|
// ui
|
||||||
@ -28,6 +29,8 @@ import useToast from "hooks/use-toast";
|
|||||||
import cyclesService from "services/cycles.service";
|
import cyclesService from "services/cycles.service";
|
||||||
// components
|
// components
|
||||||
import { SidebarProgressStats } from "components/core";
|
import { SidebarProgressStats } from "components/core";
|
||||||
|
import ProgressChart from "components/core/sidebar/progress-chart";
|
||||||
|
import ConfirmCycleDeletion from "components/project/cycles/confirm-cycle-deletion";
|
||||||
// helpers
|
// helpers
|
||||||
import { copyTextToClipboard } from "helpers/string.helper";
|
import { copyTextToClipboard } from "helpers/string.helper";
|
||||||
import { groupBy } from "helpers/array.helper";
|
import { groupBy } from "helpers/array.helper";
|
||||||
@ -36,7 +39,7 @@ import { renderDateFormat, renderShortNumericDateFormat } from "helpers/date-tim
|
|||||||
import { CycleIssueResponse, ICycle, IIssue } from "types";
|
import { CycleIssueResponse, ICycle, IIssue } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { CYCLE_DETAILS } from "constants/fetch-keys";
|
import { CYCLE_DETAILS } from "constants/fetch-keys";
|
||||||
import ProgressChart from "components/core/sidebar/progress-chart";
|
// constants
|
||||||
import { CYCLE_STATUS } from "constants/cycle";
|
import { CYCLE_STATUS } from "constants/cycle";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -47,6 +50,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CycleDetailSidebar: React.FC<Props> = ({ issues, cycle, isOpen, cycleIssues }) => {
|
const CycleDetailSidebar: React.FC<Props> = ({ issues, cycle, isOpen, cycleIssues }) => {
|
||||||
|
const [cycleDeleteModal, setCycleDeleteModal] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
|
|
||||||
@ -103,212 +108,227 @@ const CycleDetailSidebar: React.FC<Props> = ({ issues, cycle, isOpen, cycleIssue
|
|||||||
|
|
||||||
const isStartValid = new Date(`${cycle?.start_date}`) <= new Date();
|
const isStartValid = new Date(`${cycle?.start_date}`) <= new Date();
|
||||||
const isEndValid = new Date(`${cycle?.end_date}`) >= new Date(`${cycle?.start_date}`);
|
const isEndValid = new Date(`${cycle?.end_date}`) >= new Date(`${cycle?.start_date}`);
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`fixed top-0 ${
|
|
||||||
isOpen ? "right-0" : "-right-[24rem]"
|
|
||||||
} z-20 h-full w-[24rem] overflow-y-auto border-l bg-gray-50 p-5 duration-300`}
|
|
||||||
>
|
|
||||||
{cycle ? (
|
|
||||||
<>
|
|
||||||
<div className="flex gap-1 text-sm my-2">
|
|
||||||
<div className="flex items-center ">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="status"
|
|
||||||
render={({ field: { value } }) => (
|
|
||||||
<CustomSelect
|
|
||||||
label={
|
|
||||||
<span
|
|
||||||
className={`flex items-center gap-1 text-left capitalize p-1 text-xs h-full w-full text-gray-900`}
|
|
||||||
>
|
|
||||||
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
|
|
||||||
{watch("status")}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
value={value}
|
|
||||||
onChange={(value: any) => {
|
|
||||||
submitChanges({ status: value });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{CYCLE_STATUS.map((option) => (
|
|
||||||
<CustomSelect.Option key={option.value} value={option.value}>
|
|
||||||
<span className="text-xs">{option.label}</span>
|
|
||||||
</CustomSelect.Option>
|
|
||||||
))}
|
|
||||||
</CustomSelect>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Popover className="flex justify-center items-center relative rounded-lg">
|
|
||||||
{({ open }) => (
|
|
||||||
<>
|
|
||||||
<Popover.Button
|
|
||||||
className={`group flex items-center gap-2 rounded-md border bg-transparent h-full w-full p-2 px-4 text-xs font-medium text-gray-900 hover:bg-gray-100 hover:text-gray-900 focus:outline-none ${
|
|
||||||
open ? "bg-gray-100" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<CalendarDaysIcon className="h-4 w-4 flex-shrink-0" />
|
|
||||||
<span>
|
|
||||||
{renderShortNumericDateFormat(`${cycle.start_date}`)
|
|
||||||
? renderShortNumericDateFormat(`${cycle.start_date}`)
|
|
||||||
: "N/A"}{" "}
|
|
||||||
-{" "}
|
|
||||||
{renderShortNumericDateFormat(`${cycle.end_date}`)
|
|
||||||
? renderShortNumericDateFormat(`${cycle.end_date}`)
|
|
||||||
: "N/A"}
|
|
||||||
</span>
|
|
||||||
</Popover.Button>
|
|
||||||
|
|
||||||
<Transition
|
return (
|
||||||
as={React.Fragment}
|
<>
|
||||||
enter="transition ease-out duration-200"
|
<ConfirmCycleDeletion
|
||||||
enterFrom="opacity-0 translate-y-1"
|
isOpen={cycleDeleteModal}
|
||||||
enterTo="opacity-100 translate-y-0"
|
setIsOpen={setCycleDeleteModal}
|
||||||
leave="transition ease-in duration-150"
|
data={cycle}
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
/>
|
||||||
leaveTo="opacity-0 translate-y-1"
|
<div
|
||||||
>
|
className={`fixed top-0 ${
|
||||||
<Popover.Panel className="absolute top-10 left-0 z-20 transform overflow-hidden">
|
isOpen ? "right-0" : "-right-[24rem]"
|
||||||
<DatePicker
|
} z-20 h-full w-[24rem] overflow-y-auto border-l bg-gray-50 p-5 duration-300`}
|
||||||
selected={startDateRange}
|
>
|
||||||
onChange={(dates) => {
|
{cycle ? (
|
||||||
const [start, end] = dates;
|
<>
|
||||||
submitChanges({
|
<div className="flex gap-1 text-sm my-2">
|
||||||
start_date: renderDateFormat(start),
|
<div className="flex items-center ">
|
||||||
end_date: renderDateFormat(end),
|
<Controller
|
||||||
});
|
control={control}
|
||||||
if (setStartDateRange) {
|
name="status"
|
||||||
setStartDateRange(start);
|
render={({ field: { value } }) => (
|
||||||
}
|
<CustomSelect
|
||||||
if (setEndDateRange) {
|
label={
|
||||||
setEndDateRange(end);
|
<span
|
||||||
}
|
className={`flex items-center gap-1 text-left capitalize p-1 text-xs h-full w-full text-gray-900`}
|
||||||
}}
|
>
|
||||||
startDate={startDateRange}
|
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
|
||||||
endDate={endDateRange}
|
{watch("status")}
|
||||||
selectsRange
|
</span>
|
||||||
inline
|
}
|
||||||
/>
|
value={value}
|
||||||
</Popover.Panel>
|
onChange={(value: any) => {
|
||||||
</Transition>
|
submitChanges({ status: value });
|
||||||
</>
|
}}
|
||||||
)}
|
>
|
||||||
</Popover>
|
{CYCLE_STATUS.map((option) => (
|
||||||
</div>
|
<CustomSelect.Option key={option.value} value={option.value}>
|
||||||
<div className="flex items-center justify-between pb-3">
|
<span className="text-xs">{option.label}</span>
|
||||||
<h4 className="text-sm font-medium">{cycle.name}</h4>
|
</CustomSelect.Option>
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
))}
|
||||||
<button
|
</CustomSelect>
|
||||||
type="button"
|
)}
|
||||||
className="rounded-md border p-2 shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
|
||||||
onClick={() =>
|
|
||||||
copyTextToClipboard(
|
|
||||||
`https://app.plane.so/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
setToastAlert({
|
|
||||||
type: "success",
|
|
||||||
title: "Cycle link copied to clipboard",
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setToastAlert({
|
|
||||||
type: "error",
|
|
||||||
title: "Some error occurred",
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<LinkIcon className="h-3.5 w-3.5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="divide-y-2 divide-gray-100 text-xs">
|
|
||||||
<div className="py-1">
|
|
||||||
<div className="flex flex-wrap items-center py-2">
|
|
||||||
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
|
||||||
<UserIcon className="h-4 w-4 flex-shrink-0" />
|
|
||||||
<p>Owned by</p>
|
|
||||||
</div>
|
|
||||||
<div className="sm:basis-1/2 flex items-center gap-1">
|
|
||||||
{cycle.owned_by &&
|
|
||||||
(cycle.owned_by.avatar && cycle.owned_by.avatar !== "" ? (
|
|
||||||
<div className="h-5 w-5 rounded-full border-2 border-transparent">
|
|
||||||
<Image
|
|
||||||
src={cycle.owned_by.avatar}
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
className="rounded-full"
|
|
||||||
alt={cycle.owned_by?.first_name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="grid h-5 w-5 place-items-center rounded-full border-2 border-white bg-gray-700 capitalize text-white">
|
|
||||||
{cycle.owned_by?.first_name && cycle.owned_by.first_name !== ""
|
|
||||||
? cycle.owned_by.first_name.charAt(0)
|
|
||||||
: cycle.owned_by?.email.charAt(0)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{cycle.owned_by.first_name !== ""
|
|
||||||
? cycle.owned_by.first_name
|
|
||||||
: cycle.owned_by.email}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-wrap items-center py-2">
|
|
||||||
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
|
||||||
<ChartPieIcon className="h-4 w-4 flex-shrink-0" />
|
|
||||||
<p>Progress</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 sm:basis-1/2">
|
|
||||||
<div className="grid flex-shrink-0 place-items-center">
|
|
||||||
<span className="h-4 w-4">
|
|
||||||
<CircularProgressbar
|
|
||||||
value={groupedIssues.completed.length}
|
|
||||||
maxValue={cycleIssues?.length}
|
|
||||||
strokeWidth={10}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{groupedIssues.completed.length}/{cycleIssues?.length}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="py-1" />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center justify-center w-full gap-2 ">
|
|
||||||
{isStartValid && isEndValid ? (
|
|
||||||
<div className="relative h-[200px] w-full ">
|
|
||||||
<ProgressChart
|
|
||||||
issues={issues}
|
|
||||||
start={cycle?.start_date ?? ""}
|
|
||||||
end={cycle?.end_date ?? ""}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
<Popover className="flex justify-center items-center relative rounded-lg">
|
||||||
""
|
{({ open }) => (
|
||||||
)}
|
<>
|
||||||
{issues.length > 0 ? (
|
<Popover.Button
|
||||||
<SidebarProgressStats issues={issues} groupedIssues={groupedIssues} />
|
className={`group flex items-center gap-2 rounded-md border bg-transparent h-full w-full p-2 px-4 text-xs font-medium text-gray-900 hover:bg-gray-100 hover:text-gray-900 focus:outline-none ${
|
||||||
) : (
|
open ? "bg-gray-100" : ""
|
||||||
""
|
}`}
|
||||||
)}
|
>
|
||||||
</div>
|
<CalendarDaysIcon className="h-4 w-4 flex-shrink-0" />
|
||||||
</>
|
<span>
|
||||||
) : (
|
{renderShortNumericDateFormat(`${cycle.start_date}`)
|
||||||
<Loader>
|
? renderShortNumericDateFormat(`${cycle.start_date}`)
|
||||||
<div className="space-y-2">
|
: "N/A"}{" "}
|
||||||
<Loader.Item height="15px" width="50%" />
|
-{" "}
|
||||||
<Loader.Item height="15px" width="30%" />
|
{renderShortNumericDateFormat(`${cycle.end_date}`)
|
||||||
</div>
|
? renderShortNumericDateFormat(`${cycle.end_date}`)
|
||||||
<div className="mt-8 space-y-3">
|
: "N/A"}
|
||||||
<Loader.Item height="30px" />
|
</span>
|
||||||
<Loader.Item height="30px" />
|
</Popover.Button>
|
||||||
<Loader.Item height="30px" />
|
|
||||||
</div>
|
<Transition
|
||||||
</Loader>
|
as={React.Fragment}
|
||||||
)}
|
enter="transition ease-out duration-200"
|
||||||
</div>
|
enterFrom="opacity-0 translate-y-1"
|
||||||
|
enterTo="opacity-100 translate-y-0"
|
||||||
|
leave="transition ease-in duration-150"
|
||||||
|
leaveFrom="opacity-100 translate-y-0"
|
||||||
|
leaveTo="opacity-0 translate-y-1"
|
||||||
|
>
|
||||||
|
<Popover.Panel className="absolute top-10 left-0 z-20 transform overflow-hidden">
|
||||||
|
<DatePicker
|
||||||
|
selected={startDateRange}
|
||||||
|
onChange={(dates) => {
|
||||||
|
const [start, end] = dates;
|
||||||
|
submitChanges({
|
||||||
|
start_date: renderDateFormat(start),
|
||||||
|
end_date: renderDateFormat(end),
|
||||||
|
});
|
||||||
|
if (setStartDateRange) {
|
||||||
|
setStartDateRange(start);
|
||||||
|
}
|
||||||
|
if (setEndDateRange) {
|
||||||
|
setEndDateRange(end);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
startDate={startDateRange}
|
||||||
|
endDate={endDateRange}
|
||||||
|
selectsRange
|
||||||
|
inline
|
||||||
|
/>
|
||||||
|
</Popover.Panel>
|
||||||
|
</Transition>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between pb-3">
|
||||||
|
<h4 className="text-sm font-medium">{cycle.name}</h4>
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="rounded-md border p-2 shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
||||||
|
onClick={() =>
|
||||||
|
copyTextToClipboard(
|
||||||
|
`https://app.plane.so/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
setToastAlert({
|
||||||
|
type: "success",
|
||||||
|
title: "Cycle link copied to clipboard",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setToastAlert({
|
||||||
|
type: "error",
|
||||||
|
title: "Some error occurred",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<LinkIcon className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="rounded-md border border-red-500 p-2 text-red-500 shadow-sm duration-300 hover:bg-red-50 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
||||||
|
onClick={() => setCycleDeleteModal(true)}
|
||||||
|
>
|
||||||
|
<TrashIcon className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="divide-y-2 divide-gray-100 text-xs">
|
||||||
|
<div className="py-1">
|
||||||
|
<div className="flex flex-wrap items-center py-2">
|
||||||
|
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
||||||
|
<UserIcon className="h-4 w-4 flex-shrink-0" />
|
||||||
|
<p>Owned by</p>
|
||||||
|
</div>
|
||||||
|
<div className="sm:basis-1/2 flex items-center gap-1">
|
||||||
|
{cycle.owned_by &&
|
||||||
|
(cycle.owned_by.avatar && cycle.owned_by.avatar !== "" ? (
|
||||||
|
<div className="h-5 w-5 rounded-full border-2 border-transparent">
|
||||||
|
<Image
|
||||||
|
src={cycle.owned_by.avatar}
|
||||||
|
height="100%"
|
||||||
|
width="100%"
|
||||||
|
className="rounded-full"
|
||||||
|
alt={cycle.owned_by?.first_name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid h-5 w-5 place-items-center rounded-full border-2 border-white bg-gray-700 capitalize text-white">
|
||||||
|
{cycle.owned_by?.first_name && cycle.owned_by.first_name !== ""
|
||||||
|
? cycle.owned_by.first_name.charAt(0)
|
||||||
|
: cycle.owned_by?.email.charAt(0)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{cycle.owned_by.first_name !== ""
|
||||||
|
? cycle.owned_by.first_name
|
||||||
|
: cycle.owned_by.email}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap items-center py-2">
|
||||||
|
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
||||||
|
<ChartPieIcon className="h-4 w-4 flex-shrink-0" />
|
||||||
|
<p>Progress</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 sm:basis-1/2">
|
||||||
|
<div className="grid flex-shrink-0 place-items-center">
|
||||||
|
<span className="h-4 w-4">
|
||||||
|
<CircularProgressbar
|
||||||
|
value={groupedIssues.completed.length}
|
||||||
|
maxValue={cycleIssues?.length}
|
||||||
|
strokeWidth={10}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{groupedIssues.completed.length}/{cycleIssues?.length}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="py-1" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center justify-center w-full gap-2 ">
|
||||||
|
{isStartValid && isEndValid ? (
|
||||||
|
<div className="relative h-[200px] w-full ">
|
||||||
|
<ProgressChart
|
||||||
|
issues={issues}
|
||||||
|
start={cycle?.start_date ?? ""}
|
||||||
|
end={cycle?.end_date ?? ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
{issues.length > 0 ? (
|
||||||
|
<SidebarProgressStats issues={issues} groupedIssues={groupedIssues} />
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Loader>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Loader.Item height="15px" width="50%" />
|
||||||
|
<Loader.Item height="15px" width="30%" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 space-y-3">
|
||||||
|
<Loader.Item height="30px" />
|
||||||
|
<Loader.Item height="30px" />
|
||||||
|
<Loader.Item height="30px" />
|
||||||
|
</div>
|
||||||
|
</Loader>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,9 +148,7 @@ export const SingleState: React.FC<Props> = ({
|
|||||||
backgroundColor: state.color,
|
backgroundColor: state.color,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<h6 className="text-sm">
|
<h6 className="text-sm">{addSpaceIfCamelCase(state.name)}</h6>
|
||||||
{addSpaceIfCamelCase(state.name)} {state.sequence}
|
|
||||||
</h6>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{index !== 0 && (
|
{index !== 0 && (
|
||||||
|
@ -16,7 +16,7 @@ export const WorkspaceHomeCardsList: FC<WorkspaceHomeCardsListProps> = (props) =
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Issues pending",
|
title: "Issues pending",
|
||||||
number: myIssues?.length ?? 0 - groupedIssues.completed.length,
|
number: myIssues.length - groupedIssues.completed.length,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Projects",
|
title: "Projects",
|
||||||
|
@ -58,54 +58,56 @@ const MyIssuesPage: NextPage = () => {
|
|||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Popover className="relative">
|
{myIssues && myIssues.length > 0 && (
|
||||||
{({ open }) => (
|
<Popover className="relative">
|
||||||
<>
|
{({ open }) => (
|
||||||
<Popover.Button
|
<>
|
||||||
className={`group flex items-center gap-2 rounded-md border bg-transparent p-2 text-xs font-medium hover:bg-gray-100 hover:text-gray-900 focus:outline-none ${
|
<Popover.Button
|
||||||
open ? "bg-gray-100 text-gray-900" : "text-gray-500"
|
className={`group flex items-center gap-2 rounded-md border bg-transparent p-2 text-xs font-medium hover:bg-gray-100 hover:text-gray-900 focus:outline-none ${
|
||||||
}`}
|
open ? "bg-gray-100 text-gray-900" : "text-gray-500"
|
||||||
>
|
}`}
|
||||||
<span>View</span>
|
>
|
||||||
<ChevronDownIcon className="h-4 w-4" aria-hidden="true" />
|
<span>View</span>
|
||||||
</Popover.Button>
|
<ChevronDownIcon className="h-4 w-4" aria-hidden="true" />
|
||||||
|
</Popover.Button>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
enter="transition ease-out duration-200"
|
enter="transition ease-out duration-200"
|
||||||
enterFrom="opacity-0 translate-y-1"
|
enterFrom="opacity-0 translate-y-1"
|
||||||
enterTo="opacity-100 translate-y-0"
|
enterTo="opacity-100 translate-y-0"
|
||||||
leave="transition ease-in duration-150"
|
leave="transition ease-in duration-150"
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
leaveFrom="opacity-100 translate-y-0"
|
||||||
leaveTo="opacity-0 translate-y-1"
|
leaveTo="opacity-0 translate-y-1"
|
||||||
>
|
>
|
||||||
<Popover.Panel className="absolute right-1/2 z-10 mr-5 mt-1 w-screen max-w-xs translate-x-1/2 transform overflow-hidden rounded-lg bg-white p-3 shadow-lg">
|
<Popover.Panel className="absolute right-1/2 z-10 mr-5 mt-1 w-screen max-w-xs translate-x-1/2 transform overflow-hidden rounded-lg bg-white p-3 shadow-lg">
|
||||||
<div className="relative flex flex-col gap-1 gap-y-4">
|
<div className="relative flex flex-col gap-1 gap-y-4">
|
||||||
<div className="relative flex flex-col gap-1">
|
<div className="relative flex flex-col gap-1">
|
||||||
<h4 className="text-base text-gray-600">Properties</h4>
|
<h4 className="text-base text-gray-600">Properties</h4>
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
{Object.keys(properties).map((key) => (
|
{Object.keys(properties).map((key) => (
|
||||||
<button
|
<button
|
||||||
key={key}
|
key={key}
|
||||||
type="button"
|
type="button"
|
||||||
className={`rounded border border-theme px-2 py-1 text-xs capitalize ${
|
className={`rounded border border-theme px-2 py-1 text-xs capitalize ${
|
||||||
properties[key as keyof Properties]
|
properties[key as keyof Properties]
|
||||||
? "border-theme bg-theme text-white"
|
? "border-theme bg-theme text-white"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setProperties(key as keyof Properties)}
|
onClick={() => setProperties(key as keyof Properties)}
|
||||||
>
|
>
|
||||||
{replaceUnderscoreIfSnakeCase(key)}
|
{replaceUnderscoreIfSnakeCase(key)}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Popover.Panel>
|
||||||
</Popover.Panel>
|
</Transition>
|
||||||
</Transition>
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
</Popover>
|
||||||
</Popover>
|
)}
|
||||||
<HeaderButton
|
<HeaderButton
|
||||||
Icon={PlusIcon}
|
Icon={PlusIcon}
|
||||||
label="Add Issue"
|
label="Add Issue"
|
||||||
|
Loading…
Reference in New Issue
Block a user