mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: store setup
This commit is contained in:
parent
50c330db65
commit
a328c530d0
@ -7,19 +7,14 @@ import analyticsService from "services/analytics.service";
|
||||
import projectService from "services/project.service";
|
||||
import cyclesService from "services/cycles.service";
|
||||
import modulesService from "services/modules.service";
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// hooks
|
||||
import useProjects from "hooks/use-projects";
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
import { PrimaryButton, SecondaryButton } from "components/ui";
|
||||
// icons
|
||||
import {
|
||||
ArrowDownTrayIcon,
|
||||
ArrowPathIcon,
|
||||
CalendarDaysIcon,
|
||||
UserGroupIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { ArrowDownTrayIcon, ArrowPathIcon, CalendarDaysIcon, UserGroupIcon } from "@heroicons/react/24/outline";
|
||||
import { ContrastIcon, LayerDiagonalIcon } from "components/icons";
|
||||
// helpers
|
||||
import { renderShortDate } from "helpers/date-time.helper";
|
||||
@ -46,13 +41,7 @@ type Props = {
|
||||
user: ICurrentUserResponse | undefined;
|
||||
};
|
||||
|
||||
export const AnalyticsSidebar: React.FC<Props> = ({
|
||||
analytics,
|
||||
params,
|
||||
fullScreen,
|
||||
isProjectLevel = false,
|
||||
user,
|
||||
}) => {
|
||||
export const AnalyticsSidebar: React.FC<Props> = ({ analytics, params, fullScreen, isProjectLevel = false, user }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
||||
|
||||
@ -61,9 +50,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { data: projectDetails } = useSWR(
|
||||
workspaceSlug && projectId && !(cycleId || moduleId)
|
||||
? PROJECT_DETAILS(projectId.toString())
|
||||
: null,
|
||||
workspaceSlug && projectId && !(cycleId || moduleId) ? PROJECT_DETAILS(projectId.toString()) : null,
|
||||
workspaceSlug && projectId && !(cycleId || moduleId)
|
||||
? () => projectService.getProject(workspaceSlug.toString(), projectId.toString())
|
||||
: null
|
||||
@ -72,24 +59,14 @@ export const AnalyticsSidebar: React.FC<Props> = ({
|
||||
const { data: cycleDetails } = useSWR(
|
||||
workspaceSlug && projectId && cycleId ? CYCLE_DETAILS(cycleId.toString()) : null,
|
||||
workspaceSlug && projectId && cycleId
|
||||
? () =>
|
||||
cyclesService.getCycleDetails(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
cycleId.toString()
|
||||
)
|
||||
? () => cyclesService.getCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: moduleDetails } = useSWR(
|
||||
workspaceSlug && projectId && moduleId ? MODULE_DETAILS(moduleId.toString()) : null,
|
||||
workspaceSlug && projectId && moduleId
|
||||
? () =>
|
||||
modulesService.getModuleDetails(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
moduleId.toString()
|
||||
)
|
||||
? () => modulesService.getModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
@ -178,8 +155,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const selectedProjects =
|
||||
params.project && params.project.length > 0 ? params.project : projects?.map((p) => p.id);
|
||||
const selectedProjects = params.project && params.project.length > 0 ? params.project : projects?.map((p) => p.id);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -236,9 +212,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
|
||||
)}
|
||||
<h5 className="flex items-center gap-1">
|
||||
<p className="break-words">{truncateText(project.name, 20)}</p>
|
||||
<span className="text-custom-text-200 text-xs ml-1">
|
||||
({project.identifier})
|
||||
</span>
|
||||
<span className="text-custom-text-200 text-xs ml-1">({project.identifier})</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div className="mt-4 space-y-3 pl-2 w-full">
|
||||
@ -344,10 +318,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
|
||||
<div className="space-y-4 mt-4">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<h6 className="text-custom-text-200">Network</h6>
|
||||
<span>
|
||||
{NETWORK_CHOICES.find((n) => n.key === projectDetails?.network)?.label ??
|
||||
""}
|
||||
</span>
|
||||
<span>{NETWORK_CHOICES.find((n) => n.key === projectDetails?.network)?.label ?? ""}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -13,15 +13,11 @@ import analyticsService from "services/analytics.service";
|
||||
import projectService from "services/project.service";
|
||||
import cyclesService from "services/cycles.service";
|
||||
import modulesService from "services/modules.service";
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// components
|
||||
import { CustomAnalytics, ScopeAndDemand } from "components/analytics";
|
||||
// icons
|
||||
import {
|
||||
ArrowsPointingInIcon,
|
||||
ArrowsPointingOutIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { ArrowsPointingInIcon, ArrowsPointingOutIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { IAnalyticsParams, IWorkspace } from "types";
|
||||
// fetch-keys
|
||||
@ -67,9 +63,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
||||
);
|
||||
|
||||
const { data: projectDetails } = useSWR(
|
||||
workspaceSlug && projectId && !(cycleId || moduleId)
|
||||
? PROJECT_DETAILS(projectId.toString())
|
||||
: null,
|
||||
workspaceSlug && projectId && !(cycleId || moduleId) ? PROJECT_DETAILS(projectId.toString()) : null,
|
||||
workspaceSlug && projectId && !(cycleId || moduleId)
|
||||
? () => projectService.getProject(workspaceSlug.toString(), projectId.toString())
|
||||
: null
|
||||
@ -78,24 +72,14 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
||||
const { data: cycleDetails } = useSWR(
|
||||
workspaceSlug && projectId && cycleId ? CYCLE_DETAILS(cycleId.toString()) : null,
|
||||
workspaceSlug && projectId && cycleId
|
||||
? () =>
|
||||
cyclesService.getCycleDetails(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
cycleId.toString()
|
||||
)
|
||||
? () => cyclesService.getCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: moduleDetails } = useSWR(
|
||||
workspaceSlug && projectId && moduleId ? MODULE_DETAILS(moduleId.toString()) : null,
|
||||
workspaceSlug && projectId && moduleId
|
||||
? () =>
|
||||
modulesService.getModuleDetails(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
moduleId.toString()
|
||||
)
|
||||
? () => modulesService.getModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
@ -134,8 +118,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
||||
eventPayload.moduleName = moduleDetails.name;
|
||||
}
|
||||
|
||||
const eventType =
|
||||
tab === "Scope and Demand" ? "SCOPE_AND_DEMAND_ANALYTICS" : "CUSTOM_ANALYTICS";
|
||||
const eventType = tab === "Scope and Demand" ? "SCOPE_AND_DEMAND_ANALYTICS" : "CUSTOM_ANALYTICS";
|
||||
|
||||
trackEventServices.trackAnalyticsEvent(
|
||||
eventPayload,
|
||||
@ -150,9 +133,9 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`absolute top-0 z-30 h-full bg-custom-background-90 ${
|
||||
fullScreen ? "p-2 w-full" : "w-1/2"
|
||||
} ${isOpen ? "right-0" : "-right-full"} duration-300 transition-all`}
|
||||
className={`absolute top-0 z-30 h-full bg-custom-background-90 ${fullScreen ? "p-2 w-full" : "w-1/2"} ${
|
||||
isOpen ? "right-0" : "-right-full"
|
||||
} duration-300 transition-all`}
|
||||
>
|
||||
<div
|
||||
className={`flex h-full flex-col overflow-hidden border-custom-border-200 bg-custom-background-100 text-left ${
|
||||
@ -161,8 +144,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
||||
>
|
||||
<div className="flex items-center justify-between gap-4 bg-custom-background-100 px-5 py-4 text-sm">
|
||||
<h3 className="break-words">
|
||||
Analytics for{" "}
|
||||
{cycleId ? cycleDetails?.name : moduleId ? moduleDetails?.name : projectDetails?.name}
|
||||
Analytics for {cycleId ? cycleDetails?.name : moduleId ? moduleDetails?.name : projectDetails?.name}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
|
@ -12,7 +12,7 @@ import { Squares2X2Icon } from "@heroicons/react/24/outline";
|
||||
import { StateGroupIcon } from "components/icons";
|
||||
import { ArchiveX } from "lucide-react";
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// constants
|
||||
import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
|
||||
import { STATES_LIST } from "constants/fetch-keys";
|
||||
@ -34,9 +34,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
||||
|
||||
const { data: stateGroups } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
const states = getStatesList(stateGroups);
|
||||
|
||||
@ -57,9 +55,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
||||
|
||||
const defaultState = stateGroups && stateGroups.cancelled ? stateGroups.cancelled[0].id : null;
|
||||
|
||||
const selectedOption = states?.find(
|
||||
(s) => s.id === projectDetails?.default_state ?? defaultState
|
||||
);
|
||||
const selectedOption = states?.find((s) => s.id === projectDetails?.default_state ?? defaultState);
|
||||
const currentDefaultState = states?.find((s) => s.id === defaultState);
|
||||
|
||||
const initialValues: Partial<IProject> = {
|
||||
@ -105,15 +101,11 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
||||
<div className="ml-12">
|
||||
<div className="flex flex-col rounded bg-custom-background-90 border border-custom-border-200 p-2">
|
||||
<div className="flex items-center justify-between px-5 py-4 gap-2 w-full">
|
||||
<div className="w-1/2 text-sm font-medium">
|
||||
Auto-close issues that are inactive for
|
||||
</div>
|
||||
<div className="w-1/2 text-sm font-medium">Auto-close issues that are inactive for</div>
|
||||
<div className="w-1/2">
|
||||
<CustomSelect
|
||||
value={projectDetails?.close_in}
|
||||
label={`${projectDetails?.close_in} ${
|
||||
projectDetails?.close_in === 1 ? "Month" : "Months"
|
||||
}`}
|
||||
label={`${projectDetails?.close_in} ${projectDetails?.close_in === 1 ? "Month" : "Months"}`}
|
||||
onChange={(val: number) => {
|
||||
handleChange({ close_in: val });
|
||||
}}
|
||||
@ -142,9 +134,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
||||
<div className="w-1/2 text-sm font-medium">Auto-close Status</div>
|
||||
<div className="w-1/2 ">
|
||||
<CustomSearchSelect
|
||||
value={
|
||||
projectDetails?.default_state ? projectDetails?.default_state : defaultState
|
||||
}
|
||||
value={projectDetails?.default_state ? projectDetails?.default_state : defaultState}
|
||||
label={
|
||||
<div className="flex items-center gap-2">
|
||||
{selectedOption ? (
|
||||
@ -166,9 +156,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
||||
)}
|
||||
{selectedOption?.name
|
||||
? selectedOption.name
|
||||
: currentDefaultState?.name ?? (
|
||||
<span className="text-custom-text-200">State</span>
|
||||
)}
|
||||
: currentDefaultState?.name ?? <span className="text-custom-text-200">State</span>}
|
||||
</div>
|
||||
}
|
||||
onChange={(val: string) => {
|
||||
|
@ -10,7 +10,7 @@ import { Command } from "cmdk";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import workspaceService from "services/workspace.service";
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import inboxService from "services/inbox.service";
|
||||
// hooks
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
@ -79,16 +79,13 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
const { data: issueDetails } = useSWR(
|
||||
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
||||
workspaceSlug && projectId && issueId
|
||||
? () =>
|
||||
issuesService.retrieve(workspaceSlug as string, projectId as string, issueId as string)
|
||||
? () => issuesService.retrieve(workspaceSlug as string, projectId as string, issueId as string)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: inboxList } = useSWR(
|
||||
workspaceSlug && projectId ? INBOX_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => inboxService.getInboxes(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => inboxService.getInboxes(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
|
||||
const updateIssue = useCallback(
|
||||
@ -272,8 +269,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
>
|
||||
{issueDetails && (
|
||||
<div className="overflow-hidden truncate rounded-md bg-custom-background-80 p-2 text-xs font-medium text-custom-text-200">
|
||||
{issueDetails.project_detail.identifier}-{issueDetails.sequence_id}{" "}
|
||||
{issueDetails.name}
|
||||
{issueDetails.project_detail.identifier}-{issueDetails.sequence_id} {issueDetails.name}
|
||||
</div>
|
||||
)}
|
||||
{projectId && (
|
||||
@ -324,12 +320,9 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
</h5>
|
||||
)}
|
||||
|
||||
{!isLoading &&
|
||||
resultsCount === 0 &&
|
||||
searchTerm !== "" &&
|
||||
debouncedSearchTerm !== "" && (
|
||||
<div className="my-4 text-center text-custom-text-200">No results found.</div>
|
||||
)}
|
||||
{!isLoading && resultsCount === 0 && searchTerm !== "" && debouncedSearchTerm !== "" && (
|
||||
<div className="my-4 text-center text-custom-text-200">No results found.</div>
|
||||
)}
|
||||
|
||||
{(isLoading || isSearching) && (
|
||||
<Command.Loading>
|
||||
@ -362,9 +355,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
>
|
||||
<div className="flex items-center gap-2 overflow-hidden text-custom-text-200">
|
||||
<Icon iconName={currentSection.icon} />
|
||||
<p className="block flex-1 truncate">
|
||||
{currentSection.itemName(item)}
|
||||
</p>
|
||||
<p className="block flex-1 truncate">{currentSection.itemName(item)}</p>
|
||||
</div>
|
||||
</Command.Item>
|
||||
))}
|
||||
@ -577,9 +568,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
redirect(
|
||||
`/${workspaceSlug}/projects/${projectId}/inbox/${inboxList?.[0]?.id}`
|
||||
);
|
||||
redirect(`/${workspaceSlug}/projects/${projectId}/inbox/${inboxList?.[0]?.id}`);
|
||||
}}
|
||||
className="focus:outline-none"
|
||||
>
|
||||
@ -672,10 +661,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
window.open(
|
||||
"https://github.com/makeplane/plane/issues/new/choose",
|
||||
"_blank"
|
||||
);
|
||||
window.open("https://github.com/makeplane/plane/issues/new/choose", "_blank");
|
||||
}}
|
||||
className="focus:outline-none"
|
||||
>
|
||||
@ -759,29 +745,15 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
</>
|
||||
)}
|
||||
{page === "change-issue-state" && issueDetails && (
|
||||
<ChangeIssueState
|
||||
issue={issueDetails}
|
||||
setIsPaletteOpen={setIsPaletteOpen}
|
||||
user={user}
|
||||
/>
|
||||
<ChangeIssueState issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} />
|
||||
)}
|
||||
{page === "change-issue-priority" && issueDetails && (
|
||||
<ChangeIssuePriority
|
||||
issue={issueDetails}
|
||||
setIsPaletteOpen={setIsPaletteOpen}
|
||||
user={user}
|
||||
/>
|
||||
<ChangeIssuePriority issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} />
|
||||
)}
|
||||
{page === "change-issue-assignee" && issueDetails && (
|
||||
<ChangeIssueAssignee
|
||||
issue={issueDetails}
|
||||
setIsPaletteOpen={setIsPaletteOpen}
|
||||
user={user}
|
||||
/>
|
||||
)}
|
||||
{page === "change-interface-theme" && (
|
||||
<ChangeInterfaceTheme setIsPaletteOpen={setIsPaletteOpen} />
|
||||
<ChangeIssueAssignee issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} />
|
||||
)}
|
||||
{page === "change-interface-theme" && <ChangeInterfaceTheme setIsPaletteOpen={setIsPaletteOpen} />}
|
||||
</Command.List>
|
||||
</Command>
|
||||
</Dialog.Panel>
|
||||
|
@ -17,7 +17,7 @@ import { CreateUpdatePageModal } from "components/pages";
|
||||
// helpers
|
||||
import { copyTextToClipboard } from "helpers/string.helper";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import inboxService from "services/inbox.service";
|
||||
// fetch keys
|
||||
import { INBOX_LIST, ISSUE_DETAILS } from "constants/fetch-keys";
|
||||
@ -50,8 +50,7 @@ export const CommandPalette: React.FC = observer(() => {
|
||||
const { data: issueDetails } = useSWR(
|
||||
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
||||
workspaceSlug && projectId && issueId
|
||||
? () =>
|
||||
issuesService.retrieve(workspaceSlug as string, projectId as string, issueId as string)
|
||||
? () => issuesService.retrieve(workspaceSlug as string, projectId as string, issueId as string)
|
||||
: null
|
||||
);
|
||||
|
||||
@ -141,11 +140,7 @@ export const CommandPalette: React.FC = observer(() => {
|
||||
<>
|
||||
<ShortcutsModal isOpen={isShortcutsModalOpen} setIsOpen={setIsShortcutsModalOpen} />
|
||||
{workspaceSlug && (
|
||||
<CreateProjectModal
|
||||
isOpen={isProjectModalOpen}
|
||||
setIsOpen={setIsProjectModalOpen}
|
||||
user={user}
|
||||
/>
|
||||
<CreateProjectModal isOpen={isProjectModalOpen} setIsOpen={setIsProjectModalOpen} user={user} />
|
||||
)}
|
||||
{projectId && (
|
||||
<>
|
||||
@ -184,11 +179,7 @@ export const CommandPalette: React.FC = observer(() => {
|
||||
handleClose={() => setIsIssueModalOpen(false)}
|
||||
fieldsToShow={inboxId ? ["name", "description", "priority"] : ["all"]}
|
||||
prePopulateData={
|
||||
cycleId
|
||||
? { cycle: cycleId.toString() }
|
||||
: moduleId
|
||||
? { module: moduleId.toString() }
|
||||
: undefined
|
||||
cycleId ? { cycle: cycleId.toString() } : moduleId ? { module: moduleId.toString() } : undefined
|
||||
}
|
||||
/>
|
||||
<BulkDeleteIssuesModal
|
||||
@ -196,11 +187,7 @@ export const CommandPalette: React.FC = observer(() => {
|
||||
setIsOpen={setIsBulkDeleteIssuesModalOpen}
|
||||
user={user}
|
||||
/>
|
||||
<CommandK
|
||||
deleteIssue={deleteIssue}
|
||||
isPaletteOpen={isPaletteOpen}
|
||||
setIsPaletteOpen={setIsPaletteOpen}
|
||||
/>
|
||||
<CommandK deleteIssue={deleteIssue} isPaletteOpen={isPaletteOpen} setIsPaletteOpen={setIsPaletteOpen} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// cmdk
|
||||
import { Command } from "cmdk";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useProjectMembers from "hooks/use-project-members";
|
||||
// constants
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// cmdk
|
||||
import { Command } from "cmdk";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// types
|
||||
import { ICurrentUserResponse, IIssue, TIssuePriorities } from "types";
|
||||
// constants
|
||||
@ -64,11 +64,7 @@ export const ChangeIssuePriority: React.FC<Props> = ({ setIsPaletteOpen, issue,
|
||||
return (
|
||||
<>
|
||||
{PRIORITIES.map((priority) => (
|
||||
<Command.Item
|
||||
key={priority}
|
||||
onSelect={() => handleIssueState(priority)}
|
||||
className="focus:outline-none"
|
||||
>
|
||||
<Command.Item key={priority} onSelect={() => handleIssueState(priority)} className="focus:outline-none">
|
||||
<div className="flex items-center space-x-3">
|
||||
<PriorityIcon priority={priority} />
|
||||
<span className="capitalize">{priority ?? "None"}</span>
|
||||
|
@ -7,8 +7,8 @@ import useSWR, { mutate } from "swr";
|
||||
// cmdk
|
||||
import { Command } from "cmdk";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import stateService from "services/state.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// ui
|
||||
import { Spinner } from "components/ui";
|
||||
// icons
|
||||
@ -32,9 +32,7 @@ export const ChangeIssueState: React.FC<Props> = ({ setIsPaletteOpen, issue, use
|
||||
|
||||
const { data: stateGroups, mutate: mutateIssueDetails } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
const states = getStatesList(stateGroups);
|
||||
|
||||
@ -78,18 +76,9 @@ export const ChangeIssueState: React.FC<Props> = ({ setIsPaletteOpen, issue, use
|
||||
{states ? (
|
||||
states.length > 0 ? (
|
||||
states.map((state) => (
|
||||
<Command.Item
|
||||
key={state.id}
|
||||
onSelect={() => handleIssueState(state.id)}
|
||||
className="focus:outline-none"
|
||||
>
|
||||
<Command.Item key={state.id} onSelect={() => handleIssueState(state.id)} className="focus:outline-none">
|
||||
<div className="flex items-center space-x-3">
|
||||
<StateGroupIcon
|
||||
stateGroup={state.group}
|
||||
color={state.color}
|
||||
height="16px"
|
||||
width="16px"
|
||||
/>
|
||||
<StateGroupIcon stateGroup={state.group} color={state.color} height="16px" width="16px" />
|
||||
<p>{state.name}</p>
|
||||
</div>
|
||||
<div>{state.id === issue.state && <CheckIcon className="h-3 w-3" />}</div>
|
||||
|
@ -9,7 +9,7 @@ import { SubmitHandler, useForm } from "react-hook-form";
|
||||
// headless ui
|
||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesServices from "services/issues.service";
|
||||
import issuesServices from "services/issue.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
@ -49,12 +49,8 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user
|
||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||
|
||||
const { data: issues } = useSWR(
|
||||
workspaceSlug && projectId
|
||||
? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)
|
||||
: null,
|
||||
workspaceSlug && projectId
|
||||
? () => issuesServices.getIssues(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null,
|
||||
workspaceSlug && projectId ? () => issuesServices.getIssues(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
@ -155,9 +151,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user
|
||||
: issues?.filter(
|
||||
(issue) =>
|
||||
issue.name.toLowerCase().includes(query.toLowerCase()) ||
|
||||
`${issue.project_detail.identifier}-${issue.sequence_id}`
|
||||
.toLowerCase()
|
||||
.includes(query.toLowerCase())
|
||||
`${issue.project_detail.identifier}-${issue.sequence_id}`.toLowerCase().includes(query.toLowerCase())
|
||||
) ?? [];
|
||||
|
||||
return (
|
||||
|
@ -4,7 +4,7 @@ import { useRouter } from "next/router";
|
||||
import { useForm } from "react-hook-form";
|
||||
// services
|
||||
import aiService from "services/ai.service";
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
@ -110,9 +110,7 @@ export const GptAssistantModal: React.FC<Props> = ({
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message:
|
||||
error ||
|
||||
"You have reached the maximum number of requests of 50 requests per month per user.",
|
||||
message: error || "You have reached the maximum number of requests of 50 requests per month per user.",
|
||||
});
|
||||
else
|
||||
setToastAlert({
|
||||
@ -166,8 +164,7 @@ export const GptAssistantModal: React.FC<Props> = ({
|
||||
)}
|
||||
{invalidResponse && (
|
||||
<div className="text-sm text-red-500">
|
||||
No response could be generated. This may be due to insufficient content or task
|
||||
information. Please try again.
|
||||
No response could be generated. This may be due to insufficient content or task information. Please try again.
|
||||
</div>
|
||||
)}
|
||||
<Input
|
||||
@ -175,9 +172,7 @@ export const GptAssistantModal: React.FC<Props> = ({
|
||||
name="task"
|
||||
register={register}
|
||||
placeholder={`${
|
||||
content && content !== ""
|
||||
? "Tell AI what action to perform on this content..."
|
||||
: "Ask AI anything..."
|
||||
content && content !== "" ? "Tell AI what action to perform on this content..." : "Ask AI anything..."
|
||||
}`}
|
||||
autoComplete="off"
|
||||
/>
|
||||
@ -187,18 +182,8 @@ export const GptAssistantModal: React.FC<Props> = ({
|
||||
onClick={() => {
|
||||
onResponse(response);
|
||||
onClose();
|
||||
if (block)
|
||||
trackEventServices.trackUseGPTResponseEvent(
|
||||
block,
|
||||
"USE_GPT_RESPONSE_IN_PAGE_BLOCK",
|
||||
user
|
||||
);
|
||||
else if (issue)
|
||||
trackEventServices.trackUseGPTResponseEvent(
|
||||
issue,
|
||||
"USE_GPT_RESPONSE_IN_ISSUE",
|
||||
user
|
||||
);
|
||||
if (block) trackEventServices.trackUseGPTResponseEvent(block, "USE_GPT_RESPONSE_IN_PAGE_BLOCK", user);
|
||||
else if (issue) trackEventServices.trackUseGPTResponseEvent(issue, "USE_GPT_RESPONSE_IN_ISSUE", user);
|
||||
}}
|
||||
>
|
||||
Use this response
|
||||
@ -206,16 +191,8 @@ export const GptAssistantModal: React.FC<Props> = ({
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
<SecondaryButton onClick={onClose}>Close</SecondaryButton>
|
||||
<PrimaryButton
|
||||
type="button"
|
||||
onClick={handleSubmit(handleResponse)}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{isSubmitting
|
||||
? "Generating response..."
|
||||
: response === ""
|
||||
? "Generate response"
|
||||
: "Generate again"}
|
||||
<PrimaryButton type="button" onClick={handleSubmit(handleResponse)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Generating response..." : response === "" ? "Generate response" : "Generate again"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,18 +8,12 @@ import useSWR from "swr";
|
||||
import { DragDropContext, DropResult } from "react-beautiful-dnd";
|
||||
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// components
|
||||
import {
|
||||
AllLists,
|
||||
AllBoards,
|
||||
CalendarView,
|
||||
SpreadsheetView,
|
||||
GanttChartView,
|
||||
} from "components/core";
|
||||
import { AllLists, AllBoards, CalendarView, SpreadsheetView, GanttChartView } from "components/core";
|
||||
// ui
|
||||
import { EmptyState, Spinner } from "components/ui";
|
||||
// icons
|
||||
@ -88,9 +82,7 @@ export const AllViews: React.FC<Props> = ({
|
||||
|
||||
const { data: stateGroups } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
const states = getStatesList(stateGroups);
|
||||
|
||||
@ -180,9 +172,7 @@ export const AllViews: React.FC<Props> = ({
|
||||
userAuth={memberRole}
|
||||
/>
|
||||
) : (
|
||||
displayFilters?.layout === "gantt_chart" && (
|
||||
<GanttChartView disableUserActions={disableUserActions} />
|
||||
)
|
||||
displayFilters?.layout === "gantt_chart" && <GanttChartView disableUserActions={disableUserActions} />
|
||||
)}
|
||||
</>
|
||||
) : router.pathname.includes("archived-issues") ? (
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import projectService from "services/project.service";
|
||||
// hooks
|
||||
import useProjects from "hooks/use-projects";
|
||||
@ -106,12 +106,7 @@ export const BoardHeader: React.FC<Props> = ({
|
||||
switch (displayFilters?.group_by) {
|
||||
case "state":
|
||||
icon = currentState && (
|
||||
<StateGroupIcon
|
||||
stateGroup={currentState.group}
|
||||
color={currentState.color}
|
||||
height="16px"
|
||||
width="16px"
|
||||
/>
|
||||
<StateGroupIcon stateGroup={currentState.group} color={currentState.color} height="16px" width="16px" />
|
||||
);
|
||||
break;
|
||||
case "state_detail.group":
|
||||
@ -138,14 +133,8 @@ export const BoardHeader: React.FC<Props> = ({
|
||||
: null);
|
||||
break;
|
||||
case "labels":
|
||||
const labelColor =
|
||||
issueLabels?.find((label) => label.id === groupTitle)?.color ?? "#000000";
|
||||
icon = (
|
||||
<span
|
||||
className="h-3.5 w-3.5 flex-shrink-0 rounded-full"
|
||||
style={{ backgroundColor: labelColor }}
|
||||
/>
|
||||
);
|
||||
const labelColor = issueLabels?.find((label) => label.id === groupTitle)?.color ?? "#000000";
|
||||
icon = <span className="h-3.5 w-3.5 flex-shrink-0 rounded-full" style={{ backgroundColor: labelColor }} />;
|
||||
break;
|
||||
case "assignees":
|
||||
case "created_by":
|
||||
@ -196,10 +185,7 @@ export const BoardHeader: React.FC<Props> = ({
|
||||
}}
|
||||
>
|
||||
{isCollapsed ? (
|
||||
<Icon
|
||||
iconName="close_fullscreen"
|
||||
className="text-base font-medium text-custom-text-900"
|
||||
/>
|
||||
<Icon iconName="close_fullscreen" className="text-base font-medium text-custom-text-900" />
|
||||
) : (
|
||||
<Icon iconName="open_in_full" className="text-base font-medium text-custom-text-900" />
|
||||
)}
|
||||
|
@ -5,14 +5,9 @@ import { useRouter } from "next/router";
|
||||
import { mutate } from "swr";
|
||||
|
||||
// react-beautiful-dnd
|
||||
import {
|
||||
DraggableProvided,
|
||||
DraggableStateSnapshot,
|
||||
DraggingStyle,
|
||||
NotDraggingStyle,
|
||||
} from "react-beautiful-dnd";
|
||||
import { DraggableProvided, DraggableStateSnapshot, DraggingStyle, NotDraggingStyle } from "react-beautiful-dnd";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
@ -147,22 +142,17 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
);
|
||||
}
|
||||
|
||||
issuesService
|
||||
.patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user)
|
||||
.then(() => {
|
||||
mutateIssues();
|
||||
issuesService.patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user).then(() => {
|
||||
mutateIssues();
|
||||
|
||||
if (cycleId) mutate(CYCLE_DETAILS(cycleId as string));
|
||||
if (moduleId) mutate(MODULE_DETAILS(moduleId as string));
|
||||
});
|
||||
if (cycleId) mutate(CYCLE_DETAILS(cycleId as string));
|
||||
if (moduleId) mutate(MODULE_DETAILS(moduleId as string));
|
||||
});
|
||||
},
|
||||
[displayFilters, workspaceSlug, cycleId, moduleId, groupTitle, index, mutateIssues, user]
|
||||
);
|
||||
|
||||
const getStyle = (
|
||||
style: DraggingStyle | NotDraggingStyle | undefined,
|
||||
snapshot: DraggableStateSnapshot
|
||||
) => {
|
||||
const getStyle = (style: DraggingStyle | NotDraggingStyle | undefined, snapshot: DraggableStateSnapshot) => {
|
||||
if (displayFilters?.order_by === "sort_order") return style;
|
||||
if (!snapshot.isDragging) return {};
|
||||
if (!snapshot.isDropAnimating) return style;
|
||||
@ -174,11 +164,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const handleCopyText = () => {
|
||||
const originURL =
|
||||
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(
|
||||
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`
|
||||
).then(() => {
|
||||
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Link Copied!",
|
||||
@ -253,9 +240,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}>
|
||||
Open issue in new tab
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}>Open issue in new tab</ContextMenu.Item>
|
||||
</a>
|
||||
)}
|
||||
</ContextMenu>
|
||||
@ -277,9 +262,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
{!isNotAllowed && (
|
||||
<div
|
||||
ref={actionSectionRef}
|
||||
className={`z-1 absolute top-1.5 right-1.5 hidden group-hover/card:!flex ${
|
||||
isMenuActive ? "!flex" : ""
|
||||
}`}
|
||||
className={`z-1 absolute top-1.5 right-1.5 hidden group-hover/card:!flex ${isMenuActive ? "!flex" : ""}`}
|
||||
>
|
||||
{type && !isNotAllowed && (
|
||||
<CustomMenu
|
||||
@ -353,11 +336,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`flex items-center gap-2 text-xs ${
|
||||
isDropdownActive ? "" : "overflow-x-scroll"
|
||||
}`}
|
||||
>
|
||||
<div className={`flex items-center gap-2 text-xs ${isDropdownActive ? "" : "overflow-x-scroll"}`}>
|
||||
{properties.priority && (
|
||||
<ViewPrioritySelect
|
||||
issue={issue}
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// react-beautiful-dnd
|
||||
import { DragDropContext, DropResult } from "react-beautiful-dnd";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useCalendarIssuesView from "hooks/use-calendar-issues-view";
|
||||
// components
|
||||
@ -17,13 +17,7 @@ import { IssuePeekOverview } from "components/issues";
|
||||
import { Spinner } from "components/ui";
|
||||
// helpers
|
||||
import { renderDateFormat } from "helpers/date-time.helper";
|
||||
import {
|
||||
startOfWeek,
|
||||
lastDayOfWeek,
|
||||
eachDayOfInterval,
|
||||
weekDayInterval,
|
||||
formatDate,
|
||||
} from "helpers/calendar.helper";
|
||||
import { startOfWeek, lastDayOfWeek, eachDayOfInterval, weekDayInterval, formatDate } from "helpers/calendar.helper";
|
||||
// types
|
||||
import { ICalendarRange, ICurrentUserResponse, IIssue, UserAuth } from "types";
|
||||
// fetch-keys
|
||||
@ -61,8 +55,7 @@ export const CalendarView: React.FC<Props> = ({
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||
|
||||
const { calendarIssues, mutateIssues, params, displayFilters, setDisplayFilters } =
|
||||
useCalendarIssuesView();
|
||||
const { calendarIssues, mutateIssues, params, displayFilters, setDisplayFilters } = useCalendarIssuesView();
|
||||
|
||||
const totalDate = eachDayOfInterval({
|
||||
start: calendarDates.startDate,
|
||||
@ -80,8 +73,7 @@ export const CalendarView: React.FC<Props> = ({
|
||||
const filterIssue =
|
||||
calendarIssues.length > 0
|
||||
? calendarIssues.filter(
|
||||
(issue) =>
|
||||
issue.target_date && renderDateFormat(issue.target_date) === renderDateFormat(date)
|
||||
(issue) => issue.target_date && renderDateFormat(issue.target_date) === renderDateFormat(date)
|
||||
)
|
||||
: [];
|
||||
return {
|
||||
@ -155,18 +147,16 @@ export const CalendarView: React.FC<Props> = ({
|
||||
});
|
||||
|
||||
setDisplayFilters({
|
||||
calendar_date_range: `${renderDateFormat(startDate)};after,${renderDateFormat(
|
||||
endDate
|
||||
)};before`,
|
||||
calendar_date_range: `${renderDateFormat(startDate)};after,${renderDateFormat(endDate)};before`,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!displayFilters || displayFilters.calendar_date_range === "")
|
||||
setDisplayFilters({
|
||||
calendar_date_range: `${renderDateFormat(
|
||||
startOfWeek(currentDate)
|
||||
)};after,${renderDateFormat(lastDayOfWeek(currentDate))};before`,
|
||||
calendar_date_range: `${renderDateFormat(startOfWeek(currentDate))};after,${renderDateFormat(
|
||||
lastDayOfWeek(currentDate)
|
||||
)};before`,
|
||||
});
|
||||
}, [currentDate, displayFilters, setDisplayFilters]);
|
||||
|
||||
@ -214,11 +204,7 @@ export const CalendarView: React.FC<Props> = ({
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<span>
|
||||
{isMonthlyView
|
||||
? formatDate(date, "eee").substring(0, 3)
|
||||
: formatDate(date, "eee")}
|
||||
</span>
|
||||
<span>{isMonthlyView ? formatDate(date, "eee").substring(0, 3) : formatDate(date, "eee")}</span>
|
||||
{!isMonthlyView && <span>{formatDate(date, "d")}</span>}
|
||||
</div>
|
||||
))}
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// react-beautiful-dnd
|
||||
import { DraggableProvided, DraggableStateSnapshot } from "react-beautiful-dnd";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useCalendarIssuesView from "hooks/use-calendar-issues-view";
|
||||
import useIssuesProperties from "hooks/use-issue-properties";
|
||||
@ -122,13 +122,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
|
||||
}
|
||||
|
||||
issuesService
|
||||
.patchIssue(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issue.id as string,
|
||||
formData,
|
||||
user
|
||||
)
|
||||
.patchIssue(workspaceSlug as string, projectId as string, issue.id as string, formData, user)
|
||||
.then(() => {
|
||||
mutate(fetchKey);
|
||||
})
|
||||
@ -140,11 +134,8 @@ export const SingleCalendarIssue: React.FC<Props> = ({
|
||||
);
|
||||
|
||||
const handleCopyText = () => {
|
||||
const originURL =
|
||||
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(
|
||||
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`
|
||||
).then(() => {
|
||||
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Link Copied!",
|
||||
@ -153,9 +144,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const displayProperties = properties
|
||||
? Object.values(properties).some((value) => value === true)
|
||||
: false;
|
||||
const displayProperties = properties ? Object.values(properties).some((value) => value === true) : false;
|
||||
|
||||
const openPeekOverview = () => {
|
||||
const { query } = router;
|
||||
|
@ -7,10 +7,10 @@ import useSWR, { mutate } from "swr";
|
||||
// react-beautiful-dnd
|
||||
import { DropResult } from "react-beautiful-dnd";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import stateService from "services/state.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import stateService from "services/project_state.service";
|
||||
import modulesService from "services/modules.service";
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
@ -51,10 +51,7 @@ type Props = {
|
||||
disableUserActions?: boolean;
|
||||
};
|
||||
|
||||
export const IssuesView: React.FC<Props> = ({
|
||||
openIssuesListModal,
|
||||
disableUserActions = false,
|
||||
}) => {
|
||||
export const IssuesView: React.FC<Props> = ({ openIssuesListModal, disableUserActions = false }) => {
|
||||
// create issue modal
|
||||
const [createIssueModal, setCreateIssueModal] = useState(false);
|
||||
const [createViewModal, setCreateViewModal] = useState<any>(null);
|
||||
@ -64,9 +61,7 @@ export const IssuesView: React.FC<Props> = ({
|
||||
|
||||
// update issue modal
|
||||
const [editIssueModal, setEditIssueModal] = useState(false);
|
||||
const [issueToEdit, setIssueToEdit] = useState<
|
||||
(IIssue & { actionType: "edit" | "delete" }) | undefined
|
||||
>(undefined);
|
||||
const [issueToEdit, setIssueToEdit] = useState<(IIssue & { actionType: "edit" | "delete" }) | undefined>(undefined);
|
||||
|
||||
// delete issue modal
|
||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
@ -87,15 +82,12 @@ export const IssuesView: React.FC<Props> = ({
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { groupedByIssues, mutateIssues, displayFilters, filters, isEmpty, setFilters, params } =
|
||||
useIssuesView();
|
||||
const { groupedByIssues, mutateIssues, displayFilters, filters, isEmpty, setFilters, params } = useIssuesView();
|
||||
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
|
||||
|
||||
const { data: stateGroups } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
const states = getStatesList(stateGroups);
|
||||
|
||||
@ -141,12 +133,10 @@ export const IssuesView: React.FC<Props> = ({
|
||||
// check if dropping in the same group
|
||||
if (source.droppableId === destination.droppableId) {
|
||||
// check if dropping at beginning
|
||||
if (destination.index === 0)
|
||||
newSortOrder = destinationGroupArray[0].sort_order - 10000;
|
||||
if (destination.index === 0) newSortOrder = destinationGroupArray[0].sort_order - 10000;
|
||||
// check if dropping at last
|
||||
else if (destination.index === destinationGroupArray.length - 1)
|
||||
newSortOrder =
|
||||
destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
|
||||
newSortOrder = destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
|
||||
else {
|
||||
if (destination.index > source.index)
|
||||
newSortOrder =
|
||||
@ -161,12 +151,10 @@ export const IssuesView: React.FC<Props> = ({
|
||||
}
|
||||
} else {
|
||||
// check if dropping at beginning
|
||||
if (destination.index === 0)
|
||||
newSortOrder = destinationGroupArray[0].sort_order - 10000;
|
||||
if (destination.index === 0) newSortOrder = destinationGroupArray[0].sort_order - 10000;
|
||||
// check if dropping at last
|
||||
else if (destination.index === destinationGroupArray.length)
|
||||
newSortOrder =
|
||||
destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
|
||||
newSortOrder = destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
|
||||
else
|
||||
newSortOrder =
|
||||
(destinationGroupArray[destination.index - 1].sort_order +
|
||||
@ -180,18 +168,14 @@ export const IssuesView: React.FC<Props> = ({
|
||||
|
||||
const destinationGroup = destination.droppableId; // destination group id
|
||||
|
||||
if (
|
||||
displayFilters.order_by === "sort_order" ||
|
||||
source.droppableId !== destination.droppableId
|
||||
) {
|
||||
if (displayFilters.order_by === "sort_order" || source.droppableId !== destination.droppableId) {
|
||||
// different group/column;
|
||||
|
||||
// source.droppableId !== destination.droppableId -> even if order by is not sort_order,
|
||||
// if the issue is moved to a different group, then we will change the group of the
|
||||
// dragged item(or issue)
|
||||
|
||||
if (displayFilters.group_by === "priority")
|
||||
draggedItem.priority = destinationGroup as TIssuePriorities;
|
||||
if (displayFilters.group_by === "priority") draggedItem.priority = destinationGroup as TIssuePriorities;
|
||||
else if (displayFilters.group_by === "state") {
|
||||
draggedItem.state = destinationGroup;
|
||||
draggedItem.state_detail = states?.find((s) => s.id === destinationGroup) as IState;
|
||||
@ -219,14 +203,8 @@ export const IssuesView: React.FC<Props> = ({
|
||||
|
||||
return {
|
||||
...prevData,
|
||||
[sourceGroup]: orderArrayBy(
|
||||
sourceGroupArray,
|
||||
displayFilters.order_by ?? "-created_at"
|
||||
),
|
||||
[destinationGroup]: orderArrayBy(
|
||||
destinationGroupArray,
|
||||
displayFilters.order_by ?? "-created_at"
|
||||
),
|
||||
[sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
[destinationGroup]: orderArrayBy(destinationGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
};
|
||||
},
|
||||
false
|
||||
@ -246,14 +224,9 @@ export const IssuesView: React.FC<Props> = ({
|
||||
user
|
||||
)
|
||||
.then((response) => {
|
||||
const sourceStateBeforeDrag = states?.find(
|
||||
(state) => state.name === source.droppableId
|
||||
);
|
||||
const sourceStateBeforeDrag = states?.find((state) => state.name === source.droppableId);
|
||||
|
||||
if (
|
||||
sourceStateBeforeDrag?.group !== "completed" &&
|
||||
response?.state_detail?.group === "completed"
|
||||
)
|
||||
if (sourceStateBeforeDrag?.group !== "completed" && response?.state_detail?.group === "completed")
|
||||
trackEventServices.trackIssueMarkedAsDoneEvent(
|
||||
{
|
||||
workspaceSlug,
|
||||
@ -387,12 +360,7 @@ export const IssuesView: React.FC<Props> = ({
|
||||
);
|
||||
|
||||
issuesService
|
||||
.removeIssueFromCycle(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
cycleId as string,
|
||||
bridgeId
|
||||
)
|
||||
.removeIssueFromCycle(workspaceSlug as string, projectId as string, cycleId as string, bridgeId)
|
||||
.then(() => {
|
||||
setToastAlert({
|
||||
title: "Success",
|
||||
@ -430,12 +398,7 @@ export const IssuesView: React.FC<Props> = ({
|
||||
);
|
||||
|
||||
modulesService
|
||||
.removeIssueFromModule(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
moduleId as string,
|
||||
bridgeId
|
||||
)
|
||||
.removeIssueFromModule(workspaceSlug as string, projectId as string, moduleId as string, bridgeId)
|
||||
.then(() => {
|
||||
setToastAlert({
|
||||
title: "Success",
|
||||
@ -450,12 +413,9 @@ export const IssuesView: React.FC<Props> = ({
|
||||
[displayFilters.group_by, workspaceSlug, projectId, moduleId, params, setToastAlert]
|
||||
);
|
||||
|
||||
const nullFilters = Object.keys(filters).filter(
|
||||
(key) => filters[key as keyof IIssueFilterOptions] === null
|
||||
);
|
||||
const nullFilters = Object.keys(filters).filter((key) => filters[key as keyof IIssueFilterOptions] === null);
|
||||
|
||||
const areFiltersApplied =
|
||||
Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length;
|
||||
const areFiltersApplied = Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -581,10 +541,7 @@ export const IssuesView: React.FC<Props> = ({
|
||||
: undefined,
|
||||
secondaryButton:
|
||||
cycleId || moduleId ? (
|
||||
<SecondaryButton
|
||||
className="flex items-center gap-1.5"
|
||||
onClick={openIssuesListModal ?? (() => {})}
|
||||
>
|
||||
<SecondaryButton className="flex items-center gap-1.5" onClick={openIssuesListModal ?? (() => {})}>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add an existing issue
|
||||
</SecondaryButton>
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
@ -45,12 +45,7 @@ import {
|
||||
UserAuth,
|
||||
} from "types";
|
||||
// fetch-keys
|
||||
import {
|
||||
CYCLE_DETAILS,
|
||||
MODULE_DETAILS,
|
||||
SUB_ISSUES,
|
||||
USER_PROFILE_PROJECT_SEGREGATION,
|
||||
} from "constants/fetch-keys";
|
||||
import { CYCLE_DETAILS, MODULE_DETAILS, SUB_ISSUES, USER_PROFILE_PROJECT_SEGREGATION } from "constants/fetch-keys";
|
||||
|
||||
type Props = {
|
||||
type?: string;
|
||||
@ -140,39 +135,24 @@ export const SingleListIssue: React.FC<Props> = ({
|
||||
);
|
||||
}
|
||||
|
||||
issuesService
|
||||
.patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user)
|
||||
.then(() => {
|
||||
mutateIssues();
|
||||
issuesService.patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user).then(() => {
|
||||
mutateIssues();
|
||||
|
||||
if (userId)
|
||||
mutate<IUserProfileProjectSegregation>(
|
||||
USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString())
|
||||
);
|
||||
if (userId)
|
||||
mutate<IUserProfileProjectSegregation>(
|
||||
USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString())
|
||||
);
|
||||
|
||||
if (cycleId) mutate(CYCLE_DETAILS(cycleId as string));
|
||||
if (moduleId) mutate(MODULE_DETAILS(moduleId as string));
|
||||
});
|
||||
if (cycleId) mutate(CYCLE_DETAILS(cycleId as string));
|
||||
if (moduleId) mutate(MODULE_DETAILS(moduleId as string));
|
||||
});
|
||||
},
|
||||
[
|
||||
displayFilters,
|
||||
workspaceSlug,
|
||||
cycleId,
|
||||
moduleId,
|
||||
userId,
|
||||
groupTitle,
|
||||
index,
|
||||
mutateIssues,
|
||||
user,
|
||||
]
|
||||
[displayFilters, workspaceSlug, cycleId, moduleId, userId, groupTitle, index, mutateIssues, user]
|
||||
);
|
||||
|
||||
const handleCopyText = () => {
|
||||
const originURL =
|
||||
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(
|
||||
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`
|
||||
).then(() => {
|
||||
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Link Copied!",
|
||||
@ -197,8 +177,7 @@ export const SingleListIssue: React.FC<Props> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const isNotAllowed =
|
||||
userAuth.isGuest || userAuth.isViewer || disableUserActions || isArchivedIssues;
|
||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || disableUserActions || isArchivedIssues;
|
||||
|
||||
console.log("properties", properties);
|
||||
|
||||
@ -243,9 +222,7 @@ export const SingleListIssue: React.FC<Props> = ({
|
||||
Copy issue link
|
||||
</ContextMenu.Item>
|
||||
<a href={issuePath} target="_blank" rel="noreferrer noopener">
|
||||
<ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}>
|
||||
Open issue in new tab
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}>Open issue in new tab</ContextMenu.Item>
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
@ -286,11 +263,7 @@ export const SingleListIssue: React.FC<Props> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`flex flex-shrink-0 items-center gap-2 text-xs ${
|
||||
isArchivedIssues ? "opacity-60" : ""
|
||||
}`}
|
||||
>
|
||||
<div className={`flex flex-shrink-0 items-center gap-2 text-xs ${isArchivedIssues ? "opacity-60" : ""}`}>
|
||||
{properties.priority && (
|
||||
<ViewPrioritySelect
|
||||
issue={issue}
|
||||
|
@ -5,7 +5,7 @@ import useSWR from "swr";
|
||||
// headless ui
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import projectService from "services/project.service";
|
||||
// hooks
|
||||
import useProjects from "hooks/use-projects";
|
||||
@ -77,9 +77,7 @@ export const SingleList: React.FC<Props> = ({
|
||||
|
||||
const { data: issueLabels } = useSWR<IIssueLabels[]>(
|
||||
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
|
||||
const { data: members } = useSWR(
|
||||
@ -120,12 +118,7 @@ export const SingleList: React.FC<Props> = ({
|
||||
switch (displayFilters?.group_by) {
|
||||
case "state":
|
||||
icon = currentState && (
|
||||
<StateGroupIcon
|
||||
stateGroup={currentState.group}
|
||||
color={currentState.color}
|
||||
height="16px"
|
||||
width="16px"
|
||||
/>
|
||||
<StateGroupIcon stateGroup={currentState.group} color={currentState.color} height="16px" width="16px" />
|
||||
);
|
||||
break;
|
||||
case "state_detail.group":
|
||||
@ -152,14 +145,8 @@ export const SingleList: React.FC<Props> = ({
|
||||
: null);
|
||||
break;
|
||||
case "labels":
|
||||
const labelColor =
|
||||
issueLabels?.find((label) => label.id === groupTitle)?.color ?? "#000000";
|
||||
icon = (
|
||||
<span
|
||||
className="h-3 w-3 flex-shrink-0 rounded-full"
|
||||
style={{ backgroundColor: labelColor }}
|
||||
/>
|
||||
);
|
||||
const labelColor = issueLabels?.find((label) => label.id === groupTitle)?.color ?? "#000000";
|
||||
icon = <span className="h-3 w-3 flex-shrink-0 rounded-full" style={{ backgroundColor: labelColor }} />;
|
||||
break;
|
||||
case "assignees":
|
||||
case "created_by":
|
||||
@ -181,9 +168,7 @@ export const SingleList: React.FC<Props> = ({
|
||||
<div className="flex items-center justify-between px-4 py-2.5 bg-custom-background-90">
|
||||
<Disclosure.Button>
|
||||
<div className="flex items-center gap-x-3">
|
||||
{displayFilters?.group_by !== null && (
|
||||
<div className="flex items-center">{getGroupIcon()}</div>
|
||||
)}
|
||||
{displayFilters?.group_by !== null && <div className="flex items-center">{getGroupIcon()}</div>}
|
||||
{displayFilters?.group_by !== null ? (
|
||||
<h2
|
||||
className={`text-sm font-semibold leading-6 text-custom-text-100 ${
|
||||
@ -226,9 +211,7 @@ export const SingleList: React.FC<Props> = ({
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={addIssueToGroup}>Create new</CustomMenu.MenuItem>
|
||||
{openIssuesListModal && (
|
||||
<CustomMenu.MenuItem onClick={openIssuesListModal}>
|
||||
Add an existing issue
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem onClick={openIssuesListModal}>Add an existing issue</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
)}
|
||||
@ -256,19 +239,14 @@ export const SingleList: React.FC<Props> = ({
|
||||
makeIssueCopy={() => handleIssueAction(issue, "copy")}
|
||||
handleDeleteIssue={() => handleIssueAction(issue, "delete")}
|
||||
handleDraftIssueSelect={
|
||||
handleDraftIssueAction
|
||||
? () => handleDraftIssueAction(issue, "edit")
|
||||
: undefined
|
||||
handleDraftIssueAction ? () => handleDraftIssueAction(issue, "edit") : undefined
|
||||
}
|
||||
handleDraftIssueDelete={
|
||||
handleDraftIssueAction
|
||||
? () => handleDraftIssueAction(issue, "delete")
|
||||
: undefined
|
||||
handleDraftIssueAction ? () => handleDraftIssueAction(issue, "delete") : undefined
|
||||
}
|
||||
handleMyIssueOpen={handleMyIssueOpen}
|
||||
removeIssue={() => {
|
||||
if (removeIssue !== null && issue.bridge_id)
|
||||
removeIssue(issue.bridge_id, issue.id);
|
||||
if (removeIssue !== null && issue.bridge_id) removeIssue(issue.bridge_id, issue.id);
|
||||
}}
|
||||
disableUserActions={disableUserActions}
|
||||
user={user}
|
||||
@ -277,9 +255,7 @@ export const SingleList: React.FC<Props> = ({
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<p className="bg-custom-background-100 px-4 py-2.5 text-sm text-custom-text-200">
|
||||
No issues.
|
||||
</p>
|
||||
<p className="bg-custom-background-100 px-4 py-2.5 text-sm text-custom-text-200">No issues.</p>
|
||||
)
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">Loading...</div>
|
||||
|
@ -17,17 +17,12 @@ import {
|
||||
import { Popover2 } from "@blueprintjs/popover2";
|
||||
// icons
|
||||
import { Icon } from "components/ui";
|
||||
import {
|
||||
EllipsisHorizontalIcon,
|
||||
LinkIcon,
|
||||
PencilIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { EllipsisHorizontalIcon, LinkIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
// hooks
|
||||
import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view";
|
||||
import useToast from "hooks/use-toast";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// constant
|
||||
import {
|
||||
CYCLE_DETAILS,
|
||||
@ -133,13 +128,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
|
||||
);
|
||||
|
||||
issuesService
|
||||
.patchIssue(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issue.id as string,
|
||||
formData,
|
||||
user
|
||||
)
|
||||
.patchIssue(workspaceSlug as string, projectId as string, issue.id as string, formData, user)
|
||||
.then(() => {
|
||||
if (issue.parent) {
|
||||
mutate(SUB_ISSUES(issue.parent as string));
|
||||
@ -167,11 +156,8 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const handleCopyText = () => {
|
||||
const originURL =
|
||||
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(
|
||||
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`
|
||||
).then(() => {
|
||||
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Link Copied!",
|
||||
|
@ -9,7 +9,7 @@ import { useForm } from "react-hook-form";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import estimatesService from "services/estimates.service";
|
||||
import estimatesService from "services/project_estimates.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -119,13 +119,7 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
|
||||
);
|
||||
|
||||
await estimatesService
|
||||
.patchEstimate(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
data?.id as string,
|
||||
payload,
|
||||
user
|
||||
)
|
||||
.patchEstimate(workspaceSlug as string, projectId as string, data?.id as string, payload, user)
|
||||
.then(() => {
|
||||
mutate(ESTIMATES_LIST(projectId.toString()));
|
||||
mutate(ESTIMATE_DETAILS(data.id));
|
||||
@ -257,9 +251,7 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
|
||||
<Dialog.Panel className="relative transform rounded-lg border border-custom-border-200 bg-custom-background-100 px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="space-y-3">
|
||||
<div className="text-lg font-medium leading-6">
|
||||
{data ? "Update" : "Create"} Estimate
|
||||
</div>
|
||||
<div className="text-lg font-medium leading-6">{data ? "Update" : "Create"} Estimate</div>
|
||||
<div>
|
||||
<Input
|
||||
id="name"
|
||||
|
@ -1,10 +1,9 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import CSVIntegrationService from "services/integration/csv.services";
|
||||
import CSVIntegrationService from "services/csv.services";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -23,13 +22,9 @@ type Props = {
|
||||
mutateServices: () => void;
|
||||
};
|
||||
|
||||
export const Exporter: React.FC<Props> = ({
|
||||
isOpen,
|
||||
handleClose,
|
||||
user,
|
||||
provider,
|
||||
mutateServices,
|
||||
}) => {
|
||||
const cvsService = new CSVIntegrationService();
|
||||
|
||||
export const Exporter: React.FC<Props> = ({ isOpen, handleClose, user, provider, mutateServices }) => {
|
||||
const [exportLoading, setExportLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
@ -60,7 +55,8 @@ export const Exporter: React.FC<Props> = ({
|
||||
project: value,
|
||||
multiple: multiple,
|
||||
};
|
||||
await CSVIntegrationService.exportCSVService(workspaceSlug as string, payload, user)
|
||||
await cvsService
|
||||
.exportCSVService(workspaceSlug as string, payload, user)
|
||||
.then(() => {
|
||||
mutateServices();
|
||||
router.push(`/${workspaceSlug}/settings/exports`);
|
||||
@ -69,13 +65,7 @@ export const Exporter: React.FC<Props> = ({
|
||||
type: "success",
|
||||
title: "Export Successful",
|
||||
message: `You will be able to download the exported ${
|
||||
provider === "csv"
|
||||
? "CSV"
|
||||
: provider === "xlsx"
|
||||
? "Excel"
|
||||
: provider === "json"
|
||||
? "JSON"
|
||||
: ""
|
||||
provider === "csv" ? "CSV" : provider === "xlsx" ? "Excel" : provider === "json" ? "JSON" : ""
|
||||
} from the previous export.`,
|
||||
});
|
||||
})
|
||||
@ -122,13 +112,7 @@ export const Exporter: React.FC<Props> = ({
|
||||
<span className="flex items-center justify-start">
|
||||
<h3 className="text-xl font-medium 2xl:text-2xl">
|
||||
Export to{" "}
|
||||
{provider === "csv"
|
||||
? "CSV"
|
||||
: provider === "xlsx"
|
||||
? "Excel"
|
||||
: provider === "json"
|
||||
? "JSON"
|
||||
: ""}
|
||||
{provider === "csv" ? "CSV" : provider === "xlsx" ? "Excel" : provider === "json" ? "JSON" : ""}
|
||||
</h3>
|
||||
</span>
|
||||
</div>
|
||||
@ -155,22 +139,12 @@ export const Exporter: React.FC<Props> = ({
|
||||
onClick={() => setMultiple(!multiple)}
|
||||
className="flex items-center gap-2 max-w-min cursor-pointer"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={multiple}
|
||||
onChange={() => setMultiple(!multiple)}
|
||||
/>
|
||||
<div className="text-sm whitespace-nowrap">
|
||||
Export the data into separate files
|
||||
</div>
|
||||
<input type="checkbox" checked={multiple} onChange={() => setMultiple(!multiple)} />
|
||||
<div className="text-sm whitespace-nowrap">Export the data into separate files</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
||||
<PrimaryButton
|
||||
onClick={ExportCSVToMail}
|
||||
disabled={exportLoading}
|
||||
loading={exportLoading}
|
||||
>
|
||||
<PrimaryButton onClick={ExportCSVToMail} disabled={exportLoading} loading={exportLoading}>
|
||||
{exportLoading ? "Exporting..." : "Export"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@ import useSWR, { mutate } from "swr";
|
||||
// hooks
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
// services
|
||||
import IntegrationService from "services/integration";
|
||||
import IntegrationService from "services/integration.service";
|
||||
// components
|
||||
import { Exporter, SingleExport } from "components/exporter";
|
||||
// ui
|
||||
@ -32,9 +32,7 @@ const IntegrationGuide = () => {
|
||||
const { user } = useUserAuth();
|
||||
|
||||
const { data: exporterServices } = useSWR(
|
||||
workspaceSlug && cursor
|
||||
? EXPORT_SERVICES_LIST(workspaceSlug as string, cursor, `${per_page}`)
|
||||
: null,
|
||||
workspaceSlug && cursor ? EXPORT_SERVICES_LIST(workspaceSlug as string, cursor, `${per_page}`) : null,
|
||||
workspaceSlug && cursor
|
||||
? () => IntegrationService.getExportsServicesList(workspaceSlug as string, cursor, per_page)
|
||||
: null
|
||||
@ -57,20 +55,11 @@ const IntegrationGuide = () => {
|
||||
<div className="flex items-start justify-between gap-4 w-full">
|
||||
<div className="flex item-center gap-2.5">
|
||||
<div className="relative h-10 w-10 flex-shrink-0">
|
||||
<Image
|
||||
src={service.logo}
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
alt={`${service.title} Logo`}
|
||||
/>
|
||||
<Image src={service.logo} layout="fill" objectFit="cover" alt={`${service.title} Logo`} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="flex items-center gap-4 text-sm font-medium">
|
||||
{service.title}
|
||||
</h3>
|
||||
<p className="text-sm text-custom-text-200 tracking-tight">
|
||||
{service.description}
|
||||
</p>
|
||||
<h3 className="flex items-center gap-4 text-sm font-medium">{service.title}</h3>
|
||||
<p className="text-sm text-custom-text-200 tracking-tight">{service.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-shrink-0">
|
||||
@ -96,9 +85,9 @@ const IntegrationGuide = () => {
|
||||
className="flex flex-shrink-0 items-center gap-1 rounded bg-custom-background-80 py-1 px-1.5 text-xs outline-none"
|
||||
onClick={() => {
|
||||
setRefreshing(true);
|
||||
mutate(
|
||||
EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`)
|
||||
).then(() => setRefreshing(false));
|
||||
mutate(EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`)).then(() =>
|
||||
setRefreshing(false)
|
||||
);
|
||||
}}
|
||||
>
|
||||
<ArrowPathIcon className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`} />{" "}
|
||||
@ -108,9 +97,7 @@ const IntegrationGuide = () => {
|
||||
<div className="flex gap-2 items-center text-xs">
|
||||
<button
|
||||
disabled={!exporterServices?.prev_page_results}
|
||||
onClick={() =>
|
||||
exporterServices?.prev_page_results && setCursor(exporterServices?.prev_cursor)
|
||||
}
|
||||
onClick={() => exporterServices?.prev_page_results && setCursor(exporterServices?.prev_cursor)}
|
||||
className={`flex items-center border border-custom-primary-100 text-custom-primary-100 px-1 rounded ${
|
||||
exporterServices?.prev_page_results
|
||||
? "cursor-pointer hover:bg-custom-primary-100 hover:text-white"
|
||||
@ -122,9 +109,7 @@ const IntegrationGuide = () => {
|
||||
</button>
|
||||
<button
|
||||
disabled={!exporterServices?.next_page_results}
|
||||
onClick={() =>
|
||||
exporterServices?.next_page_results && setCursor(exporterServices?.next_cursor)
|
||||
}
|
||||
onClick={() => exporterServices?.next_page_results && setCursor(exporterServices?.next_cursor)}
|
||||
className={`flex items-center border border-custom-primary-100 text-custom-primary-100 px-1 rounded ${
|
||||
exporterServices?.next_page_results
|
||||
? "cursor-pointer hover:bg-custom-primary-100 hover:text-white"
|
||||
@ -147,9 +132,7 @@ const IntegrationGuide = () => {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-custom-text-200 px-4 py-6">
|
||||
No previous export available.
|
||||
</p>
|
||||
<p className="text-sm text-custom-text-200 px-4 py-6">No previous export available.</p>
|
||||
)
|
||||
) : (
|
||||
<Loader className="mt-6 grid grid-cols-1 gap-3">
|
||||
@ -169,9 +152,7 @@ const IntegrationGuide = () => {
|
||||
data={null}
|
||||
user={user}
|
||||
provider={provider}
|
||||
mutateServices={() =>
|
||||
mutate(EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`))
|
||||
}
|
||||
mutateServices={() => mutate(EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`))}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { KeyedMutator } from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// types
|
||||
import { ICurrentUserResponse, IIssue } from "types";
|
||||
import { IBlockUpdateData } from "../types";
|
||||
@ -34,8 +34,7 @@ export const updateGanttIssue = (
|
||||
|
||||
const newPayload: any = { ...payload };
|
||||
|
||||
if (newPayload.sort_order && payload.sort_order)
|
||||
newPayload.sort_order = payload.sort_order.newSortOrder;
|
||||
if (newPayload.sort_order && payload.sort_order) newPayload.sort_order = payload.sort_order.newSortOrder;
|
||||
|
||||
issuesService.patchIssue(workspaceSlug, issue.project, issue.id, newPayload, user);
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ import useSWR, { mutate } from "swr";
|
||||
// components
|
||||
import { AddComment, IssueActivitySection } from "components/issues";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useToast from "hooks/use-toast";
|
||||
@ -25,16 +25,9 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
|
||||
const { user } = useUser();
|
||||
|
||||
const { data: issueActivity, mutate: mutateIssueActivity } = useSWR(
|
||||
workspaceSlug && projectId && inboxIssueId ? PROJECT_ISSUES_ACTIVITY(inboxIssueId.toString()) : null,
|
||||
workspaceSlug && projectId && inboxIssueId
|
||||
? PROJECT_ISSUES_ACTIVITY(inboxIssueId.toString())
|
||||
: null,
|
||||
workspaceSlug && projectId && inboxIssueId
|
||||
? () =>
|
||||
issuesService.getIssueActivities(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
inboxIssueId.toString()
|
||||
)
|
||||
? () => issuesService.getIssueActivities(workspaceSlug.toString(), projectId.toString(), inboxIssueId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
@ -42,14 +35,7 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
|
||||
if (!workspaceSlug || !projectId || !inboxIssueId) return;
|
||||
|
||||
await issuesService
|
||||
.patchIssueComment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
inboxIssueId as string,
|
||||
commentId,
|
||||
data,
|
||||
user
|
||||
)
|
||||
.patchIssueComment(workspaceSlug as string, projectId as string, inboxIssueId as string, commentId, data, user)
|
||||
.then(() => mutateIssueActivity());
|
||||
};
|
||||
|
||||
@ -59,13 +45,7 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
|
||||
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
|
||||
|
||||
await issuesService
|
||||
.deleteIssueComment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
inboxIssueId as string,
|
||||
commentId,
|
||||
user
|
||||
)
|
||||
.deleteIssueComment(workspaceSlug as string, projectId as string, inboxIssueId as string, commentId, user)
|
||||
.then(() => mutateIssueActivity());
|
||||
};
|
||||
|
||||
@ -73,13 +53,7 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
|
||||
if (!workspaceSlug || !issueDetails) return;
|
||||
|
||||
await issuesService
|
||||
.createIssueComment(
|
||||
workspaceSlug.toString(),
|
||||
issueDetails.project,
|
||||
issueDetails.id,
|
||||
formData,
|
||||
user
|
||||
)
|
||||
.createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData, user)
|
||||
.then(() => {
|
||||
mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id));
|
||||
})
|
||||
|
@ -11,7 +11,7 @@ import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// services
|
||||
import issuesServices from "services/issues.service";
|
||||
import issuesServices from "services/issue.service";
|
||||
// ui
|
||||
import { PrimaryButton, SecondaryButton } from "components/ui";
|
||||
// icons
|
||||
@ -39,9 +39,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
||||
const { workspaceSlug, projectId, issueId } = router.query;
|
||||
|
||||
const { data: issues } = useSWR(
|
||||
workspaceSlug && projectId
|
||||
? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)
|
||||
: null,
|
||||
workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () =>
|
||||
issuesServices
|
||||
@ -71,8 +69,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const filteredIssues =
|
||||
(query === "" ? issues : issues?.filter((issue) => issue.name.includes(query))) ?? [];
|
||||
const filteredIssues = (query === "" ? issues : issues?.filter((issue) => issue.name.includes(query))) ?? [];
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={React.Fragment} afterLeave={() => setQuery("")} appear>
|
||||
@ -128,9 +125,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
||||
{filteredIssues.length > 0 ? (
|
||||
<li className="p-2">
|
||||
{query === "" && (
|
||||
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-custom-text-100">
|
||||
Select issue
|
||||
</h2>
|
||||
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-custom-text-100">Select issue</h2>
|
||||
)}
|
||||
<ul className="text-sm text-custom-text-100">
|
||||
{filteredIssues.map((issue) => (
|
||||
@ -140,9 +135,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
||||
value={issue.id}
|
||||
className={({ active, selected }) =>
|
||||
`flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${
|
||||
active || selected
|
||||
? "bg-custom-background-80 text-custom-text-100"
|
||||
: ""
|
||||
active || selected ? "bg-custom-background-80 text-custom-text-100" : ""
|
||||
} `
|
||||
}
|
||||
>
|
||||
@ -154,11 +147,8 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
||||
}}
|
||||
/>
|
||||
<span className="flex-shrink-0 text-xs text-custom-text-200">
|
||||
{
|
||||
issues?.find((i) => i.id === issue.id)?.project_detail
|
||||
?.identifier
|
||||
}
|
||||
-{issue.sequence_id}
|
||||
{issues?.find((i) => i.id === issue.id)?.project_detail?.identifier}-
|
||||
{issue.sequence_id}
|
||||
</span>
|
||||
<span className="text-custom-text-200">{issue.name}</span>
|
||||
</div>
|
||||
@ -171,10 +161,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
||||
<LayerDiagonalIcon height="56" width="56" />
|
||||
<h3 className="text-sm text-custom-text-200">
|
||||
No issues found. Create a new issue with{" "}
|
||||
<pre className="inline rounded bg-custom-background-80 px-2 py-1">
|
||||
C
|
||||
</pre>
|
||||
.
|
||||
<pre className="inline rounded bg-custom-background-80 px-2 py-1">C</pre>.
|
||||
</h3>
|
||||
</div>
|
||||
)}
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import IntegrationService from "services/integration";
|
||||
import IntegrationService from "services/integration.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -92,10 +92,7 @@ export const DeleteImportModal: React.FC<Props> = ({ isOpen, handleClose, data,
|
||||
<div className="flex flex-col gap-6 p-6">
|
||||
<div className="flex w-full items-center justify-start gap-6">
|
||||
<span className="place-items-center rounded-full bg-red-500/20 p-4">
|
||||
<ExclamationTriangleIcon
|
||||
className="h-6 w-6 text-red-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-500" aria-hidden="true" />
|
||||
</span>
|
||||
<span className="flex items-center justify-start">
|
||||
<h3 className="text-xl font-medium 2xl:text-2xl">Delete Project</h3>
|
||||
@ -104,17 +101,13 @@ export const DeleteImportModal: React.FC<Props> = ({ isOpen, handleClose, data,
|
||||
<span>
|
||||
<p className="text-sm leading-7 text-custom-text-200">
|
||||
Are you sure you want to delete import from{" "}
|
||||
<span className="break-words font-semibold capitalize text-custom-text-100">
|
||||
{data?.service}
|
||||
</span>
|
||||
? All of the data related to the import will be permanently removed. This
|
||||
action cannot be undone.
|
||||
<span className="break-words font-semibold capitalize text-custom-text-100">{data?.service}</span>
|
||||
? All of the data related to the import will be permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
To confirm, type{" "}
|
||||
<span className="font-medium text-custom-text-100">delete import</span> below:
|
||||
To confirm, type <span className="font-medium text-custom-text-100">delete import</span> below:
|
||||
</p>
|
||||
<Input
|
||||
type="text"
|
||||
@ -129,11 +122,7 @@ export const DeleteImportModal: React.FC<Props> = ({ isOpen, handleClose, data,
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
||||
<DangerButton
|
||||
onClick={handleDeletion}
|
||||
disabled={!confirmDeleteImport}
|
||||
loading={deleteLoading}
|
||||
>
|
||||
<DangerButton onClick={handleDeletion} disabled={!confirmDeleteImport} loading={deleteLoading}>
|
||||
{deleteLoading ? "Deleting..." : "Delete Project"}
|
||||
</DangerButton>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@ import useSWR from "swr";
|
||||
// react-hook-form
|
||||
import { UseFormSetValue } from "react-hook-form";
|
||||
// services
|
||||
import GithubIntegrationService from "services/integration/github.service";
|
||||
import GithubIntegrationService from "services/github.service";
|
||||
// ui
|
||||
import { Loader, PrimaryButton, SecondaryButton } from "components/ui";
|
||||
// types
|
||||
@ -22,19 +22,12 @@ type Props = {
|
||||
setValue: UseFormSetValue<TFormValues>;
|
||||
};
|
||||
|
||||
export const GithubRepoDetails: FC<Props> = ({
|
||||
selectedRepo,
|
||||
handleStepChange,
|
||||
setUsers,
|
||||
setValue,
|
||||
}) => {
|
||||
export const GithubRepoDetails: FC<Props> = ({ selectedRepo, handleStepChange, setUsers, setValue }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { data: repoInfo } = useSWR(
|
||||
workspaceSlug && selectedRepo
|
||||
? GITHUB_REPOSITORY_INFO(workspaceSlug as string, selectedRepo.name)
|
||||
: null,
|
||||
workspaceSlug && selectedRepo ? GITHUB_REPOSITORY_INFO(workspaceSlug as string, selectedRepo.name) : null,
|
||||
workspaceSlug && selectedRepo
|
||||
? () =>
|
||||
GithubIntegrationService.getGithubRepoInfo(workspaceSlug as string, {
|
||||
|
@ -9,8 +9,8 @@ import useSWR, { mutate } from "swr";
|
||||
// react-hook-form
|
||||
import { useForm } from "react-hook-form";
|
||||
// services
|
||||
import IntegrationService from "services/integration";
|
||||
import GithubIntegrationService from "services/integration/github.service";
|
||||
import IntegrationService from "services/integration.service";
|
||||
import GithubIntegrationService from "services/github.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
@ -29,18 +29,9 @@ import GithubLogo from "public/services/github.png";
|
||||
// types
|
||||
import { ICurrentUserResponse, IGithubRepoCollaborator, IGithubServiceImportFormData } from "types";
|
||||
// fetch-keys
|
||||
import {
|
||||
APP_INTEGRATIONS,
|
||||
IMPORTER_SERVICES_LIST,
|
||||
WORKSPACE_INTEGRATIONS,
|
||||
} from "constants/fetch-keys";
|
||||
import { APP_INTEGRATIONS, IMPORTER_SERVICES_LIST, WORKSPACE_INTEGRATIONS } from "constants/fetch-keys";
|
||||
|
||||
export type TIntegrationSteps =
|
||||
| "import-configure"
|
||||
| "import-data"
|
||||
| "repo-details"
|
||||
| "import-users"
|
||||
| "import-confirm";
|
||||
export type TIntegrationSteps = "import-configure" | "import-data" | "repo-details" | "import-users" | "import-confirm";
|
||||
export interface IIntegrationData {
|
||||
state: TIntegrationSteps;
|
||||
}
|
||||
@ -108,21 +99,15 @@ export const GithubImporterRoot: React.FC<Props> = ({ user }) => {
|
||||
defaultValues: defaultFormValues,
|
||||
});
|
||||
|
||||
const { data: appIntegrations } = useSWR(APP_INTEGRATIONS, () =>
|
||||
IntegrationService.getAppIntegrationsList()
|
||||
);
|
||||
const { data: appIntegrations } = useSWR(APP_INTEGRATIONS, () => IntegrationService.getAppIntegrationsList());
|
||||
|
||||
const { data: workspaceIntegrations } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
|
||||
workspaceSlug
|
||||
? () => IntegrationService.getWorkspaceIntegrationsList(workspaceSlug as string)
|
||||
: null
|
||||
workspaceSlug ? () => IntegrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null
|
||||
);
|
||||
|
||||
const activeIntegrationState = () => {
|
||||
const currentElementIndex = integrationWorkflowData.findIndex(
|
||||
(i) => i?.key === currentStep?.state
|
||||
);
|
||||
const currentElementIndex = integrationWorkflowData.findIndex((i) => i?.key === currentStep?.state);
|
||||
|
||||
return currentElementIndex;
|
||||
};
|
||||
@ -133,14 +118,11 @@ export const GithubImporterRoot: React.FC<Props> = ({ user }) => {
|
||||
|
||||
// current integration from all the integrations available
|
||||
const integration =
|
||||
appIntegrations &&
|
||||
appIntegrations.length > 0 &&
|
||||
appIntegrations.find((i) => i.provider === provider);
|
||||
appIntegrations && appIntegrations.length > 0 && appIntegrations.find((i) => i.provider === provider);
|
||||
|
||||
// current integration from workspace integrations
|
||||
const workspaceIntegration =
|
||||
integration &&
|
||||
workspaceIntegrations?.find((i: any) => i.integration_detail.id === integration.id);
|
||||
integration && workspaceIntegrations?.find((i: any) => i.integration_detail.id === integration.id);
|
||||
|
||||
const createGithubImporterService = async (formData: TFormValues) => {
|
||||
if (!formData.github || !formData.project) return;
|
||||
@ -214,9 +196,7 @@ export const GithubImporterRoot: React.FC<Props> = ({ user }) => {
|
||||
<div
|
||||
key={index}
|
||||
className={`border-b px-7 ${
|
||||
index <= activeIntegrationState() - 1
|
||||
? `border-custom-primary`
|
||||
: `border-custom-border-200`
|
||||
index <= activeIntegrationState() - 1 ? `border-custom-primary` : `border-custom-border-200`
|
||||
}`}
|
||||
>
|
||||
{" "}
|
||||
|
@ -9,14 +9,9 @@ import useSWR, { mutate } from "swr";
|
||||
// hooks
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
// services
|
||||
import IntegrationService from "services/integration";
|
||||
import IntegrationService from "services/integration.service";
|
||||
// components
|
||||
import {
|
||||
DeleteImportModal,
|
||||
GithubImporterRoot,
|
||||
JiraImporterRoot,
|
||||
SingleImport,
|
||||
} from "components/integration";
|
||||
import { DeleteImportModal, GithubImporterRoot, JiraImporterRoot, SingleImport } from "components/integration";
|
||||
// ui
|
||||
import { Loader, PrimaryButton } from "components/ui";
|
||||
// icons
|
||||
@ -85,18 +80,11 @@ const IntegrationGuide = () => {
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="relative h-10 w-10 flex-shrink-0">
|
||||
<Image
|
||||
src={service.logo}
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
alt={`${service.title} Logo`}
|
||||
/>
|
||||
<Image src={service.logo} layout="fill" objectFit="cover" alt={`${service.title} Logo`} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="flex items-center gap-4 text-sm font-medium">{service.title}</h3>
|
||||
<p className="text-sm text-custom-text-200 tracking-tight">
|
||||
{service.description}
|
||||
</p>
|
||||
<p className="text-sm text-custom-text-200 tracking-tight">{service.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-shrink-0">
|
||||
@ -119,9 +107,7 @@ const IntegrationGuide = () => {
|
||||
className="flex flex-shrink-0 items-center gap-1 rounded bg-custom-background-80 py-1 px-1.5 text-xs outline-none"
|
||||
onClick={() => {
|
||||
setRefreshing(true);
|
||||
mutate(IMPORTER_SERVICES_LIST(workspaceSlug as string)).then(() =>
|
||||
setRefreshing(false)
|
||||
);
|
||||
mutate(IMPORTER_SERVICES_LIST(workspaceSlug as string)).then(() => setRefreshing(false));
|
||||
}}
|
||||
>
|
||||
<ArrowPathIcon className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`} />{" "}
|
||||
@ -145,9 +131,7 @@ const IntegrationGuide = () => {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-custom-text-200 px-4 py-6">
|
||||
No previous imports available.
|
||||
</p>
|
||||
<p className="text-sm text-custom-text-200 px-4 py-6">No previous imports available.</p>
|
||||
)
|
||||
) : (
|
||||
<Loader className="mt-6 grid grid-cols-1 gap-3">
|
||||
|
@ -10,7 +10,7 @@ import useSWR from "swr";
|
||||
import { useFormContext, Controller } from "react-hook-form";
|
||||
|
||||
// services
|
||||
import jiraImporterService from "services/integration/jira.service";
|
||||
import jiraImporterService from "services/jira.service";
|
||||
|
||||
// fetch keys
|
||||
import { JIRA_IMPORTER_DETAIL } from "constants/fetch-keys";
|
||||
@ -157,9 +157,7 @@ export const JiraProjectDetail: React.FC<Props> = (props) => {
|
||||
<Controller
|
||||
control={control}
|
||||
name="config.epics_to_modules"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<ToggleSwitch onChange={onChange} value={value} />
|
||||
)}
|
||||
render={({ field: { value, onChange } }) => <ToggleSwitch onChange={onChange} value={value} />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@ import { ArrowLeftIcon, ListBulletIcon } from "@heroicons/react/24/outline";
|
||||
import { CogIcon, UsersIcon, CheckIcon } from "components/icons";
|
||||
|
||||
// services
|
||||
import jiraImporterService from "services/integration/jira.service";
|
||||
import jiraImporterService from "services/jira.service";
|
||||
|
||||
// fetch keys
|
||||
import { IMPORTER_SERVICES_LIST } from "constants/fetch-keys";
|
||||
@ -100,9 +100,7 @@ export const JiraImporterRoot: React.FC<Props> = ({ user }) => {
|
||||
};
|
||||
|
||||
const activeIntegrationState = () => {
|
||||
const currentElementIndex = integrationWorkflowData.findIndex(
|
||||
(i) => i?.key === currentStep?.state
|
||||
);
|
||||
const currentElementIndex = integrationWorkflowData.findIndex((i) => i?.key === currentStep?.state);
|
||||
|
||||
return currentElementIndex;
|
||||
};
|
||||
@ -155,9 +153,7 @@ export const JiraImporterRoot: React.FC<Props> = ({ user }) => {
|
||||
<div
|
||||
key={index}
|
||||
className={`border-b px-7 ${
|
||||
index <= activeIntegrationState() - 1
|
||||
? `border-custom-primary`
|
||||
: `border-custom-border-200`
|
||||
index <= activeIntegrationState() - 1 ? `border-custom-primary` : `border-custom-border-200`
|
||||
}`}
|
||||
>
|
||||
{" "}
|
||||
@ -174,10 +170,7 @@ export const JiraImporterRoot: React.FC<Props> = ({ user }) => {
|
||||
<div className="h-full w-full overflow-y-auto">
|
||||
{currentStep.state === "import-configure" && <JiraGetImportDetail />}
|
||||
{currentStep.state === "display-import-data" && (
|
||||
<JiraProjectDetail
|
||||
setDisableTopBarAfter={setDisableTopBarAfter}
|
||||
setCurrentStep={setCurrentStep}
|
||||
/>
|
||||
<JiraProjectDetail setDisableTopBarAfter={setDisableTopBarAfter} setCurrentStep={setCurrentStep} />
|
||||
)}
|
||||
{currentStep?.state === "import-users" && <JiraImportUsers />}
|
||||
{currentStep?.state === "import-confirmation" && <JiraConfirmImport />}
|
||||
@ -199,15 +192,9 @@ export const JiraImporterRoot: React.FC<Props> = ({ user }) => {
|
||||
</SecondaryButton>
|
||||
)}
|
||||
<PrimaryButton
|
||||
disabled={
|
||||
disableTopBarAfter === currentStep?.state ||
|
||||
!isValid ||
|
||||
methods.formState.isSubmitting
|
||||
}
|
||||
disabled={disableTopBarAfter === currentStep?.state || !isValid || methods.formState.isSubmitting}
|
||||
onClick={() => {
|
||||
const currentElementIndex = integrationWorkflowData.findIndex(
|
||||
(i) => i?.key === currentStep?.state
|
||||
);
|
||||
const currentElementIndex = integrationWorkflowData.findIndex((i) => i?.key === currentStep?.state);
|
||||
|
||||
if (currentElementIndex === integrationWorkflowData.length - 1) {
|
||||
methods.handleSubmit(onSubmit)();
|
||||
|
@ -6,7 +6,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import IntegrationService from "services/integration";
|
||||
import IntegrationService from "services/integration.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useIntegrationPopup from "hooks/use-integration-popup";
|
||||
@ -50,25 +50,17 @@ export const SingleIntegrationCard: React.FC<Props> = ({ integration }) => {
|
||||
|
||||
const { data: workspaceIntegrations } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
|
||||
() =>
|
||||
workspaceSlug
|
||||
? IntegrationService.getWorkspaceIntegrationsList(workspaceSlug as string)
|
||||
: null
|
||||
() => (workspaceSlug ? IntegrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null)
|
||||
);
|
||||
|
||||
const handleRemoveIntegration = async () => {
|
||||
if (!workspaceSlug || !integration || !workspaceIntegrations) return;
|
||||
|
||||
const workspaceIntegrationId = workspaceIntegrations?.find(
|
||||
(i) => i.integration === integration.id
|
||||
)?.id;
|
||||
const workspaceIntegrationId = workspaceIntegrations?.find((i) => i.integration === integration.id)?.id;
|
||||
|
||||
setDeletingIntegration(true);
|
||||
|
||||
await IntegrationService.deleteWorkspaceIntegration(
|
||||
workspaceSlug as string,
|
||||
workspaceIntegrationId ?? ""
|
||||
)
|
||||
await IntegrationService.deleteWorkspaceIntegration(workspaceSlug as string, workspaceIntegrationId ?? "")
|
||||
.then(() => {
|
||||
mutate<IWorkspaceIntegration[]>(
|
||||
WORKSPACE_INTEGRATIONS(workspaceSlug as string),
|
||||
@ -94,18 +86,13 @@ export const SingleIntegrationCard: React.FC<Props> = ({ integration }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const isInstalled = workspaceIntegrations?.find(
|
||||
(i: any) => i.integration_detail.id === integration.id
|
||||
);
|
||||
const isInstalled = workspaceIntegrations?.find((i: any) => i.integration_detail.id === integration.id);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-2 border-b border-custom-border-200 bg-custom-background-100 px-4 py-6">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="h-10 w-10 flex-shrink-0">
|
||||
<Image
|
||||
src={integrationDetails[integration.provider].logo}
|
||||
alt={`${integration.title} Logo`}
|
||||
/>
|
||||
<Image src={integrationDetails[integration.provider].logo} alt={`${integration.title} Logo`} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="flex items-center gap-2 text-sm font-medium">
|
||||
|
@ -4,7 +4,7 @@ import { useRouter } from "next/router";
|
||||
|
||||
import useSWR, { mutate } from "swr";
|
||||
// services
|
||||
import appinstallationsService from "services/app-installations.service";
|
||||
import appinstallationsService from "services/app_installation.service";
|
||||
// ui
|
||||
import { Loader } from "components/ui";
|
||||
// hooks
|
||||
@ -20,8 +20,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export const SelectChannel: React.FC<Props> = ({ integration }) => {
|
||||
const [slackChannelAvailabilityToggle, setSlackChannelAvailabilityToggle] =
|
||||
useState<boolean>(false);
|
||||
const [slackChannelAvailabilityToggle, setSlackChannelAvailabilityToggle] = useState<boolean>(false);
|
||||
const [slackChannel, setSlackChannel] = useState<ISlackIntegration | null>(null);
|
||||
|
||||
const router = useRouter();
|
||||
@ -65,12 +64,7 @@ export const SelectChannel: React.FC<Props> = ({ integration }) => {
|
||||
setSlackChannel(null);
|
||||
});
|
||||
appinstallationsService
|
||||
.removeSlackChannel(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
integration.id as string,
|
||||
slackChannel?.id
|
||||
)
|
||||
.removeSlackChannel(workspaceSlug as string, projectId as string, integration.id as string, slackChannel?.id)
|
||||
.catch((err) => console.log(err));
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { observer } from "mobx-react-lite";
|
||||
import { RootStore } from "store/root";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// types and default data
|
||||
import { TIssueLayouts } from "store/issue-filters";
|
||||
import { TIssueLayouts } from "store/issue_filters.legacy";
|
||||
import { issueFilterVisibilityData } from "store/helpers/issue-data";
|
||||
|
||||
export const LayoutSelection = observer(() => {
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// react-dropzone
|
||||
import { useDropzone } from "react-dropzone";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// types
|
||||
@ -44,12 +44,7 @@ export const IssueAttachmentUpload: React.FC<Props> = ({ disabled = false }) =>
|
||||
setIsLoading(true);
|
||||
|
||||
issuesService
|
||||
.uploadIssueAttachment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
formData
|
||||
)
|
||||
.uploadIssueAttachment(workspaceSlug as string, projectId as string, issueId as string, formData)
|
||||
.then((res) => {
|
||||
mutate<IIssueAttachment[]>(
|
||||
ISSUE_ATTACHMENTS(issueId as string),
|
||||
@ -83,9 +78,7 @@ export const IssueAttachmentUpload: React.FC<Props> = ({ disabled = false }) =>
|
||||
});
|
||||
|
||||
const fileError =
|
||||
fileRejections.length > 0
|
||||
? `Invalid file type or size (max ${maxFileSize / 1024 / 1024} MB)`
|
||||
: null;
|
||||
fileRejections.length > 0 ? `Invalid file type or size (max ${maxFileSize / 1024 / 1024} MB)` : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -12,7 +12,7 @@ import { DeleteAttachmentModal } from "./delete-attachment-modal";
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { ExclamationIcon, getFileIcon } from "components/icons";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import projectService from "services/project.service";
|
||||
// fetch-key
|
||||
import { ISSUE_ATTACHMENTS, PROJECT_MEMBERS } from "constants/fetch-keys";
|
||||
@ -33,12 +33,7 @@ export const IssueAttachments = () => {
|
||||
const { data: attachments } = useSWR<IIssueAttachment[]>(
|
||||
workspaceSlug && projectId && issueId ? ISSUE_ATTACHMENTS(issueId as string) : null,
|
||||
workspaceSlug && projectId && issueId
|
||||
? () =>
|
||||
issuesService.getIssueAttachment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string
|
||||
)
|
||||
? () => issuesService.getIssueAttachment(workspaceSlug as string, projectId as string, issueId as string)
|
||||
: null
|
||||
);
|
||||
|
||||
@ -70,14 +65,11 @@ export const IssueAttachments = () => {
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Tooltip tooltipContent={getFileName(file.attributes.name)}>
|
||||
<span className="text-sm">
|
||||
{truncateText(`${getFileName(file.attributes.name)}`, 10)}
|
||||
</span>
|
||||
<span className="text-sm">{truncateText(`${getFileName(file.attributes.name)}`, 10)}</span>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
tooltipContent={`${
|
||||
people?.find((person) => person.member.id === file.updated_by)?.member
|
||||
.display_name ?? ""
|
||||
people?.find((person) => person.member.id === file.updated_by)?.member.display_name ?? ""
|
||||
} uploaded on ${renderLongDateFormat(file.updated_at)}`}
|
||||
>
|
||||
<span>
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -47,12 +47,7 @@ export const DeleteAttachmentModal: React.FC<Props> = ({ isOpen, setIsOpen, data
|
||||
);
|
||||
|
||||
await issuesService
|
||||
.deleteIssueAttachment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
assetId as string
|
||||
)
|
||||
.deleteIssueAttachment(workspaceSlug as string, projectId as string, issueId as string, assetId as string)
|
||||
.then(() => mutate(PROJECT_ISSUES_ACTIVITY(issueId as string)))
|
||||
.catch(() => {
|
||||
setToastAlert({
|
||||
@ -94,24 +89,17 @@ export const DeleteAttachmentModal: React.FC<Props> = ({ isOpen, setIsOpen, data
|
||||
<div className="bg-custom-background-80 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationTriangleIcon
|
||||
className="h-6 w-6 text-red-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-custom-text-100"
|
||||
>
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
Delete Attachment
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
Are you sure you want to delete attachment-{" "}
|
||||
<span className="font-bold">{getFileName(data.attributes.name)}</span>?
|
||||
This attachment will be permanently removed. This action cannot be
|
||||
undone.
|
||||
<span className="font-bold">{getFileName(data.attributes.name)}</span>? This attachment will
|
||||
be permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issueServices from "services/issues.service";
|
||||
import issueServices from "services/issue.service";
|
||||
// hooks
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
import useToast from "hooks/use-toast";
|
||||
@ -109,10 +109,7 @@ export const DeleteDraftIssueModal: React.FC<Props> = (props) => {
|
||||
<div className="flex flex-col gap-6 p-6">
|
||||
<div className="flex w-full items-center justify-start gap-6">
|
||||
<span className="place-items-center rounded-full bg-red-500/20 p-4">
|
||||
<ExclamationTriangleIcon
|
||||
className="h-6 w-6 text-red-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</span>
|
||||
<span className="flex items-center justify-start">
|
||||
<h3 className="text-xl font-medium 2xl:text-2xl">Delete Draft Issue</h3>
|
||||
@ -124,8 +121,8 @@ export const DeleteDraftIssueModal: React.FC<Props> = (props) => {
|
||||
<span className="break-words font-medium text-custom-text-100">
|
||||
{data?.project_detail.identifier}-{data?.sequence_id}
|
||||
</span>
|
||||
{""}? All of the data related to the draft issue will be permanently removed.
|
||||
This action cannot be undone.
|
||||
{""}? All of the data related to the draft issue will be permanently removed. This action cannot
|
||||
be undone.
|
||||
</p>
|
||||
</span>
|
||||
<div className="flex justify-end gap-2">
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issueServices from "services/issues.service";
|
||||
import issueServices from "services/issue.service";
|
||||
// hooks
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
import useCalendarIssuesView from "hooks/use-calendar-issues-view";
|
||||
@ -37,13 +37,7 @@ type Props = {
|
||||
onSubmit?: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const DeleteIssueModal: React.FC<Props> = ({
|
||||
isOpen,
|
||||
handleClose,
|
||||
data,
|
||||
user,
|
||||
onSubmit,
|
||||
}) => {
|
||||
export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data, user, onSubmit }) => {
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
@ -82,11 +76,7 @@ export const DeleteIssueModal: React.FC<Props> = ({
|
||||
? VIEW_ISSUES(viewId.toString(), calendarParams)
|
||||
: PROJECT_ISSUES_LIST_WITH_PARAMS(data.project, calendarParams);
|
||||
|
||||
mutate<IIssue[]>(
|
||||
calendarFetchKey,
|
||||
(prevData) => (prevData ?? []).filter((p) => p.id !== data.id),
|
||||
false
|
||||
);
|
||||
mutate<IIssue[]>(calendarFetchKey, (prevData) => (prevData ?? []).filter((p) => p.id !== data.id), false);
|
||||
} else if (displayFilters.layout === "spreadsheet") {
|
||||
const spreadsheetFetchKey = cycleId
|
||||
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams)
|
||||
@ -163,8 +153,7 @@ export const DeleteIssueModal: React.FC<Props> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const handleIssueDelete = () =>
|
||||
isArchivedIssues ? handleArchivedIssueDeletion() : handleDeletion();
|
||||
const handleIssueDelete = () => (isArchivedIssues ? handleArchivedIssueDeletion() : handleDeletion());
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||
@ -196,10 +185,7 @@ export const DeleteIssueModal: React.FC<Props> = ({
|
||||
<div className="flex flex-col gap-6 p-6">
|
||||
<div className="flex w-full items-center justify-start gap-6">
|
||||
<span className="place-items-center rounded-full bg-red-500/20 p-4">
|
||||
<ExclamationTriangleIcon
|
||||
className="h-6 w-6 text-red-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</span>
|
||||
<span className="flex items-center justify-start">
|
||||
<h3 className="text-xl font-medium 2xl:text-2xl">Delete Issue</h3>
|
||||
@ -211,8 +197,8 @@ export const DeleteIssueModal: React.FC<Props> = ({
|
||||
<span className="break-words font-medium text-custom-text-100">
|
||||
{data?.project_detail.identifier}-{data?.sequence_id}
|
||||
</span>
|
||||
{""}? All of the data related to the issue will be permanently removed. This
|
||||
action cannot be undone.
|
||||
{""}? All of the data related to the issue will be permanently removed. This action cannot be
|
||||
undone.
|
||||
</p>
|
||||
</span>
|
||||
<div className="flex justify-end gap-2">
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
@ -114,8 +114,7 @@ export const CreateUpdateDraftIssueModal: React.FC<IssuesModalProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (prePopulateData && prePopulateData.project)
|
||||
return setActiveProject(prePopulateData.project);
|
||||
if (prePopulateData && prePopulateData.project) return setActiveProject(prePopulateData.project);
|
||||
|
||||
// if data is not present, set active project to the project
|
||||
// in the url. This has the least priority.
|
||||
|
@ -4,7 +4,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
import useToast from "hooks/use-toast";
|
||||
@ -37,11 +37,7 @@ type Props = {
|
||||
uneditable?: boolean;
|
||||
};
|
||||
|
||||
export const IssueMainContent: React.FC<Props> = ({
|
||||
issueDetails,
|
||||
submitChanges,
|
||||
uneditable = false,
|
||||
}) => {
|
||||
export const IssueMainContent: React.FC<Props> = ({ issueDetails, submitChanges, uneditable = false }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, issueId, archivedIssueId } = router.query;
|
||||
|
||||
@ -55,12 +51,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
const { data: siblingIssues } = useSWR(
|
||||
workspaceSlug && projectId && issueDetails?.parent ? SUB_ISSUES(issueDetails.parent) : null,
|
||||
workspaceSlug && projectId && issueDetails?.parent
|
||||
? () =>
|
||||
issuesService.subIssues(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueDetails.parent ?? ""
|
||||
)
|
||||
? () => issuesService.subIssues(workspaceSlug as string, projectId as string, issueDetails.parent ?? "")
|
||||
: null
|
||||
);
|
||||
const siblingIssuesList = siblingIssues?.sub_issues.filter((i) => i.id !== issueDetails.id);
|
||||
@ -68,12 +59,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
const { data: issueActivity, mutate: mutateIssueActivity } = useSWR(
|
||||
workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY(issueId.toString()) : null,
|
||||
workspaceSlug && projectId && issueId
|
||||
? () =>
|
||||
issuesService.getIssueActivities(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
issueId.toString()
|
||||
)
|
||||
? () => issuesService.getIssueActivities(workspaceSlug.toString(), projectId.toString(), issueId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
@ -81,14 +67,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
if (!workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
await issuesService
|
||||
.patchIssueComment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
commentId,
|
||||
data,
|
||||
user
|
||||
)
|
||||
.patchIssueComment(workspaceSlug as string, projectId as string, issueId as string, commentId, data, user)
|
||||
.then(() => mutateIssueActivity());
|
||||
};
|
||||
|
||||
@ -98,13 +77,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
|
||||
|
||||
await issuesService
|
||||
.deleteIssueComment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
commentId,
|
||||
user
|
||||
)
|
||||
.deleteIssueComment(workspaceSlug as string, projectId as string, issueId as string, commentId, user)
|
||||
.then(() => mutateIssueActivity());
|
||||
};
|
||||
|
||||
@ -112,13 +85,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
if (!workspaceSlug || !issueDetails) return;
|
||||
|
||||
await issuesService
|
||||
.createIssueComment(
|
||||
workspaceSlug.toString(),
|
||||
issueDetails.project,
|
||||
issueDetails.id,
|
||||
formData,
|
||||
user
|
||||
)
|
||||
.createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData, user)
|
||||
.then(() => {
|
||||
mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id));
|
||||
})
|
||||
@ -148,8 +115,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
}}
|
||||
/>
|
||||
<span className="flex-shrink-0 text-custom-text-200">
|
||||
{issueDetails.parent_detail?.project_detail.identifier}-
|
||||
{issueDetails.parent_detail?.sequence_id}
|
||||
{issueDetails.parent_detail?.project_detail.identifier}-{issueDetails.parent_detail?.sequence_id}
|
||||
</span>
|
||||
</div>
|
||||
<span className="truncate text-custom-text-100">
|
||||
@ -169,9 +135,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
||||
<CustomMenu.MenuItem
|
||||
key={issue.id}
|
||||
renderAs="a"
|
||||
href={`/${workspaceSlug}/projects/${projectId as string}/issues/${
|
||||
issue.id
|
||||
}`}
|
||||
href={`/${workspaceSlug}/projects/${projectId as string}/issues/${issue.id}`}
|
||||
className="flex items-center gap-2 py-2"
|
||||
>
|
||||
<LayerDiagonalIcon className="h-4 w-4" />
|
||||
|
@ -8,7 +8,7 @@ import { mutate } from "swr";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import modulesService from "services/modules.service";
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import inboxServices from "services/inbox.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
@ -93,8 +93,10 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
||||
|
||||
const { groupedIssues, mutateMyIssues } = useMyIssues(workspaceSlug?.toString());
|
||||
|
||||
const { setValue: setValueInLocalStorage, clearValue: clearLocalStorageValue } =
|
||||
useLocalStorage<any>("draftedIssue", {});
|
||||
const { setValue: setValueInLocalStorage, clearValue: clearLocalStorageValue } = useLocalStorage<any>(
|
||||
"draftedIssue",
|
||||
{}
|
||||
);
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
@ -201,13 +203,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
||||
};
|
||||
|
||||
await inboxServices
|
||||
.createInboxIssue(
|
||||
workspaceSlug.toString(),
|
||||
activeProject.toString(),
|
||||
inboxId.toString(),
|
||||
payload,
|
||||
user
|
||||
)
|
||||
.createInboxIssue(workspaceSlug.toString(), activeProject.toString(), inboxId.toString(), payload, user)
|
||||
.then((res) => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
@ -264,8 +260,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
||||
.then(async (res) => {
|
||||
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
|
||||
if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle);
|
||||
if (payload.module && payload.module !== "")
|
||||
await addIssueToModule(res.id, payload.module);
|
||||
if (payload.module && payload.module !== "") await addIssueToModule(res.id, payload.module);
|
||||
|
||||
if (displayFilters.layout === "calendar") mutate(calendarFetchKey);
|
||||
if (displayFilters.layout === "gantt_chart")
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// components
|
||||
import { DateFilterModal } from "components/core";
|
||||
// ui
|
||||
@ -29,12 +29,7 @@ type Props = {
|
||||
height?: "sm" | "md" | "rg" | "lg";
|
||||
};
|
||||
|
||||
export const MyIssuesSelectFilters: React.FC<Props> = ({
|
||||
filters,
|
||||
onSelect,
|
||||
direction = "right",
|
||||
height = "md",
|
||||
}) => {
|
||||
export const MyIssuesSelectFilters: React.FC<Props> = ({ filters, onSelect, direction = "right", height = "md" }) => {
|
||||
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
|
||||
const [dateFilterType, setDateFilterType] = useState<{
|
||||
title: string;
|
||||
@ -50,9 +45,7 @@ export const MyIssuesSelectFilters: React.FC<Props> = ({
|
||||
|
||||
const { data: labels } = useSWR(
|
||||
workspaceSlug && fetchLabels ? WORKSPACE_LABELS(workspaceSlug.toString()) : null,
|
||||
workspaceSlug && fetchLabels
|
||||
? () => issuesService.getWorkspaceLabels(workspaceSlug.toString())
|
||||
: null
|
||||
workspaceSlug && fetchLabels ? () => issuesService.getWorkspaceLabels(workspaceSlug.toString()) : null
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -7,7 +7,7 @@ import useSWR, { mutate } from "swr";
|
||||
// react-beautiful-dnd
|
||||
import { DropResult } from "react-beautiful-dnd";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useMyIssues from "hooks/my-issues/use-my-issues";
|
||||
import useMyIssuesFilters from "hooks/my-issues/use-my-issues-filter";
|
||||
@ -28,10 +28,7 @@ type Props = {
|
||||
disableUserActions?: false;
|
||||
};
|
||||
|
||||
export const MyIssuesView: React.FC<Props> = ({
|
||||
openIssuesListModal,
|
||||
disableUserActions = false,
|
||||
}) => {
|
||||
export const MyIssuesView: React.FC<Props> = ({ openIssuesListModal, disableUserActions = false }) => {
|
||||
// create issue modal
|
||||
const [createIssueModal, setCreateIssueModal] = useState(false);
|
||||
const [preloadedData, setPreloadedData] = useState<
|
||||
@ -40,9 +37,7 @@ export const MyIssuesView: React.FC<Props> = ({
|
||||
|
||||
// update issue modal
|
||||
const [editIssueModal, setEditIssueModal] = useState(false);
|
||||
const [issueToEdit, setIssueToEdit] = useState<
|
||||
(IIssue & { actionType: "edit" | "delete" }) | undefined
|
||||
>(undefined);
|
||||
const [issueToEdit, setIssueToEdit] = useState<(IIssue & { actionType: "edit" | "delete" }) | undefined>(undefined);
|
||||
|
||||
// delete issue modal
|
||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
@ -57,14 +52,10 @@ export const MyIssuesView: React.FC<Props> = ({
|
||||
const { user } = useUserAuth();
|
||||
|
||||
const { groupedIssues, mutateMyIssues, isEmpty, params } = useMyIssues(workspaceSlug?.toString());
|
||||
const { filters, setFilters, displayFilters, properties } = useMyIssuesFilters(
|
||||
workspaceSlug?.toString()
|
||||
);
|
||||
const { filters, setFilters, displayFilters, properties } = useMyIssuesFilters(workspaceSlug?.toString());
|
||||
|
||||
const { data: labels } = useSWR(
|
||||
workspaceSlug && (filters?.labels ?? []).length > 0
|
||||
? WORKSPACE_LABELS(workspaceSlug.toString())
|
||||
: null,
|
||||
workspaceSlug && (filters?.labels ?? []).length > 0 ? WORKSPACE_LABELS(workspaceSlug.toString()) : null,
|
||||
workspaceSlug && (filters?.labels ?? []).length > 0
|
||||
? () => issuesService.getWorkspaceLabels(workspaceSlug.toString())
|
||||
: null
|
||||
@ -82,13 +73,7 @@ export const MyIssuesView: React.FC<Props> = ({
|
||||
async (result: DropResult) => {
|
||||
setTrashBox(false);
|
||||
|
||||
if (
|
||||
!result.destination ||
|
||||
!workspaceSlug ||
|
||||
!groupedIssues ||
|
||||
displayFilters?.group_by !== "priority"
|
||||
)
|
||||
return;
|
||||
if (!result.destination || !workspaceSlug || !groupedIssues || displayFilters?.group_by !== "priority") return;
|
||||
|
||||
const { source, destination } = result;
|
||||
|
||||
@ -120,14 +105,8 @@ export const MyIssuesView: React.FC<Props> = ({
|
||||
|
||||
return {
|
||||
...prevData,
|
||||
[sourceGroup]: orderArrayBy(
|
||||
sourceGroupArray,
|
||||
displayFilters.order_by ?? "-created_at"
|
||||
),
|
||||
[destinationGroup]: orderArrayBy(
|
||||
destinationGroupArray,
|
||||
displayFilters.order_by ?? "-created_at"
|
||||
),
|
||||
[sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
[destinationGroup]: orderArrayBy(destinationGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
};
|
||||
},
|
||||
false
|
||||
@ -219,15 +198,11 @@ export const MyIssuesView: React.FC<Props> = ({
|
||||
(key) => filtersToDisplay[key as keyof IIssueFilterOptions] === null
|
||||
);
|
||||
const areFiltersApplied =
|
||||
Object.keys(filtersToDisplay).length > 0 &&
|
||||
nullFilters.length !== Object.keys(filtersToDisplay).length;
|
||||
Object.keys(filtersToDisplay).length > 0 && nullFilters.length !== Object.keys(filtersToDisplay).length;
|
||||
|
||||
const isSubscribedIssuesRoute = router.pathname.includes("subscribed");
|
||||
const isMySubscribedIssues =
|
||||
(filters.subscriber &&
|
||||
filters.subscriber.length > 0 &&
|
||||
router.pathname.includes("my-issues")) ??
|
||||
false;
|
||||
(filters.subscriber && filters.subscriber.length > 0 && router.pathname.includes("my-issues")) ?? false;
|
||||
|
||||
const disableAddIssueOption = isSubscribedIssuesRoute || isMySubscribedIssues;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useToast from "hooks/use-toast";
|
||||
@ -78,10 +78,7 @@ export const PeekOverviewIssueActivity: React.FC<Props> = ({ workspaceSlug, issu
|
||||
showAccessSpecifier={projectDetails && projectDetails.is_deployed}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<AddComment
|
||||
onSubmit={handleAddComment}
|
||||
showAccessSpecifier={projectDetails && projectDetails.is_deployed}
|
||||
/>
|
||||
<AddComment onSubmit={handleAddComment} showAccessSpecifier={projectDetails && projectDetails.is_deployed} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,17 +7,11 @@ import useSWR from "swr";
|
||||
// headless ui
|
||||
import { Combobox, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesServices from "services/issues.service";
|
||||
import issuesServices from "services/issue.service";
|
||||
// ui
|
||||
import { IssueLabelsList } from "components/ui";
|
||||
// icons
|
||||
import {
|
||||
CheckIcon,
|
||||
MagnifyingGlassIcon,
|
||||
PlusIcon,
|
||||
RectangleGroupIcon,
|
||||
TagIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { CheckIcon, MagnifyingGlassIcon, PlusIcon, RectangleGroupIcon, TagIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import type { IIssueLabels } from "types";
|
||||
// fetch-keys
|
||||
@ -39,24 +33,14 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
|
||||
|
||||
const { data: issueLabels } = useSWR<IIssueLabels[]>(
|
||||
projectId ? PROJECT_ISSUE_LABELS(projectId) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => issuesServices.getIssueLabels(workspaceSlug as string, projectId)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => issuesServices.getIssueLabels(workspaceSlug as string, projectId) : null
|
||||
);
|
||||
|
||||
const filteredOptions =
|
||||
query === ""
|
||||
? issueLabels
|
||||
: issueLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()));
|
||||
query === "" ? issueLabels : issueLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()));
|
||||
|
||||
return (
|
||||
<Combobox
|
||||
as="div"
|
||||
value={value}
|
||||
onChange={(val) => onChange(val)}
|
||||
className="relative flex-shrink-0"
|
||||
multiple
|
||||
>
|
||||
<Combobox as="div" value={value} onChange={(val) => onChange(val)} className="relative flex-shrink-0" multiple>
|
||||
{({ open }: any) => (
|
||||
<>
|
||||
<Combobox.Button className="flex items-center gap-2 cursor-pointer text-xs text-custom-text-200">
|
||||
@ -129,11 +113,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
|
||||
<span>{label.name}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-center rounded p-1">
|
||||
<CheckIcon
|
||||
className={`h-3 w-3 ${
|
||||
selected ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
/>
|
||||
<CheckIcon className={`h-3 w-3 ${selected ? "opacity-100" : "opacity-0"}`} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -168,11 +148,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
|
||||
<span>{child.name}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-center rounded p-1">
|
||||
<CheckIcon
|
||||
className={`h-3 w-3 ${
|
||||
selected ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
/>
|
||||
<CheckIcon className={`h-3 w-3 ${selected ? "opacity-100" : "opacity-0"}`} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// ui
|
||||
import { CustomSearchSelect } from "components/ui";
|
||||
// icons
|
||||
@ -30,9 +30,7 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
|
||||
|
||||
const { data: stateGroups } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId) : null
|
||||
);
|
||||
const states = getStatesList(stateGroups);
|
||||
|
||||
@ -60,10 +58,7 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
|
||||
{selectedOption ? (
|
||||
<StateGroupIcon stateGroup={selectedOption.group} color={selectedOption.color} />
|
||||
) : currentDefaultState ? (
|
||||
<StateGroupIcon
|
||||
stateGroup={currentDefaultState.group}
|
||||
color={currentDefaultState.color}
|
||||
/>
|
||||
<StateGroupIcon stateGroup={currentDefaultState.group} color={currentDefaultState.color} />
|
||||
) : (
|
||||
<Squares2X2Icon className="h-3.5 w-3.5 text-custom-text-200" />
|
||||
)}
|
||||
|
@ -7,7 +7,7 @@ import { UseFormWatch } from "react-hook-form";
|
||||
import useToast from "hooks/use-toast";
|
||||
import useUser from "hooks/use-user";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// components
|
||||
import { ExistingIssuesListModal } from "components/core";
|
||||
// icons
|
||||
@ -23,12 +23,7 @@ type Props = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SidebarBlockedSelect: React.FC<Props> = ({
|
||||
issueId,
|
||||
submitChanges,
|
||||
watch,
|
||||
disabled = false,
|
||||
}) => {
|
||||
export const SidebarBlockedSelect: React.FC<Props> = ({ issueId, submitChanges, watch, disabled = false }) => {
|
||||
const [isBlockedModalOpen, setIsBlockedModalOpen] = useState(false);
|
||||
|
||||
const { user } = useUser();
|
||||
@ -80,10 +75,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
|
||||
})
|
||||
.then((response) => {
|
||||
submitChanges({
|
||||
related_issues: [
|
||||
...watch("related_issues")?.filter((i) => i.relation_type !== "blocked_by"),
|
||||
...response,
|
||||
],
|
||||
related_issues: [...watch("related_issues")?.filter((i) => i.relation_type !== "blocked_by"), ...response],
|
||||
});
|
||||
});
|
||||
|
||||
@ -127,9 +119,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
|
||||
type="button"
|
||||
className="opacity-0 duration-300 group-hover:opacity-100"
|
||||
onClick={() => {
|
||||
const updatedRelations = watch("related_issues")?.filter(
|
||||
(i) => i.id !== relation.id
|
||||
);
|
||||
const updatedRelations = watch("related_issues")?.filter((i) => i.id !== relation.id);
|
||||
|
||||
submitChanges({
|
||||
related_issues: updatedRelations,
|
||||
|
@ -10,7 +10,7 @@ import useUser from "hooks/use-user";
|
||||
// components
|
||||
import { ExistingIssuesListModal } from "components/core";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// icons
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { BlockerIcon } from "components/icons";
|
||||
@ -24,12 +24,7 @@ type Props = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SidebarBlockerSelect: React.FC<Props> = ({
|
||||
issueId,
|
||||
submitChanges,
|
||||
watch,
|
||||
disabled = false,
|
||||
}) => {
|
||||
export const SidebarBlockerSelect: React.FC<Props> = ({ issueId, submitChanges, watch, disabled = false }) => {
|
||||
const [isBlockerModalOpen, setIsBlockerModalOpen] = useState(false);
|
||||
|
||||
const { user } = useUser();
|
||||
@ -42,8 +37,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
|
||||
setIsBlockerModalOpen(false);
|
||||
};
|
||||
|
||||
const blockerIssue =
|
||||
watch("issue_relations")?.filter((i) => i.relation_type === "blocked_by") || [];
|
||||
const blockerIssue = watch("issue_relations")?.filter((i) => i.relation_type === "blocked_by") || [];
|
||||
|
||||
const onSubmit = async (data: ISearchIssueResponse[]) => {
|
||||
if (data.length === 0) {
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import cyclesService from "services/cycles.service";
|
||||
// ui
|
||||
import { Spinner, CustomSelect, Tooltip } from "components/ui";
|
||||
@ -22,23 +22,14 @@ type Props = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SidebarCycleSelect: React.FC<Props> = ({
|
||||
issueDetail,
|
||||
handleCycleChange,
|
||||
disabled = false,
|
||||
}) => {
|
||||
export const SidebarCycleSelect: React.FC<Props> = ({ issueDetail, handleCycleChange, disabled = false }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, issueId } = router.query;
|
||||
|
||||
const { data: incompleteCycles } = useSWR(
|
||||
workspaceSlug && projectId ? INCOMPLETE_CYCLES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () =>
|
||||
cyclesService.getCyclesWithParams(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
"incomplete"
|
||||
)
|
||||
? () => cyclesService.getCyclesWithParams(workspaceSlug as string, projectId as string, "incomplete")
|
||||
: null
|
||||
);
|
||||
|
||||
@ -63,21 +54,14 @@ export const SidebarCycleSelect: React.FC<Props> = ({
|
||||
<CustomSelect
|
||||
customButton={
|
||||
<div>
|
||||
<Tooltip
|
||||
position="left"
|
||||
tooltipContent={`${issueCycle ? issueCycle.cycle_detail.name : "No cycle"}`}
|
||||
>
|
||||
<Tooltip position="left" tooltipContent={`${issueCycle ? issueCycle.cycle_detail.name : "No cycle"}`}>
|
||||
<button
|
||||
type="button"
|
||||
className={`bg-custom-background-80 text-xs rounded px-2.5 py-0.5 w-full flex ${
|
||||
disabled ? "cursor-not-allowed" : ""
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`truncate ${
|
||||
issueCycle ? "text-custom-text-100" : "text-custom-text-200"
|
||||
}`}
|
||||
>
|
||||
<span className={`truncate ${issueCycle ? "text-custom-text-100" : "text-custom-text-200"}`}>
|
||||
{issueCycle ? issueCycle.cycle_detail.name : "No cycle"}
|
||||
</span>
|
||||
</button>
|
||||
|
@ -12,7 +12,7 @@ import { X, CopyPlus } from "lucide-react";
|
||||
import { BlockerIcon } from "components/icons";
|
||||
import { ExistingIssuesListModal } from "components/core";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// types
|
||||
import { BlockeIssueDetail, IIssue, ISearchIssueResponse } from "types";
|
||||
|
||||
|
@ -11,7 +11,7 @@ import { TwitterPicker } from "react-color";
|
||||
// headless ui
|
||||
import { Listbox, Popover, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// ui
|
||||
@ -66,25 +66,21 @@ export const SidebarLabelSelect: React.FC<Props> = ({
|
||||
|
||||
const { data: issueLabels, mutate: issueLabelMutate } = useSWR<IIssueLabels[]>(
|
||||
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
|
||||
const handleNewLabel = async (formData: Partial<IIssueLabels>) => {
|
||||
if (!workspaceSlug || !projectId || isSubmitting) return;
|
||||
|
||||
await issuesService
|
||||
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user)
|
||||
.then((res) => {
|
||||
reset(defaultValues);
|
||||
await issuesService.createIssueLabel(workspaceSlug as string, projectId as string, formData, user).then((res) => {
|
||||
reset(defaultValues);
|
||||
|
||||
issueLabelMutate((prevData: any) => [...(prevData ?? []), res], false);
|
||||
issueLabelMutate((prevData: any) => [...(prevData ?? []), res], false);
|
||||
|
||||
submitChanges({ labels_list: [...(issueDetails?.labels ?? []), res.id] });
|
||||
submitChanges({ labels_list: [...(issueDetails?.labels ?? []), res.id] });
|
||||
|
||||
setCreateLabelForm(false);
|
||||
});
|
||||
setCreateLabelForm(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -165,9 +161,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
|
||||
{issueLabels ? (
|
||||
issueLabels.length > 0 ? (
|
||||
issueLabels.map((label: IIssueLabels) => {
|
||||
const children = issueLabels?.filter(
|
||||
(l) => l.parent === label.id
|
||||
);
|
||||
const children = issueLabels?.filter((l) => l.parent === label.id);
|
||||
|
||||
if (children.length === 0) {
|
||||
if (!label.parent)
|
||||
@ -175,9 +169,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
|
||||
<Listbox.Option
|
||||
key={label.id}
|
||||
className={({ active, selected }) =>
|
||||
`${
|
||||
active || selected ? "bg-custom-background-90" : ""
|
||||
} ${
|
||||
`${active || selected ? "bg-custom-background-90" : ""} ${
|
||||
selected ? "" : "text-custom-text-200"
|
||||
} flex cursor-pointer select-none items-center gap-2 truncate p-2`
|
||||
}
|
||||
@ -186,10 +178,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
|
||||
<span
|
||||
className="h-2 w-2 flex-shrink-0 rounded-full"
|
||||
style={{
|
||||
backgroundColor:
|
||||
label.color && label.color !== ""
|
||||
? label.color
|
||||
: "#000",
|
||||
backgroundColor: label.color && label.color !== "" ? label.color : "#000",
|
||||
}}
|
||||
/>
|
||||
{label.name}
|
||||
@ -207,11 +196,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
|
||||
<Listbox.Option
|
||||
key={child.id}
|
||||
className={({ active, selected }) =>
|
||||
`${
|
||||
active || selected
|
||||
? "bg-custom-background-100"
|
||||
: ""
|
||||
} ${
|
||||
`${active || selected ? "bg-custom-background-100" : ""} ${
|
||||
selected ? "" : "text-custom-text-200"
|
||||
} flex cursor-pointer select-none items-center gap-2 truncate p-2`
|
||||
}
|
||||
@ -248,9 +233,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
|
||||
<button
|
||||
type="button"
|
||||
className={`flex ${
|
||||
isNotAllowed || uneditable
|
||||
? "cursor-not-allowed"
|
||||
: "cursor-pointer hover:bg-custom-background-90"
|
||||
isNotAllowed || uneditable ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-90"
|
||||
} items-center gap-1 rounded-2xl border border-custom-border-100 px-2 py-0.5 text-xs text-custom-text-200`}
|
||||
onClick={() => setCreateLabelForm((prevData) => !prevData)}
|
||||
disabled={uneditable}
|
||||
@ -326,11 +309,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
|
||||
>
|
||||
<XMarkIcon className="h-4 w-4 text-white" />
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="grid place-items-center rounded bg-green-500 p-2.5"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
<button type="submit" className="grid place-items-center rounded bg-green-500 p-2.5" disabled={isSubmitting}>
|
||||
<PlusIcon className="h-4 w-4 text-white" />
|
||||
</button>
|
||||
</form>
|
||||
|
@ -12,7 +12,7 @@ import { BlockerIcon, RelatedIcon } from "components/icons";
|
||||
// components
|
||||
import { ExistingIssuesListModal } from "components/core";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// types
|
||||
import { BlockeIssueDetail, IIssue, ISearchIssueResponse } from "types";
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// ui
|
||||
import { Spinner, CustomSelect } from "components/ui";
|
||||
// icons
|
||||
@ -28,9 +28,7 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, disabled
|
||||
|
||||
const { data: stateGroups } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
const states = getStatesList(stateGroups);
|
||||
|
||||
|
@ -12,7 +12,7 @@ import useUserAuth from "hooks/use-user-auth";
|
||||
import useUserIssueNotificationSubscription from "hooks/use-issue-notification-subscription";
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import modulesService from "services/modules.service";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
@ -103,8 +103,11 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
|
||||
const { isEstimateActive } = useEstimateOption();
|
||||
|
||||
const { loading, handleSubscribe, handleUnsubscribe, subscribed } =
|
||||
useUserIssueNotificationSubscription(workspaceSlug, projectId, issueId);
|
||||
const { loading, handleSubscribe, handleUnsubscribe, subscribed } = useUserIssueNotificationSubscription(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
issueId
|
||||
);
|
||||
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
|
||||
@ -198,13 +201,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
);
|
||||
|
||||
await issuesService
|
||||
.updateIssueLink(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueDetail.id,
|
||||
linkId,
|
||||
payload
|
||||
)
|
||||
.updateIssueLink(workspaceSlug as string, projectId as string, issueDetail.id, linkId, payload)
|
||||
.then((res) => {
|
||||
mutate(ISSUE_DETAILS(issueDetail.id));
|
||||
})
|
||||
@ -235,12 +232,9 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const handleCopyText = () => {
|
||||
const originURL =
|
||||
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
|
||||
copyTextToClipboard(
|
||||
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issueDetail?.id}`
|
||||
).then(() => {
|
||||
copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issueDetail?.id}`).then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Link Copied!",
|
||||
@ -264,9 +258,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
fieldsToShow.includes("dueDate");
|
||||
|
||||
const showThirdSection =
|
||||
fieldsToShow.includes("all") ||
|
||||
fieldsToShow.includes("cycle") ||
|
||||
fieldsToShow.includes("module");
|
||||
fieldsToShow.includes("all") || fieldsToShow.includes("cycle") || fieldsToShow.includes("module");
|
||||
|
||||
const startDate = watchIssue("start_date");
|
||||
const targetDate = watchIssue("target_date");
|
||||
@ -413,30 +405,27 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("estimate")) &&
|
||||
isEstimateActive && (
|
||||
<div className="flex flex-wrap items-center py-2">
|
||||
<div className="flex items-center gap-x-2 text-sm text-custom-text-200 sm:basis-1/2">
|
||||
<PlayIcon className="h-4 w-4 flex-shrink-0 -rotate-90" />
|
||||
<p>Estimate</p>
|
||||
</div>
|
||||
<div className="sm:basis-1/2">
|
||||
<Controller
|
||||
control={control}
|
||||
name="estimate_point"
|
||||
render={({ field: { value } }) => (
|
||||
<SidebarEstimateSelect
|
||||
value={value}
|
||||
onChange={(val: number | null) =>
|
||||
submitChanges({ estimate_point: val })
|
||||
}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("estimate")) && isEstimateActive && (
|
||||
<div className="flex flex-wrap items-center py-2">
|
||||
<div className="flex items-center gap-x-2 text-sm text-custom-text-200 sm:basis-1/2">
|
||||
<PlayIcon className="h-4 w-4 flex-shrink-0 -rotate-90" />
|
||||
<p>Estimate</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="sm:basis-1/2">
|
||||
<Controller
|
||||
control={control}
|
||||
name="estimate_point"
|
||||
render={({ field: { value } }) => (
|
||||
<SidebarEstimateSelect
|
||||
value={value}
|
||||
onChange={(val: number | null) => submitChanges({ estimate_point: val })}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{showSecondSection && (
|
||||
|
@ -8,7 +8,7 @@ import useSWR, { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// components
|
||||
@ -126,10 +126,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, user, disabled = false }
|
||||
<div className="flex items-center justify-start gap-3 text-custom-text-200">
|
||||
<Disclosure.Button className="flex items-center gap-1 rounded px-2 py-1 text-xs text-custom-text-100 hover:bg-custom-background-80">
|
||||
<ChevronRightIcon className={`h-3 w-3 ${open ? "rotate-90" : ""}`} />
|
||||
Sub-issues{" "}
|
||||
<span className="ml-1 text-custom-text-200">
|
||||
{subIssuesResponse.sub_issues.length}
|
||||
</span>
|
||||
Sub-issues <span className="ml-1 text-custom-text-200">{subIssuesResponse.sub_issues.length}</span>
|
||||
</Disclosure.Button>
|
||||
<div className="flex w-60 items-center gap-2">
|
||||
<div className="bar relative h-1.5 w-full rounded bg-custom-background-80">
|
||||
@ -186,10 +183,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, user, disabled = false }
|
||||
>
|
||||
<Disclosure.Panel className="mt-3 flex flex-col gap-y-1">
|
||||
{subIssuesResponse.sub_issues.map((issue) => (
|
||||
<Link
|
||||
key={issue.id}
|
||||
href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}
|
||||
>
|
||||
<Link key={issue.id} href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}>
|
||||
<a className="group flex items-center justify-between gap-2 rounded p-2 hover:bg-custom-background-90">
|
||||
<div className="flex items-center gap-2 rounded text-xs">
|
||||
<span
|
||||
@ -240,9 +234,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, user, disabled = false }
|
||||
noChevron
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={handleCreateIssueModal}>Create new</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem onClick={() => setSubIssuesListModal(true)}>
|
||||
Add an existing issue
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem onClick={() => setSubIssuesListModal(true)}>Add an existing issue</CustomMenu.MenuItem>
|
||||
</CustomMenu>
|
||||
)
|
||||
)}
|
||||
|
@ -6,7 +6,7 @@ import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import projectService from "services/project.service";
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// ui
|
||||
import { AssigneesList, Avatar, CustomSearchSelect, Icon, Tooltip } from "components/ui";
|
||||
// icons
|
||||
|
@ -5,7 +5,7 @@ import { CustomDatePicker, Tooltip } from "components/ui";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft, renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||
// services
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// types
|
||||
import { ICurrentUserResponse, IIssue } from "types";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
@ -42,9 +42,7 @@ export const ViewDueDateSelect: React.FC<Props> = ({
|
||||
return (
|
||||
<Tooltip
|
||||
tooltipHeading="Due date"
|
||||
tooltipContent={
|
||||
issue.target_date ? renderShortDateWithYearFormat(issue.target_date) ?? "N/A" : "N/A"
|
||||
}
|
||||
tooltipContent={issue.target_date ? renderShortDateWithYearFormat(issue.target_date) ?? "N/A" : "N/A"}
|
||||
position={tooltipPosition}
|
||||
>
|
||||
<div
|
||||
@ -80,9 +78,7 @@ export const ViewDueDateSelect: React.FC<Props> = ({
|
||||
);
|
||||
}}
|
||||
className={`${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${
|
||||
displayFilters.layout === "kanban"
|
||||
? "bg-custom-background-90"
|
||||
: "bg-custom-background-100"
|
||||
displayFilters.layout === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100"
|
||||
}`}
|
||||
minDate={minDate ?? undefined}
|
||||
noBorder={noBorder}
|
||||
|
@ -3,7 +3,7 @@ import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// services
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// hooks
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
// ui
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// component
|
||||
import { CreateLabelModal } from "components/labels";
|
||||
// ui
|
||||
@ -45,9 +45,7 @@ export const ViewLabelSelect: React.FC<Props> = ({
|
||||
|
||||
const { data: issueLabels } = useSWR<IIssueLabels[]>(
|
||||
projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
|
||||
const options = issueLabels?.map((label) => ({
|
||||
|
@ -3,7 +3,7 @@ import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// services
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// ui
|
||||
import { CustomSelect, Tooltip } from "components/ui";
|
||||
// icons
|
||||
@ -61,9 +61,9 @@ export const ViewPrioritySelect: React.FC<Props> = ({
|
||||
customButton={
|
||||
<button
|
||||
type="button"
|
||||
className={`grid place-items-center rounded ${
|
||||
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
||||
} ${noBorder ? "" : "h-6 w-6 border shadow-sm"} ${
|
||||
className={`grid place-items-center rounded ${isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"} ${
|
||||
noBorder ? "" : "h-6 w-6 border shadow-sm"
|
||||
} ${
|
||||
noBorder
|
||||
? ""
|
||||
: issue.priority === "urgent"
|
||||
@ -71,11 +71,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
|
||||
: "border-custom-border-300 bg-custom-background-100"
|
||||
} items-center`}
|
||||
>
|
||||
<Tooltip
|
||||
tooltipHeading="Priority"
|
||||
tooltipContent={issue.priority ?? "None"}
|
||||
position={tooltipPosition}
|
||||
>
|
||||
<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}
|
||||
|
@ -5,7 +5,7 @@ import { CustomDatePicker, Tooltip } from "components/ui";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft, renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||
// services
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// types
|
||||
import { ICurrentUserResponse, IIssue } from "types";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
@ -42,9 +42,7 @@ export const ViewStartDateSelect: React.FC<Props> = ({
|
||||
return (
|
||||
<Tooltip
|
||||
tooltipHeading="Start date"
|
||||
tooltipContent={
|
||||
issue.start_date ? renderShortDateWithYearFormat(issue.start_date) ?? "N/A" : "N/A"
|
||||
}
|
||||
tooltipContent={issue.start_date ? renderShortDateWithYearFormat(issue.start_date) ?? "N/A" : "N/A"}
|
||||
position={tooltipPosition}
|
||||
>
|
||||
<div className="group flex-shrink-0 relative max-w-[6.5rem]">
|
||||
@ -72,9 +70,7 @@ export const ViewStartDateSelect: React.FC<Props> = ({
|
||||
);
|
||||
}}
|
||||
className={`${issue?.start_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${
|
||||
displayFilters.layout === "kanban"
|
||||
? "bg-custom-background-90"
|
||||
: "bg-custom-background-100"
|
||||
displayFilters.layout === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100"
|
||||
}`}
|
||||
maxDate={maxDate ?? undefined}
|
||||
noBorder={noBorder}
|
||||
|
@ -5,8 +5,8 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import trackEventServices from "services/track-event.service";
|
||||
import stateService from "services/project_state.service";
|
||||
import trackEventServices from "services/track_event.service";
|
||||
// ui
|
||||
import { CustomSearchSelect, Tooltip } from "components/ui";
|
||||
// icons
|
||||
@ -48,9 +48,7 @@ export const ViewStateSelect: React.FC<Props> = ({
|
||||
|
||||
const { data: stateGroups } = useSWR(
|
||||
workspaceSlug && issue && fetchStates ? STATES_LIST(issue.project) : null,
|
||||
workspaceSlug && issue && fetchStates
|
||||
? () => stateService.getStates(workspaceSlug as string, issue.project)
|
||||
: null
|
||||
workspaceSlug && issue && fetchStates ? () => stateService.getStates(workspaceSlug as string, issue.project) : null
|
||||
);
|
||||
const states = getStatesList(stateGroups);
|
||||
|
||||
@ -68,16 +66,10 @@ export const ViewStateSelect: React.FC<Props> = ({
|
||||
const selectedOption = issue.state_detail;
|
||||
|
||||
const stateLabel = (
|
||||
<Tooltip
|
||||
tooltipHeading="State"
|
||||
tooltipContent={selectedOption?.name ?? ""}
|
||||
position={tooltipPosition}
|
||||
>
|
||||
<Tooltip tooltipHeading="State" tooltipContent={selectedOption?.name ?? ""} position={tooltipPosition}>
|
||||
<div className="flex items-center cursor-pointer w-full gap-2 text-custom-text-200">
|
||||
<span className="h-3.5 w-3.5">
|
||||
{selectedOption && (
|
||||
<StateGroupIcon stateGroup={selectedOption.group} color={selectedOption.color} />
|
||||
)}
|
||||
{selectedOption && <StateGroupIcon stateGroup={selectedOption.group} color={selectedOption.color} />}
|
||||
</span>
|
||||
<span className="truncate">{selectedOption?.name ?? "State"}</span>
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@ import { TwitterPicker } from "react-color";
|
||||
// headless ui
|
||||
import { Dialog, Popover, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// ui
|
||||
import { Input, PrimaryButton, SecondaryButton } from "components/ui";
|
||||
// icons
|
||||
@ -36,13 +36,7 @@ const defaultValues: Partial<IState> = {
|
||||
color: "rgb(var(--color-text-200))",
|
||||
};
|
||||
|
||||
export const CreateLabelModal: React.FC<Props> = ({
|
||||
isOpen,
|
||||
projectId,
|
||||
handleClose,
|
||||
user,
|
||||
onSuccess,
|
||||
}) => {
|
||||
export const CreateLabelModal: React.FC<Props> = ({ isOpen, projectId, handleClose, user, onSuccess }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
@ -73,11 +67,7 @@ export const CreateLabelModal: React.FC<Props> = ({
|
||||
await issuesService
|
||||
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user)
|
||||
.then((res) => {
|
||||
mutate<IIssueLabels[]>(
|
||||
PROJECT_ISSUE_LABELS(projectId),
|
||||
(prevData) => [res, ...(prevData ?? [])],
|
||||
false
|
||||
);
|
||||
mutate<IIssueLabels[]>(PROJECT_ISSUE_LABELS(projectId), (prevData) => [res, ...(prevData ?? [])], false);
|
||||
onClose();
|
||||
if (onSuccess) onSuccess(res);
|
||||
})
|
||||
@ -115,10 +105,7 @@ export const CreateLabelModal: React.FC<Props> = ({
|
||||
<Dialog.Panel className="relative transform rounded-lg bg-custom-background-90 px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div>
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-custom-text-100"
|
||||
>
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
Create Label
|
||||
</Dialog.Title>
|
||||
<div className="mt-8 flex items-center gap-2">
|
||||
|
@ -13,7 +13,7 @@ import { TwitterPicker } from "react-color";
|
||||
// headless ui
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// ui
|
||||
import { Input, PrimaryButton, SecondaryButton } from "components/ui";
|
||||
// types
|
||||
@ -35,174 +35,157 @@ const defaultValues: Partial<IIssueLabels> = {
|
||||
color: "rgb(var(--color-text-200))",
|
||||
};
|
||||
|
||||
export const CreateUpdateLabelInline = forwardRef<HTMLDivElement, Props>(
|
||||
function CreateUpdateLabelInline(props, ref) {
|
||||
const { labelForm, setLabelForm, isUpdating, labelToUpdate, onClose } = props;
|
||||
export const CreateUpdateLabelInline = forwardRef<HTMLDivElement, Props>(function CreateUpdateLabelInline(props, ref) {
|
||||
const { labelForm, setLabelForm, isUpdating, labelToUpdate, onClose } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { user } = useUserAuth();
|
||||
const { user } = useUserAuth();
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
register,
|
||||
reset,
|
||||
formState: { errors, isSubmitting },
|
||||
watch,
|
||||
setValue,
|
||||
} = useForm<IIssueLabels>({
|
||||
defaultValues,
|
||||
});
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
register,
|
||||
reset,
|
||||
formState: { errors, isSubmitting },
|
||||
watch,
|
||||
setValue,
|
||||
} = useForm<IIssueLabels>({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
setLabelForm(false);
|
||||
reset(defaultValues);
|
||||
if (onClose) onClose();
|
||||
};
|
||||
const handleClose = () => {
|
||||
setLabelForm(false);
|
||||
reset(defaultValues);
|
||||
if (onClose) onClose();
|
||||
};
|
||||
|
||||
const handleLabelCreate: SubmitHandler<IIssueLabels> = async (formData) => {
|
||||
if (!workspaceSlug || !projectId || isSubmitting) return;
|
||||
const handleLabelCreate: SubmitHandler<IIssueLabels> = async (formData) => {
|
||||
if (!workspaceSlug || !projectId || isSubmitting) return;
|
||||
|
||||
await issuesService
|
||||
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user)
|
||||
.then((res) => {
|
||||
mutate<IIssueLabels[]>(
|
||||
PROJECT_ISSUE_LABELS(projectId as string),
|
||||
(prevData) => [res, ...(prevData ?? [])],
|
||||
false
|
||||
);
|
||||
handleClose();
|
||||
});
|
||||
};
|
||||
|
||||
const handleLabelUpdate: SubmitHandler<IIssueLabels> = async (formData) => {
|
||||
if (!workspaceSlug || !projectId || isSubmitting) return;
|
||||
|
||||
await issuesService
|
||||
.patchIssueLabel(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
labelToUpdate?.id ?? "",
|
||||
formData,
|
||||
user
|
||||
)
|
||||
.then(() => {
|
||||
reset(defaultValues);
|
||||
mutate<IIssueLabels[]>(
|
||||
PROJECT_ISSUE_LABELS(projectId as string),
|
||||
(prevData) =>
|
||||
prevData?.map((p) => (p.id === labelToUpdate?.id ? { ...p, ...formData } : p)),
|
||||
false
|
||||
);
|
||||
handleClose();
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!labelForm && isUpdating) return;
|
||||
|
||||
reset();
|
||||
}, [labelForm, isUpdating, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!labelToUpdate) return;
|
||||
|
||||
setValue(
|
||||
"color",
|
||||
labelToUpdate.color && labelToUpdate.color !== "" ? labelToUpdate.color : "#000"
|
||||
await issuesService.createIssueLabel(workspaceSlug as string, projectId as string, formData, user).then((res) => {
|
||||
mutate<IIssueLabels[]>(
|
||||
PROJECT_ISSUE_LABELS(projectId as string),
|
||||
(prevData) => [res, ...(prevData ?? [])],
|
||||
false
|
||||
);
|
||||
setValue("name", labelToUpdate.name);
|
||||
}, [labelToUpdate, setValue]);
|
||||
handleClose();
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (labelToUpdate) {
|
||||
setValue(
|
||||
"color",
|
||||
labelToUpdate.color && labelToUpdate.color !== "" ? labelToUpdate.color : "#000"
|
||||
const handleLabelUpdate: SubmitHandler<IIssueLabels> = async (formData) => {
|
||||
if (!workspaceSlug || !projectId || isSubmitting) return;
|
||||
|
||||
await issuesService
|
||||
.patchIssueLabel(workspaceSlug as string, projectId as string, labelToUpdate?.id ?? "", formData, user)
|
||||
.then(() => {
|
||||
reset(defaultValues);
|
||||
mutate<IIssueLabels[]>(
|
||||
PROJECT_ISSUE_LABELS(projectId as string),
|
||||
(prevData) => prevData?.map((p) => (p.id === labelToUpdate?.id ? { ...p, ...formData } : p)),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
handleClose();
|
||||
});
|
||||
};
|
||||
|
||||
setValue("color", getRandomLabelColor());
|
||||
}, [labelToUpdate, setValue]);
|
||||
useEffect(() => {
|
||||
if (!labelForm && isUpdating) return;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex scroll-m-8 items-center gap-2 rounded border border-custom-border-200 bg-custom-background-100 px-3.5 py-2 ${
|
||||
labelForm ? "" : "hidden"
|
||||
}`}
|
||||
ref={ref}
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<Popover className="relative z-10 flex h-full w-full items-center justify-center">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className={`group inline-flex items-center text-base font-medium focus:outline-none ${
|
||||
open ? "text-custom-text-100" : "text-custom-text-200"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className="h-4 w-4 rounded-full"
|
||||
style={{
|
||||
backgroundColor: watch("color"),
|
||||
}}
|
||||
reset();
|
||||
}, [labelForm, isUpdating, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!labelToUpdate) return;
|
||||
|
||||
setValue("color", labelToUpdate.color && labelToUpdate.color !== "" ? labelToUpdate.color : "#000");
|
||||
setValue("name", labelToUpdate.name);
|
||||
}, [labelToUpdate, setValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (labelToUpdate) {
|
||||
setValue("color", labelToUpdate.color && labelToUpdate.color !== "" ? labelToUpdate.color : "#000");
|
||||
return;
|
||||
}
|
||||
|
||||
setValue("color", getRandomLabelColor());
|
||||
}, [labelToUpdate, setValue]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex scroll-m-8 items-center gap-2 rounded border border-custom-border-200 bg-custom-background-100 px-3.5 py-2 ${
|
||||
labelForm ? "" : "hidden"
|
||||
}`}
|
||||
ref={ref}
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<Popover className="relative z-10 flex h-full w-full items-center justify-center">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className={`group inline-flex items-center text-base font-medium focus:outline-none ${
|
||||
open ? "text-custom-text-100" : "text-custom-text-200"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className="h-4 w-4 rounded-full"
|
||||
style={{
|
||||
backgroundColor: watch("color"),
|
||||
}}
|
||||
/>
|
||||
</Popover.Button>
|
||||
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel className="absolute top-full left-0 z-20 mt-3 w-screen max-w-xs px-2 sm:px-0">
|
||||
<Controller
|
||||
name="color"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<TwitterPicker
|
||||
colors={LABEL_COLOR_OPTIONS}
|
||||
color={value}
|
||||
onChange={(value) => onChange(value.hex)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Popover.Button>
|
||||
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel className="absolute top-full left-0 z-20 mt-3 w-screen max-w-xs px-2 sm:px-0">
|
||||
<Controller
|
||||
name="color"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<TwitterPicker
|
||||
colors={LABEL_COLOR_OPTIONS}
|
||||
color={value}
|
||||
onChange={(value) => onChange(value.hex)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col justify-center">
|
||||
<Input
|
||||
type="text"
|
||||
id="labelName"
|
||||
name="name"
|
||||
register={register}
|
||||
placeholder="Label title"
|
||||
validations={{
|
||||
required: "Label title is required",
|
||||
}}
|
||||
error={errors.name}
|
||||
/>
|
||||
</div>
|
||||
<SecondaryButton onClick={() => handleClose()}>Cancel</SecondaryButton>
|
||||
{isUpdating ? (
|
||||
<PrimaryButton onClick={handleSubmit(handleLabelUpdate)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Updating" : "Update"}
|
||||
</PrimaryButton>
|
||||
) : (
|
||||
<PrimaryButton onClick={handleSubmit(handleLabelCreate)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Adding" : "Add"}
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
<div className="flex flex-1 flex-col justify-center">
|
||||
<Input
|
||||
type="text"
|
||||
id="labelName"
|
||||
name="name"
|
||||
register={register}
|
||||
placeholder="Label title"
|
||||
validations={{
|
||||
required: "Label title is required",
|
||||
}}
|
||||
error={errors.name}
|
||||
/>
|
||||
</div>
|
||||
<SecondaryButton onClick={() => handleClose()}>Cancel</SecondaryButton>
|
||||
{isUpdating ? (
|
||||
<PrimaryButton onClick={handleSubmit(handleLabelUpdate)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Updating" : "Update"}
|
||||
</PrimaryButton>
|
||||
) : (
|
||||
<PrimaryButton onClick={handleSubmit(handleLabelCreate)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Adding" : "Add"}
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ import { Dialog, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -95,23 +95,17 @@ export const DeleteLabelModal: React.FC<Props> = ({ isOpen, onClose, data, user
|
||||
<div className="px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationTriangleIcon
|
||||
className="h-6 w-6 text-red-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-custom-text-100"
|
||||
>
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
Delete Label
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
Are you sure you want to delete label-{" "}
|
||||
<span className="font-medium text-custom-text-100">{data?.name}</span>?
|
||||
The label will be removed from all the issues.
|
||||
<span className="font-medium text-custom-text-100">{data?.name}</span>? The label will be
|
||||
removed from all the issues.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@ import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { RectangleStackIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// types
|
||||
import { ICurrentUserResponse, IIssueLabels } from "types";
|
||||
// constants
|
||||
@ -30,9 +30,7 @@ export const LabelsListModal: React.FC<Props> = ({ isOpen, handleClose, parent,
|
||||
|
||||
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
|
||||
workspaceSlug && projectId ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
|
||||
const filteredLabels: IIssueLabels[] =
|
||||
@ -114,9 +112,7 @@ export const LabelsListModal: React.FC<Props> = ({ isOpen, handleClose, parent,
|
||||
{filteredLabels.length > 0 && (
|
||||
<li className="p-2">
|
||||
{query === "" && (
|
||||
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-custom-text-100">
|
||||
Labels
|
||||
</h2>
|
||||
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-custom-text-100">Labels</h2>
|
||||
)}
|
||||
<ul className="text-sm text-gray-700">
|
||||
{filteredLabels.map((label) => {
|
||||
|
@ -7,17 +7,11 @@ import { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// ui
|
||||
import { CustomMenu } from "components/ui";
|
||||
// icons
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
XMarkIcon,
|
||||
PlusIcon,
|
||||
PencilIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { ChevronDownIcon, XMarkIcon, PlusIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
import { Component, X } from "lucide-react";
|
||||
// types
|
||||
import { ICurrentUserResponse, IIssueLabels } from "types";
|
||||
@ -110,9 +104,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
|
||||
<Disclosure.Button>
|
||||
<span>
|
||||
<ChevronDownIcon
|
||||
className={`h-4 w-4 text-custom-sidebar-text-400 ${
|
||||
!open ? "rotate-90 transform" : ""
|
||||
}`}
|
||||
className={`h-4 w-4 text-custom-sidebar-text-400 ${!open ? "rotate-90 transform" : ""}`}
|
||||
/>
|
||||
</span>
|
||||
</Disclosure.Button>
|
||||
@ -138,8 +130,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
|
||||
<span
|
||||
className="h-3.5 w-3.5 flex-shrink-0 rounded-full"
|
||||
style={{
|
||||
backgroundColor:
|
||||
child.color && child.color !== "" ? child.color : "#000000",
|
||||
backgroundColor: child.color && child.color !== "" ? child.color : "#000000",
|
||||
}}
|
||||
/>
|
||||
{child.name}
|
||||
@ -169,10 +160,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
className="flex items-center justify-start gap-2"
|
||||
onClick={handleLabelDelete}
|
||||
>
|
||||
<button className="flex items-center justify-start gap-2" onClick={handleLabelDelete}>
|
||||
<X className="h-[18px] w-[18px] text-custom-sidebar-text-400 flex-shrink-0" />
|
||||
</button>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@ import { PaperAirplaneIcon } from "@heroicons/react/24/outline";
|
||||
// react-hook-form
|
||||
import { useForm } from "react-hook-form";
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
@ -81,8 +81,7 @@ export const CreateBlock: React.FC<Props> = ({ user }) => {
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: any) => {
|
||||
const keyCombination =
|
||||
((e.ctrlKey || e.metaKey) && e.key === "Enter") || (e.shiftKey && e.key === "Enter");
|
||||
const keyCombination = ((e.ctrlKey || e.metaKey) && e.key === "Enter") || (e.shiftKey && e.key === "Enter");
|
||||
|
||||
if (e.key === "Enter" && !keyCombination) {
|
||||
if (watch("name") && watch("name") !== "") {
|
||||
|
@ -4,8 +4,8 @@ import { mutate } from "swr";
|
||||
import { SparklesIcon } from "@heroicons/react/24/outline";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import issuesService from "services/issues.service";
|
||||
import pagesService from "services/page.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import aiService from "services/ai.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
@ -195,8 +195,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message:
|
||||
"You have reached the maximum number of requests of 50 requests per month per user.",
|
||||
message: "You have reached the maximum number of requests of 50 requests per month per user.",
|
||||
});
|
||||
else
|
||||
setToastAlert({
|
||||
@ -294,9 +293,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
/>
|
||||
);
|
||||
else if (!value || !watch("description_html"))
|
||||
return (
|
||||
<div className="h-32 w-full flex items-center justify-center text-custom-text-200 text-sm" />
|
||||
);
|
||||
return <div className="h-32 w-full flex items-center justify-center text-custom-text-200 text-sm" />;
|
||||
|
||||
return (
|
||||
<TipTapEditor
|
||||
@ -351,13 +348,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
<div className="flex items-center justify-end gap-2 p-4">
|
||||
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
||||
<PrimaryButton type="submit" disabled={watch("name") === ""} loading={isSubmitting}>
|
||||
{data
|
||||
? isSubmitting
|
||||
? "Updating..."
|
||||
: "Update block"
|
||||
: isSubmitting
|
||||
? "Adding..."
|
||||
: "Add block"}
|
||||
{data ? (isSubmitting ? "Updating..." : "Update block") : isSubmitting ? "Adding..." : "Add block"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
@ -371,9 +362,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
onResponse={(response) => {
|
||||
if (data && handleAiAssistance) {
|
||||
handleAiAssistance(response);
|
||||
editorRef.current?.setEditorValue(
|
||||
`${watch("description_html")}<p>${response}</p>` ?? ""
|
||||
);
|
||||
editorRef.current?.setEditorValue(`${watch("description_html")}<p>${response}</p>` ?? "");
|
||||
} else {
|
||||
setValue("description", {});
|
||||
setValue("description_html", `${watch("description_html")}<p>${response}</p>`);
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
@ -15,12 +15,7 @@ import { PageForm } from "./page-form";
|
||||
// types
|
||||
import { ICurrentUserResponse, IPage } from "types";
|
||||
// fetch-keys
|
||||
import {
|
||||
ALL_PAGES_LIST,
|
||||
FAVORITE_PAGES_LIST,
|
||||
MY_PAGES_LIST,
|
||||
RECENT_PAGES_LIST,
|
||||
} from "constants/fetch-keys";
|
||||
import { ALL_PAGES_LIST, FAVORITE_PAGES_LIST, MY_PAGES_LIST, RECENT_PAGES_LIST } from "constants/fetch-keys";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -17,12 +17,7 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import type { ICurrentUserResponse, IPage } from "types";
|
||||
// fetch-keys
|
||||
import {
|
||||
ALL_PAGES_LIST,
|
||||
FAVORITE_PAGES_LIST,
|
||||
MY_PAGES_LIST,
|
||||
RECENT_PAGES_LIST,
|
||||
} from "constants/fetch-keys";
|
||||
import { ALL_PAGES_LIST, FAVORITE_PAGES_LIST, MY_PAGES_LIST, RECENT_PAGES_LIST } from "constants/fetch-keys";
|
||||
|
||||
type TConfirmPageDeletionProps = {
|
||||
isOpen: boolean;
|
||||
@ -31,12 +26,7 @@ type TConfirmPageDeletionProps = {
|
||||
user: ICurrentUserResponse | undefined;
|
||||
};
|
||||
|
||||
export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
data,
|
||||
user,
|
||||
}) => {
|
||||
export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = ({ isOpen, setIsOpen, data, user }) => {
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
@ -121,26 +111,17 @@ export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = ({
|
||||
<div className="px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-500/20 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationTriangleIcon
|
||||
className="h-6 w-6 text-red-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-custom-text-100"
|
||||
>
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
Delete Page
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
Are you sure you want to delete Page-{" "}
|
||||
<span className="break-words font-medium text-custom-text-100">
|
||||
{data?.name}
|
||||
</span>
|
||||
? All of the data related to the page will be permanently removed. This
|
||||
action cannot be undone.
|
||||
<span className="break-words font-medium text-custom-text-100">{data?.name}</span>? All of the
|
||||
data related to the page will be permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
// components
|
||||
import { PagesView } from "components/pages";
|
||||
// types
|
||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
// components
|
||||
import { PagesView } from "components/pages";
|
||||
// types
|
||||
@ -18,8 +18,7 @@ export const FavoritePagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
const { data: pages } = useSWR(
|
||||
workspaceSlug && projectId ? FAVORITE_PAGES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () =>
|
||||
pagesService.getPagesWithParams(workspaceSlug as string, projectId as string, "favorite")
|
||||
? () => pagesService.getPagesWithParams(workspaceSlug as string, projectId as string, "favorite")
|
||||
: null
|
||||
);
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
// components
|
||||
import { PagesView } from "components/pages";
|
||||
// types
|
||||
@ -18,12 +18,7 @@ export const MyPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
const { data: pages } = useSWR(
|
||||
workspaceSlug && projectId ? MY_PAGES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () =>
|
||||
pagesService.getPagesWithParams(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
"created_by_me"
|
||||
)
|
||||
? () => pagesService.getPagesWithParams(workspaceSlug as string, projectId as string, "created_by_me")
|
||||
: null
|
||||
);
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
// components
|
||||
import { PagesView } from "components/pages";
|
||||
// types
|
||||
@ -18,12 +18,7 @@ export const OtherPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
const { data: pages } = useSWR(
|
||||
workspaceSlug && projectId ? OTHER_PAGES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () =>
|
||||
pagesService.getPagesWithParams(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
"created_by_other"
|
||||
)
|
||||
? () => pagesService.getPagesWithParams(workspaceSlug as string, projectId as string, "created_by_other")
|
||||
: null
|
||||
);
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
// components
|
||||
import { PagesView } from "components/pages";
|
||||
// ui
|
||||
@ -28,9 +28,7 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
|
||||
const { data: pages } = useSWR(
|
||||
workspaceSlug && projectId ? RECENT_PAGES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => pagesService.getRecentPages(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => pagesService.getRecentPages(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
|
||||
const isEmpty = pages && Object.keys(pages).every((key) => pages[key].length === 0);
|
||||
@ -44,9 +42,7 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
|
||||
return (
|
||||
<div key={key} className="h-full overflow-hidden">
|
||||
<h2 className="text-xl font-semibold capitalize mb-2">
|
||||
{replaceUnderscoreIfSnakeCase(key)}
|
||||
</h2>
|
||||
<h2 className="text-xl font-semibold capitalize mb-2">{replaceUnderscoreIfSnakeCase(key)}</h2>
|
||||
<PagesView pages={pages[key as keyof RecentPagesResponse]} viewType={viewType} />
|
||||
</div>
|
||||
);
|
||||
|
@ -4,18 +4,13 @@ import useSWR, { mutate } from "swr";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import pagesService from "services/page.service";
|
||||
import projectService from "services/project.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
// components
|
||||
import {
|
||||
CreateUpdatePageModal,
|
||||
DeletePageModal,
|
||||
SinglePageDetailedItem,
|
||||
SinglePageListItem,
|
||||
} from "components/pages";
|
||||
import { CreateUpdatePageModal, DeletePageModal, SinglePageDetailedItem, SinglePageListItem } from "components/pages";
|
||||
// ui
|
||||
import { EmptyState, Loader } from "components/ui";
|
||||
// icons
|
||||
@ -91,11 +86,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||
}),
|
||||
false
|
||||
);
|
||||
mutate<IPage[]>(
|
||||
FAVORITE_PAGES_LIST(projectId.toString()),
|
||||
(prevData) => [page, ...(prevData ?? [])],
|
||||
false
|
||||
);
|
||||
mutate<IPage[]>(FAVORITE_PAGES_LIST(projectId.toString()), (prevData) => [page, ...(prevData ?? [])], false);
|
||||
|
||||
pagesService
|
||||
.addPageToFavorites(workspaceSlug.toString(), projectId.toString(), {
|
||||
@ -185,11 +176,9 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||
false
|
||||
);
|
||||
|
||||
pagesService
|
||||
.patchPage(workspaceSlug.toString(), projectId.toString(), page.id, formData, user)
|
||||
.then(() => {
|
||||
mutate(RECENT_PAGES_LIST(projectId.toString()));
|
||||
});
|
||||
pagesService.patchPage(workspaceSlug.toString(), projectId.toString(), page.id, formData, user).then(() => {
|
||||
mutate(RECENT_PAGES_LIST(projectId.toString()));
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -10,8 +10,8 @@ import { useForm } from "react-hook-form";
|
||||
// react-beautiful-dnd
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
// services
|
||||
import pagesService from "services/pages.service";
|
||||
import issuesService from "services/issues.service";
|
||||
import pagesService from "services/page.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import aiService from "services/ai.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
@ -48,13 +48,7 @@ type Props = {
|
||||
user: ICurrentUserResponse | undefined;
|
||||
};
|
||||
|
||||
export const SinglePageBlock: React.FC<Props> = ({
|
||||
block,
|
||||
projectDetails,
|
||||
showBlockDetails,
|
||||
index,
|
||||
user,
|
||||
}) => {
|
||||
export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, showBlockDetails, index, user }) => {
|
||||
const [isSyncing, setIsSyncing] = useState(false);
|
||||
const [createBlockForm, setCreateBlockForm] = useState(false);
|
||||
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
|
||||
@ -131,13 +125,7 @@ export const SinglePageBlock: React.FC<Props> = ({
|
||||
if (!workspaceSlug || !projectId || !pageId) return;
|
||||
|
||||
await pagesService
|
||||
.convertPageBlockToIssue(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
pageId as string,
|
||||
block.id,
|
||||
user
|
||||
)
|
||||
.convertPageBlockToIssue(workspaceSlug as string, projectId as string, pageId as string, block.id, user)
|
||||
.then((res: IIssue) => {
|
||||
mutate<IPageBlock[]>(
|
||||
PAGE_BLOCKS_LIST(pageId as string),
|
||||
@ -175,13 +163,7 @@ export const SinglePageBlock: React.FC<Props> = ({
|
||||
);
|
||||
|
||||
await pagesService
|
||||
.deletePageBlock(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
pageId as string,
|
||||
block.id,
|
||||
user
|
||||
)
|
||||
.deletePageBlock(workspaceSlug as string, projectId as string, pageId as string, block.id, user)
|
||||
.catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
@ -221,8 +203,7 @@ export const SinglePageBlock: React.FC<Props> = ({
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message:
|
||||
"You have reached the maximum number of requests of 50 requests per month per user.",
|
||||
message: "You have reached the maximum number of requests of 50 requests per month per user.",
|
||||
});
|
||||
else
|
||||
setToastAlert({
|
||||
@ -283,12 +264,9 @@ export const SinglePageBlock: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const handleCopyText = () => {
|
||||
const originURL =
|
||||
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
|
||||
copyTextToClipboard(
|
||||
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${block.issue}`
|
||||
).then(() => {
|
||||
copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${block.issue}`).then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Link Copied!",
|
||||
@ -343,11 +321,7 @@ export const SinglePageBlock: React.FC<Props> = ({
|
||||
>
|
||||
{block.issue && block.sync && (
|
||||
<div className="flex flex-shrink-0 cursor-default items-center gap-1 rounded py-1 px-1.5 text-xs">
|
||||
{isSyncing ? (
|
||||
<ArrowPathIcon className="h-3 w-3 animate-spin" />
|
||||
) : (
|
||||
<CheckIcon className="h-3 w-3" />
|
||||
)}
|
||||
{isSyncing ? <ArrowPathIcon className="h-3 w-3 animate-spin" /> : <CheckIcon className="h-3 w-3" />}
|
||||
{isSyncing ? "Syncing..." : "Synced"}
|
||||
</div>
|
||||
)}
|
||||
@ -431,9 +405,7 @@ export const SinglePageBlock: React.FC<Props> = ({
|
||||
<div className="flex items-center">
|
||||
{block.issue && (
|
||||
<div className="mr-1.5 flex">
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${block.issue}`}
|
||||
>
|
||||
<Link href={`/${workspaceSlug}/projects/${projectId}/issues/${block.issue}`}>
|
||||
<a className="flex h-6 flex-shrink-0 items-center gap-1 rounded bg-custom-background-80 px-1.5 py-1 text-xs">
|
||||
<LayerDiagonalIcon height="16" width="16" />
|
||||
{projectDetails?.identifier}-{block.issue_detail?.sequence_id}
|
||||
|
@ -7,7 +7,7 @@ import useSWR from "swr";
|
||||
// react-beautiful-dnd
|
||||
import { DropResult } from "react-beautiful-dnd";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import userService from "services/user.service";
|
||||
// hooks
|
||||
import useProfileIssues from "hooks/use-profile-issues";
|
||||
@ -31,9 +31,7 @@ export const ProfileIssuesView = () => {
|
||||
|
||||
// update issue modal
|
||||
const [editIssueModal, setEditIssueModal] = useState(false);
|
||||
const [issueToEdit, setIssueToEdit] = useState<
|
||||
(IIssue & { actionType: "edit" | "delete" }) | undefined
|
||||
>(undefined);
|
||||
const [issueToEdit, setIssueToEdit] = useState<(IIssue & { actionType: "edit" | "delete" }) | undefined>(undefined);
|
||||
|
||||
// delete issue modal
|
||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
@ -60,19 +58,14 @@ export const ProfileIssuesView = () => {
|
||||
} = useProfileIssues(workspaceSlug?.toString(), userId?.toString());
|
||||
|
||||
const { data: profileData } = useSWR(
|
||||
workspaceSlug && userId ? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) : null,
|
||||
workspaceSlug && userId
|
||||
? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString())
|
||||
: null,
|
||||
workspaceSlug && userId
|
||||
? () =>
|
||||
userService.getUserProfileProjectsSegregation(workspaceSlug.toString(), userId.toString())
|
||||
? () => userService.getUserProfileProjectsSegregation(workspaceSlug.toString(), userId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: labels } = useSWR(
|
||||
workspaceSlug && (filters?.labels ?? []).length > 0
|
||||
? WORKSPACE_LABELS(workspaceSlug.toString())
|
||||
: null,
|
||||
workspaceSlug && (filters?.labels ?? []).length > 0 ? WORKSPACE_LABELS(workspaceSlug.toString()) : null,
|
||||
workspaceSlug && (filters?.labels ?? []).length > 0
|
||||
? () => issuesService.getWorkspaceLabels(workspaceSlug.toString())
|
||||
: null
|
||||
@ -90,13 +83,7 @@ export const ProfileIssuesView = () => {
|
||||
async (result: DropResult) => {
|
||||
setTrashBox(false);
|
||||
|
||||
if (
|
||||
!result.destination ||
|
||||
!workspaceSlug ||
|
||||
!groupedIssues ||
|
||||
displayFilters?.group_by !== "priority"
|
||||
)
|
||||
return;
|
||||
if (!result.destination || !workspaceSlug || !groupedIssues || displayFilters?.group_by !== "priority") return;
|
||||
|
||||
const { source, destination } = result;
|
||||
|
||||
@ -125,10 +112,7 @@ export const ProfileIssuesView = () => {
|
||||
return {
|
||||
...prevData,
|
||||
[sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
[destinationGroup]: orderArrayBy(
|
||||
destinationGroupArray,
|
||||
displayFilters.order_by ?? "-created_at"
|
||||
),
|
||||
[destinationGroup]: orderArrayBy(destinationGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
};
|
||||
}, false);
|
||||
|
||||
@ -218,15 +202,11 @@ export const ProfileIssuesView = () => {
|
||||
(key) => filtersToDisplay[key as keyof IIssueFilterOptions] === null
|
||||
);
|
||||
const areFiltersApplied =
|
||||
Object.keys(filtersToDisplay).length > 0 &&
|
||||
nullFilters.length !== Object.keys(filtersToDisplay).length;
|
||||
Object.keys(filtersToDisplay).length > 0 && nullFilters.length !== Object.keys(filtersToDisplay).length;
|
||||
|
||||
const isSubscribedIssuesRoute = router.pathname.includes("subscribed");
|
||||
const isMySubscribedIssues =
|
||||
(filters.subscriber &&
|
||||
filters.subscriber.length > 0 &&
|
||||
router.pathname.includes("my-issues")) ??
|
||||
false;
|
||||
(filters.subscriber && filters.subscriber.length > 0 && router.pathname.includes("my-issues")) ?? false;
|
||||
|
||||
const disableAddIssueOption = isSubscribedIssuesRoute || isMySubscribedIssues;
|
||||
|
||||
|
@ -6,21 +6,14 @@ import { Controller, useForm } from "react-hook-form";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// ui components
|
||||
import {
|
||||
ToggleSwitch,
|
||||
PrimaryButton,
|
||||
SecondaryButton,
|
||||
Icon,
|
||||
DangerButton,
|
||||
Loader,
|
||||
} from "components/ui";
|
||||
import { ToggleSwitch, PrimaryButton, SecondaryButton, Icon, DangerButton, Loader } from "components/ui";
|
||||
import { CustomPopover } from "./popover";
|
||||
// mobx react lite
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { RootStore } from "store/root";
|
||||
import { IProjectPublishSettings, TProjectPublishViews } from "store/project-publish";
|
||||
import { IProjectPublishSettings, TProjectPublishViews } from "store/project_publish";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
@ -65,8 +58,8 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
||||
|
||||
let plane_deploy_url = process.env.NEXT_PUBLIC_DEPLOY_URL;
|
||||
|
||||
if (typeof window !== 'undefined' && !plane_deploy_url) {
|
||||
plane_deploy_url= window.location.protocol + "//" + window.location.host + "/spaces";
|
||||
if (typeof window !== "undefined" && !plane_deploy_url) {
|
||||
plane_deploy_url = window.location.protocol + "//" + window.location.host + "/spaces";
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
@ -101,10 +94,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
||||
|
||||
// prefill form with the saved settings if the project is already published
|
||||
useEffect(() => {
|
||||
if (
|
||||
projectPublish.projectPublishSettings &&
|
||||
projectPublish.projectPublishSettings !== "not-initialized"
|
||||
) {
|
||||
if (projectPublish.projectPublishSettings && projectPublish.projectPublishSettings !== "not-initialized") {
|
||||
let userBoards: TProjectPublishViews[] = [];
|
||||
|
||||
if (projectPublish.projectPublishSettings?.views) {
|
||||
@ -143,11 +133,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
||||
projectPublish.project_id !== null &&
|
||||
projectPublish?.projectPublishSettings === "not-initialized"
|
||||
) {
|
||||
projectPublish.getProjectSettingsAsync(
|
||||
workspaceSlug.toString(),
|
||||
projectPublish.project_id,
|
||||
null
|
||||
);
|
||||
projectPublish.getProjectSettingsAsync(workspaceSlug.toString(), projectPublish.project_id, null);
|
||||
}
|
||||
}, [workspaceSlug, projectPublish, projectPublish.projectPublishModal]);
|
||||
|
||||
@ -202,12 +188,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
||||
setIsUnpublishing(true);
|
||||
|
||||
projectPublish
|
||||
.unPublishProject(
|
||||
workspaceSlug.toString(),
|
||||
projectPublish.project_id as string,
|
||||
publishId,
|
||||
null
|
||||
)
|
||||
.unPublishProject(workspaceSlug.toString(), projectPublish.project_id as string, publishId, null)
|
||||
.then((res) => {
|
||||
mutateProjectDetails();
|
||||
|
||||
@ -269,11 +250,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
||||
|
||||
// check if an update is required or not
|
||||
const checkIfUpdateIsRequired = () => {
|
||||
if (
|
||||
!projectPublish.projectPublishSettings ||
|
||||
projectPublish.projectPublishSettings === "not-initialized"
|
||||
)
|
||||
return;
|
||||
if (!projectPublish.projectPublishSettings || projectPublish.projectPublishSettings === "not-initialized") return;
|
||||
|
||||
const currentSettings = projectPublish.projectPublishSettings as IProjectPublishSettings;
|
||||
const newSettings = getValues();
|
||||
@ -289,8 +266,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
||||
|
||||
let viewCheckFlag = 0;
|
||||
viewOptions.forEach((option) => {
|
||||
if (currentSettings.views[option.key] !== newSettings.views.includes(option.key))
|
||||
viewCheckFlag++;
|
||||
if (currentSettings.views[option.key] !== newSettings.views.includes(option.key)) viewCheckFlag++;
|
||||
});
|
||||
|
||||
if (viewCheckFlag !== 0) {
|
||||
@ -416,9 +392,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
||||
}}
|
||||
>
|
||||
<div className="text-sm">{option.label}</div>
|
||||
<div
|
||||
className={`w-[18px] h-[18px] relative flex justify-center items-center`}
|
||||
>
|
||||
<div className={`w-[18px] h-[18px] relative flex justify-center items-center`}>
|
||||
{value.length > 0 && value.includes(option.key) && (
|
||||
<Icon iconName="done" className="!text-lg" />
|
||||
)}
|
||||
|
@ -11,7 +11,7 @@ import { TwitterPicker } from "react-color";
|
||||
// headless ui
|
||||
import { Dialog, Popover, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -131,10 +131,7 @@ export const CreateStateModal: React.FC<Props> = ({ isOpen, projectId, handleClo
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-custom-background-80 px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div>
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-custom-text-100"
|
||||
>
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
Create State
|
||||
</Dialog.Title>
|
||||
<div className="mt-2 space-y-3">
|
||||
@ -215,10 +212,7 @@ export const CreateStateModal: React.FC<Props> = ({ isOpen, projectId, handleClo
|
||||
name="color"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<TwitterPicker
|
||||
color={value}
|
||||
onChange={(value) => onChange(value.hex)}
|
||||
/>
|
||||
<TwitterPicker color={value} onChange={(value) => onChange(value.hex)} />
|
||||
)}
|
||||
/>
|
||||
</Popover.Panel>
|
||||
|
@ -11,7 +11,7 @@ import { TwitterPicker } from "react-color";
|
||||
// headless ui
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -39,13 +39,7 @@ const defaultValues: Partial<IState> = {
|
||||
group: "backlog",
|
||||
};
|
||||
|
||||
export const CreateUpdateStateInline: React.FC<Props> = ({
|
||||
data,
|
||||
onClose,
|
||||
selectedGroup,
|
||||
user,
|
||||
groupLength,
|
||||
}) => {
|
||||
export const CreateUpdateStateInline: React.FC<Props> = ({ data, onClose, selectedGroup, user, groupLength }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
@ -153,8 +147,7 @@ export const CreateUpdateStateInline: React.FC<Props> = ({
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message:
|
||||
"Another state exists with the same name. Please try again with another name.",
|
||||
message: "Another state exists with the same name. Please try again with another name.",
|
||||
});
|
||||
else
|
||||
setToastAlert({
|
||||
@ -230,9 +223,7 @@ export const CreateUpdateStateInline: React.FC<Props> = ({
|
||||
name="group"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Tooltip
|
||||
tooltipContent={groupLength === 1 ? "Cannot have an empty group." : "Choose State"}
|
||||
>
|
||||
<Tooltip tooltipContent={groupLength === 1 ? "Cannot have an empty group." : "Choose State"}>
|
||||
<div>
|
||||
<CustomSelect
|
||||
disabled={groupLength === 1}
|
||||
|
@ -9,7 +9,7 @@ import { Dialog, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||
// services
|
||||
import stateServices from "services/state.service";
|
||||
import stateServices from "services/project_state.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -112,24 +112,17 @@ export const DeleteStateModal: React.FC<Props> = ({ isOpen, onClose, data, user
|
||||
<div className="bg-custom-background-80 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationTriangleIcon
|
||||
className="h-6 w-6 text-red-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-custom-text-100"
|
||||
>
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
Delete State
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
Are you sure you want to delete state-{" "}
|
||||
<span className="font-medium text-custom-text-100">{data?.name}</span>?
|
||||
All of the data related to the state will be permanently removed. This
|
||||
action cannot be undone.
|
||||
<span className="font-medium text-custom-text-100">{data?.name}</span>? All of the data
|
||||
related to the state will be permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// ui
|
||||
import { Tooltip } from "components/ui";
|
||||
// icons
|
||||
@ -58,11 +58,7 @@ export const SingleState: React.FC<Props> = ({
|
||||
}));
|
||||
newStatesList = orderArrayBy(newStatesList, "sequence", "ascending");
|
||||
|
||||
mutate(
|
||||
STATES_LIST(projectId as string),
|
||||
orderStateGroups(groupBy(newStatesList, "group")),
|
||||
false
|
||||
);
|
||||
mutate(STATES_LIST(projectId as string), orderStateGroups(groupBy(newStatesList, "group")), false);
|
||||
|
||||
if (currentDefaultState)
|
||||
stateService
|
||||
@ -131,11 +127,7 @@ export const SingleState: React.FC<Props> = ({
|
||||
}));
|
||||
newStatesList = orderArrayBy(newStatesList, "sequence", "ascending");
|
||||
|
||||
mutate(
|
||||
STATES_LIST(projectId as string),
|
||||
orderStateGroups(groupBy(newStatesList, "group")),
|
||||
false
|
||||
);
|
||||
mutate(STATES_LIST(projectId as string), orderStateGroups(groupBy(newStatesList, "group")), false);
|
||||
|
||||
stateService
|
||||
.patchState(
|
||||
@ -216,26 +208,14 @@ export const SingleState: React.FC<Props> = ({
|
||||
>
|
||||
{state.default ? (
|
||||
<Tooltip tooltipContent="Cannot delete the default state.">
|
||||
<X
|
||||
className={`h-4 w-4 ${
|
||||
groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"
|
||||
}`}
|
||||
/>
|
||||
<X className={`h-4 w-4 ${groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"}`} />
|
||||
</Tooltip>
|
||||
) : groupLength === 1 ? (
|
||||
<Tooltip tooltipContent="Cannot have an empty group.">
|
||||
<X
|
||||
className={`h-4 w-4 ${
|
||||
groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"
|
||||
}`}
|
||||
/>
|
||||
<X className={`h-4 w-4 ${groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"}`} />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<X
|
||||
className={`h-4 w-4 ${
|
||||
groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"
|
||||
}`}
|
||||
/>
|
||||
<X className={`h-4 w-4 ${groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"}`} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@ import useSWR from "swr";
|
||||
// react-hook-form
|
||||
import { useForm } from "react-hook-form";
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
// hooks
|
||||
import useProjectMembers from "hooks/use-project-members";
|
||||
// components
|
||||
@ -20,7 +20,7 @@ import { checkIfArraysHaveSameElements } from "helpers/array.helper";
|
||||
import { getStatesList } from "helpers/state.helper";
|
||||
// types
|
||||
import { IQuery, IView } from "types";
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// fetch-keys
|
||||
import { PROJECT_ISSUE_LABELS, STATES_LIST } from "constants/fetch-keys";
|
||||
|
||||
@ -37,13 +37,7 @@ const defaultValues: Partial<IView> = {
|
||||
description: "",
|
||||
};
|
||||
|
||||
export const ViewForm: React.FC<Props> = ({
|
||||
handleFormSubmit,
|
||||
handleClose,
|
||||
status,
|
||||
data,
|
||||
preLoadedData,
|
||||
}) => {
|
||||
export const ViewForm: React.FC<Props> = ({ handleFormSubmit, handleClose, status, data, preLoadedData }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
@ -60,9 +54,7 @@ export const ViewForm: React.FC<Props> = ({
|
||||
const filters = watch("query");
|
||||
|
||||
const { data: stateGroups } = useSWR(
|
||||
workspaceSlug && projectId && (filters?.state ?? []).length > 0
|
||||
? STATES_LIST(projectId as string)
|
||||
: null,
|
||||
workspaceSlug && projectId && (filters?.state ?? []).length > 0 ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug && (filters?.state ?? []).length > 0
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
@ -117,9 +109,7 @@ export const ViewForm: React.FC<Props> = ({
|
||||
return (
|
||||
<form onSubmit={handleSubmit(handleCreateUpdateView)}>
|
||||
<div className="space-y-5">
|
||||
<h3 className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
{status ? "Update" : "Create"} View
|
||||
</h3>
|
||||
<h3 className="text-lg font-medium leading-6 text-custom-text-100">{status ? "Update" : "Create"} View</h3>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Input
|
||||
@ -157,10 +147,7 @@ export const ViewForm: React.FC<Props> = ({
|
||||
const key = option.key as keyof typeof filters;
|
||||
|
||||
if (key === "start_date" || key === "target_date") {
|
||||
const valueExists = checkIfArraysHaveSameElements(
|
||||
filters?.[key] ?? [],
|
||||
option.value
|
||||
);
|
||||
const valueExists = checkIfArraysHaveSameElements(filters?.[key] ?? [], option.value);
|
||||
|
||||
setValue("query", {
|
||||
...filters,
|
||||
|
@ -5,9 +5,9 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
import projectService from "services/project.service";
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// components
|
||||
import { DateFilterModal } from "components/core";
|
||||
// ui
|
||||
@ -32,12 +32,7 @@ type Props = {
|
||||
height?: "sm" | "md" | "rg" | "lg";
|
||||
};
|
||||
|
||||
export const SelectFilters: React.FC<Props> = ({
|
||||
filters,
|
||||
onSelect,
|
||||
direction = "right",
|
||||
height = "md",
|
||||
}) => {
|
||||
export const SelectFilters: React.FC<Props> = ({ filters, onSelect, direction = "right", height = "md" }) => {
|
||||
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
|
||||
const [dateFilterType, setDateFilterType] = useState<{
|
||||
title: string;
|
||||
@ -52,9 +47,7 @@ export const SelectFilters: React.FC<Props> = ({
|
||||
|
||||
const { data: states } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
const statesList = getStatesList(states);
|
||||
|
||||
|
@ -11,7 +11,7 @@ import { mutate } from "swr";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
|
||||
// fetch keys
|
||||
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
||||
@ -75,12 +75,7 @@ export const CreateUpdateLinkForm: React.FC<Props> = (props) => {
|
||||
|
||||
if (!data)
|
||||
await issuesService
|
||||
.createIssueLink(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
issueId.toString(),
|
||||
payload
|
||||
)
|
||||
.createIssueLink(workspaceSlug.toString(), projectId.toString(), issueId.toString(), payload)
|
||||
.then(() => {
|
||||
onSuccess();
|
||||
mutate(ISSUE_DETAILS(issueId.toString()));
|
||||
@ -110,20 +105,10 @@ export const CreateUpdateLinkForm: React.FC<Props> = (props) => {
|
||||
: l
|
||||
);
|
||||
|
||||
mutate(
|
||||
ISSUE_DETAILS(issueId.toString()),
|
||||
(prevData) => ({ ...prevData, issue_link: updatedLinks }),
|
||||
false
|
||||
);
|
||||
mutate(ISSUE_DETAILS(issueId.toString()), (prevData) => ({ ...prevData, issue_link: updatedLinks }), false);
|
||||
|
||||
await issuesService
|
||||
.updateIssueLink(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
issueId.toString(),
|
||||
data!.id,
|
||||
payload
|
||||
)
|
||||
.updateIssueLink(workspaceSlug.toString(), projectId.toString(), issueId.toString(), data!.id, payload)
|
||||
.then(() => {
|
||||
onSuccess();
|
||||
mutate(ISSUE_DETAILS(issueId.toString()));
|
||||
@ -172,13 +157,7 @@ export const CreateUpdateLinkForm: React.FC<Props> = (props) => {
|
||||
loading={isSubmitting}
|
||||
className="w-full !py-2 text-custom-text-300 !text-base flex items-center justify-center"
|
||||
>
|
||||
{data
|
||||
? isSubmitting
|
||||
? "Updating Link..."
|
||||
: "Update Link"
|
||||
: isSubmitting
|
||||
? "Adding Link..."
|
||||
: "Add Link"}
|
||||
{data ? (isSubmitting ? "Updating Link..." : "Update Link") : isSubmitting ? "Adding Link..." : "Add Link"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -11,7 +11,7 @@ import useSWR, { mutate } from "swr";
|
||||
import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
@ -45,12 +45,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
const { data: issueActivities, mutate: mutateIssueActivity } = useSWR(
|
||||
workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY(issueId.toString()) : null,
|
||||
workspaceSlug && projectId && issueId
|
||||
? () =>
|
||||
issuesService.getIssueActivities(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
issueId.toString()
|
||||
)
|
||||
? () => issuesService.getIssueActivities(workspaceSlug.toString(), projectId.toString(), issueId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
@ -58,14 +53,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
if (!workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
await issuesService
|
||||
.patchIssueComment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
comment.id,
|
||||
comment,
|
||||
user
|
||||
)
|
||||
.patchIssueComment(workspaceSlug as string, projectId as string, issueId as string, comment.id, comment, user)
|
||||
.then(() => mutateIssueActivity());
|
||||
};
|
||||
|
||||
@ -75,13 +63,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
|
||||
|
||||
await issuesService
|
||||
.deleteIssueComment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
commentId,
|
||||
user
|
||||
)
|
||||
.deleteIssueComment(workspaceSlug as string, projectId as string, issueId as string, commentId, user)
|
||||
.then(() => mutateIssueActivity());
|
||||
};
|
||||
|
||||
@ -89,13 +71,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
if (!workspaceSlug || !issueDetails) return;
|
||||
|
||||
await issuesService
|
||||
.createIssueComment(
|
||||
workspaceSlug.toString(),
|
||||
issueDetails.project,
|
||||
issueDetails.id,
|
||||
formData,
|
||||
user
|
||||
)
|
||||
.createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData, user)
|
||||
.then(() => {
|
||||
mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id));
|
||||
})
|
||||
@ -118,11 +94,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
<ul role="list" className="-mb-4">
|
||||
{issueActivities?.map((activityItem, index) => {
|
||||
// determines what type of action is performed
|
||||
const message = activityItem.field ? (
|
||||
<ActivityMessage activity={activityItem} />
|
||||
) : (
|
||||
"created the issue."
|
||||
);
|
||||
const message = activityItem.field ? <ActivityMessage activity={activityItem} /> : "created the issue.";
|
||||
|
||||
if ("field" in activityItem && activityItem.field !== "updated_by") {
|
||||
return (
|
||||
@ -141,15 +113,11 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
<div className="ring-6 flex h-7 w-7 items-center justify-center rounded-full bg-custom-background-80 text-custom-text-200 ring-white">
|
||||
{activityItem.field ? (
|
||||
activityItem.new_value === "restore" ? (
|
||||
<Icon
|
||||
iconName="history"
|
||||
className="text-sm text-custom-text-200"
|
||||
/>
|
||||
<Icon iconName="history" className="text-sm text-custom-text-200" />
|
||||
) : (
|
||||
<ActivityIcon activity={activityItem} />
|
||||
)
|
||||
) : activityItem.actor_detail.avatar &&
|
||||
activityItem.actor_detail.avatar !== "" ? (
|
||||
) : activityItem.actor_detail.avatar && activityItem.actor_detail.avatar !== "" ? (
|
||||
<img
|
||||
src={activityItem.actor_detail.avatar}
|
||||
alt={activityItem.actor_detail.display_name}
|
||||
@ -172,13 +140,10 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 py-3">
|
||||
<div className="text-xs text-custom-text-200 break-words">
|
||||
{activityItem.field === "archived_at" &&
|
||||
activityItem.new_value !== "restore" ? (
|
||||
{activityItem.field === "archived_at" && activityItem.new_value !== "restore" ? (
|
||||
<span className="text-gray font-medium">Plane</span>
|
||||
) : activityItem.actor_detail.is_bot ? (
|
||||
<span className="text-gray font-medium">
|
||||
{activityItem.actor_detail.first_name} Bot
|
||||
</span>
|
||||
<span className="text-gray font-medium">{activityItem.actor_detail.first_name} Bot</span>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
@ -190,10 +155,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
: activityItem.actor_detail.display_name}
|
||||
</button>
|
||||
)}{" "}
|
||||
{message}{" "}
|
||||
<span className="whitespace-nowrap">
|
||||
{timeAgo(activityItem.created_at)}
|
||||
</span>
|
||||
{message} <span className="whitespace-nowrap">{timeAgo(activityItem.created_at)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -217,10 +179,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
|
||||
<AddComment
|
||||
onSubmit={handleAddComment}
|
||||
disabled={
|
||||
!allowed ||
|
||||
!issueDetails ||
|
||||
issueDetails.state === "closed" ||
|
||||
issueDetails.state === "archived"
|
||||
!allowed || !issueDetails || issueDetails.state === "closed" || issueDetails.state === "archived"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
|
||||
// react dropzone
|
||||
import { useDropzone } from "react-dropzone";
|
||||
@ -61,12 +61,7 @@ export const IssueAttachments: React.FC<Props> = (props) => {
|
||||
setIsLoading(true);
|
||||
|
||||
issuesService
|
||||
.uploadIssueAttachment(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
formData
|
||||
)
|
||||
.uploadIssueAttachment(workspaceSlug as string, projectId as string, issueId as string, formData)
|
||||
.then((res) => {
|
||||
mutate<IIssueAttachment[]>(
|
||||
ISSUE_ATTACHMENTS(issueId as string),
|
||||
@ -109,12 +104,7 @@ export const IssueAttachments: React.FC<Props> = (props) => {
|
||||
const { data: attachments } = useSWR<IIssueAttachment[]>(
|
||||
workspaceSlug && projectId && issueId ? ISSUE_ATTACHMENTS(issueId as string) : null,
|
||||
workspaceSlug && projectId && issueId
|
||||
? () =>
|
||||
issuesService.getIssueAttachment(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
issueId.toString()
|
||||
)
|
||||
? () => issuesService.getIssueAttachment(workspaceSlug.toString(), projectId.toString(), issueId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { useRouter } from "next/router";
|
||||
import { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
|
||||
// icons
|
||||
// import { LinkIcon, PlusIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
|
@ -11,7 +11,7 @@ import { mutate } from "swr";
|
||||
import { Control, Controller, useWatch } from "react-hook-form";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
@ -105,10 +105,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
control={control}
|
||||
name="state"
|
||||
render={({ field: { value } }) => (
|
||||
<StateSelect
|
||||
value={value}
|
||||
onChange={(val: string) => submitChanges({ state: val })}
|
||||
/>
|
||||
<StateSelect value={value} onChange={(val: string) => submitChanges({ state: val })} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -117,13 +114,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
<div className="mb-[6px]">
|
||||
<div className="border border-custom-border-200 rounded-[4px] p-2 flex justify-between items-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.5862 14.5239C13.3459 14.5239 13.1416 14.4398 12.9733 14.2715C12.805 14.1032 12.7209 13.8989 12.7209 13.6585V3.76429C12.7209 3.52391 12.805 3.31958 12.9733 3.15132C13.1416 2.98306 13.3459 2.89893 13.5862 2.89893C13.8266 2.89893 14.031 2.98306 14.1992 3.15132C14.3675 3.31958 14.4516 3.52391 14.4516 3.76429V13.6585C14.4516 13.8989 14.3675 14.1032 14.1992 14.2715C14.031 14.4398 13.8266 14.5239 13.5862 14.5239ZM5.1629 14.5239C5.04676 14.5239 4.93557 14.5018 4.82932 14.4576C4.72308 14.4133 4.63006 14.3513 4.55025 14.2715C4.47045 14.1917 4.40843 14.0986 4.36419 13.9922C4.31996 13.8858 4.29785 13.7746 4.29785 13.6585V11.2643C4.29785 11.0239 4.38198 10.8196 4.55025 10.6513C4.71851 10.4831 4.92283 10.3989 5.16322 10.3989C5.40359 10.3989 5.60791 10.4831 5.77618 10.6513C5.94445 10.8196 6.02859 11.0239 6.02859 11.2643V13.6585C6.02859 13.7746 6.00647 13.8858 5.96223 13.9922C5.91801 14.0986 5.85599 14.1917 5.77618 14.2715C5.69638 14.3513 5.60325 14.4133 5.49678 14.4576C5.39033 14.5018 5.27904 14.5239 5.1629 14.5239ZM9.37473 14.5239C9.13436 14.5239 8.93003 14.4398 8.76176 14.2715C8.59349 14.1032 8.50936 13.8989 8.50936 13.6585V7.5143C8.50936 7.27391 8.59349 7.06958 8.76176 6.90132C8.93003 6.73306 9.13436 6.64893 9.37473 6.64893C9.61511 6.64893 9.81943 6.73306 9.98771 6.90132C10.156 7.06958 10.2401 7.27391 10.2401 7.5143V13.6585C10.2401 13.8989 10.156 14.1032 9.98771 14.2715C9.81943 14.4398 9.61511 14.5239 9.37473 14.5239Z"
|
||||
fill="#A3A3A3"
|
||||
@ -137,10 +128,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
control={control}
|
||||
name="priority"
|
||||
render={({ field: { value } }) => (
|
||||
<PrioritySelect
|
||||
value={value}
|
||||
onChange={(val) => submitChanges({ priority: val })}
|
||||
/>
|
||||
<PrioritySelect value={value} onChange={(val) => submitChanges({ priority: val })} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -157,10 +145,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
control={control}
|
||||
name="assignees_list"
|
||||
render={({ field: { value } }) => (
|
||||
<AssigneeSelect
|
||||
value={value}
|
||||
onChange={(val: string) => submitChanges({ assignees_list: [val] })}
|
||||
/>
|
||||
<AssigneeSelect value={value} onChange={(val: string) => submitChanges({ assignees_list: [val] })} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -180,10 +165,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
control={control}
|
||||
name="estimate_point"
|
||||
render={({ field: { value } }) => (
|
||||
<EstimateSelect
|
||||
value={value}
|
||||
onChange={(val) => submitChanges({ estimate_point: val })}
|
||||
/>
|
||||
<EstimateSelect value={value} onChange={(val) => submitChanges({ estimate_point: val })} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -201,10 +183,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
control={control}
|
||||
name="parent"
|
||||
render={({ field: { value } }) => (
|
||||
<ParentSelect
|
||||
value={value}
|
||||
onChange={(val) => submitChanges({ parent: val })}
|
||||
/>
|
||||
<ParentSelect value={value} onChange={(val) => submitChanges({ parent: val })} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -224,22 +203,16 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
if (!user || !workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
issuesService
|
||||
.createIssueRelation(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
user,
|
||||
{
|
||||
related_list: [
|
||||
...val.map((issue: any) => ({
|
||||
issue: issue.blocker_issue_detail.id,
|
||||
relation_type: "blocked_by" as const,
|
||||
related_issue: issueId as string,
|
||||
related_issue_detail: issue.blocker_issue_detail,
|
||||
})),
|
||||
],
|
||||
}
|
||||
)
|
||||
.createIssueRelation(workspaceSlug as string, projectId as string, issueId as string, user, {
|
||||
related_list: [
|
||||
...val.map((issue: any) => ({
|
||||
issue: issue.blocker_issue_detail.id,
|
||||
relation_type: "blocked_by" as const,
|
||||
related_issue: issueId as string,
|
||||
related_issue_detail: issue.blocker_issue_detail,
|
||||
})),
|
||||
],
|
||||
})
|
||||
.then((response) => {
|
||||
handleMutation({
|
||||
issue_relations: [
|
||||
@ -315,22 +288,16 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
if (!user || !workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
issuesService
|
||||
.createIssueRelation(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
issueId as string,
|
||||
user,
|
||||
{
|
||||
related_list: [
|
||||
...val.map((issue: any) => ({
|
||||
issue: issue.blocked_issue_detail.id,
|
||||
relation_type: "blocked_by" as const,
|
||||
related_issue: issueId as string,
|
||||
related_issue_detail: issue.blocked_issue_detail,
|
||||
})),
|
||||
],
|
||||
}
|
||||
)
|
||||
.createIssueRelation(workspaceSlug as string, projectId as string, issueId as string, user, {
|
||||
related_list: [
|
||||
...val.map((issue: any) => ({
|
||||
issue: issue.blocked_issue_detail.id,
|
||||
relation_type: "blocked_by" as const,
|
||||
related_issue: issueId as string,
|
||||
related_issue_detail: issue.blocked_issue_detail,
|
||||
})),
|
||||
],
|
||||
})
|
||||
.then((response) => {
|
||||
handleMutation({
|
||||
related_issues: [
|
||||
@ -429,13 +396,8 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
|
||||
onClick={() => setIsViewAllOpen((prev) => !prev)}
|
||||
className="w-full flex justify-center items-center gap-1 !py-2"
|
||||
>
|
||||
<span className="text-base text-custom-primary-100">
|
||||
{isViewAllOpen ? "View less" : "View all"}
|
||||
</span>
|
||||
<ChevronDown
|
||||
size={16}
|
||||
className={`ml-1 text-custom-primary-100 ${isViewAllOpen ? "-rotate-180" : ""}`}
|
||||
/>
|
||||
<span className="text-base text-custom-primary-100">{isViewAllOpen ? "View less" : "View all"}</span>
|
||||
<ChevronDown size={16} className={`ml-1 text-custom-primary-100 ${isViewAllOpen ? "-rotate-180" : ""}`} />
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
|
||||
// fetch key
|
||||
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
||||
@ -37,8 +37,7 @@ export const ParentSelect: React.FC<Props> = (props) => {
|
||||
const { data: issueDetails } = useSWR(
|
||||
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId.toString()) : null,
|
||||
workspaceSlug && projectId && issueId
|
||||
? () =>
|
||||
issuesService.retrieve(workspaceSlug.toString(), projectId.toString(), issueId.toString())
|
||||
? () => issuesService.retrieve(workspaceSlug.toString(), projectId.toString(), issueId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
|
@ -11,7 +11,7 @@ import useSWR from "swr";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
|
||||
// services
|
||||
import stateService from "services/state.service";
|
||||
import stateService from "services/project_state.service";
|
||||
|
||||
// fetch key
|
||||
import { STATES_LIST } from "constants/fetch-keys";
|
||||
@ -39,9 +39,7 @@ export const StateSelect: React.FC<Props> = (props) => {
|
||||
|
||||
const { data: stateGroups } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => stateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
);
|
||||
const states = getStatesList(stateGroups);
|
||||
|
||||
|
@ -11,7 +11,7 @@ import useSWR, { mutate } from "swr";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
|
||||
// fetch key
|
||||
import { SUB_ISSUES } from "constants/fetch-keys";
|
||||
@ -41,8 +41,7 @@ export const SubIssueList: React.FC<Props> = (props) => {
|
||||
const { data: subIssuesResponse } = useSWR(
|
||||
workspaceSlug && issueDetails ? SUB_ISSUES(issueDetails.id) : null,
|
||||
workspaceSlug && issueDetails
|
||||
? () =>
|
||||
issuesService.subIssues(workspaceSlug as string, issueDetails.project, issueDetails.id)
|
||||
? () => issuesService.subIssues(workspaceSlug as string, issueDetails.project, issueDetails.id)
|
||||
: null
|
||||
);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
// fetch-keys
|
||||
@ -28,8 +28,7 @@ const useGanttChartIssues = (workspaceSlug: string | undefined, projectId: strin
|
||||
const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR(
|
||||
workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId, params) : null,
|
||||
workspaceSlug && projectId
|
||||
? () =>
|
||||
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
: null
|
||||
);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
// hooks
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
// fetch-keys
|
||||
|
@ -7,7 +7,7 @@ import useSWR from "swr";
|
||||
// contexts
|
||||
import { issueViewContext } from "contexts/issue-view.context";
|
||||
// services
|
||||
import issuesService from "services/issues.service";
|
||||
import issuesService from "services/issue.service";
|
||||
import cyclesService from "services/cycles.service";
|
||||
import modulesService from "services/modules.service";
|
||||
// types
|
||||
@ -45,19 +45,14 @@ const useCalendarIssuesView = () => {
|
||||
};
|
||||
|
||||
const { data: projectCalendarIssues, mutate: mutateProjectCalendarIssues } = useSWR(
|
||||
workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params) : null,
|
||||
workspaceSlug && projectId
|
||||
? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params)
|
||||
: null,
|
||||
workspaceSlug && projectId
|
||||
? () =>
|
||||
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: cycleCalendarIssues, mutate: mutateCycleCalendarIssues } = useSWR(
|
||||
workspaceSlug && projectId && cycleId
|
||||
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params)
|
||||
: null,
|
||||
workspaceSlug && projectId && cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params) : null,
|
||||
workspaceSlug && projectId && cycleId
|
||||
? () =>
|
||||
cyclesService.getCycleIssuesWithParams(
|
||||
@ -70,9 +65,7 @@ const useCalendarIssuesView = () => {
|
||||
);
|
||||
|
||||
const { data: moduleCalendarIssues, mutate: mutateModuleCalendarIssues } = useSWR(
|
||||
workspaceSlug && projectId && moduleId
|
||||
? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params)
|
||||
: null,
|
||||
workspaceSlug && projectId && moduleId ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) : null,
|
||||
workspaceSlug && projectId && moduleId
|
||||
? () =>
|
||||
modulesService.getModuleIssuesWithParams(
|
||||
@ -87,8 +80,7 @@ const useCalendarIssuesView = () => {
|
||||
const { data: viewCalendarIssues, mutate: mutateViewCalendarIssues } = useSWR(
|
||||
workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null,
|
||||
workspaceSlug && projectId && viewId && params
|
||||
? () =>
|
||||
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
: null
|
||||
);
|
||||
|
||||
|
@ -4,7 +4,7 @@ import useSWR from "swr";
|
||||
import { COMMENT_REACTION_LIST } from "constants/fetch-keys";
|
||||
|
||||
// services
|
||||
import reactionService from "services/reaction.service";
|
||||
import reactionService from "services/issue_reaction.service";
|
||||
|
||||
// helpers
|
||||
import { groupReactions } from "helpers/emoji.helper";
|
||||
@ -69,8 +69,7 @@ const useCommentReaction = (
|
||||
if (!workspaceSlug || !projectId || !commendId) return;
|
||||
|
||||
mutateCommentReactions(
|
||||
(prevData: any) =>
|
||||
prevData?.filter((r: any) => r.actor !== user?.user?.id || r.reaction !== reaction) || [],
|
||||
(prevData: any) => prevData?.filter((r: any) => r.actor !== user?.user?.id || r.reaction !== reaction) || [],
|
||||
false
|
||||
);
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import estimatesService from "services/estimates.service";
|
||||
import estimatesService from "services/project_estimates.service";
|
||||
// hooks
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
// helpers
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user