Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/app/components/core/board-view/all-boards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { DragDropContext, DropResult } from "react-beautiful-dnd";
// hooks
import useIssueView from "hooks/use-issue-view";
// components
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import { SingleBoard } from "components/core/board-view/single-board";
// types
import { IIssue, IProjectMember, IState, UserAuth } from "types";
Expand Down
16 changes: 6 additions & 10 deletions apps/app/components/core/issues-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,12 @@ import modulesService from "services/modules.service";
// hooks
import useIssueView from "hooks/use-issue-view";
// components
import { AllLists, AllBoards, ExistingIssuesListModal } from "components/core";
import { AllLists, AllBoards } from "components/core";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
// helpers
import { getStatesList } from "helpers/state.helper";
// types
import {
CycleIssueResponse,
IIssue,
IssueResponse,
IState,
ModuleIssueResponse,
UserAuth,
} from "types";
import { CycleIssueResponse, IIssue, IssueResponse, ModuleIssueResponse, UserAuth } from "types";
// fetch-keys
import {
CYCLE_ISSUES,
Expand Down Expand Up @@ -68,12 +63,13 @@ export const IssuesView: React.FC<Props> = ({

const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues);

const { data: states } = useSWR<IState[]>(
const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATE_LIST(projectId as string) : null,
workspaceSlug
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
);
const states = getStatesList(stateGroups ?? {});

const { data: members } = useSWR(
projectId ? PROJECT_MEMBERS(projectId as string) : null,
Expand Down
5 changes: 4 additions & 1 deletion apps/app/components/issues/select/state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import stateService from "services/state.service";
import { Squares2X2Icon, PlusIcon } from "@heroicons/react/24/outline";
// icons
import { Combobox, Transition } from "@headlessui/react";
// helpers
import { getStatesList } from "helpers/state.helper";
// fetch keys
import { STATE_LIST } from "constants/fetch-keys";

Expand All @@ -27,12 +29,13 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
const router = useRouter();
const { workspaceSlug } = router.query;

const { data: states } = useSWR(
const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATE_LIST(projectId) : null,
workspaceSlug && projectId
? () => stateService.getStates(workspaceSlug as string, projectId)
: null
);
const states = getStatesList(stateGroups ?? {});

const options = states?.map((state) => ({
value: state.id,
Expand Down
8 changes: 6 additions & 2 deletions apps/app/components/issues/sidebar-select/state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { useRouter } from "next/router";

import useSWR from "swr";

// react-hook-form
import { Control, Controller } from "react-hook-form";
// services
import { Squares2X2Icon } from "@heroicons/react/24/outline";
import stateService from "services/state.service";
// ui
import { Spinner, CustomSelect } from "components/ui";
// icons
import { Squares2X2Icon } from "@heroicons/react/24/outline";
// helpers
import { getStatesList } from "helpers/state.helper";
// types
import { IIssue, UserAuth } from "types";
// constants
Expand All @@ -26,12 +29,13 @@ export const SidebarStateSelect: React.FC<Props> = ({ control, submitChanges, us
const router = useRouter();
const { workspaceSlug, projectId } = router.query;

const { data: states } = useSWR(
const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATE_LIST(projectId as string) : null,
workspaceSlug && projectId
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
);
const states = getStatesList(stateGroups ?? {});

const isNotAllowed = userAuth.isGuest || userAuth.isViewer;

Expand Down
4 changes: 2 additions & 2 deletions apps/app/components/issues/sub-issues-list-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const SubIssuesListModal: React.FC<Props> = ({ isOpen, handleClose, paren
: issues?.results.filter((issue) => issue.name.toLowerCase().includes(query.toLowerCase())) ??
[];

const handleCommandPaletteClose = () => {
const handleModalClose = () => {
handleClose();
setQuery("");
};
Expand Down Expand Up @@ -93,7 +93,7 @@ export const SubIssuesListModal: React.FC<Props> = ({ isOpen, handleClose, paren

return (
<Transition.Root show={isOpen} as={React.Fragment} afterLeave={() => setQuery("")} appear>
<Dialog as="div" className="relative z-20" onClose={handleCommandPaletteClose}>
<Dialog as="div" className="relative z-20" onClose={handleModalClose}>
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
Expand Down
12 changes: 6 additions & 6 deletions apps/app/components/issues/view-select/state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import stateService from "services/state.service";
import { CustomSelect } from "components/ui";
// helpers
import { addSpaceIfCamelCase } from "helpers/string.helper";
import { getStatesList } from "helpers/state.helper";
// types
import { IIssue, IState } from "types";
import { IIssue } from "types";
// fetch-keys
import { STATE_LIST } from "constants/fetch-keys";

Expand All @@ -29,12 +30,11 @@ export const ViewStateSelect: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug, projectId } = router.query;

const { data: states } = useSWR<IState[]>(
workspaceSlug && projectId ? STATE_LIST(projectId as string) : null,
workspaceSlug
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATE_LIST(issue.project) : null,
workspaceSlug ? () => stateService.getStates(workspaceSlug as string, issue.project) : null
);
const states = getStatesList(stateGroups ?? {});

return (
<CustomSelect
Expand Down
2 changes: 2 additions & 0 deletions apps/app/components/labels/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./labels-list-modal";
export * from "./single-label";
180 changes: 180 additions & 0 deletions apps/app/components/labels/labels-list-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import React, { useState } from "react";

import { useRouter } from "next/router";

import useSWR, { mutate } from "swr";

// headless ui
import { Combobox, Dialog, Transition } from "@headlessui/react";
// icons
import { RectangleStackIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
// services
import issuesService from "services/issues.service";
// types
import { IIssueLabels } from "types";
// constants
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";

type Props = {
isOpen: boolean;
handleClose: () => void;
parent: IIssueLabels | undefined;
};

export const LabelsListModal: React.FC<Props> = ({ isOpen, handleClose, parent }) => {
const [query, setQuery] = useState("");

const router = useRouter();
const { workspaceSlug, projectId } = router.query;

const { data: issueLabels, mutate } = useSWR<IIssueLabels[]>(
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
workspaceSlug && projectId
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
: null
);

const filteredLabels: IIssueLabels[] =
query === ""
? issueLabels ?? []
: issueLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase())) ?? [];

const handleModalClose = () => {
handleClose();
setQuery("");
};

const addChildLabel = async (label: IIssueLabels) => {
if (!workspaceSlug || !projectId) return;

mutate(
(prevData) =>
prevData?.map((l) => {
if (l.id === label.id) return { ...l, parent: parent?.id ?? "" };

return l;
}),
false
);

await issuesService
.patchIssueLabel(workspaceSlug as string, projectId as string, label.id, {
parent: parent?.id ?? "",
})
.then((res) => {
console.log(res);
mutate();
});
};

return (
<Transition.Root show={isOpen} as={React.Fragment} afterLeave={() => setQuery("")} appear>
<Dialog as="div" className="relative z-20" onClose={handleModalClose}>
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-25 transition-opacity" />
</Transition.Child>

<div className="fixed inset-0 z-20 overflow-y-auto p-4 sm:p-6 md:p-20">
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white bg-opacity-80 shadow-2xl ring-1 ring-black ring-opacity-5 backdrop-blur backdrop-filter transition-all">
<Combobox>
<div className="relative m-1">
<MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40"
aria-hidden="true"
/>
<Combobox.Input
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 sm:text-sm"
placeholder="Search..."
onChange={(e) => setQuery(e.target.value)}
/>
</div>

<Combobox.Options
static
className="max-h-80 scroll-py-2 divide-y divide-gray-500 divide-opacity-10 overflow-y-auto"
>
{filteredLabels.length > 0 && (
<>
<li className="p-2">
{query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900">
Labels
</h2>
)}
<ul className="text-sm text-gray-700">
{filteredLabels.map((label) => {
const children = issueLabels?.filter((l) => l.parent === label.id);

if (
(label.parent === "" || label.parent === null) && // issue does not have any other parent
label.id !== parent?.id && // issue is not itself
children?.length === 0 // issue doesn't have any othe children
)
return (
<Combobox.Option
key={label.id}
value={{
name: label.name,
}}
className={({ active }) =>
`flex cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : ""
}`
}
onClick={() => {
addChildLabel(label);
handleClose();
}}
>
<span
className="block flex-shrink-0 h-1.5 w-1.5 rounded-full"
style={{
backgroundColor: label.color,
}}
/>
{label.name}
</Combobox.Option>
);
})}
</ul>
</li>
</>
)}
</Combobox.Options>

{query !== "" && filteredLabels.length === 0 && (
<div className="py-14 px-6 text-center sm:px-14">
<RectangleStackIcon
className="mx-auto h-6 w-6 text-gray-900 text-opacity-40"
aria-hidden="true"
/>
<p className="mt-4 text-sm text-gray-900">
We couldn{"'"}t find any label with that term. Please try again.
</p>
</div>
)}
</Combobox>
</Dialog.Panel>
</Transition.Child>
</div>
</Dialog>
</Transition.Root>
);
};
Loading