forked from github/plane
chore: reove unused files (#2567)
This commit is contained in:
parent
8072bbb559
commit
98716859d5
@ -1,175 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
// headless ui
|
|
||||||
import { Combobox } from "@headlessui/react";
|
|
||||||
// lucide icons
|
|
||||||
import { ChevronDown, Search, X, Check } from "lucide-react";
|
|
||||||
// hooks
|
|
||||||
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
|
|
||||||
|
|
||||||
interface IFiltersOption {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IIssuePropertyState {
|
|
||||||
options: IFiltersOption[];
|
|
||||||
value?: any;
|
|
||||||
onChange?: (id: any, data: IFiltersOption) => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
className?: string;
|
|
||||||
buttonClassName?: string;
|
|
||||||
optionsClassName?: string;
|
|
||||||
dropdownArrow?: boolean;
|
|
||||||
|
|
||||||
children?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IssuePropertyState: React.FC<IIssuePropertyState> = ({
|
|
||||||
options,
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
disabled,
|
|
||||||
|
|
||||||
className,
|
|
||||||
buttonClassName,
|
|
||||||
optionsClassName,
|
|
||||||
dropdownArrow = true,
|
|
||||||
|
|
||||||
children,
|
|
||||||
}) => {
|
|
||||||
const dropdownBtn = React.useRef<any>(null);
|
|
||||||
const dropdownOptions = React.useRef<any>(null);
|
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = React.useState<boolean>(false);
|
|
||||||
const [search, setSearch] = React.useState<string>("");
|
|
||||||
|
|
||||||
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
|
|
||||||
|
|
||||||
const selectedOption: IFiltersOption | null | undefined =
|
|
||||||
(value && options.find((person: IFiltersOption) => person.id === value)) || null;
|
|
||||||
|
|
||||||
const filteredOptions: IFiltersOption[] =
|
|
||||||
search === ""
|
|
||||||
? options && options.length > 0
|
|
||||||
? options
|
|
||||||
: []
|
|
||||||
: options && options.length > 0
|
|
||||||
? options.filter((person: IFiltersOption) =>
|
|
||||||
person.title.toLowerCase().replace(/\s+/g, "").includes(search.toLowerCase().replace(/\s+/g, ""))
|
|
||||||
)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Combobox
|
|
||||||
as="div"
|
|
||||||
className={`${className}`}
|
|
||||||
value={selectedOption && selectedOption.id}
|
|
||||||
onChange={(data: string) => {
|
|
||||||
if (onChange && selectedOption) onChange(data, selectedOption);
|
|
||||||
}}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
{({ open }: { open: boolean }) => {
|
|
||||||
if (open) {
|
|
||||||
if (!isOpen) setIsOpen(true);
|
|
||||||
} else if (isOpen) setIsOpen(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Combobox.Button
|
|
||||||
ref={dropdownBtn}
|
|
||||||
type="button"
|
|
||||||
className={`flex items-center justify-between gap-1 w-full px-1 py-1 rounded-sm shadow-sm border border-custom-border-300 duration-300 outline-none ${
|
|
||||||
disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${buttonClassName}`}
|
|
||||||
>
|
|
||||||
{children ? (
|
|
||||||
children
|
|
||||||
) : (
|
|
||||||
<div className="text-xs">{(selectedOption && selectedOption?.title) || "Select option"}</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{dropdownArrow && !disabled && (
|
|
||||||
<div className="flex-shrink-0 w-[14px] h-[14px] flex justify-center items-center">
|
|
||||||
<ChevronDown width={14} strokeWidth={2} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Combobox.Button>
|
|
||||||
|
|
||||||
{options && options.length > 0 ? (
|
|
||||||
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
|
|
||||||
<Combobox.Options
|
|
||||||
ref={dropdownOptions}
|
|
||||||
className={`absolute z-10 border border-custom-border-300 p-2 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none min-w-48 max-w-60 whitespace-nowrap mt-1 space-y-1 ${optionsClassName}`}
|
|
||||||
>
|
|
||||||
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-1">
|
|
||||||
<div className="flex-shrink-0 flex justify-center items-center w-[16px] h-[16px] rounded-sm">
|
|
||||||
<Search width={12} strokeWidth={2} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Combobox.Input
|
|
||||||
className="w-full bg-transparent p-1 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none"
|
|
||||||
value={search}
|
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
|
||||||
placeholder="Search"
|
|
||||||
displayValue={(assigned: any) => assigned?.name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{search && search.length > 0 && (
|
|
||||||
<div
|
|
||||||
onClick={() => setSearch("")}
|
|
||||||
className="flex-shrink-0 flex justify-center items-center w-[16px] h-[16px] rounded-sm cursor-pointer hover:bg-custom-background-80"
|
|
||||||
>
|
|
||||||
<X width={12} strokeWidth={2} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`space-y-0.5 max-h-48 overflow-y-scroll`}>
|
|
||||||
{filteredOptions ? (
|
|
||||||
filteredOptions.length > 0 ? (
|
|
||||||
filteredOptions.map((option) => (
|
|
||||||
<Combobox.Option
|
|
||||||
key={option.id}
|
|
||||||
value={option.id}
|
|
||||||
className={({ active, selected }) =>
|
|
||||||
`cursor-pointer select-none truncate rounded px-1 py-1.5 ${
|
|
||||||
active || selected ? "bg-custom-background-80" : ""
|
|
||||||
} ${selected ? "text-custom-text-100" : "text-custom-text-200"}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{({ selected }) => (
|
|
||||||
<div className="flex justify-between items-center gap-1 w-full px-1">
|
|
||||||
<div className="line-clamp-1">{option.title}</div>
|
|
||||||
{selected && (
|
|
||||||
<div className="flex-shrink-0 w-[13px] h-[13px] flex justify-center items-center">
|
|
||||||
<Check width={13} strokeWidth={2} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Combobox.Option>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<span className="flex items-center gap-2 p-1">
|
|
||||||
<p className="text-left text-custom-text-200 ">No matching results</p>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<p className="text-center text-custom-text-200">Loading...</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Combobox.Options>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="text-center text-custom-text-200">No options available.</p>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Combobox>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,6 +1,6 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// components
|
// components
|
||||||
import { LabelSelect } from "components/project";
|
import { LabelSelect } from "components/labels";
|
||||||
// types
|
// types
|
||||||
import { IIssueLabels } from "types";
|
import { IIssueLabels } from "types";
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { LabelSelect } from "components/project";
|
import { LabelSelect } from "components/labels";
|
||||||
// hooks
|
// hooks
|
||||||
import useSubIssue from "hooks/use-sub-issue";
|
import useSubIssue from "hooks/use-sub-issue";
|
||||||
// types
|
// types
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
// services
|
|
||||||
import { TrackEventService } from "services/track_event.service";
|
|
||||||
// ui
|
|
||||||
import { AssigneesList, Avatar } from "components/ui";
|
|
||||||
import { CustomSearchSelect, Tooltip } from "@plane/ui";
|
|
||||||
import { User2 } from "lucide-react";
|
|
||||||
// types
|
|
||||||
import { IUser, IIssue } from "types";
|
|
||||||
// hooks
|
|
||||||
import useProjectMembers from "hooks/use-project-members";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
issue: IIssue;
|
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issue: IIssue) => void;
|
|
||||||
position?: "left" | "right";
|
|
||||||
tooltipPosition?: "top" | "bottom";
|
|
||||||
selfPositioned?: boolean;
|
|
||||||
customButton?: boolean;
|
|
||||||
user: IUser | undefined;
|
|
||||||
isNotAllowed: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const trackEventService = new TrackEventService();
|
|
||||||
|
|
||||||
export const ViewAssigneeSelect: React.FC<Props> = ({
|
|
||||||
issue,
|
|
||||||
partialUpdateIssue,
|
|
||||||
// position = "left",
|
|
||||||
// selfPositioned = false,
|
|
||||||
tooltipPosition = "top",
|
|
||||||
user,
|
|
||||||
isNotAllowed,
|
|
||||||
customButton = false,
|
|
||||||
}) => {
|
|
||||||
const [fetchAssignees, setFetchAssignees] = useState(false);
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug } = router.query;
|
|
||||||
|
|
||||||
const { members } = useProjectMembers(workspaceSlug?.toString(), issue.project, fetchAssignees);
|
|
||||||
|
|
||||||
const options = members?.map((member: any) => ({
|
|
||||||
value: member.member.id,
|
|
||||||
query: member.member.display_name,
|
|
||||||
content: (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Avatar user={member.member} />
|
|
||||||
{member.member.display_name}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const assigneeLabel = (
|
|
||||||
<Tooltip
|
|
||||||
position={tooltipPosition}
|
|
||||||
tooltipHeading="Assignees"
|
|
||||||
tooltipContent={
|
|
||||||
issue.assignee_details.length > 0
|
|
||||||
? issue.assignee_details.map((assignee) => assignee?.display_name).join(", ")
|
|
||||||
: "No Assignee"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`flex ${
|
|
||||||
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
|
||||||
} items-center gap-2 text-custom-text-200`}
|
|
||||||
>
|
|
||||||
{issue.assignees && issue.assignees.length > 0 && Array.isArray(issue.assignees) ? (
|
|
||||||
<div className="-my-0.5 flex items-center justify-center gap-2">
|
|
||||||
<AssigneesList userIds={issue.assignees} length={3} showLength={true} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center justify-center gap-2 px-1.5 py-1 rounded shadow-sm border border-custom-border-300">
|
|
||||||
<User2 className="h-4 w-4" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CustomSearchSelect
|
|
||||||
value={issue.assignees}
|
|
||||||
buttonClassName="!p-0"
|
|
||||||
onChange={(data: any) => {
|
|
||||||
const newData = issue.assignees ?? [];
|
|
||||||
|
|
||||||
if (newData.includes(data)) newData.splice(newData.indexOf(data), 1);
|
|
||||||
else newData.push(data);
|
|
||||||
|
|
||||||
partialUpdateIssue({ assignees: data }, issue);
|
|
||||||
|
|
||||||
trackEventService.trackIssuePartialPropertyUpdateEvent(
|
|
||||||
{
|
|
||||||
workspaceSlug,
|
|
||||||
workspaceId: issue.workspace,
|
|
||||||
projectId: issue.project_detail.id,
|
|
||||||
projectIdentifier: issue.project_detail.identifier,
|
|
||||||
projectName: issue.project_detail.name,
|
|
||||||
issueId: issue.id,
|
|
||||||
},
|
|
||||||
"ISSUE_PROPERTY_UPDATE_ASSIGNEE",
|
|
||||||
user as IUser
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
options={options}
|
|
||||||
{...(customButton ? { customButton: assigneeLabel } : { label: assigneeLabel })}
|
|
||||||
multiple
|
|
||||||
noChevron
|
|
||||||
disabled={isNotAllowed}
|
|
||||||
onOpen={() => setFetchAssignees(true)}
|
|
||||||
width="w-full min-w-[12rem]"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,6 +1,3 @@
|
|||||||
export * from "./assignee";
|
|
||||||
export * from "./due-date";
|
export * from "./due-date";
|
||||||
export * from "./estimate";
|
export * from "./estimate";
|
||||||
export * from "./label";
|
export * from "./start-date";
|
||||||
export * from "./priority";
|
|
||||||
export * from "./start-date";
|
|
||||||
|
@ -1,154 +0,0 @@
|
|||||||
import { useState, FC } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import useSWR from "swr";
|
|
||||||
// services
|
|
||||||
import { IssueLabelService } from "services/issue";
|
|
||||||
// component
|
|
||||||
import { CreateLabelModal } from "components/labels";
|
|
||||||
// ui
|
|
||||||
import { CustomSearchSelect, Tooltip } from "@plane/ui";
|
|
||||||
// icons
|
|
||||||
import { Plus, Tag } from "lucide-react";
|
|
||||||
// types
|
|
||||||
import { IUser, IIssue, IIssueLabels } from "types";
|
|
||||||
// fetch-keys
|
|
||||||
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
issue: IIssue;
|
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issue: IIssue) => void;
|
|
||||||
position?: "left" | "right";
|
|
||||||
selfPositioned?: boolean;
|
|
||||||
tooltipPosition?: "top" | "bottom";
|
|
||||||
customButton?: boolean;
|
|
||||||
user: IUser | undefined;
|
|
||||||
isNotAllowed: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const issueLabelStore = new IssueLabelService();
|
|
||||||
|
|
||||||
export const ViewLabelSelect: FC<Props> = ({
|
|
||||||
issue,
|
|
||||||
partialUpdateIssue,
|
|
||||||
// position = "left",
|
|
||||||
// selfPositioned = false,
|
|
||||||
tooltipPosition = "top",
|
|
||||||
user,
|
|
||||||
isNotAllowed,
|
|
||||||
customButton = false,
|
|
||||||
}) => {
|
|
||||||
const [labelModal, setLabelModal] = useState(false);
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug, projectId } = router.query;
|
|
||||||
|
|
||||||
const { data: issueLabels } = useSWR<IIssueLabels[]>(
|
|
||||||
projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null,
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? () => issueLabelStore.getProjectIssueLabels(workspaceSlug as string, projectId as string)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
const options = issueLabels?.map((label) => ({
|
|
||||||
value: label.id,
|
|
||||||
query: label.name,
|
|
||||||
content: (
|
|
||||||
<div className="flex items-center justify-start gap-2">
|
|
||||||
<span
|
|
||||||
className="h-2.5 w-2.5 flex-shrink-0 rounded-full"
|
|
||||||
style={{
|
|
||||||
backgroundColor: label.color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>{label.name}</span>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const labelsLabel = (
|
|
||||||
<Tooltip
|
|
||||||
position={tooltipPosition}
|
|
||||||
tooltipHeading="Labels"
|
|
||||||
tooltipContent={
|
|
||||||
issue.labels.length > 0
|
|
||||||
? issue.labels
|
|
||||||
.map((labelId) => {
|
|
||||||
const label = issueLabels?.find((l) => l.id === labelId);
|
|
||||||
|
|
||||||
return label?.name ?? "";
|
|
||||||
})
|
|
||||||
.join(", ")
|
|
||||||
: "No label"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`flex ${
|
|
||||||
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
|
||||||
} items-center gap-2 text-custom-text-200`}
|
|
||||||
>
|
|
||||||
{issue.labels.length > 0 ? (
|
|
||||||
<>
|
|
||||||
{issue.labels.slice(0, 4).map((labelId, index) => {
|
|
||||||
const label = issueLabels?.find((l) => l.id === labelId);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`flex h-4 w-4 rounded-full ${index ? "-ml-3.5" : ""}`}>
|
|
||||||
<span
|
|
||||||
className={`h-4 w-4 flex-shrink-0 rounded-full border group-hover:bg-custom-background-80 border-custom-border-200`}
|
|
||||||
style={{
|
|
||||||
backgroundColor: label?.color ?? "#000000",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{issue.labels.length > 4 ? <span>+{issue.labels.length - 4}</span> : null}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Tag className="h-3.5 w-3.5 text-custom-text-200" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
|
|
||||||
const footerOption = (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="flex w-full select-none items-center rounded py-2 px-1 hover:bg-custom-background-80"
|
|
||||||
onClick={() => setLabelModal(true)}
|
|
||||||
>
|
|
||||||
<span className="flex items-center justify-start gap-1 text-custom-text-200">
|
|
||||||
<Plus className="h-4 w-4" aria-hidden="true" />
|
|
||||||
<span>Create New Label</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{projectId && (
|
|
||||||
<CreateLabelModal
|
|
||||||
isOpen={labelModal}
|
|
||||||
handleClose={() => setLabelModal(false)}
|
|
||||||
projectId={projectId.toString()}
|
|
||||||
user={user}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<CustomSearchSelect
|
|
||||||
value={issue.labels}
|
|
||||||
onChange={(data: string[]) => {
|
|
||||||
partialUpdateIssue({ labels: data }, issue);
|
|
||||||
}}
|
|
||||||
options={options}
|
|
||||||
{...(customButton ? { customButton: labelsLabel } : { label: labelsLabel })}
|
|
||||||
multiple
|
|
||||||
noChevron
|
|
||||||
disabled={isNotAllowed}
|
|
||||||
footerOption={footerOption}
|
|
||||||
width="w-full min-w-[12rem]"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,106 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
// services
|
|
||||||
import { TrackEventService } from "services/track_event.service";
|
|
||||||
// ui
|
|
||||||
import { CustomSelect, Tooltip, PriorityIcon } from "@plane/ui";
|
|
||||||
// helpers
|
|
||||||
import { capitalizeFirstLetter } from "helpers/string.helper";
|
|
||||||
// types
|
|
||||||
import { IUser, IIssue, TIssuePriorities } from "types";
|
|
||||||
// constants
|
|
||||||
import { PRIORITIES } from "constants/project";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
issue: IIssue;
|
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issue: IIssue) => void;
|
|
||||||
position?: "left" | "right";
|
|
||||||
tooltipPosition?: "top" | "bottom";
|
|
||||||
selfPositioned?: boolean;
|
|
||||||
noBorder?: boolean;
|
|
||||||
user: IUser | undefined;
|
|
||||||
isNotAllowed: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const trackEventService = new TrackEventService();
|
|
||||||
|
|
||||||
export const ViewPrioritySelect: React.FC<Props> = ({
|
|
||||||
issue,
|
|
||||||
partialUpdateIssue,
|
|
||||||
// position = "left",
|
|
||||||
tooltipPosition = "top",
|
|
||||||
// selfPositioned = false,
|
|
||||||
noBorder = false,
|
|
||||||
user,
|
|
||||||
isNotAllowed,
|
|
||||||
}) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug } = router.query;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CustomSelect
|
|
||||||
value={issue.priority}
|
|
||||||
onChange={(data: TIssuePriorities) => {
|
|
||||||
partialUpdateIssue({ priority: data }, issue);
|
|
||||||
trackEventService.trackIssuePartialPropertyUpdateEvent(
|
|
||||||
{
|
|
||||||
workspaceSlug,
|
|
||||||
workspaceId: issue.workspace,
|
|
||||||
projectId: issue.project_detail.id,
|
|
||||||
projectIdentifier: issue.project_detail.identifier,
|
|
||||||
projectName: issue.project_detail.name,
|
|
||||||
issueId: issue.id,
|
|
||||||
},
|
|
||||||
"ISSUE_PROPERTY_UPDATE_PRIORITY",
|
|
||||||
user as IUser
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
maxHeight="md"
|
|
||||||
customButton={
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`grid place-items-center rounded ${isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"} ${
|
|
||||||
noBorder ? "" : "h-6 w-6 border shadow-sm"
|
|
||||||
} ${
|
|
||||||
noBorder
|
|
||||||
? ""
|
|
||||||
: issue.priority === "urgent"
|
|
||||||
? "border-red-500/20 bg-red-500"
|
|
||||||
: "border-custom-border-300 bg-custom-background-100"
|
|
||||||
} items-center`}
|
|
||||||
>
|
|
||||||
<Tooltip tooltipHeading="Priority" tooltipContent={issue.priority ?? "None"} position={tooltipPosition}>
|
|
||||||
<span className="flex gap-1 items-center text-custom-text-200 text-xs">
|
|
||||||
<PriorityIcon
|
|
||||||
priority={issue.priority}
|
|
||||||
className={`h-3.5 w-3.5 ${
|
|
||||||
issue.priority === "urgent"
|
|
||||||
? "text-white"
|
|
||||||
: issue.priority === "high"
|
|
||||||
? "text-orange-500"
|
|
||||||
: issue.priority === "medium"
|
|
||||||
? "text-yellow-500"
|
|
||||||
: issue.priority === "low"
|
|
||||||
? "text-green-500"
|
|
||||||
: "text-custom-text-200"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
{noBorder ? capitalizeFirstLetter(issue.priority ?? "None") : ""}
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
noChevron
|
|
||||||
disabled={isNotAllowed}
|
|
||||||
>
|
|
||||||
{PRIORITIES?.map((priority) => (
|
|
||||||
<CustomSelect.Option key={priority} value={priority} className="capitalize">
|
|
||||||
<>
|
|
||||||
<PriorityIcon priority={priority} className="h-3.5 w-3.5" />
|
|
||||||
{priority ?? "None"}
|
|
||||||
</>
|
|
||||||
</CustomSelect.Option>
|
|
||||||
))}
|
|
||||||
</CustomSelect>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,6 +1,7 @@
|
|||||||
export * from "./create-label-modal";
|
export * from "./create-label-modal";
|
||||||
export * from "./create-update-label-inline";
|
export * from "./create-update-label-inline";
|
||||||
export * from "./delete-label-modal";
|
export * from "./delete-label-modal";
|
||||||
|
export * from "./label-select";
|
||||||
export * from "./labels-list-modal";
|
export * from "./labels-list-modal";
|
||||||
export * from "./single-label-group";
|
export * from "./single-label-group";
|
||||||
export * from "./single-label";
|
export * from "./single-label";
|
||||||
|
@ -7,7 +7,6 @@ export * from "./delete-project-modal";
|
|||||||
export * from "./form-loader";
|
export * from "./form-loader";
|
||||||
export * from "./form";
|
export * from "./form";
|
||||||
export * from "./join-project-modal";
|
export * from "./join-project-modal";
|
||||||
export * from "./label-select";
|
|
||||||
export * from "./leave-project-modal";
|
export * from "./leave-project-modal";
|
||||||
export * from "./member-select";
|
export * from "./member-select";
|
||||||
export * from "./members-select";
|
export * from "./members-select";
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
import { createContext, useCallback, useReducer, useEffect } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
// swr
|
|
||||||
import useSWR from "swr";
|
|
||||||
// components
|
|
||||||
import ToastAlert from "components/toast-alert";
|
|
||||||
// hooks
|
|
||||||
import useUserAuth from "hooks/use-user-auth";
|
|
||||||
// services
|
|
||||||
import { ProjectService } from "services/project";
|
|
||||||
// fetch-keys
|
|
||||||
import { USER_PROJECT_VIEW } from "constants/fetch-keys";
|
|
||||||
// helper
|
|
||||||
import { applyTheme, unsetCustomCssVariables } from "helpers/theme.helper";
|
|
||||||
// constants
|
|
||||||
|
|
||||||
export const themeContext = createContext<ContextType>({} as ContextType);
|
|
||||||
|
|
||||||
// services
|
|
||||||
const projectService = new ProjectService();
|
|
||||||
|
|
||||||
type ThemeProps = {
|
|
||||||
collapsed: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ReducerActionType = {
|
|
||||||
type: "TOGGLE_SIDEBAR" | "REHYDRATE_THEME";
|
|
||||||
payload?: Partial<ThemeProps>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
collapsed: boolean;
|
|
||||||
toggleCollapsed: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
type StateType = {
|
|
||||||
collapsed: boolean;
|
|
||||||
};
|
|
||||||
type ReducerFunctionType = (state: StateType, action: ReducerActionType) => StateType;
|
|
||||||
|
|
||||||
export const initialState: StateType = {
|
|
||||||
collapsed: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const reducer: ReducerFunctionType = (state, action) => {
|
|
||||||
const { type, payload } = action;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "TOGGLE_SIDEBAR":
|
|
||||||
const newState = {
|
|
||||||
...state,
|
|
||||||
collapsed: !state.collapsed,
|
|
||||||
};
|
|
||||||
localStorage.setItem("collapsed", JSON.stringify(newState.collapsed));
|
|
||||||
return newState;
|
|
||||||
|
|
||||||
case "REHYDRATE_THEME": {
|
|
||||||
let collapsed: any = localStorage.getItem("collapsed");
|
|
||||||
collapsed = collapsed ? JSON.parse(collapsed) : false;
|
|
||||||
return { ...initialState, ...payload, collapsed };
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
|
||||||
const { user } = useUserAuth(null);
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug, projectId } = router.query;
|
|
||||||
|
|
||||||
const { data: myViewProps } = useSWR(
|
|
||||||
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId as string) : null,
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? () => projectService.projectMemberMe(workspaceSlug as string, projectId as string)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
const toggleCollapsed = useCallback(() => {
|
|
||||||
dispatch({
|
|
||||||
type: "TOGGLE_SIDEBAR",
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({
|
|
||||||
type: "REHYDRATE_THEME",
|
|
||||||
payload: myViewProps?.view_props as any,
|
|
||||||
});
|
|
||||||
}, [myViewProps]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const theme = localStorage.getItem("theme");
|
|
||||||
|
|
||||||
if (theme) {
|
|
||||||
if (theme === "custom") {
|
|
||||||
if (user && user.theme.palette) {
|
|
||||||
applyTheme(
|
|
||||||
user.theme.palette !== ",,,," ? user.theme.palette : "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
|
||||||
user.theme.darkPalette
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else unsetCustomCssVariables();
|
|
||||||
}
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<themeContext.Provider
|
|
||||||
value={{
|
|
||||||
collapsed: state.collapsed,
|
|
||||||
toggleCollapsed,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToastAlert />
|
|
||||||
{children}
|
|
||||||
</themeContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,50 +0,0 @@
|
|||||||
import { FC, ReactElement, createContext } from "react";
|
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
|
|
||||||
import useSWR from "swr";
|
|
||||||
|
|
||||||
// services
|
|
||||||
import { WorkspaceService } from "services/workspace.service";
|
|
||||||
// types
|
|
||||||
import { IWorkspace } from "types";
|
|
||||||
// constants
|
|
||||||
import { USER_WORKSPACES } from "constants/fetch-keys";
|
|
||||||
|
|
||||||
export interface WorkspaceProviderProps {
|
|
||||||
children: ReactElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WorkspaceContextProps {
|
|
||||||
workspaces: IWorkspace[];
|
|
||||||
activeWorkspace: IWorkspace | undefined;
|
|
||||||
mutateWorkspaces: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// services
|
|
||||||
const workspaceService = new WorkspaceService();
|
|
||||||
|
|
||||||
export const WorkspaceContext = createContext<WorkspaceContextProps>({} as WorkspaceContextProps);
|
|
||||||
|
|
||||||
export const WorkspaceProvider: FC<WorkspaceProviderProps> = (props) => {
|
|
||||||
const { children } = props;
|
|
||||||
// router
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug } = router.query;
|
|
||||||
// API to fetch user information
|
|
||||||
const { data = [], error, mutate } = useSWR<IWorkspace[]>(USER_WORKSPACES, () => workspaceService.userWorkspaces());
|
|
||||||
// active workspace
|
|
||||||
const activeWorkspace = data?.find((w) => w.slug === workspaceSlug);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WorkspaceContext.Provider
|
|
||||||
value={{
|
|
||||||
workspaces: error ? [] : data,
|
|
||||||
activeWorkspace,
|
|
||||||
mutateWorkspaces: mutate,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</WorkspaceContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user