fix: blocked issues, style: issue details sidebar
This commit is contained in:
parent
afcf1083ff
commit
8ae9c3f15a
15 changed files with 163 additions and 141 deletions
|
|
@ -101,7 +101,7 @@ const IssueDetailSidebar: React.FC<Props> = ({
|
|||
if (activeWorkspace && activeProject && issueDetail)
|
||||
issuesServices
|
||||
.addIssueToCycle(activeWorkspace.slug, activeProject.id, cycleId, {
|
||||
issue: [issueDetail.id],
|
||||
issues: [issueDetail.id],
|
||||
})
|
||||
.then(() => {
|
||||
submitChanges({});
|
||||
|
|
@ -211,7 +211,7 @@ const IssueDetailSidebar: React.FC<Props> = ({
|
|||
watch={watchIssue}
|
||||
/>
|
||||
<SelectBlocked
|
||||
submitChanges={submitChanges}
|
||||
issueDetail={issueDetail}
|
||||
issuesList={issues?.results.filter((i) => i.id !== issueDetail?.id) ?? []}
|
||||
watch={watchIssue}
|
||||
/>
|
||||
|
|
@ -227,6 +227,7 @@ const IssueDetailSidebar: React.FC<Props> = ({
|
|||
render={({ field: { value, onChange } }) => (
|
||||
<input
|
||||
type="date"
|
||||
id="issueDate"
|
||||
value={value ?? ""}
|
||||
onChange={(e: any) => {
|
||||
submitChanges({ target_date: e.target.value });
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ const SelectAssignee: React.FC<Props> = ({ control, submitChanges }) => {
|
|||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Listbox.Options className="absolute z-10 right-0 mt-1 w-auto bg-white shadow-lg max-h-28 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
|
||||
<Listbox.Options className="absolute z-10 right-0 mt-1 w-auto bg-white shadow-lg max-h-48 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
|
||||
<div className="py-1">
|
||||
{people ? (
|
||||
people.length > 0 ? (
|
||||
|
|
|
|||
|
|
@ -10,32 +10,28 @@ import { Combobox, Dialog, Transition } from "@headlessui/react";
|
|||
// ui
|
||||
import { Button } from "ui";
|
||||
// icons
|
||||
import {
|
||||
FolderIcon,
|
||||
MagnifyingGlassIcon,
|
||||
UserGroupIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { FolderIcon, MagnifyingGlassIcon, FlagIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import { IIssue, IssueResponse } from "types";
|
||||
// constants
|
||||
import { classNames } from "constants/common";
|
||||
import issuesService from "lib/services/issues.service";
|
||||
|
||||
type FormInput = {
|
||||
issue_ids: string[];
|
||||
};
|
||||
|
||||
type Props = {
|
||||
submitChanges: (formData: Partial<IIssue>) => void;
|
||||
issueDetail: IIssue | undefined;
|
||||
issuesList: IIssue[];
|
||||
watch: UseFormWatch<IIssue>;
|
||||
};
|
||||
|
||||
const SelectBlocked: React.FC<Props> = ({ submitChanges, issuesList, watch }) => {
|
||||
const SelectBlocked: React.FC<Props> = ({ issueDetail, issuesList, watch }) => {
|
||||
const [query, setQuery] = useState("");
|
||||
const [isBlockedModalOpen, setIsBlockedModalOpen] = useState(false);
|
||||
|
||||
const { activeProject, issues } = useUser();
|
||||
const { activeWorkspace, activeProject, issues, mutateIssues } = useUser();
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { register, handleSubmit, reset, watch: watchIssues } = useForm<FormInput>();
|
||||
|
|
@ -54,16 +50,73 @@ const SelectBlocked: React.FC<Props> = ({ submitChanges, issuesList, watch }) =>
|
|||
});
|
||||
return;
|
||||
}
|
||||
const newBlocked = [...watch("blocked_list"), ...data.issue_ids];
|
||||
submitChanges({ blocked_list: newBlocked });
|
||||
handleClose();
|
||||
|
||||
data.issue_ids.map((issue) => {
|
||||
if (!activeWorkspace || !activeProject || !issueDetail) return;
|
||||
|
||||
const currentBlockers =
|
||||
issues?.results
|
||||
.find((i) => i.id === issue)
|
||||
?.blocker_issues.map((b) => b.blocker_issue_detail?.id ?? "") ?? [];
|
||||
|
||||
issuesService
|
||||
.patchIssue(activeWorkspace.slug, activeProject.id, issue, {
|
||||
blockers_list: [...currentBlockers, issueDetail.id],
|
||||
})
|
||||
.then((response) => {
|
||||
mutateIssues((prevData) => ({
|
||||
...(prevData as IssueResponse),
|
||||
results: (prevData?.results ?? []).map((issue) => {
|
||||
if (issue.id === issueDetail.id) {
|
||||
return { ...issue, ...response };
|
||||
}
|
||||
return issue;
|
||||
}),
|
||||
}));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
|
||||
// handleClose();
|
||||
};
|
||||
|
||||
const removeBlocked = (issueId: string) => {
|
||||
if (!activeWorkspace || !activeProject || !issueDetail) return;
|
||||
|
||||
const currentBlockers =
|
||||
issues?.results
|
||||
.find((i) => i.id === issueId)
|
||||
?.blocker_issues.map((b) => b.blocker_issue_detail?.id ?? "") ?? [];
|
||||
|
||||
const updatedBlockers = currentBlockers.filter((b) => b !== issueDetail.id);
|
||||
|
||||
issuesService
|
||||
.patchIssue(activeWorkspace.slug, activeProject.id, issueId, {
|
||||
blockers_list: updatedBlockers,
|
||||
})
|
||||
.then((response) => {
|
||||
mutateIssues((prevData) => ({
|
||||
...(prevData as IssueResponse),
|
||||
results: (prevData?.results ?? []).map((issue) => {
|
||||
if (issue.id === issueDetail.id) {
|
||||
return { ...issue, ...response };
|
||||
}
|
||||
return issue;
|
||||
}),
|
||||
}));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-start py-2 flex-wrap">
|
||||
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
||||
<UserGroupIcon className="flex-shrink-0 h-4 w-4" />
|
||||
<p>Blocked issues</p>
|
||||
<FlagIcon className="flex-shrink-0 h-4 w-4" />
|
||||
<p>Blocked by</p>
|
||||
</div>
|
||||
<div className="sm:basis-1/2 space-y-1">
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
|
|
@ -71,13 +124,8 @@ const SelectBlocked: React.FC<Props> = ({ submitChanges, issuesList, watch }) =>
|
|||
? watch("blocked_list").map((issue) => (
|
||||
<span
|
||||
key={issue}
|
||||
className="group flex items-center gap-1 border rounded-2xl text-xs px-1 py-0.5 hover:bg-red-50 hover:border-red-500 cursor-pointer"
|
||||
onClick={() => {
|
||||
const updatedBlockers = watch("blocked_list").filter((i) => i !== issue);
|
||||
submitChanges({
|
||||
blocked_list: updatedBlockers,
|
||||
});
|
||||
}}
|
||||
className="group flex items-center gap-1 border rounded-2xl text-xs px-1.5 py-0.5 text-red-500 hover:bg-red-50 border-red-500 cursor-pointer"
|
||||
onClick={() => removeBlocked(issue)}
|
||||
>
|
||||
{`${activeProject?.identifier}-${
|
||||
issues?.results.find((i) => i.id === issue)?.sequence_id
|
||||
|
|
@ -145,7 +193,10 @@ const SelectBlocked: React.FC<Props> = ({ submitChanges, issuesList, watch }) =>
|
|||
)}
|
||||
<ul className="text-sm text-gray-700">
|
||||
{issuesList.map((issue) => {
|
||||
if (!watch("blocked_list").includes(issue.id)) {
|
||||
if (
|
||||
!watch("blocked_list").includes(issue.id) &&
|
||||
!watch("blockers_list").includes(issue.id)
|
||||
) {
|
||||
return (
|
||||
<Combobox.Option
|
||||
key={issue.id}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,7 @@ import { Combobox, Dialog, Transition } from "@headlessui/react";
|
|||
// ui
|
||||
import { Button } from "ui";
|
||||
// icons
|
||||
import {
|
||||
FolderIcon,
|
||||
MagnifyingGlassIcon,
|
||||
UserGroupIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { FlagIcon, FolderIcon, MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
// constants
|
||||
|
|
@ -62,8 +57,8 @@ const SelectBlocker: React.FC<Props> = ({ submitChanges, issuesList, watch }) =>
|
|||
return (
|
||||
<div className="flex items-start py-2 flex-wrap">
|
||||
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
||||
<UserGroupIcon className="flex-shrink-0 h-4 w-4" />
|
||||
<p>Blocker issues</p>
|
||||
<FlagIcon className="flex-shrink-0 h-4 w-4" />
|
||||
<p>Blocking</p>
|
||||
</div>
|
||||
<div className="sm:basis-1/2 space-y-1">
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
|
|
@ -71,7 +66,7 @@ const SelectBlocker: React.FC<Props> = ({ submitChanges, issuesList, watch }) =>
|
|||
? watch("blockers_list").map((issue) => (
|
||||
<span
|
||||
key={issue}
|
||||
className="group flex items-center gap-1 border rounded-2xl text-xs px-1 py-0.5 hover:bg-red-50 hover:border-red-500 cursor-pointer"
|
||||
className="group flex items-center gap-1 border rounded-2xl text-xs px-1.5 py-0.5 text-yellow-500 hover:bg-yellow-50 border-yellow-500 cursor-pointer"
|
||||
onClick={() => {
|
||||
const updatedBlockers = watch("blockers_list").filter((i) => i !== issue);
|
||||
submitChanges({
|
||||
|
|
@ -145,7 +140,10 @@ const SelectBlocker: React.FC<Props> = ({ submitChanges, issuesList, watch }) =>
|
|||
)}
|
||||
<ul className="text-sm text-gray-700">
|
||||
{issuesList.map((issue) => {
|
||||
if (!watch("blockers_list").includes(issue.id)) {
|
||||
if (
|
||||
!watch("blockers_list").includes(issue.id) &&
|
||||
!watch("blocked_list").includes(issue.id)
|
||||
) {
|
||||
return (
|
||||
<Combobox.Option
|
||||
key={issue.id}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { IIssue } from "types";
|
|||
import { classNames } from "constants/common";
|
||||
import { PRIORITIES } from "constants/";
|
||||
import CustomSelect from "ui/custom-select";
|
||||
import { getPriorityIcon } from "constants/global";
|
||||
|
||||
type Props = {
|
||||
control: Control<IIssue, any>;
|
||||
|
|
@ -33,7 +34,18 @@ const SelectPriority: React.FC<Props> = ({ control, submitChanges, watch }) => {
|
|||
render={({ field: { value } }) => (
|
||||
<CustomSelect
|
||||
label={
|
||||
<span className={classNames(value ? "" : "text-gray-900", "text-left capitalize")}>
|
||||
<span
|
||||
className={classNames(
|
||||
value ? "" : "text-gray-900",
|
||||
"text-left capitalize flex items-center gap-2"
|
||||
)}
|
||||
>
|
||||
{getPriorityIcon(
|
||||
watch("priority") && watch("priority") !== ""
|
||||
? watch("priority") ?? ""
|
||||
: "None",
|
||||
"text-sm"
|
||||
)}
|
||||
{watch("priority") && watch("priority") !== "" ? watch("priority") : "None"}
|
||||
</span>
|
||||
}
|
||||
|
|
@ -44,7 +56,10 @@ const SelectPriority: React.FC<Props> = ({ control, submitChanges, watch }) => {
|
|||
>
|
||||
{PRIORITIES.map((option) => (
|
||||
<CustomSelect.Option key={option} value={option} className="capitalize">
|
||||
{option}
|
||||
<>
|
||||
{getPriorityIcon(option, "text-sm")}
|
||||
{option}
|
||||
</>
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
</CustomSelect>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue