chore: user auth layer (#749)
* chore: use estimate points hook created * chore: user auth layer * fix: build error
This commit is contained in:
parent
3fe32606a9
commit
1026ae3eb1
20 changed files with 603 additions and 487 deletions
|
|
@ -21,9 +21,10 @@ import useToast from "hooks/use-toast";
|
|||
import {
|
||||
ViewAssigneeSelect,
|
||||
ViewDueDateSelect,
|
||||
ViewEstimateSelect,
|
||||
ViewPrioritySelect,
|
||||
ViewStateSelect,
|
||||
} from "components/issues/view-select";
|
||||
} from "components/issues";
|
||||
// ui
|
||||
import { ContextMenu, CustomMenu } from "components/ui";
|
||||
// icons
|
||||
|
|
@ -48,6 +49,7 @@ import {
|
|||
MODULE_ISSUES_WITH_PARAMS,
|
||||
PROJECT_ISSUES_LIST_WITH_PARAMS,
|
||||
} from "constants/fetch-keys";
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
|
||||
type Props = {
|
||||
type?: string;
|
||||
|
|
@ -90,6 +92,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
|||
|
||||
const { orderBy, params } = useIssuesView();
|
||||
|
||||
const { estimateValue } = useEstimateOption(issue.estimate_point);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
||||
|
||||
|
|
@ -342,6 +346,14 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
|||
selfPositioned
|
||||
/>
|
||||
)}
|
||||
{properties.estimate && (
|
||||
<ViewEstimateSelect
|
||||
issue={issue}
|
||||
partialUpdateIssue={partialUpdateIssue}
|
||||
isNotAllowed={isNotAllowed}
|
||||
selfPositioned
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,44 +1,41 @@
|
|||
import React, { useState } from "react";
|
||||
|
||||
// ui
|
||||
import { CustomMenu, PrimaryButton } from "components/ui";
|
||||
// types
|
||||
import { IEstimate, IProject } from "types";
|
||||
//icons
|
||||
import { PencilIcon, TrashIcon, SquaresPlusIcon, ListBulletIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
import useToast from "hooks/use-toast";
|
||||
|
||||
import estimatesService from "services/estimates.service";
|
||||
import projectService from "services/project.service";
|
||||
|
||||
import { EstimatePointsModal } from "./estimate-points-modal";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { ESTIMATE_POINTS_LIST } from "constants/fetch-keys";
|
||||
import { PlusIcon } from "components/icons";
|
||||
import useSWR from "swr";
|
||||
|
||||
interface IEstimatePoints {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
// services
|
||||
import estimatesService from "services/estimates.service";
|
||||
import projectService from "services/project.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
// components
|
||||
import { EstimatePointsModal } from "components/estimates";
|
||||
// ui
|
||||
import { CustomMenu } from "components/ui";
|
||||
//icons
|
||||
import {
|
||||
PencilIcon,
|
||||
TrashIcon,
|
||||
SquaresPlusIcon,
|
||||
ListBulletIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { IEstimate, IProject } from "types";
|
||||
// fetch-keys
|
||||
import { ESTIMATE_POINTS_LIST } from "constants/fetch-keys";
|
||||
|
||||
type Props = {
|
||||
estimate: IEstimate;
|
||||
editEstimate: (estimate: IEstimate) => void;
|
||||
handleEstimateDelete: (estimateId: string) => void;
|
||||
activeEstimate: IEstimate | null;
|
||||
setActiveEstimate: React.Dispatch<React.SetStateAction<IEstimate | null>>;
|
||||
};
|
||||
|
||||
export const SingleEstimate: React.FC<Props> = ({
|
||||
estimate,
|
||||
editEstimate,
|
||||
handleEstimateDelete,
|
||||
activeEstimate,
|
||||
setActiveEstimate,
|
||||
}) => {
|
||||
const [isEstimatePointsModalOpen, setIsEstimatePointsModalOpen] = useState(false);
|
||||
|
||||
|
|
@ -47,6 +44,8 @@ export const SingleEstimate: React.FC<Props> = ({
|
|||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { projectDetails, mutateProjectDetails } = useProjectDetails();
|
||||
|
||||
const { data: estimatePoints } = useSWR(
|
||||
workspaceSlug && projectId ? ESTIMATE_POINTS_LIST(estimate.id) : null,
|
||||
workspaceSlug && projectId
|
||||
|
|
@ -59,12 +58,19 @@ export const SingleEstimate: React.FC<Props> = ({
|
|||
: null
|
||||
);
|
||||
|
||||
const handleActiveEstimate = async () => {
|
||||
if (!workspaceSlug || !projectId || !estimate) return;
|
||||
const payload: Partial<IProject> = {
|
||||
const handleUseEstimate = async () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const payload = {
|
||||
estimate: estimate.id,
|
||||
};
|
||||
setActiveEstimate(estimate);
|
||||
|
||||
mutateProjectDetails((prevData) => {
|
||||
if (!prevData) return prevData;
|
||||
|
||||
return { ...prevData, estimate: estimate.id };
|
||||
}, false);
|
||||
|
||||
await projectService
|
||||
.updateProject(workspaceSlug as string, projectId as string, payload)
|
||||
.catch(() => {
|
||||
|
|
@ -77,56 +83,63 @@ export const SingleEstimate: React.FC<Props> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="divide-y">
|
||||
<>
|
||||
<EstimatePointsModal
|
||||
isOpen={isEstimatePointsModalOpen}
|
||||
estimate={estimate}
|
||||
onClose={() => setIsEstimatePointsModalOpen(false)}
|
||||
/>
|
||||
<div className="gap-2 space-y-3 my-3 bg-white">
|
||||
<div className="gap-2 py-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="items-start">
|
||||
<h6 className="font-medium text-base w-[40vw] truncate">{estimate.name}</h6>
|
||||
<div>
|
||||
<h6 className="flex items-center gap-2 font-medium text-base w-[40vw] truncate">
|
||||
{estimate.name}
|
||||
{projectDetails?.estimate && projectDetails?.estimate === estimate.id && (
|
||||
<span className="capitalize px-2 py-0.5 text-xs rounded bg-green-100 text-green-500">
|
||||
In use
|
||||
</span>
|
||||
)}
|
||||
</h6>
|
||||
<p className="font-sm text-gray-400 font-normal text-[14px] w-[40vw] truncate">
|
||||
{estimate.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-8">
|
||||
<CustomMenu ellipsis>
|
||||
<CustomMenu.MenuItem onClick={handleActiveEstimate}>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<SquaresPlusIcon className="h-4 w-4" />
|
||||
<CustomMenu ellipsis>
|
||||
{projectDetails?.estimate && projectDetails?.estimate !== estimate.id && (
|
||||
<CustomMenu.MenuItem onClick={handleUseEstimate}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<SquaresPlusIcon className="h-3.5 w-3.5" />
|
||||
<span>Use estimate</span>
|
||||
</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem onClick={() => setIsEstimatePointsModalOpen(true)}>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<ListBulletIcon className="h-4 w-4" />
|
||||
{estimatePoints?.length === 8 ? "Update points" : "Create points"}
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
editEstimate(estimate);
|
||||
}}
|
||||
>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<PencilIcon className="h-4 w-4" />
|
||||
<span>Edit estimate</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
handleEstimateDelete(estimate.id);
|
||||
}}
|
||||
>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
<span>Delete estimate</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
</CustomMenu>
|
||||
</div>
|
||||
)}
|
||||
<CustomMenu.MenuItem onClick={() => setIsEstimatePointsModalOpen(true)}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<ListBulletIcon className="h-3.5 w-3.5" />
|
||||
<span>{estimatePoints?.length === 8 ? "Update points" : "Create points"}</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
editEstimate(estimate);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<PencilIcon className="h-3.5 w-3.5" />
|
||||
<span>Edit estimate</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
handleEstimateDelete(estimate.id);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<TrashIcon className="h-3.5 w-3.5" />
|
||||
<span>Delete estimate</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
</CustomMenu>
|
||||
</div>
|
||||
{estimatePoints && estimatePoints.length > 0 ? (
|
||||
<div className="flex gap-2">
|
||||
|
|
@ -140,11 +153,11 @@ export const SingleEstimate: React.FC<Props> = ({
|
|||
{estimatePoints.length > 0 && ")"}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<p className= " text-sm text-gray-300">No estimate points</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className=" text-sm text-gray-300">No estimate points</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export * from "./comment";
|
||||
export * from "./sidebar-select";
|
||||
export * from "./view-select";
|
||||
export * from "./activity";
|
||||
export * from "./delete-issue-modal";
|
||||
export * from "./description-form";
|
||||
|
|
|
|||
69
apps/app/components/issues/view-select/estimate.tsx
Normal file
69
apps/app/components/issues/view-select/estimate.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import React from "react";
|
||||
|
||||
// ui
|
||||
import { CustomSelect, Tooltip } from "components/ui";
|
||||
// icons
|
||||
import { getPriorityIcon } from "components/icons/priority-icon";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
// constants
|
||||
import { PRIORITIES } from "constants/project";
|
||||
// services
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
|
||||
type Props = {
|
||||
issue: IIssue;
|
||||
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
||||
position?: "left" | "right";
|
||||
selfPositioned?: boolean;
|
||||
isNotAllowed: boolean;
|
||||
};
|
||||
|
||||
export const ViewEstimateSelect: React.FC<Props> = ({
|
||||
issue,
|
||||
partialUpdateIssue,
|
||||
position = "left",
|
||||
selfPositioned = false,
|
||||
isNotAllowed,
|
||||
}) => {
|
||||
const { isEstimateActive, estimatePoints, estimateValue } = useEstimateOption(
|
||||
issue.estimate_point
|
||||
);
|
||||
|
||||
return (
|
||||
<CustomSelect
|
||||
value={issue.priority}
|
||||
onChange={(data: string) => {
|
||||
partialUpdateIssue({ priority: data, state: issue.state, target_date: issue.target_date });
|
||||
trackEventServices.trackIssuePartialPropertyUpdateEvent(
|
||||
{
|
||||
workspaceSlug: issue.workspace_detail.slug,
|
||||
workspaceId: issue.workspace_detail.id,
|
||||
projectId: issue.project_detail.id,
|
||||
projectIdentifier: issue.project_detail.identifier,
|
||||
projectName: issue.project_detail.name,
|
||||
issueId: issue.id,
|
||||
},
|
||||
"ISSUE_PROPERTY_UPDATE_PRIORITY"
|
||||
);
|
||||
}}
|
||||
label={
|
||||
<Tooltip tooltipHeading="Estimate" tooltipContent={estimateValue}>
|
||||
<>{estimateValue}</>
|
||||
</Tooltip>
|
||||
}
|
||||
maxHeight="md"
|
||||
noChevron
|
||||
disabled={isNotAllowed}
|
||||
position={position}
|
||||
selfPositioned={selfPositioned}
|
||||
>
|
||||
{estimatePoints?.map((estimate) => (
|
||||
<CustomSelect.Option key={estimate.id} value={estimate.key} className="capitalize">
|
||||
<>{estimate.value}</>
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
</CustomSelect>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export * from "./assignee";
|
||||
export * from "./due-date";
|
||||
export * from "./estimate";
|
||||
export * from "./priority";
|
||||
export * from "./state";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue