forked from github/plane
chore: no estimates option, estimates activity (#838)
This commit is contained in:
parent
61761fedc5
commit
e23075b7b9
@ -73,6 +73,10 @@ const activityDetails: {
|
||||
message: "updated the description.",
|
||||
icon: <ChatBubbleBottomCenterTextIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />,
|
||||
},
|
||||
estimate_point: {
|
||||
message: "set the estimate point to",
|
||||
icon: <PlayIcon className="h-3 w-3 text-gray-500 -rotate-90" aria-hidden="true" />,
|
||||
},
|
||||
target_date: {
|
||||
message: "set the due date to",
|
||||
icon: <CalendarDaysIcon className="h-3 w-3 text-gray-500" aria-hidden="true" />,
|
||||
@ -136,8 +140,6 @@ export const Feeds: React.FC<any> = ({ activities }) => (
|
||||
action = `${activity.verb} the`;
|
||||
} else if (activity.field === "link") {
|
||||
action = `${activity.verb} the`;
|
||||
} else if (activity.field === "estimate") {
|
||||
action = "updated the";
|
||||
}
|
||||
// for values that are after the action clause
|
||||
let value: any = activity.new_value ? activity.new_value : activity.old_value;
|
||||
@ -188,8 +190,10 @@ export const Feeds: React.FC<any> = ({ activities }) => (
|
||||
value = "attachment";
|
||||
} else if (activity.field === "link") {
|
||||
value = "link";
|
||||
} else if (activity.field === "estimate") {
|
||||
value = "estimate";
|
||||
} else if (activity.field === "estimate_point") {
|
||||
value = activity.new_value
|
||||
? activity.new_value + ` Point${parseInt(activity.new_value ?? "", 10) > 1 ? "s" : ""}`
|
||||
: "None";
|
||||
}
|
||||
|
||||
if (activity.field === "comment") {
|
||||
|
@ -388,36 +388,33 @@ export const IssuesView: React.FC<Props> = ({
|
||||
handleClose={() => setTransferIssuesModal(false)}
|
||||
isOpen={transferIssuesModal}
|
||||
/>
|
||||
<div>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<FilterList filters={filters} setFilters={setFilters} />
|
||||
{Object.keys(filters).length > 0 &&
|
||||
nullFilters.length !== Object.keys(filters).length && (
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
if (viewId) {
|
||||
setFilters({}, true);
|
||||
setToastAlert({
|
||||
title: "View updated",
|
||||
message: "Your view has been updated",
|
||||
type: "success",
|
||||
});
|
||||
} else
|
||||
setCreateViewModal({
|
||||
query: filters,
|
||||
});
|
||||
}}
|
||||
className="flex items-center gap-2 text-sm"
|
||||
>
|
||||
{!viewId && <PlusIcon className="h-4 w-4" />}
|
||||
{viewId ? "Update" : "Save"} view
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-2 -mt-2">
|
||||
<FilterList filters={filters} setFilters={setFilters} />
|
||||
{Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length && (
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
if (viewId) {
|
||||
setFilters({}, true);
|
||||
setToastAlert({
|
||||
title: "View updated",
|
||||
message: "Your view has been updated",
|
||||
type: "success",
|
||||
});
|
||||
} else
|
||||
setCreateViewModal({
|
||||
query: filters,
|
||||
});
|
||||
}}
|
||||
className="flex items-center gap-2 text-sm"
|
||||
>
|
||||
{!viewId && <PlusIcon className="h-4 w-4" />}
|
||||
{viewId ? "Update" : "Save"} view
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length && (
|
||||
<div className="mb-5 border-t" />
|
||||
<div className="my-4 border-t" />
|
||||
)}
|
||||
|
||||
<DragDropContext onDragEnd={handleOnDragEnd}>
|
||||
|
@ -29,6 +29,7 @@ import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||
// types
|
||||
import { IIssueComment, IIssueLabels } from "types";
|
||||
import { PROJECT_ISSUES_ACTIVITY, PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
|
||||
const activityDetails: {
|
||||
[key: string]: {
|
||||
@ -56,6 +57,10 @@ const activityDetails: {
|
||||
message: "set the cycle to",
|
||||
icon: <CyclesIcon height="12" width="12" color="#6b7280" />,
|
||||
},
|
||||
estimate_point: {
|
||||
message: "set the estimate point to",
|
||||
icon: <PlayIcon className="h-3 w-3 text-gray-500 -rotate-90" aria-hidden="true" />,
|
||||
},
|
||||
labels: {
|
||||
icon: <TagIcon height="12" width="12" color="#6b7280" />,
|
||||
},
|
||||
@ -107,6 +112,8 @@ export const IssueActivitySection: React.FC<Props> = () => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, issueId } = router.query;
|
||||
|
||||
const { isEstimateActive, estimatePoints } = useEstimateOption();
|
||||
|
||||
const { data: issueActivities, mutate: mutateIssueActivities } = useSWR(
|
||||
workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY(issueId as string) : null,
|
||||
workspaceSlug && projectId && issueId
|
||||
@ -278,8 +285,14 @@ export const IssueActivitySection: React.FC<Props> = () => {
|
||||
value = "attachment";
|
||||
} else if (activityItem.field === "link") {
|
||||
value = "link";
|
||||
} else if (activityItem.field === "estimate") {
|
||||
value = "estimate";
|
||||
} else if (activityItem.field === "estimate_point") {
|
||||
value = activityItem.new_value
|
||||
? isEstimateActive
|
||||
? estimatePoints.find((e) => e.key === parseInt(activityItem.new_value ?? "", 10))
|
||||
?.value
|
||||
: activityItem.new_value +
|
||||
` Point${parseInt(activityItem.new_value ?? "", 10) > 1 ? "s" : ""}`
|
||||
: "None";
|
||||
}
|
||||
|
||||
if ("field" in activityItem && activityItem.field !== "updated_by") {
|
||||
|
@ -48,7 +48,7 @@ const defaultValues: Partial<IIssue> = {
|
||||
name: "",
|
||||
description: "",
|
||||
description_html: "<p></p>",
|
||||
estimate_point: 0,
|
||||
estimate_point: null,
|
||||
state: "",
|
||||
cycle: null,
|
||||
priority: null,
|
||||
|
@ -8,8 +8,8 @@ import { PlayIcon } from "@heroicons/react/24/outline";
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
|
||||
type Props = {
|
||||
value: number;
|
||||
onChange: (value: number) => void;
|
||||
value: number | null;
|
||||
onChange: (value: number | null) => void;
|
||||
};
|
||||
|
||||
export const IssueEstimateSelect: React.FC<Props> = ({ value, onChange }) => {
|
||||
@ -24,18 +24,26 @@ export const IssueEstimateSelect: React.FC<Props> = ({ value, onChange }) => {
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<PlayIcon className="h-4 w-4 text-gray-500 -rotate-90" />
|
||||
<span className={`${value ? "text-gray-600" : "text-gray-500"}`}>
|
||||
{estimatePoints?.find((e) => e.key === value)?.value ?? "Estimate points"}
|
||||
{estimatePoints?.find((e) => e.key === value)?.value ?? "Estimate"}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
onChange={onChange}
|
||||
position="right"
|
||||
width="w-full min-w-[6rem]"
|
||||
width="w-full min-w-[8rem]"
|
||||
noChevron
|
||||
>
|
||||
<CustomSelect.Option value={null}>
|
||||
<>
|
||||
<span>
|
||||
<PlayIcon className="h-4 w-4 -rotate-90" />
|
||||
</span>
|
||||
None
|
||||
</>
|
||||
</CustomSelect.Option>
|
||||
{estimatePoints &&
|
||||
estimatePoints.map((point) => (
|
||||
<CustomSelect.Option className="w-full " key={point.key} value={point.key}>
|
||||
<CustomSelect.Option key={point.key} value={point.key}>
|
||||
<>
|
||||
<span>
|
||||
<PlayIcon className="h-4 w-4 -rotate-90" />
|
||||
|
@ -1,17 +1,17 @@
|
||||
import React from "react";
|
||||
|
||||
// hooks
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
// ui
|
||||
import { CustomSelect } from "components/ui";
|
||||
// icons
|
||||
import { BanknotesIcon, PlayIcon } from "@heroicons/react/24/outline";
|
||||
import { PlayIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { UserAuth } from "types";
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
// constants
|
||||
|
||||
type Props = {
|
||||
value: number;
|
||||
onChange: (val: number) => void;
|
||||
value: number | null;
|
||||
onChange: (val: number | null) => void;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
@ -35,7 +35,7 @@ export const SidebarEstimateSelect: React.FC<Props> = ({ value, onChange, userAu
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<PlayIcon className="h-4 w-4 text-gray-700 -rotate-90" />
|
||||
<span className={`${value ? "text-gray-600" : "text-gray-500"}`}>
|
||||
{estimatePoints?.find((e) => e.key === value)?.value ?? "Estimate points"}
|
||||
{estimatePoints?.find((e) => e.key === value)?.value ?? "Estimate"}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
@ -44,9 +44,17 @@ export const SidebarEstimateSelect: React.FC<Props> = ({ value, onChange, userAu
|
||||
width="w-full"
|
||||
disabled={isNotAllowed}
|
||||
>
|
||||
<CustomSelect.Option value={null}>
|
||||
<>
|
||||
<span>
|
||||
<PlayIcon className="h-4 w-4 -rotate-90" />
|
||||
</span>
|
||||
None
|
||||
</>
|
||||
</CustomSelect.Option>
|
||||
{estimatePoints &&
|
||||
estimatePoints.map((point) => (
|
||||
<CustomSelect.Option className="w-full " key={point.key} value={point.key}>
|
||||
<CustomSelect.Option key={point.key} value={point.key}>
|
||||
<>
|
||||
<span>
|
||||
<PlayIcon className="h-4 w-4 -rotate-90" />
|
||||
|
@ -294,7 +294,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
render={({ field: { value } }) => (
|
||||
<SidebarEstimateSelect
|
||||
value={value}
|
||||
onChange={(val: number) => submitChanges({ estimate_point: val })}
|
||||
onChange={(val: number | null) => submitChanges({ estimate_point: val })}
|
||||
userAuth={memberRole}
|
||||
/>
|
||||
)}
|
||||
|
@ -58,7 +58,7 @@ export const ViewEstimateSelect: React.FC<Props> = ({
|
||||
<Tooltip tooltipHeading="Estimate" tooltipContent={estimateValue}>
|
||||
<div className="flex items-center gap-1 text-gray-500">
|
||||
<PlayIcon className="h-3.5 w-3.5 -rotate-90" />
|
||||
{estimateValue}
|
||||
{estimateValue ?? "Estimate"}
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
@ -67,11 +67,24 @@ export const ViewEstimateSelect: React.FC<Props> = ({
|
||||
disabled={isNotAllowed}
|
||||
position={position}
|
||||
selfPositioned={selfPositioned}
|
||||
width="w-full min-w-[6rem]"
|
||||
width="w-full min-w-[8rem]"
|
||||
>
|
||||
<CustomSelect.Option value={null}>
|
||||
<>
|
||||
<span>
|
||||
<PlayIcon className="h-4 w-4 -rotate-90" />
|
||||
</span>
|
||||
None
|
||||
</>
|
||||
</CustomSelect.Option>
|
||||
{estimatePoints?.map((estimate) => (
|
||||
<CustomSelect.Option key={estimate.id} value={estimate.key} className="capitalize">
|
||||
<>{estimate.value}</>
|
||||
<CustomSelect.Option key={estimate.id} value={estimate.key}>
|
||||
<>
|
||||
<span>
|
||||
<PlayIcon className="h-4 w-4 -rotate-90" />
|
||||
</span>
|
||||
{estimate.value}
|
||||
</>
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
</CustomSelect>
|
||||
|
@ -13,7 +13,7 @@ import { orderArrayBy } from "helpers/array.helper";
|
||||
// fetch-keys
|
||||
import { ESTIMATE_POINTS_LIST } from "constants/fetch-keys";
|
||||
|
||||
const useEstimateOption = (estimateKey?: number) => {
|
||||
const useEstimateOption = (estimateKey?: number | null) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
|
@ -34,6 +34,7 @@ const defaultValues = {
|
||||
name: "",
|
||||
description: "",
|
||||
description_html: "",
|
||||
estimate_point: null,
|
||||
state: "",
|
||||
assignees_list: [],
|
||||
priority: "low",
|
||||
|
2
apps/app/types/issues.d.ts
vendored
2
apps/app/types/issues.d.ts
vendored
@ -87,7 +87,7 @@ export interface IIssue {
|
||||
description: any;
|
||||
description_html: any;
|
||||
description_stripped: any;
|
||||
estimate_point: number;
|
||||
estimate_point: number | null;
|
||||
id: string;
|
||||
issue_cycle: IIssueCycle | null;
|
||||
issue_link: {
|
||||
|
Loading…
Reference in New Issue
Block a user