chore: store setup

This commit is contained in:
sriram veeraghanta 2023-09-20 20:33:25 +05:30
parent 50c330db65
commit a328c530d0
161 changed files with 1389 additions and 2775 deletions

View File

@ -7,19 +7,14 @@ import analyticsService from "services/analytics.service";
import projectService from "services/project.service"; import projectService from "services/project.service";
import cyclesService from "services/cycles.service"; import cyclesService from "services/cycles.service";
import modulesService from "services/modules.service"; import modulesService from "services/modules.service";
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// hooks // hooks
import useProjects from "hooks/use-projects"; import useProjects from "hooks/use-projects";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // ui
import { PrimaryButton, SecondaryButton } from "components/ui"; import { PrimaryButton, SecondaryButton } from "components/ui";
// icons // icons
import { import { ArrowDownTrayIcon, ArrowPathIcon, CalendarDaysIcon, UserGroupIcon } from "@heroicons/react/24/outline";
ArrowDownTrayIcon,
ArrowPathIcon,
CalendarDaysIcon,
UserGroupIcon,
} from "@heroicons/react/24/outline";
import { ContrastIcon, LayerDiagonalIcon } from "components/icons"; import { ContrastIcon, LayerDiagonalIcon } from "components/icons";
// helpers // helpers
import { renderShortDate } from "helpers/date-time.helper"; import { renderShortDate } from "helpers/date-time.helper";
@ -46,13 +41,7 @@ type Props = {
user: ICurrentUserResponse | undefined; user: ICurrentUserResponse | undefined;
}; };
export const AnalyticsSidebar: React.FC<Props> = ({ export const AnalyticsSidebar: React.FC<Props> = ({ analytics, params, fullScreen, isProjectLevel = false, user }) => {
analytics,
params,
fullScreen,
isProjectLevel = false,
user,
}) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
@ -61,9 +50,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { data: projectDetails } = useSWR( const { data: projectDetails } = useSWR(
workspaceSlug && projectId && !(cycleId || moduleId) workspaceSlug && projectId && !(cycleId || moduleId) ? PROJECT_DETAILS(projectId.toString()) : null,
? PROJECT_DETAILS(projectId.toString())
: null,
workspaceSlug && projectId && !(cycleId || moduleId) workspaceSlug && projectId && !(cycleId || moduleId)
? () => projectService.getProject(workspaceSlug.toString(), projectId.toString()) ? () => projectService.getProject(workspaceSlug.toString(), projectId.toString())
: null : null
@ -72,24 +59,14 @@ export const AnalyticsSidebar: React.FC<Props> = ({
const { data: cycleDetails } = useSWR( const { data: cycleDetails } = useSWR(
workspaceSlug && projectId && cycleId ? CYCLE_DETAILS(cycleId.toString()) : null, workspaceSlug && projectId && cycleId ? CYCLE_DETAILS(cycleId.toString()) : null,
workspaceSlug && projectId && cycleId workspaceSlug && projectId && cycleId
? () => ? () => cyclesService.getCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
cyclesService.getCycleDetails(
workspaceSlug.toString(),
projectId.toString(),
cycleId.toString()
)
: null : null
); );
const { data: moduleDetails } = useSWR( const { data: moduleDetails } = useSWR(
workspaceSlug && projectId && moduleId ? MODULE_DETAILS(moduleId.toString()) : null, workspaceSlug && projectId && moduleId ? MODULE_DETAILS(moduleId.toString()) : null,
workspaceSlug && projectId && moduleId workspaceSlug && projectId && moduleId
? () => ? () => modulesService.getModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
modulesService.getModuleDetails(
workspaceSlug.toString(),
projectId.toString(),
moduleId.toString()
)
: null : null
); );
@ -178,8 +155,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
); );
}; };
const selectedProjects = const selectedProjects = params.project && params.project.length > 0 ? params.project : projects?.map((p) => p.id);
params.project && params.project.length > 0 ? params.project : projects?.map((p) => p.id);
return ( return (
<div <div
@ -236,9 +212,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
)} )}
<h5 className="flex items-center gap-1"> <h5 className="flex items-center gap-1">
<p className="break-words">{truncateText(project.name, 20)}</p> <p className="break-words">{truncateText(project.name, 20)}</p>
<span className="text-custom-text-200 text-xs ml-1"> <span className="text-custom-text-200 text-xs ml-1">({project.identifier})</span>
({project.identifier})
</span>
</h5> </h5>
</div> </div>
<div className="mt-4 space-y-3 pl-2 w-full"> <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="space-y-4 mt-4">
<div className="flex items-center gap-2 text-xs"> <div className="flex items-center gap-2 text-xs">
<h6 className="text-custom-text-200">Network</h6> <h6 className="text-custom-text-200">Network</h6>
<span> <span>{NETWORK_CHOICES.find((n) => n.key === projectDetails?.network)?.label ?? ""}</span>
{NETWORK_CHOICES.find((n) => n.key === projectDetails?.network)?.label ??
""}
</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -13,15 +13,11 @@ import analyticsService from "services/analytics.service";
import projectService from "services/project.service"; import projectService from "services/project.service";
import cyclesService from "services/cycles.service"; import cyclesService from "services/cycles.service";
import modulesService from "services/modules.service"; import modulesService from "services/modules.service";
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// components // components
import { CustomAnalytics, ScopeAndDemand } from "components/analytics"; import { CustomAnalytics, ScopeAndDemand } from "components/analytics";
// icons // icons
import { import { ArrowsPointingInIcon, ArrowsPointingOutIcon, XMarkIcon } from "@heroicons/react/24/outline";
ArrowsPointingInIcon,
ArrowsPointingOutIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
// types // types
import { IAnalyticsParams, IWorkspace } from "types"; import { IAnalyticsParams, IWorkspace } from "types";
// fetch-keys // fetch-keys
@ -67,9 +63,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
); );
const { data: projectDetails } = useSWR( const { data: projectDetails } = useSWR(
workspaceSlug && projectId && !(cycleId || moduleId) workspaceSlug && projectId && !(cycleId || moduleId) ? PROJECT_DETAILS(projectId.toString()) : null,
? PROJECT_DETAILS(projectId.toString())
: null,
workspaceSlug && projectId && !(cycleId || moduleId) workspaceSlug && projectId && !(cycleId || moduleId)
? () => projectService.getProject(workspaceSlug.toString(), projectId.toString()) ? () => projectService.getProject(workspaceSlug.toString(), projectId.toString())
: null : null
@ -78,24 +72,14 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
const { data: cycleDetails } = useSWR( const { data: cycleDetails } = useSWR(
workspaceSlug && projectId && cycleId ? CYCLE_DETAILS(cycleId.toString()) : null, workspaceSlug && projectId && cycleId ? CYCLE_DETAILS(cycleId.toString()) : null,
workspaceSlug && projectId && cycleId workspaceSlug && projectId && cycleId
? () => ? () => cyclesService.getCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
cyclesService.getCycleDetails(
workspaceSlug.toString(),
projectId.toString(),
cycleId.toString()
)
: null : null
); );
const { data: moduleDetails } = useSWR( const { data: moduleDetails } = useSWR(
workspaceSlug && projectId && moduleId ? MODULE_DETAILS(moduleId.toString()) : null, workspaceSlug && projectId && moduleId ? MODULE_DETAILS(moduleId.toString()) : null,
workspaceSlug && projectId && moduleId workspaceSlug && projectId && moduleId
? () => ? () => modulesService.getModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
modulesService.getModuleDetails(
workspaceSlug.toString(),
projectId.toString(),
moduleId.toString()
)
: null : null
); );
@ -134,8 +118,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
eventPayload.moduleName = moduleDetails.name; eventPayload.moduleName = moduleDetails.name;
} }
const eventType = const eventType = tab === "Scope and Demand" ? "SCOPE_AND_DEMAND_ANALYTICS" : "CUSTOM_ANALYTICS";
tab === "Scope and Demand" ? "SCOPE_AND_DEMAND_ANALYTICS" : "CUSTOM_ANALYTICS";
trackEventServices.trackAnalyticsEvent( trackEventServices.trackAnalyticsEvent(
eventPayload, eventPayload,
@ -150,9 +133,9 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
return ( return (
<div <div
className={`absolute top-0 z-30 h-full bg-custom-background-90 ${ className={`absolute top-0 z-30 h-full bg-custom-background-90 ${fullScreen ? "p-2 w-full" : "w-1/2"} ${
fullScreen ? "p-2 w-full" : "w-1/2" isOpen ? "right-0" : "-right-full"
} ${isOpen ? "right-0" : "-right-full"} duration-300 transition-all`} } duration-300 transition-all`}
> >
<div <div
className={`flex h-full flex-col overflow-hidden border-custom-border-200 bg-custom-background-100 text-left ${ 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"> <div className="flex items-center justify-between gap-4 bg-custom-background-100 px-5 py-4 text-sm">
<h3 className="break-words"> <h3 className="break-words">
Analytics for{" "} Analytics for {cycleId ? cycleDetails?.name : moduleId ? moduleDetails?.name : projectDetails?.name}
{cycleId ? cycleDetails?.name : moduleId ? moduleDetails?.name : projectDetails?.name}
</h3> </h3>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button

View File

@ -12,7 +12,7 @@ import { Squares2X2Icon } from "@heroicons/react/24/outline";
import { StateGroupIcon } from "components/icons"; import { StateGroupIcon } from "components/icons";
import { ArchiveX } from "lucide-react"; import { ArchiveX } from "lucide-react";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// constants // constants
import { PROJECT_AUTOMATION_MONTHS } from "constants/project"; import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
import { STATES_LIST } from "constants/fetch-keys"; import { STATES_LIST } from "constants/fetch-keys";
@ -34,9 +34,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
const { data: stateGroups } = useSWR( const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
); );
const states = getStatesList(stateGroups); 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 defaultState = stateGroups && stateGroups.cancelled ? stateGroups.cancelled[0].id : null;
const selectedOption = states?.find( const selectedOption = states?.find((s) => s.id === projectDetails?.default_state ?? defaultState);
(s) => s.id === projectDetails?.default_state ?? defaultState
);
const currentDefaultState = states?.find((s) => s.id === defaultState); const currentDefaultState = states?.find((s) => s.id === defaultState);
const initialValues: Partial<IProject> = { const initialValues: Partial<IProject> = {
@ -105,15 +101,11 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
<div className="ml-12"> <div className="ml-12">
<div className="flex flex-col rounded bg-custom-background-90 border border-custom-border-200 p-2"> <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="flex items-center justify-between px-5 py-4 gap-2 w-full">
<div className="w-1/2 text-sm font-medium"> <div className="w-1/2 text-sm font-medium">Auto-close issues that are inactive for</div>
Auto-close issues that are inactive for
</div>
<div className="w-1/2"> <div className="w-1/2">
<CustomSelect <CustomSelect
value={projectDetails?.close_in} value={projectDetails?.close_in}
label={`${projectDetails?.close_in} ${ label={`${projectDetails?.close_in} ${projectDetails?.close_in === 1 ? "Month" : "Months"}`}
projectDetails?.close_in === 1 ? "Month" : "Months"
}`}
onChange={(val: number) => { onChange={(val: number) => {
handleChange({ close_in: val }); 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 text-sm font-medium">Auto-close Status</div>
<div className="w-1/2 "> <div className="w-1/2 ">
<CustomSearchSelect <CustomSearchSelect
value={ value={projectDetails?.default_state ? projectDetails?.default_state : defaultState}
projectDetails?.default_state ? projectDetails?.default_state : defaultState
}
label={ label={
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{selectedOption ? ( {selectedOption ? (
@ -166,9 +156,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
)} )}
{selectedOption?.name {selectedOption?.name
? selectedOption.name ? selectedOption.name
: currentDefaultState?.name ?? ( : currentDefaultState?.name ?? <span className="text-custom-text-200">State</span>}
<span className="text-custom-text-200">State</span>
)}
</div> </div>
} }
onChange={(val: string) => { onChange={(val: string) => {

View File

@ -10,7 +10,7 @@ import { Command } from "cmdk";
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import workspaceService from "services/workspace.service"; import workspaceService from "services/workspace.service";
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import inboxService from "services/inbox.service"; import inboxService from "services/inbox.service";
// hooks // hooks
import useProjectDetails from "hooks/use-project-details"; import useProjectDetails from "hooks/use-project-details";
@ -79,16 +79,13 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
const { data: issueDetails } = useSWR( const { data: issueDetails } = useSWR(
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null, workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
workspaceSlug && projectId && issueId 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 : null
); );
const { data: inboxList } = useSWR( const { data: inboxList } = useSWR(
workspaceSlug && projectId ? INBOX_LIST(projectId as string) : null, workspaceSlug && projectId ? INBOX_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => inboxService.getInboxes(workspaceSlug as string, projectId as string) : null
? () => inboxService.getInboxes(workspaceSlug as string, projectId as string)
: null
); );
const updateIssue = useCallback( const updateIssue = useCallback(
@ -272,8 +269,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
> >
{issueDetails && ( {issueDetails && (
<div className="overflow-hidden truncate rounded-md bg-custom-background-80 p-2 text-xs font-medium text-custom-text-200"> <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.project_detail.identifier}-{issueDetails.sequence_id} {issueDetails.name}
{issueDetails.name}
</div> </div>
)} )}
{projectId && ( {projectId && (
@ -324,12 +320,9 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
</h5> </h5>
)} )}
{!isLoading && {!isLoading && resultsCount === 0 && searchTerm !== "" && debouncedSearchTerm !== "" && (
resultsCount === 0 && <div className="my-4 text-center text-custom-text-200">No results found.</div>
searchTerm !== "" && )}
debouncedSearchTerm !== "" && (
<div className="my-4 text-center text-custom-text-200">No results found.</div>
)}
{(isLoading || isSearching) && ( {(isLoading || isSearching) && (
<Command.Loading> <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"> <div className="flex items-center gap-2 overflow-hidden text-custom-text-200">
<Icon iconName={currentSection.icon} /> <Icon iconName={currentSection.icon} />
<p className="block flex-1 truncate"> <p className="block flex-1 truncate">{currentSection.itemName(item)}</p>
{currentSection.itemName(item)}
</p>
</div> </div>
</Command.Item> </Command.Item>
))} ))}
@ -577,9 +568,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); setIsPaletteOpen(false);
redirect( redirect(`/${workspaceSlug}/projects/${projectId}/inbox/${inboxList?.[0]?.id}`);
`/${workspaceSlug}/projects/${projectId}/inbox/${inboxList?.[0]?.id}`
);
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
@ -672,10 +661,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); setIsPaletteOpen(false);
window.open( window.open("https://github.com/makeplane/plane/issues/new/choose", "_blank");
"https://github.com/makeplane/plane/issues/new/choose",
"_blank"
);
}} }}
className="focus:outline-none" className="focus:outline-none"
> >
@ -759,29 +745,15 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
</> </>
)} )}
{page === "change-issue-state" && issueDetails && ( {page === "change-issue-state" && issueDetails && (
<ChangeIssueState <ChangeIssueState issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} />
issue={issueDetails}
setIsPaletteOpen={setIsPaletteOpen}
user={user}
/>
)} )}
{page === "change-issue-priority" && issueDetails && ( {page === "change-issue-priority" && issueDetails && (
<ChangeIssuePriority <ChangeIssuePriority issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} />
issue={issueDetails}
setIsPaletteOpen={setIsPaletteOpen}
user={user}
/>
)} )}
{page === "change-issue-assignee" && issueDetails && ( {page === "change-issue-assignee" && issueDetails && (
<ChangeIssueAssignee <ChangeIssueAssignee issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} />
issue={issueDetails}
setIsPaletteOpen={setIsPaletteOpen}
user={user}
/>
)}
{page === "change-interface-theme" && (
<ChangeInterfaceTheme setIsPaletteOpen={setIsPaletteOpen} />
)} )}
{page === "change-interface-theme" && <ChangeInterfaceTheme setIsPaletteOpen={setIsPaletteOpen} />}
</Command.List> </Command.List>
</Command> </Command>
</Dialog.Panel> </Dialog.Panel>

View File

@ -17,7 +17,7 @@ import { CreateUpdatePageModal } from "components/pages";
// helpers // helpers
import { copyTextToClipboard } from "helpers/string.helper"; import { copyTextToClipboard } from "helpers/string.helper";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import inboxService from "services/inbox.service"; import inboxService from "services/inbox.service";
// fetch keys // fetch keys
import { INBOX_LIST, ISSUE_DETAILS } from "constants/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( const { data: issueDetails } = useSWR(
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null, workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
workspaceSlug && projectId && issueId 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 : null
); );
@ -141,11 +140,7 @@ export const CommandPalette: React.FC = observer(() => {
<> <>
<ShortcutsModal isOpen={isShortcutsModalOpen} setIsOpen={setIsShortcutsModalOpen} /> <ShortcutsModal isOpen={isShortcutsModalOpen} setIsOpen={setIsShortcutsModalOpen} />
{workspaceSlug && ( {workspaceSlug && (
<CreateProjectModal <CreateProjectModal isOpen={isProjectModalOpen} setIsOpen={setIsProjectModalOpen} user={user} />
isOpen={isProjectModalOpen}
setIsOpen={setIsProjectModalOpen}
user={user}
/>
)} )}
{projectId && ( {projectId && (
<> <>
@ -184,11 +179,7 @@ export const CommandPalette: React.FC = observer(() => {
handleClose={() => setIsIssueModalOpen(false)} handleClose={() => setIsIssueModalOpen(false)}
fieldsToShow={inboxId ? ["name", "description", "priority"] : ["all"]} fieldsToShow={inboxId ? ["name", "description", "priority"] : ["all"]}
prePopulateData={ prePopulateData={
cycleId cycleId ? { cycle: cycleId.toString() } : moduleId ? { module: moduleId.toString() } : undefined
? { cycle: cycleId.toString() }
: moduleId
? { module: moduleId.toString() }
: undefined
} }
/> />
<BulkDeleteIssuesModal <BulkDeleteIssuesModal
@ -196,11 +187,7 @@ export const CommandPalette: React.FC = observer(() => {
setIsOpen={setIsBulkDeleteIssuesModalOpen} setIsOpen={setIsBulkDeleteIssuesModalOpen}
user={user} user={user}
/> />
<CommandK <CommandK deleteIssue={deleteIssue} isPaletteOpen={isPaletteOpen} setIsPaletteOpen={setIsPaletteOpen} />
deleteIssue={deleteIssue}
isPaletteOpen={isPaletteOpen}
setIsPaletteOpen={setIsPaletteOpen}
/>
</> </>
); );
}); });

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// cmdk // cmdk
import { Command } from "cmdk"; import { Command } from "cmdk";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useProjectMembers from "hooks/use-project-members"; import useProjectMembers from "hooks/use-project-members";
// constants // constants

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// cmdk // cmdk
import { Command } from "cmdk"; import { Command } from "cmdk";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// types // types
import { ICurrentUserResponse, IIssue, TIssuePriorities } from "types"; import { ICurrentUserResponse, IIssue, TIssuePriorities } from "types";
// constants // constants
@ -64,11 +64,7 @@ export const ChangeIssuePriority: React.FC<Props> = ({ setIsPaletteOpen, issue,
return ( return (
<> <>
{PRIORITIES.map((priority) => ( {PRIORITIES.map((priority) => (
<Command.Item <Command.Item key={priority} onSelect={() => handleIssueState(priority)} className="focus:outline-none">
key={priority}
onSelect={() => handleIssueState(priority)}
className="focus:outline-none"
>
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<PriorityIcon priority={priority} /> <PriorityIcon priority={priority} />
<span className="capitalize">{priority ?? "None"}</span> <span className="capitalize">{priority ?? "None"}</span>

View File

@ -7,8 +7,8 @@ import useSWR, { mutate } from "swr";
// cmdk // cmdk
import { Command } from "cmdk"; import { Command } from "cmdk";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// ui // ui
import { Spinner } from "components/ui"; import { Spinner } from "components/ui";
// icons // icons
@ -32,9 +32,7 @@ export const ChangeIssueState: React.FC<Props> = ({ setIsPaletteOpen, issue, use
const { data: stateGroups, mutate: mutateIssueDetails } = useSWR( const { data: stateGroups, mutate: mutateIssueDetails } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
); );
const states = getStatesList(stateGroups); const states = getStatesList(stateGroups);
@ -78,18 +76,9 @@ export const ChangeIssueState: React.FC<Props> = ({ setIsPaletteOpen, issue, use
{states ? ( {states ? (
states.length > 0 ? ( states.length > 0 ? (
states.map((state) => ( states.map((state) => (
<Command.Item <Command.Item key={state.id} onSelect={() => handleIssueState(state.id)} className="focus:outline-none">
key={state.id}
onSelect={() => handleIssueState(state.id)}
className="focus:outline-none"
>
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<StateGroupIcon <StateGroupIcon stateGroup={state.group} color={state.color} height="16px" width="16px" />
stateGroup={state.group}
color={state.color}
height="16px"
width="16px"
/>
<p>{state.name}</p> <p>{state.name}</p>
</div> </div>
<div>{state.id === issue.state && <CheckIcon className="h-3 w-3" />}</div> <div>{state.id === issue.state && <CheckIcon className="h-3 w-3" />}</div>

View File

@ -9,7 +9,7 @@ import { SubmitHandler, useForm } from "react-hook-form";
// headless ui // headless ui
import { Combobox, Dialog, Transition } from "@headlessui/react"; import { Combobox, Dialog, Transition } from "@headlessui/react";
// services // services
import issuesServices from "services/issues.service"; import issuesServices from "services/issue.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useIssuesView from "hooks/use-issues-view"; 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 { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const { data: issues } = useSWR( const { data: issues } = useSWR(
workspaceSlug && projectId workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null,
? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) workspaceSlug && projectId ? () => issuesServices.getIssues(workspaceSlug as string, projectId as string) : null
: null,
workspaceSlug && projectId
? () => issuesServices.getIssues(workspaceSlug as string, projectId as string)
: null
); );
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
@ -155,9 +151,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user
: issues?.filter( : issues?.filter(
(issue) => (issue) =>
issue.name.toLowerCase().includes(query.toLowerCase()) || issue.name.toLowerCase().includes(query.toLowerCase()) ||
`${issue.project_detail.identifier}-${issue.sequence_id}` `${issue.project_detail.identifier}-${issue.sequence_id}`.toLowerCase().includes(query.toLowerCase())
.toLowerCase()
.includes(query.toLowerCase())
) ?? []; ) ?? [];
return ( return (

View File

@ -4,7 +4,7 @@ import { useRouter } from "next/router";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
// services // services
import aiService from "services/ai.service"; import aiService from "services/ai.service";
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth"; import useUserAuth from "hooks/use-user-auth";
@ -110,9 +110,7 @@ export const GptAssistantModal: React.FC<Props> = ({
setToastAlert({ setToastAlert({
type: "error", type: "error",
title: "Error!", title: "Error!",
message: message: error || "You have reached the maximum number of requests of 50 requests per month per user.",
error ||
"You have reached the maximum number of requests of 50 requests per month per user.",
}); });
else else
setToastAlert({ setToastAlert({
@ -166,8 +164,7 @@ export const GptAssistantModal: React.FC<Props> = ({
)} )}
{invalidResponse && ( {invalidResponse && (
<div className="text-sm text-red-500"> <div className="text-sm text-red-500">
No response could be generated. This may be due to insufficient content or task No response could be generated. This may be due to insufficient content or task information. Please try again.
information. Please try again.
</div> </div>
)} )}
<Input <Input
@ -175,9 +172,7 @@ export const GptAssistantModal: React.FC<Props> = ({
name="task" name="task"
register={register} register={register}
placeholder={`${ placeholder={`${
content && content !== "" content && content !== "" ? "Tell AI what action to perform on this content..." : "Ask AI anything..."
? "Tell AI what action to perform on this content..."
: "Ask AI anything..."
}`} }`}
autoComplete="off" autoComplete="off"
/> />
@ -187,18 +182,8 @@ export const GptAssistantModal: React.FC<Props> = ({
onClick={() => { onClick={() => {
onResponse(response); onResponse(response);
onClose(); onClose();
if (block) if (block) trackEventServices.trackUseGPTResponseEvent(block, "USE_GPT_RESPONSE_IN_PAGE_BLOCK", user);
trackEventServices.trackUseGPTResponseEvent( else if (issue) trackEventServices.trackUseGPTResponseEvent(issue, "USE_GPT_RESPONSE_IN_ISSUE", user);
block,
"USE_GPT_RESPONSE_IN_PAGE_BLOCK",
user
);
else if (issue)
trackEventServices.trackUseGPTResponseEvent(
issue,
"USE_GPT_RESPONSE_IN_ISSUE",
user
);
}} }}
> >
Use this response Use this response
@ -206,16 +191,8 @@ export const GptAssistantModal: React.FC<Props> = ({
)} )}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<SecondaryButton onClick={onClose}>Close</SecondaryButton> <SecondaryButton onClick={onClose}>Close</SecondaryButton>
<PrimaryButton <PrimaryButton type="button" onClick={handleSubmit(handleResponse)} loading={isSubmitting}>
type="button" {isSubmitting ? "Generating response..." : response === "" ? "Generate response" : "Generate again"}
onClick={handleSubmit(handleResponse)}
loading={isSubmitting}
>
{isSubmitting
? "Generating response..."
: response === ""
? "Generate response"
: "Generate again"}
</PrimaryButton> </PrimaryButton>
</div> </div>
</div> </div>

View File

@ -8,18 +8,12 @@ import useSWR from "swr";
import { DragDropContext, DropResult } from "react-beautiful-dnd"; import { DragDropContext, DropResult } from "react-beautiful-dnd";
import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import StrictModeDroppable from "components/dnd/StrictModeDroppable";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
import { useProjectMyMembership } from "contexts/project-member.context"; import { useProjectMyMembership } from "contexts/project-member.context";
// components // components
import { import { AllLists, AllBoards, CalendarView, SpreadsheetView, GanttChartView } from "components/core";
AllLists,
AllBoards,
CalendarView,
SpreadsheetView,
GanttChartView,
} from "components/core";
// ui // ui
import { EmptyState, Spinner } from "components/ui"; import { EmptyState, Spinner } from "components/ui";
// icons // icons
@ -88,9 +82,7 @@ export const AllViews: React.FC<Props> = ({
const { data: stateGroups } = useSWR( const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug workspaceSlug ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
); );
const states = getStatesList(stateGroups); const states = getStatesList(stateGroups);
@ -180,9 +172,7 @@ export const AllViews: React.FC<Props> = ({
userAuth={memberRole} userAuth={memberRole}
/> />
) : ( ) : (
displayFilters?.layout === "gantt_chart" && ( displayFilters?.layout === "gantt_chart" && <GanttChartView disableUserActions={disableUserActions} />
<GanttChartView disableUserActions={disableUserActions} />
)
)} )}
</> </>
) : router.pathname.includes("archived-issues") ? ( ) : router.pathname.includes("archived-issues") ? (

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import projectService from "services/project.service"; import projectService from "services/project.service";
// hooks // hooks
import useProjects from "hooks/use-projects"; import useProjects from "hooks/use-projects";
@ -106,12 +106,7 @@ export const BoardHeader: React.FC<Props> = ({
switch (displayFilters?.group_by) { switch (displayFilters?.group_by) {
case "state": case "state":
icon = currentState && ( icon = currentState && (
<StateGroupIcon <StateGroupIcon stateGroup={currentState.group} color={currentState.color} height="16px" width="16px" />
stateGroup={currentState.group}
color={currentState.color}
height="16px"
width="16px"
/>
); );
break; break;
case "state_detail.group": case "state_detail.group":
@ -138,14 +133,8 @@ export const BoardHeader: React.FC<Props> = ({
: null); : null);
break; break;
case "labels": case "labels":
const labelColor = const labelColor = issueLabels?.find((label) => label.id === groupTitle)?.color ?? "#000000";
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 }} />;
icon = (
<span
className="h-3.5 w-3.5 flex-shrink-0 rounded-full"
style={{ backgroundColor: labelColor }}
/>
);
break; break;
case "assignees": case "assignees":
case "created_by": case "created_by":
@ -196,10 +185,7 @@ export const BoardHeader: React.FC<Props> = ({
}} }}
> >
{isCollapsed ? ( {isCollapsed ? (
<Icon <Icon iconName="close_fullscreen" className="text-base font-medium text-custom-text-900" />
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" /> <Icon iconName="open_in_full" className="text-base font-medium text-custom-text-900" />
)} )}

View File

@ -5,14 +5,9 @@ import { useRouter } from "next/router";
import { mutate } from "swr"; import { mutate } from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { import { DraggableProvided, DraggableStateSnapshot, DraggingStyle, NotDraggingStyle } from "react-beautiful-dnd";
DraggableProvided,
DraggableStateSnapshot,
DraggingStyle,
NotDraggingStyle,
} from "react-beautiful-dnd";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useOutsideClickDetector from "hooks/use-outside-click-detector"; import useOutsideClickDetector from "hooks/use-outside-click-detector";
@ -147,22 +142,17 @@ export const SingleBoardIssue: React.FC<Props> = ({
); );
} }
issuesService issuesService.patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user).then(() => {
.patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user) mutateIssues();
.then(() => {
mutateIssues();
if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); if (cycleId) mutate(CYCLE_DETAILS(cycleId as string));
if (moduleId) mutate(MODULE_DETAILS(moduleId as string)); if (moduleId) mutate(MODULE_DETAILS(moduleId as string));
}); });
}, },
[displayFilters, workspaceSlug, cycleId, moduleId, groupTitle, index, mutateIssues, user] [displayFilters, workspaceSlug, cycleId, moduleId, groupTitle, index, mutateIssues, user]
); );
const getStyle = ( const getStyle = (style: DraggingStyle | NotDraggingStyle | undefined, snapshot: DraggableStateSnapshot) => {
style: DraggingStyle | NotDraggingStyle | undefined,
snapshot: DraggableStateSnapshot
) => {
if (displayFilters?.order_by === "sort_order") return style; if (displayFilters?.order_by === "sort_order") return style;
if (!snapshot.isDragging) return {}; if (!snapshot.isDragging) return {};
if (!snapshot.isDropAnimating) return style; if (!snapshot.isDropAnimating) return style;
@ -174,11 +164,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
}; };
const handleCopyText = () => { const handleCopyText = () => {
const originURL = const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => {
copyTextToClipboard(
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`
).then(() => {
setToastAlert({ setToastAlert({
type: "success", type: "success",
title: "Link Copied!", title: "Link Copied!",
@ -253,9 +240,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
> >
<ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}> <ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}>Open issue in new tab</ContextMenu.Item>
Open issue in new tab
</ContextMenu.Item>
</a> </a>
)} )}
</ContextMenu> </ContextMenu>
@ -277,9 +262,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
{!isNotAllowed && ( {!isNotAllowed && (
<div <div
ref={actionSectionRef} ref={actionSectionRef}
className={`z-1 absolute top-1.5 right-1.5 hidden group-hover/card:!flex ${ className={`z-1 absolute top-1.5 right-1.5 hidden group-hover/card:!flex ${isMenuActive ? "!flex" : ""}`}
isMenuActive ? "!flex" : ""
}`}
> >
{type && !isNotAllowed && ( {type && !isNotAllowed && (
<CustomMenu <CustomMenu
@ -353,11 +336,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
</button> </button>
</div> </div>
<div <div className={`flex items-center gap-2 text-xs ${isDropdownActive ? "" : "overflow-x-scroll"}`}>
className={`flex items-center gap-2 text-xs ${
isDropdownActive ? "" : "overflow-x-scroll"
}`}
>
{properties.priority && ( {properties.priority && (
<ViewPrioritySelect <ViewPrioritySelect
issue={issue} issue={issue}

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { DragDropContext, DropResult } from "react-beautiful-dnd"; import { DragDropContext, DropResult } from "react-beautiful-dnd";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useCalendarIssuesView from "hooks/use-calendar-issues-view"; import useCalendarIssuesView from "hooks/use-calendar-issues-view";
// components // components
@ -17,13 +17,7 @@ import { IssuePeekOverview } from "components/issues";
import { Spinner } from "components/ui"; import { Spinner } from "components/ui";
// helpers // helpers
import { renderDateFormat } from "helpers/date-time.helper"; import { renderDateFormat } from "helpers/date-time.helper";
import { import { startOfWeek, lastDayOfWeek, eachDayOfInterval, weekDayInterval, formatDate } from "helpers/calendar.helper";
startOfWeek,
lastDayOfWeek,
eachDayOfInterval,
weekDayInterval,
formatDate,
} from "helpers/calendar.helper";
// types // types
import { ICalendarRange, ICurrentUserResponse, IIssue, UserAuth } from "types"; import { ICalendarRange, ICurrentUserResponse, IIssue, UserAuth } from "types";
// fetch-keys // fetch-keys
@ -61,8 +55,7 @@ export const CalendarView: React.FC<Props> = ({
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const { calendarIssues, mutateIssues, params, displayFilters, setDisplayFilters } = const { calendarIssues, mutateIssues, params, displayFilters, setDisplayFilters } = useCalendarIssuesView();
useCalendarIssuesView();
const totalDate = eachDayOfInterval({ const totalDate = eachDayOfInterval({
start: calendarDates.startDate, start: calendarDates.startDate,
@ -80,8 +73,7 @@ export const CalendarView: React.FC<Props> = ({
const filterIssue = const filterIssue =
calendarIssues.length > 0 calendarIssues.length > 0
? calendarIssues.filter( ? calendarIssues.filter(
(issue) => (issue) => issue.target_date && renderDateFormat(issue.target_date) === renderDateFormat(date)
issue.target_date && renderDateFormat(issue.target_date) === renderDateFormat(date)
) )
: []; : [];
return { return {
@ -155,18 +147,16 @@ export const CalendarView: React.FC<Props> = ({
}); });
setDisplayFilters({ setDisplayFilters({
calendar_date_range: `${renderDateFormat(startDate)};after,${renderDateFormat( calendar_date_range: `${renderDateFormat(startDate)};after,${renderDateFormat(endDate)};before`,
endDate
)};before`,
}); });
}; };
useEffect(() => { useEffect(() => {
if (!displayFilters || displayFilters.calendar_date_range === "") if (!displayFilters || displayFilters.calendar_date_range === "")
setDisplayFilters({ setDisplayFilters({
calendar_date_range: `${renderDateFormat( calendar_date_range: `${renderDateFormat(startOfWeek(currentDate))};after,${renderDateFormat(
startOfWeek(currentDate) lastDayOfWeek(currentDate)
)};after,${renderDateFormat(lastDayOfWeek(currentDate))};before`, )};before`,
}); });
}, [currentDate, displayFilters, setDisplayFilters]); }, [currentDate, displayFilters, setDisplayFilters]);
@ -214,11 +204,7 @@ export const CalendarView: React.FC<Props> = ({
: "" : ""
}`} }`}
> >
<span> <span>{isMonthlyView ? formatDate(date, "eee").substring(0, 3) : formatDate(date, "eee")}</span>
{isMonthlyView
? formatDate(date, "eee").substring(0, 3)
: formatDate(date, "eee")}
</span>
{!isMonthlyView && <span>{formatDate(date, "d")}</span>} {!isMonthlyView && <span>{formatDate(date, "d")}</span>}
</div> </div>
))} ))}

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { DraggableProvided, DraggableStateSnapshot } from "react-beautiful-dnd"; import { DraggableProvided, DraggableStateSnapshot } from "react-beautiful-dnd";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useCalendarIssuesView from "hooks/use-calendar-issues-view"; import useCalendarIssuesView from "hooks/use-calendar-issues-view";
import useIssuesProperties from "hooks/use-issue-properties"; import useIssuesProperties from "hooks/use-issue-properties";
@ -122,13 +122,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
} }
issuesService issuesService
.patchIssue( .patchIssue(workspaceSlug as string, projectId as string, issue.id as string, formData, user)
workspaceSlug as string,
projectId as string,
issue.id as string,
formData,
user
)
.then(() => { .then(() => {
mutate(fetchKey); mutate(fetchKey);
}) })
@ -140,11 +134,8 @@ export const SingleCalendarIssue: React.FC<Props> = ({
); );
const handleCopyText = () => { const handleCopyText = () => {
const originURL = const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => {
copyTextToClipboard(
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`
).then(() => {
setToastAlert({ setToastAlert({
type: "success", type: "success",
title: "Link Copied!", title: "Link Copied!",
@ -153,9 +144,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
}); });
}; };
const displayProperties = properties const displayProperties = properties ? Object.values(properties).some((value) => value === true) : false;
? Object.values(properties).some((value) => value === true)
: false;
const openPeekOverview = () => { const openPeekOverview = () => {
const { query } = router; const { query } = router;

View File

@ -7,10 +7,10 @@ import useSWR, { mutate } from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { DropResult } from "react-beautiful-dnd"; import { DropResult } from "react-beautiful-dnd";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import stateService from "services/state.service"; import stateService from "services/project_state.service";
import modulesService from "services/modules.service"; import modulesService from "services/modules.service";
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
@ -51,10 +51,7 @@ type Props = {
disableUserActions?: boolean; disableUserActions?: boolean;
}; };
export const IssuesView: React.FC<Props> = ({ export const IssuesView: React.FC<Props> = ({ openIssuesListModal, disableUserActions = false }) => {
openIssuesListModal,
disableUserActions = false,
}) => {
// create issue modal // create issue modal
const [createIssueModal, setCreateIssueModal] = useState(false); const [createIssueModal, setCreateIssueModal] = useState(false);
const [createViewModal, setCreateViewModal] = useState<any>(null); const [createViewModal, setCreateViewModal] = useState<any>(null);
@ -64,9 +61,7 @@ export const IssuesView: React.FC<Props> = ({
// update issue modal // update issue modal
const [editIssueModal, setEditIssueModal] = useState(false); const [editIssueModal, setEditIssueModal] = useState(false);
const [issueToEdit, setIssueToEdit] = useState< const [issueToEdit, setIssueToEdit] = useState<(IIssue & { actionType: "edit" | "delete" }) | undefined>(undefined);
(IIssue & { actionType: "edit" | "delete" }) | undefined
>(undefined);
// delete issue modal // delete issue modal
const [deleteIssueModal, setDeleteIssueModal] = useState(false); const [deleteIssueModal, setDeleteIssueModal] = useState(false);
@ -87,15 +82,12 @@ export const IssuesView: React.FC<Props> = ({
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { groupedByIssues, mutateIssues, displayFilters, filters, isEmpty, setFilters, params } = const { groupedByIssues, mutateIssues, displayFilters, filters, isEmpty, setFilters, params } = useIssuesView();
useIssuesView();
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string); const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const { data: stateGroups } = useSWR( const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug workspaceSlug ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
); );
const states = getStatesList(stateGroups); const states = getStatesList(stateGroups);
@ -141,12 +133,10 @@ export const IssuesView: React.FC<Props> = ({
// check if dropping in the same group // check if dropping in the same group
if (source.droppableId === destination.droppableId) { if (source.droppableId === destination.droppableId) {
// check if dropping at beginning // check if dropping at beginning
if (destination.index === 0) if (destination.index === 0) newSortOrder = destinationGroupArray[0].sort_order - 10000;
newSortOrder = destinationGroupArray[0].sort_order - 10000;
// check if dropping at last // check if dropping at last
else if (destination.index === destinationGroupArray.length - 1) else if (destination.index === destinationGroupArray.length - 1)
newSortOrder = newSortOrder = destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
else { else {
if (destination.index > source.index) if (destination.index > source.index)
newSortOrder = newSortOrder =
@ -161,12 +151,10 @@ export const IssuesView: React.FC<Props> = ({
} }
} else { } else {
// check if dropping at beginning // check if dropping at beginning
if (destination.index === 0) if (destination.index === 0) newSortOrder = destinationGroupArray[0].sort_order - 10000;
newSortOrder = destinationGroupArray[0].sort_order - 10000;
// check if dropping at last // check if dropping at last
else if (destination.index === destinationGroupArray.length) else if (destination.index === destinationGroupArray.length)
newSortOrder = newSortOrder = destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
else else
newSortOrder = newSortOrder =
(destinationGroupArray[destination.index - 1].sort_order + (destinationGroupArray[destination.index - 1].sort_order +
@ -180,18 +168,14 @@ export const IssuesView: React.FC<Props> = ({
const destinationGroup = destination.droppableId; // destination group id const destinationGroup = destination.droppableId; // destination group id
if ( if (displayFilters.order_by === "sort_order" || source.droppableId !== destination.droppableId) {
displayFilters.order_by === "sort_order" ||
source.droppableId !== destination.droppableId
) {
// different group/column; // different group/column;
// source.droppableId !== destination.droppableId -> even if order by is not sort_order, // 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 // if the issue is moved to a different group, then we will change the group of the
// dragged item(or issue) // dragged item(or issue)
if (displayFilters.group_by === "priority") if (displayFilters.group_by === "priority") draggedItem.priority = destinationGroup as TIssuePriorities;
draggedItem.priority = destinationGroup as TIssuePriorities;
else if (displayFilters.group_by === "state") { else if (displayFilters.group_by === "state") {
draggedItem.state = destinationGroup; draggedItem.state = destinationGroup;
draggedItem.state_detail = states?.find((s) => s.id === destinationGroup) as IState; draggedItem.state_detail = states?.find((s) => s.id === destinationGroup) as IState;
@ -219,14 +203,8 @@ export const IssuesView: React.FC<Props> = ({
return { return {
...prevData, ...prevData,
[sourceGroup]: orderArrayBy( [sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
sourceGroupArray, [destinationGroup]: orderArrayBy(destinationGroupArray, displayFilters.order_by ?? "-created_at"),
displayFilters.order_by ?? "-created_at"
),
[destinationGroup]: orderArrayBy(
destinationGroupArray,
displayFilters.order_by ?? "-created_at"
),
}; };
}, },
false false
@ -246,14 +224,9 @@ export const IssuesView: React.FC<Props> = ({
user user
) )
.then((response) => { .then((response) => {
const sourceStateBeforeDrag = states?.find( const sourceStateBeforeDrag = states?.find((state) => state.name === source.droppableId);
(state) => state.name === source.droppableId
);
if ( if (sourceStateBeforeDrag?.group !== "completed" && response?.state_detail?.group === "completed")
sourceStateBeforeDrag?.group !== "completed" &&
response?.state_detail?.group === "completed"
)
trackEventServices.trackIssueMarkedAsDoneEvent( trackEventServices.trackIssueMarkedAsDoneEvent(
{ {
workspaceSlug, workspaceSlug,
@ -387,12 +360,7 @@ export const IssuesView: React.FC<Props> = ({
); );
issuesService issuesService
.removeIssueFromCycle( .removeIssueFromCycle(workspaceSlug as string, projectId as string, cycleId as string, bridgeId)
workspaceSlug as string,
projectId as string,
cycleId as string,
bridgeId
)
.then(() => { .then(() => {
setToastAlert({ setToastAlert({
title: "Success", title: "Success",
@ -430,12 +398,7 @@ export const IssuesView: React.FC<Props> = ({
); );
modulesService modulesService
.removeIssueFromModule( .removeIssueFromModule(workspaceSlug as string, projectId as string, moduleId as string, bridgeId)
workspaceSlug as string,
projectId as string,
moduleId as string,
bridgeId
)
.then(() => { .then(() => {
setToastAlert({ setToastAlert({
title: "Success", title: "Success",
@ -450,12 +413,9 @@ export const IssuesView: React.FC<Props> = ({
[displayFilters.group_by, workspaceSlug, projectId, moduleId, params, setToastAlert] [displayFilters.group_by, workspaceSlug, projectId, moduleId, params, setToastAlert]
); );
const nullFilters = Object.keys(filters).filter( const nullFilters = Object.keys(filters).filter((key) => filters[key as keyof IIssueFilterOptions] === null);
(key) => filters[key as keyof IIssueFilterOptions] === null
);
const areFiltersApplied = const areFiltersApplied = Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length;
Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length;
return ( return (
<> <>
@ -581,10 +541,7 @@ export const IssuesView: React.FC<Props> = ({
: undefined, : undefined,
secondaryButton: secondaryButton:
cycleId || moduleId ? ( cycleId || moduleId ? (
<SecondaryButton <SecondaryButton className="flex items-center gap-1.5" onClick={openIssuesListModal ?? (() => {})}>
className="flex items-center gap-1.5"
onClick={openIssuesListModal ?? (() => {})}
>
<PlusIcon className="h-4 w-4" /> <PlusIcon className="h-4 w-4" />
Add an existing issue Add an existing issue
</SecondaryButton> </SecondaryButton>

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import { mutate } from "swr"; import { mutate } from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
@ -45,12 +45,7 @@ import {
UserAuth, UserAuth,
} from "types"; } from "types";
// fetch-keys // fetch-keys
import { import { CYCLE_DETAILS, MODULE_DETAILS, SUB_ISSUES, USER_PROFILE_PROJECT_SEGREGATION } from "constants/fetch-keys";
CYCLE_DETAILS,
MODULE_DETAILS,
SUB_ISSUES,
USER_PROFILE_PROJECT_SEGREGATION,
} from "constants/fetch-keys";
type Props = { type Props = {
type?: string; type?: string;
@ -140,39 +135,24 @@ export const SingleListIssue: React.FC<Props> = ({
); );
} }
issuesService issuesService.patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user).then(() => {
.patchIssue(workspaceSlug as string, issue.project, issue.id, formData, user) mutateIssues();
.then(() => {
mutateIssues();
if (userId) if (userId)
mutate<IUserProfileProjectSegregation>( mutate<IUserProfileProjectSegregation>(
USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString())
); );
if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); if (cycleId) mutate(CYCLE_DETAILS(cycleId as string));
if (moduleId) mutate(MODULE_DETAILS(moduleId 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 handleCopyText = () => {
const originURL = const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => {
copyTextToClipboard(
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`
).then(() => {
setToastAlert({ setToastAlert({
type: "success", type: "success",
title: "Link Copied!", title: "Link Copied!",
@ -197,8 +177,7 @@ export const SingleListIssue: React.FC<Props> = ({
}); });
}; };
const isNotAllowed = const isNotAllowed = userAuth.isGuest || userAuth.isViewer || disableUserActions || isArchivedIssues;
userAuth.isGuest || userAuth.isViewer || disableUserActions || isArchivedIssues;
console.log("properties", properties); console.log("properties", properties);
@ -243,9 +222,7 @@ export const SingleListIssue: React.FC<Props> = ({
Copy issue link Copy issue link
</ContextMenu.Item> </ContextMenu.Item>
<a href={issuePath} target="_blank" rel="noreferrer noopener"> <a href={issuePath} target="_blank" rel="noreferrer noopener">
<ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}> <ContextMenu.Item Icon={ArrowTopRightOnSquareIcon}>Open issue in new tab</ContextMenu.Item>
Open issue in new tab
</ContextMenu.Item>
</a> </a>
</> </>
)} )}
@ -286,11 +263,7 @@ export const SingleListIssue: React.FC<Props> = ({
</div> </div>
</div> </div>
<div <div className={`flex flex-shrink-0 items-center gap-2 text-xs ${isArchivedIssues ? "opacity-60" : ""}`}>
className={`flex flex-shrink-0 items-center gap-2 text-xs ${
isArchivedIssues ? "opacity-60" : ""
}`}
>
{properties.priority && ( {properties.priority && (
<ViewPrioritySelect <ViewPrioritySelect
issue={issue} issue={issue}

View File

@ -5,7 +5,7 @@ import useSWR from "swr";
// headless ui // headless ui
import { Disclosure, Transition } from "@headlessui/react"; import { Disclosure, Transition } from "@headlessui/react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import projectService from "services/project.service"; import projectService from "services/project.service";
// hooks // hooks
import useProjects from "hooks/use-projects"; import useProjects from "hooks/use-projects";
@ -77,9 +77,7 @@ export const SingleList: React.FC<Props> = ({
const { data: issueLabels } = useSWR<IIssueLabels[]>( const { data: issueLabels } = useSWR<IIssueLabels[]>(
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null, workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string) : null
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
: null
); );
const { data: members } = useSWR( const { data: members } = useSWR(
@ -120,12 +118,7 @@ export const SingleList: React.FC<Props> = ({
switch (displayFilters?.group_by) { switch (displayFilters?.group_by) {
case "state": case "state":
icon = currentState && ( icon = currentState && (
<StateGroupIcon <StateGroupIcon stateGroup={currentState.group} color={currentState.color} height="16px" width="16px" />
stateGroup={currentState.group}
color={currentState.color}
height="16px"
width="16px"
/>
); );
break; break;
case "state_detail.group": case "state_detail.group":
@ -152,14 +145,8 @@ export const SingleList: React.FC<Props> = ({
: null); : null);
break; break;
case "labels": case "labels":
const labelColor = const labelColor = issueLabels?.find((label) => label.id === groupTitle)?.color ?? "#000000";
issueLabels?.find((label) => label.id === groupTitle)?.color ?? "#000000"; icon = <span className="h-3 w-3 flex-shrink-0 rounded-full" style={{ backgroundColor: labelColor }} />;
icon = (
<span
className="h-3 w-3 flex-shrink-0 rounded-full"
style={{ backgroundColor: labelColor }}
/>
);
break; break;
case "assignees": case "assignees":
case "created_by": 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"> <div className="flex items-center justify-between px-4 py-2.5 bg-custom-background-90">
<Disclosure.Button> <Disclosure.Button>
<div className="flex items-center gap-x-3"> <div className="flex items-center gap-x-3">
{displayFilters?.group_by !== null && ( {displayFilters?.group_by !== null && <div className="flex items-center">{getGroupIcon()}</div>}
<div className="flex items-center">{getGroupIcon()}</div>
)}
{displayFilters?.group_by !== null ? ( {displayFilters?.group_by !== null ? (
<h2 <h2
className={`text-sm font-semibold leading-6 text-custom-text-100 ${ 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> <CustomMenu.MenuItem onClick={addIssueToGroup}>Create new</CustomMenu.MenuItem>
{openIssuesListModal && ( {openIssuesListModal && (
<CustomMenu.MenuItem onClick={openIssuesListModal}> <CustomMenu.MenuItem onClick={openIssuesListModal}>Add an existing issue</CustomMenu.MenuItem>
Add an existing issue
</CustomMenu.MenuItem>
)} )}
</CustomMenu> </CustomMenu>
)} )}
@ -256,19 +239,14 @@ export const SingleList: React.FC<Props> = ({
makeIssueCopy={() => handleIssueAction(issue, "copy")} makeIssueCopy={() => handleIssueAction(issue, "copy")}
handleDeleteIssue={() => handleIssueAction(issue, "delete")} handleDeleteIssue={() => handleIssueAction(issue, "delete")}
handleDraftIssueSelect={ handleDraftIssueSelect={
handleDraftIssueAction handleDraftIssueAction ? () => handleDraftIssueAction(issue, "edit") : undefined
? () => handleDraftIssueAction(issue, "edit")
: undefined
} }
handleDraftIssueDelete={ handleDraftIssueDelete={
handleDraftIssueAction handleDraftIssueAction ? () => handleDraftIssueAction(issue, "delete") : undefined
? () => handleDraftIssueAction(issue, "delete")
: undefined
} }
handleMyIssueOpen={handleMyIssueOpen} handleMyIssueOpen={handleMyIssueOpen}
removeIssue={() => { removeIssue={() => {
if (removeIssue !== null && issue.bridge_id) if (removeIssue !== null && issue.bridge_id) removeIssue(issue.bridge_id, issue.id);
removeIssue(issue.bridge_id, issue.id);
}} }}
disableUserActions={disableUserActions} disableUserActions={disableUserActions}
user={user} 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"> <p className="bg-custom-background-100 px-4 py-2.5 text-sm text-custom-text-200">No issues.</p>
No issues.
</p>
) )
) : ( ) : (
<div className="flex h-full w-full items-center justify-center">Loading...</div> <div className="flex h-full w-full items-center justify-center">Loading...</div>

View File

@ -17,17 +17,12 @@ import {
import { Popover2 } from "@blueprintjs/popover2"; import { Popover2 } from "@blueprintjs/popover2";
// icons // icons
import { Icon } from "components/ui"; import { Icon } from "components/ui";
import { import { EllipsisHorizontalIcon, LinkIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
EllipsisHorizontalIcon,
LinkIcon,
PencilIcon,
TrashIcon,
} from "@heroicons/react/24/outline";
// hooks // hooks
import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view"; import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// constant // constant
import { import {
CYCLE_DETAILS, CYCLE_DETAILS,
@ -133,13 +128,7 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
); );
issuesService issuesService
.patchIssue( .patchIssue(workspaceSlug as string, projectId as string, issue.id as string, formData, user)
workspaceSlug as string,
projectId as string,
issue.id as string,
formData,
user
)
.then(() => { .then(() => {
if (issue.parent) { if (issue.parent) {
mutate(SUB_ISSUES(issue.parent as string)); mutate(SUB_ISSUES(issue.parent as string));
@ -167,11 +156,8 @@ export const SingleSpreadsheetIssue: React.FC<Props> = ({
}; };
const handleCopyText = () => { const handleCopyText = () => {
const originURL = const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => {
copyTextToClipboard(
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`
).then(() => {
setToastAlert({ setToastAlert({
type: "success", type: "success",
title: "Link Copied!", title: "Link Copied!",

View File

@ -9,7 +9,7 @@ import { useForm } from "react-hook-form";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import estimatesService from "services/estimates.service"; import estimatesService from "services/project_estimates.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // ui
@ -119,13 +119,7 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
); );
await estimatesService await estimatesService
.patchEstimate( .patchEstimate(workspaceSlug as string, projectId as string, data?.id as string, payload, user)
workspaceSlug as string,
projectId as string,
data?.id as string,
payload,
user
)
.then(() => { .then(() => {
mutate(ESTIMATES_LIST(projectId.toString())); mutate(ESTIMATES_LIST(projectId.toString()));
mutate(ESTIMATE_DETAILS(data.id)); 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"> <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)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="space-y-3"> <div className="space-y-3">
<div className="text-lg font-medium leading-6"> <div className="text-lg font-medium leading-6">{data ? "Update" : "Create"} Estimate</div>
{data ? "Update" : "Create"} Estimate
</div>
<div> <div>
<Input <Input
id="name" id="name"

View File

@ -1,10 +1,9 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import CSVIntegrationService from "services/integration/csv.services"; import CSVIntegrationService from "services/csv.services";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // ui
@ -23,13 +22,9 @@ type Props = {
mutateServices: () => void; mutateServices: () => void;
}; };
export const Exporter: React.FC<Props> = ({ const cvsService = new CSVIntegrationService();
isOpen,
handleClose, export const Exporter: React.FC<Props> = ({ isOpen, handleClose, user, provider, mutateServices }) => {
user,
provider,
mutateServices,
}) => {
const [exportLoading, setExportLoading] = useState(false); const [exportLoading, setExportLoading] = useState(false);
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
@ -60,7 +55,8 @@ export const Exporter: React.FC<Props> = ({
project: value, project: value,
multiple: multiple, multiple: multiple,
}; };
await CSVIntegrationService.exportCSVService(workspaceSlug as string, payload, user) await cvsService
.exportCSVService(workspaceSlug as string, payload, user)
.then(() => { .then(() => {
mutateServices(); mutateServices();
router.push(`/${workspaceSlug}/settings/exports`); router.push(`/${workspaceSlug}/settings/exports`);
@ -69,13 +65,7 @@ export const Exporter: React.FC<Props> = ({
type: "success", type: "success",
title: "Export Successful", title: "Export Successful",
message: `You will be able to download the exported ${ message: `You will be able to download the exported ${
provider === "csv" provider === "csv" ? "CSV" : provider === "xlsx" ? "Excel" : provider === "json" ? "JSON" : ""
? "CSV"
: provider === "xlsx"
? "Excel"
: provider === "json"
? "JSON"
: ""
} from the previous export.`, } from the previous export.`,
}); });
}) })
@ -122,13 +112,7 @@ export const Exporter: React.FC<Props> = ({
<span className="flex items-center justify-start"> <span className="flex items-center justify-start">
<h3 className="text-xl font-medium 2xl:text-2xl"> <h3 className="text-xl font-medium 2xl:text-2xl">
Export to{" "} Export to{" "}
{provider === "csv" {provider === "csv" ? "CSV" : provider === "xlsx" ? "Excel" : provider === "json" ? "JSON" : ""}
? "CSV"
: provider === "xlsx"
? "Excel"
: provider === "json"
? "JSON"
: ""}
</h3> </h3>
</span> </span>
</div> </div>
@ -155,22 +139,12 @@ export const Exporter: React.FC<Props> = ({
onClick={() => setMultiple(!multiple)} onClick={() => setMultiple(!multiple)}
className="flex items-center gap-2 max-w-min cursor-pointer" className="flex items-center gap-2 max-w-min cursor-pointer"
> >
<input <input type="checkbox" checked={multiple} onChange={() => setMultiple(!multiple)} />
type="checkbox" <div className="text-sm whitespace-nowrap">Export the data into separate files</div>
checked={multiple}
onChange={() => setMultiple(!multiple)}
/>
<div className="text-sm whitespace-nowrap">
Export the data into separate files
</div>
</div> </div>
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton> <SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
<PrimaryButton <PrimaryButton onClick={ExportCSVToMail} disabled={exportLoading} loading={exportLoading}>
onClick={ExportCSVToMail}
disabled={exportLoading}
loading={exportLoading}
>
{exportLoading ? "Exporting..." : "Export"} {exportLoading ? "Exporting..." : "Export"}
</PrimaryButton> </PrimaryButton>
</div> </div>

View File

@ -9,7 +9,7 @@ import useSWR, { mutate } from "swr";
// hooks // hooks
import useUserAuth from "hooks/use-user-auth"; import useUserAuth from "hooks/use-user-auth";
// services // services
import IntegrationService from "services/integration"; import IntegrationService from "services/integration.service";
// components // components
import { Exporter, SingleExport } from "components/exporter"; import { Exporter, SingleExport } from "components/exporter";
// ui // ui
@ -32,9 +32,7 @@ const IntegrationGuide = () => {
const { user } = useUserAuth(); const { user } = useUserAuth();
const { data: exporterServices } = useSWR( const { data: exporterServices } = useSWR(
workspaceSlug && cursor workspaceSlug && cursor ? EXPORT_SERVICES_LIST(workspaceSlug as string, cursor, `${per_page}`) : null,
? EXPORT_SERVICES_LIST(workspaceSlug as string, cursor, `${per_page}`)
: null,
workspaceSlug && cursor workspaceSlug && cursor
? () => IntegrationService.getExportsServicesList(workspaceSlug as string, cursor, per_page) ? () => IntegrationService.getExportsServicesList(workspaceSlug as string, cursor, per_page)
: null : null
@ -57,20 +55,11 @@ const IntegrationGuide = () => {
<div className="flex items-start justify-between gap-4 w-full"> <div className="flex items-start justify-between gap-4 w-full">
<div className="flex item-center gap-2.5"> <div className="flex item-center gap-2.5">
<div className="relative h-10 w-10 flex-shrink-0"> <div className="relative h-10 w-10 flex-shrink-0">
<Image <Image src={service.logo} layout="fill" objectFit="cover" alt={`${service.title} Logo`} />
src={service.logo}
layout="fill"
objectFit="cover"
alt={`${service.title} Logo`}
/>
</div> </div>
<div> <div>
<h3 className="flex items-center gap-4 text-sm font-medium"> <h3 className="flex items-center gap-4 text-sm font-medium">{service.title}</h3>
{service.title} <p className="text-sm text-custom-text-200 tracking-tight">{service.description}</p>
</h3>
<p className="text-sm text-custom-text-200 tracking-tight">
{service.description}
</p>
</div> </div>
</div> </div>
<div className="flex-shrink-0"> <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" className="flex flex-shrink-0 items-center gap-1 rounded bg-custom-background-80 py-1 px-1.5 text-xs outline-none"
onClick={() => { onClick={() => {
setRefreshing(true); setRefreshing(true);
mutate( mutate(EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`)).then(() =>
EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`) setRefreshing(false)
).then(() => setRefreshing(false)); );
}} }}
> >
<ArrowPathIcon className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`} />{" "} <ArrowPathIcon className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`} />{" "}
@ -108,9 +97,7 @@ const IntegrationGuide = () => {
<div className="flex gap-2 items-center text-xs"> <div className="flex gap-2 items-center text-xs">
<button <button
disabled={!exporterServices?.prev_page_results} disabled={!exporterServices?.prev_page_results}
onClick={() => onClick={() => exporterServices?.prev_page_results && setCursor(exporterServices?.prev_cursor)}
exporterServices?.prev_page_results && setCursor(exporterServices?.prev_cursor)
}
className={`flex items-center border border-custom-primary-100 text-custom-primary-100 px-1 rounded ${ className={`flex items-center border border-custom-primary-100 text-custom-primary-100 px-1 rounded ${
exporterServices?.prev_page_results exporterServices?.prev_page_results
? "cursor-pointer hover:bg-custom-primary-100 hover:text-white" ? "cursor-pointer hover:bg-custom-primary-100 hover:text-white"
@ -122,9 +109,7 @@ const IntegrationGuide = () => {
</button> </button>
<button <button
disabled={!exporterServices?.next_page_results} disabled={!exporterServices?.next_page_results}
onClick={() => onClick={() => exporterServices?.next_page_results && setCursor(exporterServices?.next_cursor)}
exporterServices?.next_page_results && setCursor(exporterServices?.next_cursor)
}
className={`flex items-center border border-custom-primary-100 text-custom-primary-100 px-1 rounded ${ className={`flex items-center border border-custom-primary-100 text-custom-primary-100 px-1 rounded ${
exporterServices?.next_page_results exporterServices?.next_page_results
? "cursor-pointer hover:bg-custom-primary-100 hover:text-white" ? "cursor-pointer hover:bg-custom-primary-100 hover:text-white"
@ -147,9 +132,7 @@ const IntegrationGuide = () => {
</div> </div>
</div> </div>
) : ( ) : (
<p className="text-sm text-custom-text-200 px-4 py-6"> <p className="text-sm text-custom-text-200 px-4 py-6">No previous export available.</p>
No previous export available.
</p>
) )
) : ( ) : (
<Loader className="mt-6 grid grid-cols-1 gap-3"> <Loader className="mt-6 grid grid-cols-1 gap-3">
@ -169,9 +152,7 @@ const IntegrationGuide = () => {
data={null} data={null}
user={user} user={user}
provider={provider} provider={provider}
mutateServices={() => mutateServices={() => mutate(EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`))}
mutate(EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`))
}
/> />
)} )}
</div> </div>

View File

@ -1,7 +1,7 @@
import { KeyedMutator } from "swr"; import { KeyedMutator } from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// types // types
import { ICurrentUserResponse, IIssue } from "types"; import { ICurrentUserResponse, IIssue } from "types";
import { IBlockUpdateData } from "../types"; import { IBlockUpdateData } from "../types";
@ -34,8 +34,7 @@ export const updateGanttIssue = (
const newPayload: any = { ...payload }; const newPayload: any = { ...payload };
if (newPayload.sort_order && payload.sort_order) if (newPayload.sort_order && payload.sort_order) newPayload.sort_order = payload.sort_order.newSortOrder;
newPayload.sort_order = payload.sort_order.newSortOrder;
issuesService.patchIssue(workspaceSlug, issue.project, issue.id, newPayload, user); issuesService.patchIssue(workspaceSlug, issue.project, issue.id, newPayload, user);
}; };

View File

@ -5,7 +5,7 @@ import useSWR, { mutate } from "swr";
// components // components
import { AddComment, IssueActivitySection } from "components/issues"; import { AddComment, IssueActivitySection } from "components/issues";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -25,16 +25,9 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
const { user } = useUser(); const { user } = useUser();
const { data: issueActivity, mutate: mutateIssueActivity } = useSWR( const { data: issueActivity, mutate: mutateIssueActivity } = useSWR(
workspaceSlug && projectId && inboxIssueId ? PROJECT_ISSUES_ACTIVITY(inboxIssueId.toString()) : null,
workspaceSlug && projectId && inboxIssueId workspaceSlug && projectId && inboxIssueId
? PROJECT_ISSUES_ACTIVITY(inboxIssueId.toString()) ? () => issuesService.getIssueActivities(workspaceSlug.toString(), projectId.toString(), inboxIssueId.toString())
: null,
workspaceSlug && projectId && inboxIssueId
? () =>
issuesService.getIssueActivities(
workspaceSlug.toString(),
projectId.toString(),
inboxIssueId.toString()
)
: null : null
); );
@ -42,14 +35,7 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
if (!workspaceSlug || !projectId || !inboxIssueId) return; if (!workspaceSlug || !projectId || !inboxIssueId) return;
await issuesService await issuesService
.patchIssueComment( .patchIssueComment(workspaceSlug as string, projectId as string, inboxIssueId as string, commentId, data, user)
workspaceSlug as string,
projectId as string,
inboxIssueId as string,
commentId,
data,
user
)
.then(() => mutateIssueActivity()); .then(() => mutateIssueActivity());
}; };
@ -59,13 +45,7 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false); mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
await issuesService await issuesService
.deleteIssueComment( .deleteIssueComment(workspaceSlug as string, projectId as string, inboxIssueId as string, commentId, user)
workspaceSlug as string,
projectId as string,
inboxIssueId as string,
commentId,
user
)
.then(() => mutateIssueActivity()); .then(() => mutateIssueActivity());
}; };
@ -73,13 +53,7 @@ export const InboxIssueActivity: React.FC<Props> = ({ issueDetails }) => {
if (!workspaceSlug || !issueDetails) return; if (!workspaceSlug || !issueDetails) return;
await issuesService await issuesService
.createIssueComment( .createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData, user)
workspaceSlug.toString(),
issueDetails.project,
issueDetails.id,
formData,
user
)
.then(() => { .then(() => {
mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id)); mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id));
}) })

View File

@ -11,7 +11,7 @@ import { Combobox, Dialog, Transition } from "@headlessui/react";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// services // services
import issuesServices from "services/issues.service"; import issuesServices from "services/issue.service";
// ui // ui
import { PrimaryButton, SecondaryButton } from "components/ui"; import { PrimaryButton, SecondaryButton } from "components/ui";
// icons // icons
@ -39,9 +39,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
const { workspaceSlug, projectId, issueId } = router.query; const { workspaceSlug, projectId, issueId } = router.query;
const { data: issues } = useSWR( const { data: issues } = useSWR(
workspaceSlug && projectId workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null,
? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)
: null,
workspaceSlug && projectId workspaceSlug && projectId
? () => ? () =>
issuesServices issuesServices
@ -71,8 +69,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
handleClose(); handleClose();
}; };
const filteredIssues = const filteredIssues = (query === "" ? issues : issues?.filter((issue) => issue.name.includes(query))) ?? [];
(query === "" ? issues : issues?.filter((issue) => issue.name.includes(query))) ?? [];
return ( return (
<Transition.Root show={isOpen} as={React.Fragment} afterLeave={() => setQuery("")} appear> <Transition.Root show={isOpen} as={React.Fragment} afterLeave={() => setQuery("")} appear>
@ -128,9 +125,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
{filteredIssues.length > 0 ? ( {filteredIssues.length > 0 ? (
<li className="p-2"> <li className="p-2">
{query === "" && ( {query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-custom-text-100"> <h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-custom-text-100">Select issue</h2>
Select issue
</h2>
)} )}
<ul className="text-sm text-custom-text-100"> <ul className="text-sm text-custom-text-100">
{filteredIssues.map((issue) => ( {filteredIssues.map((issue) => (
@ -140,9 +135,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
value={issue.id} value={issue.id}
className={({ active, selected }) => className={({ active, selected }) =>
`flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${ `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${
active || selected active || selected ? "bg-custom-background-80 text-custom-text-100" : ""
? "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"> <span className="flex-shrink-0 text-xs text-custom-text-200">
{ {issues?.find((i) => i.id === issue.id)?.project_detail?.identifier}-
issues?.find((i) => i.id === issue.id)?.project_detail {issue.sequence_id}
?.identifier
}
-{issue.sequence_id}
</span> </span>
<span className="text-custom-text-200">{issue.name}</span> <span className="text-custom-text-200">{issue.name}</span>
</div> </div>
@ -171,10 +161,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
<LayerDiagonalIcon height="56" width="56" /> <LayerDiagonalIcon height="56" width="56" />
<h3 className="text-sm text-custom-text-200"> <h3 className="text-sm text-custom-text-200">
No issues found. Create a new issue with{" "} No issues found. Create a new issue with{" "}
<pre className="inline rounded bg-custom-background-80 px-2 py-1"> <pre className="inline rounded bg-custom-background-80 px-2 py-1">C</pre>.
C
</pre>
.
</h3> </h3>
</div> </div>
)} )}

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import IntegrationService from "services/integration"; import IntegrationService from "services/integration.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // 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 flex-col gap-6 p-6">
<div className="flex w-full items-center justify-start gap-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"> <span className="place-items-center rounded-full bg-red-500/20 p-4">
<ExclamationTriangleIcon <ExclamationTriangleIcon className="h-6 w-6 text-red-500" aria-hidden="true" />
className="h-6 w-6 text-red-500"
aria-hidden="true"
/>
</span> </span>
<span className="flex items-center justify-start"> <span className="flex items-center justify-start">
<h3 className="text-xl font-medium 2xl:text-2xl">Delete Project</h3> <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> <span>
<p className="text-sm leading-7 text-custom-text-200"> <p className="text-sm leading-7 text-custom-text-200">
Are you sure you want to delete import from{" "} Are you sure you want to delete import from{" "}
<span className="break-words font-semibold capitalize text-custom-text-100"> <span className="break-words font-semibold capitalize text-custom-text-100">{data?.service}</span>
{data?.service} ? All of the data related to the import will be permanently removed. This action cannot be undone.
</span>
? All of the data related to the import will be permanently removed. This
action cannot be undone.
</p> </p>
</span> </span>
<div> <div>
<p className="text-sm text-custom-text-200"> <p className="text-sm text-custom-text-200">
To confirm, type{" "} To confirm, type <span className="font-medium text-custom-text-100">delete import</span> below:
<span className="font-medium text-custom-text-100">delete import</span> below:
</p> </p>
<Input <Input
type="text" type="text"
@ -129,11 +122,7 @@ export const DeleteImportModal: React.FC<Props> = ({ isOpen, handleClose, data,
</div> </div>
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton> <SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
<DangerButton <DangerButton onClick={handleDeletion} disabled={!confirmDeleteImport} loading={deleteLoading}>
onClick={handleDeletion}
disabled={!confirmDeleteImport}
loading={deleteLoading}
>
{deleteLoading ? "Deleting..." : "Delete Project"} {deleteLoading ? "Deleting..." : "Delete Project"}
</DangerButton> </DangerButton>
</div> </div>

View File

@ -7,7 +7,7 @@ import useSWR from "swr";
// react-hook-form // react-hook-form
import { UseFormSetValue } from "react-hook-form"; import { UseFormSetValue } from "react-hook-form";
// services // services
import GithubIntegrationService from "services/integration/github.service"; import GithubIntegrationService from "services/github.service";
// ui // ui
import { Loader, PrimaryButton, SecondaryButton } from "components/ui"; import { Loader, PrimaryButton, SecondaryButton } from "components/ui";
// types // types
@ -22,19 +22,12 @@ type Props = {
setValue: UseFormSetValue<TFormValues>; setValue: UseFormSetValue<TFormValues>;
}; };
export const GithubRepoDetails: FC<Props> = ({ export const GithubRepoDetails: FC<Props> = ({ selectedRepo, handleStepChange, setUsers, setValue }) => {
selectedRepo,
handleStepChange,
setUsers,
setValue,
}) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
const { data: repoInfo } = useSWR( const { data: repoInfo } = useSWR(
workspaceSlug && selectedRepo workspaceSlug && selectedRepo ? GITHUB_REPOSITORY_INFO(workspaceSlug as string, selectedRepo.name) : null,
? GITHUB_REPOSITORY_INFO(workspaceSlug as string, selectedRepo.name)
: null,
workspaceSlug && selectedRepo workspaceSlug && selectedRepo
? () => ? () =>
GithubIntegrationService.getGithubRepoInfo(workspaceSlug as string, { GithubIntegrationService.getGithubRepoInfo(workspaceSlug as string, {

View File

@ -9,8 +9,8 @@ import useSWR, { mutate } from "swr";
// react-hook-form // react-hook-form
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
// services // services
import IntegrationService from "services/integration"; import IntegrationService from "services/integration.service";
import GithubIntegrationService from "services/integration/github.service"; import GithubIntegrationService from "services/github.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
@ -29,18 +29,9 @@ import GithubLogo from "public/services/github.png";
// types // types
import { ICurrentUserResponse, IGithubRepoCollaborator, IGithubServiceImportFormData } from "types"; import { ICurrentUserResponse, IGithubRepoCollaborator, IGithubServiceImportFormData } from "types";
// fetch-keys // fetch-keys
import { import { APP_INTEGRATIONS, IMPORTER_SERVICES_LIST, WORKSPACE_INTEGRATIONS } from "constants/fetch-keys";
APP_INTEGRATIONS,
IMPORTER_SERVICES_LIST,
WORKSPACE_INTEGRATIONS,
} from "constants/fetch-keys";
export type TIntegrationSteps = export type TIntegrationSteps = "import-configure" | "import-data" | "repo-details" | "import-users" | "import-confirm";
| "import-configure"
| "import-data"
| "repo-details"
| "import-users"
| "import-confirm";
export interface IIntegrationData { export interface IIntegrationData {
state: TIntegrationSteps; state: TIntegrationSteps;
} }
@ -108,21 +99,15 @@ export const GithubImporterRoot: React.FC<Props> = ({ user }) => {
defaultValues: defaultFormValues, defaultValues: defaultFormValues,
}); });
const { data: appIntegrations } = useSWR(APP_INTEGRATIONS, () => const { data: appIntegrations } = useSWR(APP_INTEGRATIONS, () => IntegrationService.getAppIntegrationsList());
IntegrationService.getAppIntegrationsList()
);
const { data: workspaceIntegrations } = useSWR( const { data: workspaceIntegrations } = useSWR(
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null, workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
workspaceSlug workspaceSlug ? () => IntegrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null
? () => IntegrationService.getWorkspaceIntegrationsList(workspaceSlug as string)
: null
); );
const activeIntegrationState = () => { const activeIntegrationState = () => {
const currentElementIndex = integrationWorkflowData.findIndex( const currentElementIndex = integrationWorkflowData.findIndex((i) => i?.key === currentStep?.state);
(i) => i?.key === currentStep?.state
);
return currentElementIndex; return currentElementIndex;
}; };
@ -133,14 +118,11 @@ export const GithubImporterRoot: React.FC<Props> = ({ user }) => {
// current integration from all the integrations available // current integration from all the integrations available
const integration = const integration =
appIntegrations && appIntegrations && appIntegrations.length > 0 && appIntegrations.find((i) => i.provider === provider);
appIntegrations.length > 0 &&
appIntegrations.find((i) => i.provider === provider);
// current integration from workspace integrations // current integration from workspace integrations
const workspaceIntegration = const workspaceIntegration =
integration && integration && workspaceIntegrations?.find((i: any) => i.integration_detail.id === integration.id);
workspaceIntegrations?.find((i: any) => i.integration_detail.id === integration.id);
const createGithubImporterService = async (formData: TFormValues) => { const createGithubImporterService = async (formData: TFormValues) => {
if (!formData.github || !formData.project) return; if (!formData.github || !formData.project) return;
@ -214,9 +196,7 @@ export const GithubImporterRoot: React.FC<Props> = ({ user }) => {
<div <div
key={index} key={index}
className={`border-b px-7 ${ className={`border-b px-7 ${
index <= activeIntegrationState() - 1 index <= activeIntegrationState() - 1 ? `border-custom-primary` : `border-custom-border-200`
? `border-custom-primary`
: `border-custom-border-200`
}`} }`}
> >
{" "} {" "}

View File

@ -9,14 +9,9 @@ import useSWR, { mutate } from "swr";
// hooks // hooks
import useUserAuth from "hooks/use-user-auth"; import useUserAuth from "hooks/use-user-auth";
// services // services
import IntegrationService from "services/integration"; import IntegrationService from "services/integration.service";
// components // components
import { import { DeleteImportModal, GithubImporterRoot, JiraImporterRoot, SingleImport } from "components/integration";
DeleteImportModal,
GithubImporterRoot,
JiraImporterRoot,
SingleImport,
} from "components/integration";
// ui // ui
import { Loader, PrimaryButton } from "components/ui"; import { Loader, PrimaryButton } from "components/ui";
// icons // icons
@ -85,18 +80,11 @@ const IntegrationGuide = () => {
> >
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className="relative h-10 w-10 flex-shrink-0"> <div className="relative h-10 w-10 flex-shrink-0">
<Image <Image src={service.logo} layout="fill" objectFit="cover" alt={`${service.title} Logo`} />
src={service.logo}
layout="fill"
objectFit="cover"
alt={`${service.title} Logo`}
/>
</div> </div>
<div> <div>
<h3 className="flex items-center gap-4 text-sm font-medium">{service.title}</h3> <h3 className="flex items-center gap-4 text-sm font-medium">{service.title}</h3>
<p className="text-sm text-custom-text-200 tracking-tight"> <p className="text-sm text-custom-text-200 tracking-tight">{service.description}</p>
{service.description}
</p>
</div> </div>
</div> </div>
<div className="flex-shrink-0"> <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" className="flex flex-shrink-0 items-center gap-1 rounded bg-custom-background-80 py-1 px-1.5 text-xs outline-none"
onClick={() => { onClick={() => {
setRefreshing(true); setRefreshing(true);
mutate(IMPORTER_SERVICES_LIST(workspaceSlug as string)).then(() => mutate(IMPORTER_SERVICES_LIST(workspaceSlug as string)).then(() => setRefreshing(false));
setRefreshing(false)
);
}} }}
> >
<ArrowPathIcon className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`} />{" "} <ArrowPathIcon className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`} />{" "}
@ -145,9 +131,7 @@ const IntegrationGuide = () => {
</div> </div>
</div> </div>
) : ( ) : (
<p className="text-sm text-custom-text-200 px-4 py-6"> <p className="text-sm text-custom-text-200 px-4 py-6">No previous imports available.</p>
No previous imports available.
</p>
) )
) : ( ) : (
<Loader className="mt-6 grid grid-cols-1 gap-3"> <Loader className="mt-6 grid grid-cols-1 gap-3">

View File

@ -10,7 +10,7 @@ import useSWR from "swr";
import { useFormContext, Controller } from "react-hook-form"; import { useFormContext, Controller } from "react-hook-form";
// services // services
import jiraImporterService from "services/integration/jira.service"; import jiraImporterService from "services/jira.service";
// fetch keys // fetch keys
import { JIRA_IMPORTER_DETAIL } from "constants/fetch-keys"; import { JIRA_IMPORTER_DETAIL } from "constants/fetch-keys";
@ -157,9 +157,7 @@ export const JiraProjectDetail: React.FC<Props> = (props) => {
<Controller <Controller
control={control} control={control}
name="config.epics_to_modules" name="config.epics_to_modules"
render={({ field: { value, onChange } }) => ( render={({ field: { value, onChange } }) => <ToggleSwitch onChange={onChange} value={value} />}
<ToggleSwitch onChange={onChange} value={value} />
)}
/> />
</div> </div>
</div> </div>

View File

@ -16,7 +16,7 @@ import { ArrowLeftIcon, ListBulletIcon } from "@heroicons/react/24/outline";
import { CogIcon, UsersIcon, CheckIcon } from "components/icons"; import { CogIcon, UsersIcon, CheckIcon } from "components/icons";
// services // services
import jiraImporterService from "services/integration/jira.service"; import jiraImporterService from "services/jira.service";
// fetch keys // fetch keys
import { IMPORTER_SERVICES_LIST } from "constants/fetch-keys"; import { IMPORTER_SERVICES_LIST } from "constants/fetch-keys";
@ -100,9 +100,7 @@ export const JiraImporterRoot: React.FC<Props> = ({ user }) => {
}; };
const activeIntegrationState = () => { const activeIntegrationState = () => {
const currentElementIndex = integrationWorkflowData.findIndex( const currentElementIndex = integrationWorkflowData.findIndex((i) => i?.key === currentStep?.state);
(i) => i?.key === currentStep?.state
);
return currentElementIndex; return currentElementIndex;
}; };
@ -155,9 +153,7 @@ export const JiraImporterRoot: React.FC<Props> = ({ user }) => {
<div <div
key={index} key={index}
className={`border-b px-7 ${ className={`border-b px-7 ${
index <= activeIntegrationState() - 1 index <= activeIntegrationState() - 1 ? `border-custom-primary` : `border-custom-border-200`
? `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"> <div className="h-full w-full overflow-y-auto">
{currentStep.state === "import-configure" && <JiraGetImportDetail />} {currentStep.state === "import-configure" && <JiraGetImportDetail />}
{currentStep.state === "display-import-data" && ( {currentStep.state === "display-import-data" && (
<JiraProjectDetail <JiraProjectDetail setDisableTopBarAfter={setDisableTopBarAfter} setCurrentStep={setCurrentStep} />
setDisableTopBarAfter={setDisableTopBarAfter}
setCurrentStep={setCurrentStep}
/>
)} )}
{currentStep?.state === "import-users" && <JiraImportUsers />} {currentStep?.state === "import-users" && <JiraImportUsers />}
{currentStep?.state === "import-confirmation" && <JiraConfirmImport />} {currentStep?.state === "import-confirmation" && <JiraConfirmImport />}
@ -199,15 +192,9 @@ export const JiraImporterRoot: React.FC<Props> = ({ user }) => {
</SecondaryButton> </SecondaryButton>
)} )}
<PrimaryButton <PrimaryButton
disabled={ disabled={disableTopBarAfter === currentStep?.state || !isValid || methods.formState.isSubmitting}
disableTopBarAfter === currentStep?.state ||
!isValid ||
methods.formState.isSubmitting
}
onClick={() => { onClick={() => {
const currentElementIndex = integrationWorkflowData.findIndex( const currentElementIndex = integrationWorkflowData.findIndex((i) => i?.key === currentStep?.state);
(i) => i?.key === currentStep?.state
);
if (currentElementIndex === integrationWorkflowData.length - 1) { if (currentElementIndex === integrationWorkflowData.length - 1) {
methods.handleSubmit(onSubmit)(); methods.handleSubmit(onSubmit)();

View File

@ -6,7 +6,7 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// services // services
import IntegrationService from "services/integration"; import IntegrationService from "services/integration.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useIntegrationPopup from "hooks/use-integration-popup"; import useIntegrationPopup from "hooks/use-integration-popup";
@ -50,25 +50,17 @@ export const SingleIntegrationCard: React.FC<Props> = ({ integration }) => {
const { data: workspaceIntegrations } = useSWR( const { data: workspaceIntegrations } = useSWR(
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null, workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
() => () => (workspaceSlug ? IntegrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null)
workspaceSlug
? IntegrationService.getWorkspaceIntegrationsList(workspaceSlug as string)
: null
); );
const handleRemoveIntegration = async () => { const handleRemoveIntegration = async () => {
if (!workspaceSlug || !integration || !workspaceIntegrations) return; if (!workspaceSlug || !integration || !workspaceIntegrations) return;
const workspaceIntegrationId = workspaceIntegrations?.find( const workspaceIntegrationId = workspaceIntegrations?.find((i) => i.integration === integration.id)?.id;
(i) => i.integration === integration.id
)?.id;
setDeletingIntegration(true); setDeletingIntegration(true);
await IntegrationService.deleteWorkspaceIntegration( await IntegrationService.deleteWorkspaceIntegration(workspaceSlug as string, workspaceIntegrationId ?? "")
workspaceSlug as string,
workspaceIntegrationId ?? ""
)
.then(() => { .then(() => {
mutate<IWorkspaceIntegration[]>( mutate<IWorkspaceIntegration[]>(
WORKSPACE_INTEGRATIONS(workspaceSlug as string), WORKSPACE_INTEGRATIONS(workspaceSlug as string),
@ -94,18 +86,13 @@ export const SingleIntegrationCard: React.FC<Props> = ({ integration }) => {
}); });
}; };
const isInstalled = workspaceIntegrations?.find( const isInstalled = workspaceIntegrations?.find((i: any) => i.integration_detail.id === integration.id);
(i: any) => i.integration_detail.id === integration.id
);
return ( 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-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="flex items-start gap-4">
<div className="h-10 w-10 flex-shrink-0"> <div className="h-10 w-10 flex-shrink-0">
<Image <Image src={integrationDetails[integration.provider].logo} alt={`${integration.title} Logo`} />
src={integrationDetails[integration.provider].logo}
alt={`${integration.title} Logo`}
/>
</div> </div>
<div> <div>
<h3 className="flex items-center gap-2 text-sm font-medium"> <h3 className="flex items-center gap-2 text-sm font-medium">

View File

@ -4,7 +4,7 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// services // services
import appinstallationsService from "services/app-installations.service"; import appinstallationsService from "services/app_installation.service";
// ui // ui
import { Loader } from "components/ui"; import { Loader } from "components/ui";
// hooks // hooks
@ -20,8 +20,7 @@ type Props = {
}; };
export const SelectChannel: React.FC<Props> = ({ integration }) => { export const SelectChannel: React.FC<Props> = ({ integration }) => {
const [slackChannelAvailabilityToggle, setSlackChannelAvailabilityToggle] = const [slackChannelAvailabilityToggle, setSlackChannelAvailabilityToggle] = useState<boolean>(false);
useState<boolean>(false);
const [slackChannel, setSlackChannel] = useState<ISlackIntegration | null>(null); const [slackChannel, setSlackChannel] = useState<ISlackIntegration | null>(null);
const router = useRouter(); const router = useRouter();
@ -65,12 +64,7 @@ export const SelectChannel: React.FC<Props> = ({ integration }) => {
setSlackChannel(null); setSlackChannel(null);
}); });
appinstallationsService appinstallationsService
.removeSlackChannel( .removeSlackChannel(workspaceSlug as string, projectId as string, integration.id as string, slackChannel?.id)
workspaceSlug as string,
projectId as string,
integration.id as string,
slackChannel?.id
)
.catch((err) => console.log(err)); .catch((err) => console.log(err));
}; };

View File

@ -7,7 +7,7 @@ import { observer } from "mobx-react-lite";
import { RootStore } from "store/root"; import { RootStore } from "store/root";
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
// types and default data // types and default data
import { TIssueLayouts } from "store/issue-filters"; import { TIssueLayouts } from "store/issue_filters.legacy";
import { issueFilterVisibilityData } from "store/helpers/issue-data"; import { issueFilterVisibilityData } from "store/helpers/issue-data";
export const LayoutSelection = observer(() => { export const LayoutSelection = observer(() => {

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// react-dropzone // react-dropzone
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// types // types
@ -44,12 +44,7 @@ export const IssueAttachmentUpload: React.FC<Props> = ({ disabled = false }) =>
setIsLoading(true); setIsLoading(true);
issuesService issuesService
.uploadIssueAttachment( .uploadIssueAttachment(workspaceSlug as string, projectId as string, issueId as string, formData)
workspaceSlug as string,
projectId as string,
issueId as string,
formData
)
.then((res) => { .then((res) => {
mutate<IIssueAttachment[]>( mutate<IIssueAttachment[]>(
ISSUE_ATTACHMENTS(issueId as string), ISSUE_ATTACHMENTS(issueId as string),
@ -83,9 +78,7 @@ export const IssueAttachmentUpload: React.FC<Props> = ({ disabled = false }) =>
}); });
const fileError = const fileError =
fileRejections.length > 0 fileRejections.length > 0 ? `Invalid file type or size (max ${maxFileSize / 1024 / 1024} MB)` : null;
? `Invalid file type or size (max ${maxFileSize / 1024 / 1024} MB)`
: null;
return ( return (
<div <div

View File

@ -12,7 +12,7 @@ import { DeleteAttachmentModal } from "./delete-attachment-modal";
import { XMarkIcon } from "@heroicons/react/24/outline"; import { XMarkIcon } from "@heroicons/react/24/outline";
import { ExclamationIcon, getFileIcon } from "components/icons"; import { ExclamationIcon, getFileIcon } from "components/icons";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import projectService from "services/project.service"; import projectService from "services/project.service";
// fetch-key // fetch-key
import { ISSUE_ATTACHMENTS, PROJECT_MEMBERS } from "constants/fetch-keys"; import { ISSUE_ATTACHMENTS, PROJECT_MEMBERS } from "constants/fetch-keys";
@ -33,12 +33,7 @@ export const IssueAttachments = () => {
const { data: attachments } = useSWR<IIssueAttachment[]>( const { data: attachments } = useSWR<IIssueAttachment[]>(
workspaceSlug && projectId && issueId ? ISSUE_ATTACHMENTS(issueId as string) : null, workspaceSlug && projectId && issueId ? ISSUE_ATTACHMENTS(issueId as string) : null,
workspaceSlug && projectId && issueId 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 : null
); );
@ -70,14 +65,11 @@ export const IssueAttachments = () => {
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Tooltip tooltipContent={getFileName(file.attributes.name)}> <Tooltip tooltipContent={getFileName(file.attributes.name)}>
<span className="text-sm"> <span className="text-sm">{truncateText(`${getFileName(file.attributes.name)}`, 10)}</span>
{truncateText(`${getFileName(file.attributes.name)}`, 10)}
</span>
</Tooltip> </Tooltip>
<Tooltip <Tooltip
tooltipContent={`${ tooltipContent={`${
people?.find((person) => person.member.id === file.updated_by)?.member people?.find((person) => person.member.id === file.updated_by)?.member.display_name ?? ""
.display_name ?? ""
} uploaded on ${renderLongDateFormat(file.updated_at)}`} } uploaded on ${renderLongDateFormat(file.updated_at)}`}
> >
<span> <span>

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // ui
@ -47,12 +47,7 @@ export const DeleteAttachmentModal: React.FC<Props> = ({ isOpen, setIsOpen, data
); );
await issuesService await issuesService
.deleteIssueAttachment( .deleteIssueAttachment(workspaceSlug as string, projectId as string, issueId as string, assetId as string)
workspaceSlug as string,
projectId as string,
issueId as string,
assetId as string
)
.then(() => mutate(PROJECT_ISSUES_ACTIVITY(issueId as string))) .then(() => mutate(PROJECT_ISSUES_ACTIVITY(issueId as string)))
.catch(() => { .catch(() => {
setToastAlert({ 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="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="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"> <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 <ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
className="h-6 w-6 text-red-600"
aria-hidden="true"
/>
</div> </div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<Dialog.Title <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Delete Attachment Delete Attachment
</Dialog.Title> </Dialog.Title>
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-custom-text-200"> <p className="text-sm text-custom-text-200">
Are you sure you want to delete attachment-{" "} Are you sure you want to delete attachment-{" "}
<span className="font-bold">{getFileName(data.attributes.name)}</span>? <span className="font-bold">{getFileName(data.attributes.name)}</span>? This attachment will
This attachment will be permanently removed. This action cannot be be permanently removed. This action cannot be undone.
undone.
</p> </p>
</div> </div>
</div> </div>

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import issueServices from "services/issues.service"; import issueServices from "services/issue.service";
// hooks // hooks
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
import useToast from "hooks/use-toast"; 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 flex-col gap-6 p-6">
<div className="flex w-full items-center justify-start gap-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"> <span className="place-items-center rounded-full bg-red-500/20 p-4">
<ExclamationTriangleIcon <ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
className="h-6 w-6 text-red-600"
aria-hidden="true"
/>
</span> </span>
<span className="flex items-center justify-start"> <span className="flex items-center justify-start">
<h3 className="text-xl font-medium 2xl:text-2xl">Delete Draft Issue</h3> <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"> <span className="break-words font-medium text-custom-text-100">
{data?.project_detail.identifier}-{data?.sequence_id} {data?.project_detail.identifier}-{data?.sequence_id}
</span> </span>
{""}? All of the data related to the draft issue will be permanently removed. {""}? All of the data related to the draft issue will be permanently removed. This action cannot
This action cannot be undone. be undone.
</p> </p>
</span> </span>
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import issueServices from "services/issues.service"; import issueServices from "services/issue.service";
// hooks // hooks
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
import useCalendarIssuesView from "hooks/use-calendar-issues-view"; import useCalendarIssuesView from "hooks/use-calendar-issues-view";
@ -37,13 +37,7 @@ type Props = {
onSubmit?: () => Promise<void>; onSubmit?: () => Promise<void>;
}; };
export const DeleteIssueModal: React.FC<Props> = ({ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data, user, onSubmit }) => {
isOpen,
handleClose,
data,
user,
onSubmit,
}) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter(); const router = useRouter();
@ -82,11 +76,7 @@ export const DeleteIssueModal: React.FC<Props> = ({
? VIEW_ISSUES(viewId.toString(), calendarParams) ? VIEW_ISSUES(viewId.toString(), calendarParams)
: PROJECT_ISSUES_LIST_WITH_PARAMS(data.project, calendarParams); : PROJECT_ISSUES_LIST_WITH_PARAMS(data.project, calendarParams);
mutate<IIssue[]>( mutate<IIssue[]>(calendarFetchKey, (prevData) => (prevData ?? []).filter((p) => p.id !== data.id), false);
calendarFetchKey,
(prevData) => (prevData ?? []).filter((p) => p.id !== data.id),
false
);
} else if (displayFilters.layout === "spreadsheet") { } else if (displayFilters.layout === "spreadsheet") {
const spreadsheetFetchKey = cycleId const spreadsheetFetchKey = cycleId
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams) ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams)
@ -163,8 +153,7 @@ export const DeleteIssueModal: React.FC<Props> = ({
}); });
}; };
const handleIssueDelete = () => const handleIssueDelete = () => (isArchivedIssues ? handleArchivedIssueDeletion() : handleDeletion());
isArchivedIssues ? handleArchivedIssueDeletion() : handleDeletion();
return ( return (
<Transition.Root show={isOpen} as={React.Fragment}> <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 flex-col gap-6 p-6">
<div className="flex w-full items-center justify-start gap-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"> <span className="place-items-center rounded-full bg-red-500/20 p-4">
<ExclamationTriangleIcon <ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
className="h-6 w-6 text-red-600"
aria-hidden="true"
/>
</span> </span>
<span className="flex items-center justify-start"> <span className="flex items-center justify-start">
<h3 className="text-xl font-medium 2xl:text-2xl">Delete Issue</h3> <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"> <span className="break-words font-medium text-custom-text-100">
{data?.project_detail.identifier}-{data?.sequence_id} {data?.project_detail.identifier}-{data?.sequence_id}
</span> </span>
{""}? All of the data related to the issue will be permanently removed. This {""}? All of the data related to the issue will be permanently removed. This action cannot be
action cannot be undone. undone.
</p> </p>
</span> </span>
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
@ -114,8 +114,7 @@ export const CreateUpdateDraftIssueModal: React.FC<IssuesModalProps> = ({
return; return;
} }
if (prePopulateData && prePopulateData.project) if (prePopulateData && prePopulateData.project) return setActiveProject(prePopulateData.project);
return setActiveProject(prePopulateData.project);
// if data is not present, set active project to the project // if data is not present, set active project to the project
// in the url. This has the least priority. // in the url. This has the least priority.

View File

@ -4,7 +4,7 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useUserAuth from "hooks/use-user-auth"; import useUserAuth from "hooks/use-user-auth";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -37,11 +37,7 @@ type Props = {
uneditable?: boolean; uneditable?: boolean;
}; };
export const IssueMainContent: React.FC<Props> = ({ export const IssueMainContent: React.FC<Props> = ({ issueDetails, submitChanges, uneditable = false }) => {
issueDetails,
submitChanges,
uneditable = false,
}) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, issueId, archivedIssueId } = router.query; const { workspaceSlug, projectId, issueId, archivedIssueId } = router.query;
@ -55,12 +51,7 @@ export const IssueMainContent: React.FC<Props> = ({
const { data: siblingIssues } = useSWR( const { data: siblingIssues } = useSWR(
workspaceSlug && projectId && issueDetails?.parent ? SUB_ISSUES(issueDetails.parent) : null, workspaceSlug && projectId && issueDetails?.parent ? SUB_ISSUES(issueDetails.parent) : null,
workspaceSlug && projectId && issueDetails?.parent 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 : null
); );
const siblingIssuesList = siblingIssues?.sub_issues.filter((i) => i.id !== issueDetails.id); 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( const { data: issueActivity, mutate: mutateIssueActivity } = useSWR(
workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY(issueId.toString()) : null, workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY(issueId.toString()) : null,
workspaceSlug && projectId && issueId workspaceSlug && projectId && issueId
? () => ? () => issuesService.getIssueActivities(workspaceSlug.toString(), projectId.toString(), issueId.toString())
issuesService.getIssueActivities(
workspaceSlug.toString(),
projectId.toString(),
issueId.toString()
)
: null : null
); );
@ -81,14 +67,7 @@ export const IssueMainContent: React.FC<Props> = ({
if (!workspaceSlug || !projectId || !issueId) return; if (!workspaceSlug || !projectId || !issueId) return;
await issuesService await issuesService
.patchIssueComment( .patchIssueComment(workspaceSlug as string, projectId as string, issueId as string, commentId, data, user)
workspaceSlug as string,
projectId as string,
issueId as string,
commentId,
data,
user
)
.then(() => mutateIssueActivity()); .then(() => mutateIssueActivity());
}; };
@ -98,13 +77,7 @@ export const IssueMainContent: React.FC<Props> = ({
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false); mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
await issuesService await issuesService
.deleteIssueComment( .deleteIssueComment(workspaceSlug as string, projectId as string, issueId as string, commentId, user)
workspaceSlug as string,
projectId as string,
issueId as string,
commentId,
user
)
.then(() => mutateIssueActivity()); .then(() => mutateIssueActivity());
}; };
@ -112,13 +85,7 @@ export const IssueMainContent: React.FC<Props> = ({
if (!workspaceSlug || !issueDetails) return; if (!workspaceSlug || !issueDetails) return;
await issuesService await issuesService
.createIssueComment( .createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData, user)
workspaceSlug.toString(),
issueDetails.project,
issueDetails.id,
formData,
user
)
.then(() => { .then(() => {
mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id)); 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"> <span className="flex-shrink-0 text-custom-text-200">
{issueDetails.parent_detail?.project_detail.identifier}- {issueDetails.parent_detail?.project_detail.identifier}-{issueDetails.parent_detail?.sequence_id}
{issueDetails.parent_detail?.sequence_id}
</span> </span>
</div> </div>
<span className="truncate text-custom-text-100"> <span className="truncate text-custom-text-100">
@ -169,9 +135,7 @@ export const IssueMainContent: React.FC<Props> = ({
<CustomMenu.MenuItem <CustomMenu.MenuItem
key={issue.id} key={issue.id}
renderAs="a" renderAs="a"
href={`/${workspaceSlug}/projects/${projectId as string}/issues/${ href={`/${workspaceSlug}/projects/${projectId as string}/issues/${issue.id}`}
issue.id
}`}
className="flex items-center gap-2 py-2" className="flex items-center gap-2 py-2"
> >
<LayerDiagonalIcon className="h-4 w-4" /> <LayerDiagonalIcon className="h-4 w-4" />

View File

@ -8,7 +8,7 @@ import { mutate } from "swr";
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import modulesService from "services/modules.service"; import modulesService from "services/modules.service";
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import inboxServices from "services/inbox.service"; import inboxServices from "services/inbox.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
@ -93,8 +93,10 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
const { groupedIssues, mutateMyIssues } = useMyIssues(workspaceSlug?.toString()); const { groupedIssues, mutateMyIssues } = useMyIssues(workspaceSlug?.toString());
const { setValue: setValueInLocalStorage, clearValue: clearLocalStorageValue } = const { setValue: setValueInLocalStorage, clearValue: clearLocalStorageValue } = useLocalStorage<any>(
useLocalStorage<any>("draftedIssue", {}); "draftedIssue",
{}
);
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
@ -201,13 +203,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
}; };
await inboxServices await inboxServices
.createInboxIssue( .createInboxIssue(workspaceSlug.toString(), activeProject.toString(), inboxId.toString(), payload, user)
workspaceSlug.toString(),
activeProject.toString(),
inboxId.toString(),
payload,
user
)
.then((res) => { .then((res) => {
setToastAlert({ setToastAlert({
type: "success", type: "success",
@ -264,8 +260,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
.then(async (res) => { .then(async (res) => {
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle); if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle);
if (payload.module && payload.module !== "") if (payload.module && payload.module !== "") await addIssueToModule(res.id, payload.module);
await addIssueToModule(res.id, payload.module);
if (displayFilters.layout === "calendar") mutate(calendarFetchKey); if (displayFilters.layout === "calendar") mutate(calendarFetchKey);
if (displayFilters.layout === "gantt_chart") if (displayFilters.layout === "gantt_chart")

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// components // components
import { DateFilterModal } from "components/core"; import { DateFilterModal } from "components/core";
// ui // ui
@ -29,12 +29,7 @@ type Props = {
height?: "sm" | "md" | "rg" | "lg"; height?: "sm" | "md" | "rg" | "lg";
}; };
export const MyIssuesSelectFilters: React.FC<Props> = ({ export const MyIssuesSelectFilters: React.FC<Props> = ({ filters, onSelect, direction = "right", height = "md" }) => {
filters,
onSelect,
direction = "right",
height = "md",
}) => {
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
const [dateFilterType, setDateFilterType] = useState<{ const [dateFilterType, setDateFilterType] = useState<{
title: string; title: string;
@ -50,9 +45,7 @@ export const MyIssuesSelectFilters: React.FC<Props> = ({
const { data: labels } = useSWR( const { data: labels } = useSWR(
workspaceSlug && fetchLabels ? WORKSPACE_LABELS(workspaceSlug.toString()) : null, workspaceSlug && fetchLabels ? WORKSPACE_LABELS(workspaceSlug.toString()) : null,
workspaceSlug && fetchLabels workspaceSlug && fetchLabels ? () => issuesService.getWorkspaceLabels(workspaceSlug.toString()) : null
? () => issuesService.getWorkspaceLabels(workspaceSlug.toString())
: null
); );
return ( return (

View File

@ -7,7 +7,7 @@ import useSWR, { mutate } from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { DropResult } from "react-beautiful-dnd"; import { DropResult } from "react-beautiful-dnd";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useMyIssues from "hooks/my-issues/use-my-issues"; import useMyIssues from "hooks/my-issues/use-my-issues";
import useMyIssuesFilters from "hooks/my-issues/use-my-issues-filter"; import useMyIssuesFilters from "hooks/my-issues/use-my-issues-filter";
@ -28,10 +28,7 @@ type Props = {
disableUserActions?: false; disableUserActions?: false;
}; };
export const MyIssuesView: React.FC<Props> = ({ export const MyIssuesView: React.FC<Props> = ({ openIssuesListModal, disableUserActions = false }) => {
openIssuesListModal,
disableUserActions = false,
}) => {
// create issue modal // create issue modal
const [createIssueModal, setCreateIssueModal] = useState(false); const [createIssueModal, setCreateIssueModal] = useState(false);
const [preloadedData, setPreloadedData] = useState< const [preloadedData, setPreloadedData] = useState<
@ -40,9 +37,7 @@ export const MyIssuesView: React.FC<Props> = ({
// update issue modal // update issue modal
const [editIssueModal, setEditIssueModal] = useState(false); const [editIssueModal, setEditIssueModal] = useState(false);
const [issueToEdit, setIssueToEdit] = useState< const [issueToEdit, setIssueToEdit] = useState<(IIssue & { actionType: "edit" | "delete" }) | undefined>(undefined);
(IIssue & { actionType: "edit" | "delete" }) | undefined
>(undefined);
// delete issue modal // delete issue modal
const [deleteIssueModal, setDeleteIssueModal] = useState(false); const [deleteIssueModal, setDeleteIssueModal] = useState(false);
@ -57,14 +52,10 @@ export const MyIssuesView: React.FC<Props> = ({
const { user } = useUserAuth(); const { user } = useUserAuth();
const { groupedIssues, mutateMyIssues, isEmpty, params } = useMyIssues(workspaceSlug?.toString()); const { groupedIssues, mutateMyIssues, isEmpty, params } = useMyIssues(workspaceSlug?.toString());
const { filters, setFilters, displayFilters, properties } = useMyIssuesFilters( const { filters, setFilters, displayFilters, properties } = useMyIssuesFilters(workspaceSlug?.toString());
workspaceSlug?.toString()
);
const { data: labels } = useSWR( const { data: labels } = useSWR(
workspaceSlug && (filters?.labels ?? []).length > 0 workspaceSlug && (filters?.labels ?? []).length > 0 ? WORKSPACE_LABELS(workspaceSlug.toString()) : null,
? WORKSPACE_LABELS(workspaceSlug.toString())
: null,
workspaceSlug && (filters?.labels ?? []).length > 0 workspaceSlug && (filters?.labels ?? []).length > 0
? () => issuesService.getWorkspaceLabels(workspaceSlug.toString()) ? () => issuesService.getWorkspaceLabels(workspaceSlug.toString())
: null : null
@ -82,13 +73,7 @@ export const MyIssuesView: React.FC<Props> = ({
async (result: DropResult) => { async (result: DropResult) => {
setTrashBox(false); setTrashBox(false);
if ( if (!result.destination || !workspaceSlug || !groupedIssues || displayFilters?.group_by !== "priority") return;
!result.destination ||
!workspaceSlug ||
!groupedIssues ||
displayFilters?.group_by !== "priority"
)
return;
const { source, destination } = result; const { source, destination } = result;
@ -120,14 +105,8 @@ export const MyIssuesView: React.FC<Props> = ({
return { return {
...prevData, ...prevData,
[sourceGroup]: orderArrayBy( [sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
sourceGroupArray, [destinationGroup]: orderArrayBy(destinationGroupArray, displayFilters.order_by ?? "-created_at"),
displayFilters.order_by ?? "-created_at"
),
[destinationGroup]: orderArrayBy(
destinationGroupArray,
displayFilters.order_by ?? "-created_at"
),
}; };
}, },
false false
@ -219,15 +198,11 @@ export const MyIssuesView: React.FC<Props> = ({
(key) => filtersToDisplay[key as keyof IIssueFilterOptions] === null (key) => filtersToDisplay[key as keyof IIssueFilterOptions] === null
); );
const areFiltersApplied = const areFiltersApplied =
Object.keys(filtersToDisplay).length > 0 && Object.keys(filtersToDisplay).length > 0 && nullFilters.length !== Object.keys(filtersToDisplay).length;
nullFilters.length !== Object.keys(filtersToDisplay).length;
const isSubscribedIssuesRoute = router.pathname.includes("subscribed"); const isSubscribedIssuesRoute = router.pathname.includes("subscribed");
const isMySubscribedIssues = const isMySubscribedIssues =
(filters.subscriber && (filters.subscriber && filters.subscriber.length > 0 && router.pathname.includes("my-issues")) ?? false;
filters.subscriber.length > 0 &&
router.pathname.includes("my-issues")) ??
false;
const disableAddIssueOption = isSubscribedIssuesRoute || isMySubscribedIssues; const disableAddIssueOption = isSubscribedIssuesRoute || isMySubscribedIssues;

View File

@ -1,7 +1,7 @@
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -78,10 +78,7 @@ export const PeekOverviewIssueActivity: React.FC<Props> = ({ workspaceSlug, issu
showAccessSpecifier={projectDetails && projectDetails.is_deployed} showAccessSpecifier={projectDetails && projectDetails.is_deployed}
/> />
<div className="mt-4"> <div className="mt-4">
<AddComment <AddComment onSubmit={handleAddComment} showAccessSpecifier={projectDetails && projectDetails.is_deployed} />
onSubmit={handleAddComment}
showAccessSpecifier={projectDetails && projectDetails.is_deployed}
/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -7,17 +7,11 @@ import useSWR from "swr";
// headless ui // headless ui
import { Combobox, Transition } from "@headlessui/react"; import { Combobox, Transition } from "@headlessui/react";
// services // services
import issuesServices from "services/issues.service"; import issuesServices from "services/issue.service";
// ui // ui
import { IssueLabelsList } from "components/ui"; import { IssueLabelsList } from "components/ui";
// icons // icons
import { import { CheckIcon, MagnifyingGlassIcon, PlusIcon, RectangleGroupIcon, TagIcon } from "@heroicons/react/24/outline";
CheckIcon,
MagnifyingGlassIcon,
PlusIcon,
RectangleGroupIcon,
TagIcon,
} from "@heroicons/react/24/outline";
// types // types
import type { IIssueLabels } from "types"; import type { IIssueLabels } from "types";
// fetch-keys // fetch-keys
@ -39,24 +33,14 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
const { data: issueLabels } = useSWR<IIssueLabels[]>( const { data: issueLabels } = useSWR<IIssueLabels[]>(
projectId ? PROJECT_ISSUE_LABELS(projectId) : null, projectId ? PROJECT_ISSUE_LABELS(projectId) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => issuesServices.getIssueLabels(workspaceSlug as string, projectId) : null
? () => issuesServices.getIssueLabels(workspaceSlug as string, projectId)
: null
); );
const filteredOptions = const filteredOptions =
query === "" query === "" ? issueLabels : issueLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()));
? issueLabels
: issueLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()));
return ( return (
<Combobox <Combobox as="div" value={value} onChange={(val) => onChange(val)} className="relative flex-shrink-0" multiple>
as="div"
value={value}
onChange={(val) => onChange(val)}
className="relative flex-shrink-0"
multiple
>
{({ open }: any) => ( {({ open }: any) => (
<> <>
<Combobox.Button className="flex items-center gap-2 cursor-pointer text-xs text-custom-text-200"> <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> <span>{label.name}</span>
</div> </div>
<div className="flex items-center justify-center rounded p-1"> <div className="flex items-center justify-center rounded p-1">
<CheckIcon <CheckIcon className={`h-3 w-3 ${selected ? "opacity-100" : "opacity-0"}`} />
className={`h-3 w-3 ${
selected ? "opacity-100" : "opacity-0"
}`}
/>
</div> </div>
</div> </div>
)} )}
@ -168,11 +148,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
<span>{child.name}</span> <span>{child.name}</span>
</div> </div>
<div className="flex items-center justify-center rounded p-1"> <div className="flex items-center justify-center rounded p-1">
<CheckIcon <CheckIcon className={`h-3 w-3 ${selected ? "opacity-100" : "opacity-0"}`} />
className={`h-3 w-3 ${
selected ? "opacity-100" : "opacity-0"
}`}
/>
</div> </div>
</div> </div>
)} )}

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// ui // ui
import { CustomSearchSelect } from "components/ui"; import { CustomSearchSelect } from "components/ui";
// icons // icons
@ -30,9 +30,7 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
const { data: stateGroups } = useSWR( const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId) : null, workspaceSlug && projectId ? STATES_LIST(projectId) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId) : null
? () => stateService.getStates(workspaceSlug as string, projectId)
: null
); );
const states = getStatesList(stateGroups); const states = getStatesList(stateGroups);
@ -60,10 +58,7 @@ export const IssueStateSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
{selectedOption ? ( {selectedOption ? (
<StateGroupIcon stateGroup={selectedOption.group} color={selectedOption.color} /> <StateGroupIcon stateGroup={selectedOption.group} color={selectedOption.color} />
) : currentDefaultState ? ( ) : currentDefaultState ? (
<StateGroupIcon <StateGroupIcon stateGroup={currentDefaultState.group} color={currentDefaultState.color} />
stateGroup={currentDefaultState.group}
color={currentDefaultState.color}
/>
) : ( ) : (
<Squares2X2Icon className="h-3.5 w-3.5 text-custom-text-200" /> <Squares2X2Icon className="h-3.5 w-3.5 text-custom-text-200" />
)} )}

View File

@ -7,7 +7,7 @@ import { UseFormWatch } from "react-hook-form";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// components // components
import { ExistingIssuesListModal } from "components/core"; import { ExistingIssuesListModal } from "components/core";
// icons // icons
@ -23,12 +23,7 @@ type Props = {
disabled?: boolean; disabled?: boolean;
}; };
export const SidebarBlockedSelect: React.FC<Props> = ({ export const SidebarBlockedSelect: React.FC<Props> = ({ issueId, submitChanges, watch, disabled = false }) => {
issueId,
submitChanges,
watch,
disabled = false,
}) => {
const [isBlockedModalOpen, setIsBlockedModalOpen] = useState(false); const [isBlockedModalOpen, setIsBlockedModalOpen] = useState(false);
const { user } = useUser(); const { user } = useUser();
@ -80,10 +75,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
}) })
.then((response) => { .then((response) => {
submitChanges({ submitChanges({
related_issues: [ related_issues: [...watch("related_issues")?.filter((i) => i.relation_type !== "blocked_by"), ...response],
...watch("related_issues")?.filter((i) => i.relation_type !== "blocked_by"),
...response,
],
}); });
}); });
@ -127,9 +119,7 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
type="button" type="button"
className="opacity-0 duration-300 group-hover:opacity-100" className="opacity-0 duration-300 group-hover:opacity-100"
onClick={() => { onClick={() => {
const updatedRelations = watch("related_issues")?.filter( const updatedRelations = watch("related_issues")?.filter((i) => i.id !== relation.id);
(i) => i.id !== relation.id
);
submitChanges({ submitChanges({
related_issues: updatedRelations, related_issues: updatedRelations,

View File

@ -10,7 +10,7 @@ import useUser from "hooks/use-user";
// components // components
import { ExistingIssuesListModal } from "components/core"; import { ExistingIssuesListModal } from "components/core";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// icons // icons
import { XMarkIcon } from "@heroicons/react/24/outline"; import { XMarkIcon } from "@heroicons/react/24/outline";
import { BlockerIcon } from "components/icons"; import { BlockerIcon } from "components/icons";
@ -24,12 +24,7 @@ type Props = {
disabled?: boolean; disabled?: boolean;
}; };
export const SidebarBlockerSelect: React.FC<Props> = ({ export const SidebarBlockerSelect: React.FC<Props> = ({ issueId, submitChanges, watch, disabled = false }) => {
issueId,
submitChanges,
watch,
disabled = false,
}) => {
const [isBlockerModalOpen, setIsBlockerModalOpen] = useState(false); const [isBlockerModalOpen, setIsBlockerModalOpen] = useState(false);
const { user } = useUser(); const { user } = useUser();
@ -42,8 +37,7 @@ export const SidebarBlockerSelect: React.FC<Props> = ({
setIsBlockerModalOpen(false); setIsBlockerModalOpen(false);
}; };
const blockerIssue = const blockerIssue = watch("issue_relations")?.filter((i) => i.relation_type === "blocked_by") || [];
watch("issue_relations")?.filter((i) => i.relation_type === "blocked_by") || [];
const onSubmit = async (data: ISearchIssueResponse[]) => { const onSubmit = async (data: ISearchIssueResponse[]) => {
if (data.length === 0) { if (data.length === 0) {

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import cyclesService from "services/cycles.service"; import cyclesService from "services/cycles.service";
// ui // ui
import { Spinner, CustomSelect, Tooltip } from "components/ui"; import { Spinner, CustomSelect, Tooltip } from "components/ui";
@ -22,23 +22,14 @@ type Props = {
disabled?: boolean; disabled?: boolean;
}; };
export const SidebarCycleSelect: React.FC<Props> = ({ export const SidebarCycleSelect: React.FC<Props> = ({ issueDetail, handleCycleChange, disabled = false }) => {
issueDetail,
handleCycleChange,
disabled = false,
}) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query; const { workspaceSlug, projectId, issueId } = router.query;
const { data: incompleteCycles } = useSWR( const { data: incompleteCycles } = useSWR(
workspaceSlug && projectId ? INCOMPLETE_CYCLES_LIST(projectId as string) : null, workspaceSlug && projectId ? INCOMPLETE_CYCLES_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => ? () => cyclesService.getCyclesWithParams(workspaceSlug as string, projectId as string, "incomplete")
cyclesService.getCyclesWithParams(
workspaceSlug as string,
projectId as string,
"incomplete"
)
: null : null
); );
@ -63,21 +54,14 @@ export const SidebarCycleSelect: React.FC<Props> = ({
<CustomSelect <CustomSelect
customButton={ customButton={
<div> <div>
<Tooltip <Tooltip position="left" tooltipContent={`${issueCycle ? issueCycle.cycle_detail.name : "No cycle"}`}>
position="left"
tooltipContent={`${issueCycle ? issueCycle.cycle_detail.name : "No cycle"}`}
>
<button <button
type="button" type="button"
className={`bg-custom-background-80 text-xs rounded px-2.5 py-0.5 w-full flex ${ className={`bg-custom-background-80 text-xs rounded px-2.5 py-0.5 w-full flex ${
disabled ? "cursor-not-allowed" : "" disabled ? "cursor-not-allowed" : ""
}`} }`}
> >
<span <span className={`truncate ${issueCycle ? "text-custom-text-100" : "text-custom-text-200"}`}>
className={`truncate ${
issueCycle ? "text-custom-text-100" : "text-custom-text-200"
}`}
>
{issueCycle ? issueCycle.cycle_detail.name : "No cycle"} {issueCycle ? issueCycle.cycle_detail.name : "No cycle"}
</span> </span>
</button> </button>

View File

@ -12,7 +12,7 @@ import { X, CopyPlus } from "lucide-react";
import { BlockerIcon } from "components/icons"; import { BlockerIcon } from "components/icons";
import { ExistingIssuesListModal } from "components/core"; import { ExistingIssuesListModal } from "components/core";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// types // types
import { BlockeIssueDetail, IIssue, ISearchIssueResponse } from "types"; import { BlockeIssueDetail, IIssue, ISearchIssueResponse } from "types";

View File

@ -11,7 +11,7 @@ import { TwitterPicker } from "react-color";
// headless ui // headless ui
import { Listbox, Popover, Transition } from "@headlessui/react"; import { Listbox, Popover, Transition } from "@headlessui/react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
// ui // ui
@ -66,25 +66,21 @@ export const SidebarLabelSelect: React.FC<Props> = ({
const { data: issueLabels, mutate: issueLabelMutate } = useSWR<IIssueLabels[]>( const { data: issueLabels, mutate: issueLabelMutate } = useSWR<IIssueLabels[]>(
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null, workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string) : null
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
: null
); );
const handleNewLabel = async (formData: Partial<IIssueLabels>) => { const handleNewLabel = async (formData: Partial<IIssueLabels>) => {
if (!workspaceSlug || !projectId || isSubmitting) return; if (!workspaceSlug || !projectId || isSubmitting) return;
await issuesService await issuesService.createIssueLabel(workspaceSlug as string, projectId as string, formData, user).then((res) => {
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user) reset(defaultValues);
.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(() => { useEffect(() => {
@ -165,9 +161,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
{issueLabels ? ( {issueLabels ? (
issueLabels.length > 0 ? ( issueLabels.length > 0 ? (
issueLabels.map((label: IIssueLabels) => { issueLabels.map((label: IIssueLabels) => {
const children = issueLabels?.filter( const children = issueLabels?.filter((l) => l.parent === label.id);
(l) => l.parent === label.id
);
if (children.length === 0) { if (children.length === 0) {
if (!label.parent) if (!label.parent)
@ -175,9 +169,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
<Listbox.Option <Listbox.Option
key={label.id} key={label.id}
className={({ active, selected }) => className={({ active, selected }) =>
`${ `${active || selected ? "bg-custom-background-90" : ""} ${
active || selected ? "bg-custom-background-90" : ""
} ${
selected ? "" : "text-custom-text-200" selected ? "" : "text-custom-text-200"
} flex cursor-pointer select-none items-center gap-2 truncate p-2` } flex cursor-pointer select-none items-center gap-2 truncate p-2`
} }
@ -186,10 +178,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
<span <span
className="h-2 w-2 flex-shrink-0 rounded-full" className="h-2 w-2 flex-shrink-0 rounded-full"
style={{ style={{
backgroundColor: backgroundColor: label.color && label.color !== "" ? label.color : "#000",
label.color && label.color !== ""
? label.color
: "#000",
}} }}
/> />
{label.name} {label.name}
@ -207,11 +196,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
<Listbox.Option <Listbox.Option
key={child.id} key={child.id}
className={({ active, selected }) => className={({ active, selected }) =>
`${ `${active || selected ? "bg-custom-background-100" : ""} ${
active || selected
? "bg-custom-background-100"
: ""
} ${
selected ? "" : "text-custom-text-200" selected ? "" : "text-custom-text-200"
} flex cursor-pointer select-none items-center gap-2 truncate p-2` } flex cursor-pointer select-none items-center gap-2 truncate p-2`
} }
@ -248,9 +233,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
<button <button
type="button" type="button"
className={`flex ${ className={`flex ${
isNotAllowed || uneditable isNotAllowed || uneditable ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-90"
? "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`} } 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)} onClick={() => setCreateLabelForm((prevData) => !prevData)}
disabled={uneditable} disabled={uneditable}
@ -326,11 +309,7 @@ export const SidebarLabelSelect: React.FC<Props> = ({
> >
<XMarkIcon className="h-4 w-4 text-white" /> <XMarkIcon className="h-4 w-4 text-white" />
</button> </button>
<button <button type="submit" className="grid place-items-center rounded bg-green-500 p-2.5" disabled={isSubmitting}>
type="submit"
className="grid place-items-center rounded bg-green-500 p-2.5"
disabled={isSubmitting}
>
<PlusIcon className="h-4 w-4 text-white" /> <PlusIcon className="h-4 w-4 text-white" />
</button> </button>
</form> </form>

View File

@ -12,7 +12,7 @@ import { BlockerIcon, RelatedIcon } from "components/icons";
// components // components
import { ExistingIssuesListModal } from "components/core"; import { ExistingIssuesListModal } from "components/core";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// types // types
import { BlockeIssueDetail, IIssue, ISearchIssueResponse } from "types"; import { BlockeIssueDetail, IIssue, ISearchIssueResponse } from "types";

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// ui // ui
import { Spinner, CustomSelect } from "components/ui"; import { Spinner, CustomSelect } from "components/ui";
// icons // icons
@ -28,9 +28,7 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, disabled
const { data: stateGroups } = useSWR( const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
); );
const states = getStatesList(stateGroups); const states = getStatesList(stateGroups);

View File

@ -12,7 +12,7 @@ import useUserAuth from "hooks/use-user-auth";
import useUserIssueNotificationSubscription from "hooks/use-issue-notification-subscription"; import useUserIssueNotificationSubscription from "hooks/use-issue-notification-subscription";
import useEstimateOption from "hooks/use-estimate-option"; import useEstimateOption from "hooks/use-estimate-option";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import modulesService from "services/modules.service"; import modulesService from "services/modules.service";
// contexts // contexts
import { useProjectMyMembership } from "contexts/project-member.context"; import { useProjectMyMembership } from "contexts/project-member.context";
@ -103,8 +103,11 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
const { isEstimateActive } = useEstimateOption(); const { isEstimateActive } = useEstimateOption();
const { loading, handleSubscribe, handleUnsubscribe, subscribed } = const { loading, handleSubscribe, handleUnsubscribe, subscribed } = useUserIssueNotificationSubscription(
useUserIssueNotificationSubscription(workspaceSlug, projectId, issueId); workspaceSlug,
projectId,
issueId
);
const { memberRole } = useProjectMyMembership(); const { memberRole } = useProjectMyMembership();
@ -198,13 +201,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
); );
await issuesService await issuesService
.updateIssueLink( .updateIssueLink(workspaceSlug as string, projectId as string, issueDetail.id, linkId, payload)
workspaceSlug as string,
projectId as string,
issueDetail.id,
linkId,
payload
)
.then((res) => { .then((res) => {
mutate(ISSUE_DETAILS(issueDetail.id)); mutate(ISSUE_DETAILS(issueDetail.id));
}) })
@ -235,12 +232,9 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
}; };
const handleCopyText = () => { const handleCopyText = () => {
const originURL = const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
copyTextToClipboard( copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issueDetail?.id}`).then(() => {
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issueDetail?.id}`
).then(() => {
setToastAlert({ setToastAlert({
type: "success", type: "success",
title: "Link Copied!", title: "Link Copied!",
@ -264,9 +258,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
fieldsToShow.includes("dueDate"); fieldsToShow.includes("dueDate");
const showThirdSection = const showThirdSection =
fieldsToShow.includes("all") || fieldsToShow.includes("all") || fieldsToShow.includes("cycle") || fieldsToShow.includes("module");
fieldsToShow.includes("cycle") ||
fieldsToShow.includes("module");
const startDate = watchIssue("start_date"); const startDate = watchIssue("start_date");
const targetDate = watchIssue("target_date"); const targetDate = watchIssue("target_date");
@ -413,30 +405,27 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
</div> </div>
</div> </div>
)} )}
{(fieldsToShow.includes("all") || fieldsToShow.includes("estimate")) && {(fieldsToShow.includes("all") || fieldsToShow.includes("estimate")) && isEstimateActive && (
isEstimateActive && ( <div className="flex flex-wrap items-center py-2">
<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">
<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" />
<PlayIcon className="h-4 w-4 flex-shrink-0 -rotate-90" /> <p>Estimate</p>
<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>
)} <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> </div>
)} )}
{showSecondSection && ( {showSecondSection && (

View File

@ -8,7 +8,7 @@ import useSWR, { mutate } from "swr";
// headless ui // headless ui
import { Disclosure, Transition } from "@headlessui/react"; import { Disclosure, Transition } from "@headlessui/react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// contexts // contexts
import { useProjectMyMembership } from "contexts/project-member.context"; import { useProjectMyMembership } from "contexts/project-member.context";
// components // 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"> <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"> <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" : ""}`} /> <ChevronRightIcon className={`h-3 w-3 ${open ? "rotate-90" : ""}`} />
Sub-issues{" "} Sub-issues <span className="ml-1 text-custom-text-200">{subIssuesResponse.sub_issues.length}</span>
<span className="ml-1 text-custom-text-200">
{subIssuesResponse.sub_issues.length}
</span>
</Disclosure.Button> </Disclosure.Button>
<div className="flex w-60 items-center gap-2"> <div className="flex w-60 items-center gap-2">
<div className="bar relative h-1.5 w-full rounded bg-custom-background-80"> <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"> <Disclosure.Panel className="mt-3 flex flex-col gap-y-1">
{subIssuesResponse.sub_issues.map((issue) => ( {subIssuesResponse.sub_issues.map((issue) => (
<Link <Link key={issue.id} href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}>
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"> <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"> <div className="flex items-center gap-2 rounded text-xs">
<span <span
@ -240,9 +234,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue, user, disabled = false }
noChevron noChevron
> >
<CustomMenu.MenuItem onClick={handleCreateIssueModal}>Create new</CustomMenu.MenuItem> <CustomMenu.MenuItem onClick={handleCreateIssueModal}>Create new</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => setSubIssuesListModal(true)}> <CustomMenu.MenuItem onClick={() => setSubIssuesListModal(true)}>Add an existing issue</CustomMenu.MenuItem>
Add an existing issue
</CustomMenu.MenuItem>
</CustomMenu> </CustomMenu>
) )
)} )}

View File

@ -6,7 +6,7 @@ import useSWR from "swr";
// services // services
import projectService from "services/project.service"; import projectService from "services/project.service";
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// ui // ui
import { AssigneesList, Avatar, CustomSearchSelect, Icon, Tooltip } from "components/ui"; import { AssigneesList, Avatar, CustomSearchSelect, Icon, Tooltip } from "components/ui";
// icons // icons

View File

@ -5,7 +5,7 @@ import { CustomDatePicker, Tooltip } from "components/ui";
// helpers // helpers
import { findHowManyDaysLeft, renderShortDateWithYearFormat } from "helpers/date-time.helper"; import { findHowManyDaysLeft, renderShortDateWithYearFormat } from "helpers/date-time.helper";
// services // services
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// types // types
import { ICurrentUserResponse, IIssue } from "types"; import { ICurrentUserResponse, IIssue } from "types";
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
@ -42,9 +42,7 @@ export const ViewDueDateSelect: React.FC<Props> = ({
return ( return (
<Tooltip <Tooltip
tooltipHeading="Due date" tooltipHeading="Due date"
tooltipContent={ tooltipContent={issue.target_date ? renderShortDateWithYearFormat(issue.target_date) ?? "N/A" : "N/A"}
issue.target_date ? renderShortDateWithYearFormat(issue.target_date) ?? "N/A" : "N/A"
}
position={tooltipPosition} position={tooltipPosition}
> >
<div <div
@ -80,9 +78,7 @@ export const ViewDueDateSelect: React.FC<Props> = ({
); );
}} }}
className={`${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${ className={`${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${
displayFilters.layout === "kanban" displayFilters.layout === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100"
? "bg-custom-background-90"
: "bg-custom-background-100"
}`} }`}
minDate={minDate ?? undefined} minDate={minDate ?? undefined}
noBorder={noBorder} noBorder={noBorder}

View File

@ -3,7 +3,7 @@ import React from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// services // services
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// hooks // hooks
import useEstimateOption from "hooks/use-estimate-option"; import useEstimateOption from "hooks/use-estimate-option";
// ui // ui

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// component // component
import { CreateLabelModal } from "components/labels"; import { CreateLabelModal } from "components/labels";
// ui // ui
@ -45,9 +45,7 @@ export const ViewLabelSelect: React.FC<Props> = ({
const { data: issueLabels } = useSWR<IIssueLabels[]>( const { data: issueLabels } = useSWR<IIssueLabels[]>(
projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null, projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string) : null
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
: null
); );
const options = issueLabels?.map((label) => ({ const options = issueLabels?.map((label) => ({

View File

@ -3,7 +3,7 @@ import React from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// services // services
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// ui // ui
import { CustomSelect, Tooltip } from "components/ui"; import { CustomSelect, Tooltip } from "components/ui";
// icons // icons
@ -61,9 +61,9 @@ export const ViewPrioritySelect: React.FC<Props> = ({
customButton={ customButton={
<button <button
type="button" type="button"
className={`grid place-items-center rounded ${ className={`grid place-items-center rounded ${isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"} ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer" noBorder ? "" : "h-6 w-6 border shadow-sm"
} ${noBorder ? "" : "h-6 w-6 border shadow-sm"} ${ } ${
noBorder noBorder
? "" ? ""
: issue.priority === "urgent" : issue.priority === "urgent"
@ -71,11 +71,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
: "border-custom-border-300 bg-custom-background-100" : "border-custom-border-300 bg-custom-background-100"
} items-center`} } items-center`}
> >
<Tooltip <Tooltip tooltipHeading="Priority" tooltipContent={issue.priority ?? "None"} position={tooltipPosition}>
tooltipHeading="Priority"
tooltipContent={issue.priority ?? "None"}
position={tooltipPosition}
>
<span className="flex gap-1 items-center text-custom-text-200 text-xs"> <span className="flex gap-1 items-center text-custom-text-200 text-xs">
<PriorityIcon <PriorityIcon
priority={issue.priority} priority={issue.priority}

View File

@ -5,7 +5,7 @@ import { CustomDatePicker, Tooltip } from "components/ui";
// helpers // helpers
import { findHowManyDaysLeft, renderShortDateWithYearFormat } from "helpers/date-time.helper"; import { findHowManyDaysLeft, renderShortDateWithYearFormat } from "helpers/date-time.helper";
// services // services
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// types // types
import { ICurrentUserResponse, IIssue } from "types"; import { ICurrentUserResponse, IIssue } from "types";
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
@ -42,9 +42,7 @@ export const ViewStartDateSelect: React.FC<Props> = ({
return ( return (
<Tooltip <Tooltip
tooltipHeading="Start date" tooltipHeading="Start date"
tooltipContent={ tooltipContent={issue.start_date ? renderShortDateWithYearFormat(issue.start_date) ?? "N/A" : "N/A"}
issue.start_date ? renderShortDateWithYearFormat(issue.start_date) ?? "N/A" : "N/A"
}
position={tooltipPosition} position={tooltipPosition}
> >
<div className="group flex-shrink-0 relative max-w-[6.5rem]"> <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"} ${ className={`${issue?.start_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${
displayFilters.layout === "kanban" displayFilters.layout === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100"
? "bg-custom-background-90"
: "bg-custom-background-100"
}`} }`}
maxDate={maxDate ?? undefined} maxDate={maxDate ?? undefined}
noBorder={noBorder} noBorder={noBorder}

View File

@ -5,8 +5,8 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
import trackEventServices from "services/track-event.service"; import trackEventServices from "services/track_event.service";
// ui // ui
import { CustomSearchSelect, Tooltip } from "components/ui"; import { CustomSearchSelect, Tooltip } from "components/ui";
// icons // icons
@ -48,9 +48,7 @@ export const ViewStateSelect: React.FC<Props> = ({
const { data: stateGroups } = useSWR( const { data: stateGroups } = useSWR(
workspaceSlug && issue && fetchStates ? STATES_LIST(issue.project) : null, workspaceSlug && issue && fetchStates ? STATES_LIST(issue.project) : null,
workspaceSlug && issue && fetchStates workspaceSlug && issue && fetchStates ? () => stateService.getStates(workspaceSlug as string, issue.project) : null
? () => stateService.getStates(workspaceSlug as string, issue.project)
: null
); );
const states = getStatesList(stateGroups); const states = getStatesList(stateGroups);
@ -68,16 +66,10 @@ export const ViewStateSelect: React.FC<Props> = ({
const selectedOption = issue.state_detail; const selectedOption = issue.state_detail;
const stateLabel = ( const stateLabel = (
<Tooltip <Tooltip tooltipHeading="State" tooltipContent={selectedOption?.name ?? ""} position={tooltipPosition}>
tooltipHeading="State"
tooltipContent={selectedOption?.name ?? ""}
position={tooltipPosition}
>
<div className="flex items-center cursor-pointer w-full gap-2 text-custom-text-200"> <div className="flex items-center cursor-pointer w-full gap-2 text-custom-text-200">
<span className="h-3.5 w-3.5"> <span className="h-3.5 w-3.5">
{selectedOption && ( {selectedOption && <StateGroupIcon stateGroup={selectedOption.group} color={selectedOption.color} />}
<StateGroupIcon stateGroup={selectedOption.group} color={selectedOption.color} />
)}
</span> </span>
<span className="truncate">{selectedOption?.name ?? "State"}</span> <span className="truncate">{selectedOption?.name ?? "State"}</span>
</div> </div>

View File

@ -11,7 +11,7 @@ import { TwitterPicker } from "react-color";
// headless ui // headless ui
import { Dialog, Popover, Transition } from "@headlessui/react"; import { Dialog, Popover, Transition } from "@headlessui/react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// ui // ui
import { Input, PrimaryButton, SecondaryButton } from "components/ui"; import { Input, PrimaryButton, SecondaryButton } from "components/ui";
// icons // icons
@ -36,13 +36,7 @@ const defaultValues: Partial<IState> = {
color: "rgb(var(--color-text-200))", color: "rgb(var(--color-text-200))",
}; };
export const CreateLabelModal: React.FC<Props> = ({ export const CreateLabelModal: React.FC<Props> = ({ isOpen, projectId, handleClose, user, onSuccess }) => {
isOpen,
projectId,
handleClose,
user,
onSuccess,
}) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
@ -73,11 +67,7 @@ export const CreateLabelModal: React.FC<Props> = ({
await issuesService await issuesService
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user) .createIssueLabel(workspaceSlug as string, projectId as string, formData, user)
.then((res) => { .then((res) => {
mutate<IIssueLabels[]>( mutate<IIssueLabels[]>(PROJECT_ISSUE_LABELS(projectId), (prevData) => [res, ...(prevData ?? [])], false);
PROJECT_ISSUE_LABELS(projectId),
(prevData) => [res, ...(prevData ?? [])],
false
);
onClose(); onClose();
if (onSuccess) onSuccess(res); 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"> <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)}> <form onSubmit={handleSubmit(onSubmit)}>
<div> <div>
<Dialog.Title <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Create Label Create Label
</Dialog.Title> </Dialog.Title>
<div className="mt-8 flex items-center gap-2"> <div className="mt-8 flex items-center gap-2">

View File

@ -13,7 +13,7 @@ import { TwitterPicker } from "react-color";
// headless ui // headless ui
import { Popover, Transition } from "@headlessui/react"; import { Popover, Transition } from "@headlessui/react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// ui // ui
import { Input, PrimaryButton, SecondaryButton } from "components/ui"; import { Input, PrimaryButton, SecondaryButton } from "components/ui";
// types // types
@ -35,174 +35,157 @@ const defaultValues: Partial<IIssueLabels> = {
color: "rgb(var(--color-text-200))", color: "rgb(var(--color-text-200))",
}; };
export const CreateUpdateLabelInline = forwardRef<HTMLDivElement, Props>( export const CreateUpdateLabelInline = forwardRef<HTMLDivElement, Props>(function CreateUpdateLabelInline(props, ref) {
function CreateUpdateLabelInline(props, ref) { const { labelForm, setLabelForm, isUpdating, labelToUpdate, onClose } = props;
const { labelForm, setLabelForm, isUpdating, labelToUpdate, onClose } = props;
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth(); const { user } = useUserAuth();
const { const {
handleSubmit, handleSubmit,
control, control,
register, register,
reset, reset,
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
watch, watch,
setValue, setValue,
} = useForm<IIssueLabels>({ } = useForm<IIssueLabels>({
defaultValues, defaultValues,
}); });
const handleClose = () => { const handleClose = () => {
setLabelForm(false); setLabelForm(false);
reset(defaultValues); reset(defaultValues);
if (onClose) onClose(); if (onClose) onClose();
}; };
const handleLabelCreate: SubmitHandler<IIssueLabels> = async (formData) => { const handleLabelCreate: SubmitHandler<IIssueLabels> = async (formData) => {
if (!workspaceSlug || !projectId || isSubmitting) return; if (!workspaceSlug || !projectId || isSubmitting) return;
await issuesService await issuesService.createIssueLabel(workspaceSlug as string, projectId as string, formData, user).then((res) => {
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user) mutate<IIssueLabels[]>(
.then((res) => { PROJECT_ISSUE_LABELS(projectId as string),
mutate<IIssueLabels[]>( (prevData) => [res, ...(prevData ?? [])],
PROJECT_ISSUE_LABELS(projectId as string), false
(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"
); );
setValue("name", labelToUpdate.name); handleClose();
}, [labelToUpdate, setValue]); });
};
useEffect(() => { const handleLabelUpdate: SubmitHandler<IIssueLabels> = async (formData) => {
if (labelToUpdate) { if (!workspaceSlug || !projectId || isSubmitting) return;
setValue(
"color", await issuesService
labelToUpdate.color && labelToUpdate.color !== "" ? labelToUpdate.color : "#000" .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()); useEffect(() => {
}, [labelToUpdate, setValue]); if (!labelForm && isUpdating) return;
return ( reset();
<div }, [labelForm, isUpdating, reset]);
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" useEffect(() => {
}`} if (!labelToUpdate) return;
ref={ref}
> setValue("color", labelToUpdate.color && labelToUpdate.color !== "" ? labelToUpdate.color : "#000");
<div className="flex-shrink-0"> setValue("name", labelToUpdate.name);
<Popover className="relative z-10 flex h-full w-full items-center justify-center"> }, [labelToUpdate, setValue]);
{({ open }) => (
<> useEffect(() => {
<Popover.Button if (labelToUpdate) {
className={`group inline-flex items-center text-base font-medium focus:outline-none ${ setValue("color", labelToUpdate.color && labelToUpdate.color !== "" ? labelToUpdate.color : "#000");
open ? "text-custom-text-100" : "text-custom-text-200" return;
}`} }
>
<span setValue("color", getRandomLabelColor());
className="h-4 w-4 rounded-full" }, [labelToUpdate, setValue]);
style={{
backgroundColor: watch("color"), 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> </Popover.Panel>
</Transition>
<Transition </>
as={React.Fragment} )}
enter="transition ease-out duration-200" </Popover>
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>
)}
</div> </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>
);
});

View File

@ -9,7 +9,7 @@ import { Dialog, Transition } from "@headlessui/react";
// icons // icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // 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="px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start"> <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"> <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 <ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
className="h-6 w-6 text-red-600"
aria-hidden="true"
/>
</div> </div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<Dialog.Title <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Delete Label Delete Label
</Dialog.Title> </Dialog.Title>
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-custom-text-200"> <p className="text-sm text-custom-text-200">
Are you sure you want to delete label-{" "} Are you sure you want to delete label-{" "}
<span className="font-medium text-custom-text-100">{data?.name}</span>? <span className="font-medium text-custom-text-100">{data?.name}</span>? The label will be
The label will be removed from all the issues. removed from all the issues.
</p> </p>
</div> </div>
</div> </div>

View File

@ -9,7 +9,7 @@ import { Combobox, Dialog, Transition } from "@headlessui/react";
// icons // icons
import { RectangleStackIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; import { RectangleStackIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// types // types
import { ICurrentUserResponse, IIssueLabels } from "types"; import { ICurrentUserResponse, IIssueLabels } from "types";
// constants // constants
@ -30,9 +30,7 @@ export const LabelsListModal: React.FC<Props> = ({ isOpen, handleClose, parent,
const { data: issueLabels, mutate } = useSWR<IIssueLabels[]>( const { data: issueLabels, mutate } = useSWR<IIssueLabels[]>(
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null, workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string) : null
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
: null
); );
const filteredLabels: IIssueLabels[] = const filteredLabels: IIssueLabels[] =
@ -114,9 +112,7 @@ export const LabelsListModal: React.FC<Props> = ({ isOpen, handleClose, parent,
{filteredLabels.length > 0 && ( {filteredLabels.length > 0 && (
<li className="p-2"> <li className="p-2">
{query === "" && ( {query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-custom-text-100"> <h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-custom-text-100">Labels</h2>
Labels
</h2>
)} )}
<ul className="text-sm text-gray-700"> <ul className="text-sm text-gray-700">
{filteredLabels.map((label) => { {filteredLabels.map((label) => {

View File

@ -7,17 +7,11 @@ import { mutate } from "swr";
// headless ui // headless ui
import { Disclosure, Transition } from "@headlessui/react"; import { Disclosure, Transition } from "@headlessui/react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// ui // ui
import { CustomMenu } from "components/ui"; import { CustomMenu } from "components/ui";
// icons // icons
import { import { ChevronDownIcon, XMarkIcon, PlusIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
ChevronDownIcon,
XMarkIcon,
PlusIcon,
PencilIcon,
TrashIcon,
} from "@heroicons/react/24/outline";
import { Component, X } from "lucide-react"; import { Component, X } from "lucide-react";
// types // types
import { ICurrentUserResponse, IIssueLabels } from "types"; import { ICurrentUserResponse, IIssueLabels } from "types";
@ -110,9 +104,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
<Disclosure.Button> <Disclosure.Button>
<span> <span>
<ChevronDownIcon <ChevronDownIcon
className={`h-4 w-4 text-custom-sidebar-text-400 ${ className={`h-4 w-4 text-custom-sidebar-text-400 ${!open ? "rotate-90 transform" : ""}`}
!open ? "rotate-90 transform" : ""
}`}
/> />
</span> </span>
</Disclosure.Button> </Disclosure.Button>
@ -138,8 +130,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
<span <span
className="h-3.5 w-3.5 flex-shrink-0 rounded-full" className="h-3.5 w-3.5 flex-shrink-0 rounded-full"
style={{ style={{
backgroundColor: backgroundColor: child.color && child.color !== "" ? child.color : "#000000",
child.color && child.color !== "" ? child.color : "#000000",
}} }}
/> />
{child.name} {child.name}
@ -169,10 +160,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
<button <button className="flex items-center justify-start gap-2" onClick={handleLabelDelete}>
className="flex items-center justify-start gap-2"
onClick={handleLabelDelete}
>
<X className="h-[18px] w-[18px] text-custom-sidebar-text-400 flex-shrink-0" /> <X className="h-[18px] w-[18px] text-custom-sidebar-text-400 flex-shrink-0" />
</button> </button>
</div> </div>

View File

@ -9,7 +9,7 @@ import { PaperAirplaneIcon } from "@heroicons/react/24/outline";
// react-hook-form // react-hook-form
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -81,8 +81,7 @@ export const CreateBlock: React.FC<Props> = ({ user }) => {
}; };
const handleKeyDown = (e: any) => { const handleKeyDown = (e: any) => {
const keyCombination = const keyCombination = ((e.ctrlKey || e.metaKey) && e.key === "Enter") || (e.shiftKey && e.key === "Enter");
((e.ctrlKey || e.metaKey) && e.key === "Enter") || (e.shiftKey && e.key === "Enter");
if (e.key === "Enter" && !keyCombination) { if (e.key === "Enter" && !keyCombination) {
if (watch("name") && watch("name") !== "") { if (watch("name") && watch("name") !== "") {

View File

@ -4,8 +4,8 @@ import { mutate } from "swr";
import { SparklesIcon } from "@heroicons/react/24/outline"; import { SparklesIcon } from "@heroicons/react/24/outline";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import aiService from "services/ai.service"; import aiService from "services/ai.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -195,8 +195,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
setToastAlert({ setToastAlert({
type: "error", type: "error",
title: "Error!", title: "Error!",
message: message: "You have reached the maximum number of requests of 50 requests per month per user.",
"You have reached the maximum number of requests of 50 requests per month per user.",
}); });
else else
setToastAlert({ setToastAlert({
@ -294,9 +293,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
/> />
); );
else if (!value || !watch("description_html")) else if (!value || !watch("description_html"))
return ( return <div className="h-32 w-full flex items-center justify-center text-custom-text-200 text-sm" />;
<div className="h-32 w-full flex items-center justify-center text-custom-text-200 text-sm" />
);
return ( return (
<TipTapEditor <TipTapEditor
@ -351,13 +348,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
<div className="flex items-center justify-end gap-2 p-4"> <div className="flex items-center justify-end gap-2 p-4">
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton> <SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
<PrimaryButton type="submit" disabled={watch("name") === ""} loading={isSubmitting}> <PrimaryButton type="submit" disabled={watch("name") === ""} loading={isSubmitting}>
{data {data ? (isSubmitting ? "Updating..." : "Update block") : isSubmitting ? "Adding..." : "Add block"}
? isSubmitting
? "Updating..."
: "Update block"
: isSubmitting
? "Adding..."
: "Add block"}
</PrimaryButton> </PrimaryButton>
</div> </div>
</form> </form>
@ -371,9 +362,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
onResponse={(response) => { onResponse={(response) => {
if (data && handleAiAssistance) { if (data && handleAiAssistance) {
handleAiAssistance(response); handleAiAssistance(response);
editorRef.current?.setEditorValue( editorRef.current?.setEditorValue(`${watch("description_html")}<p>${response}</p>` ?? "");
`${watch("description_html")}<p>${response}</p>` ?? ""
);
} else { } else {
setValue("description", {}); setValue("description", {});
setValue("description_html", `${watch("description_html")}<p>${response}</p>`); setValue("description_html", `${watch("description_html")}<p>${response}</p>`);

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
@ -15,12 +15,7 @@ import { PageForm } from "./page-form";
// types // types
import { ICurrentUserResponse, IPage } from "types"; import { ICurrentUserResponse, IPage } from "types";
// fetch-keys // fetch-keys
import { import { ALL_PAGES_LIST, FAVORITE_PAGES_LIST, MY_PAGES_LIST, RECENT_PAGES_LIST } from "constants/fetch-keys";
ALL_PAGES_LIST,
FAVORITE_PAGES_LIST,
MY_PAGES_LIST,
RECENT_PAGES_LIST,
} from "constants/fetch-keys";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;

View File

@ -7,7 +7,7 @@ import { mutate } from "swr";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // ui
@ -17,12 +17,7 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// types // types
import type { ICurrentUserResponse, IPage } from "types"; import type { ICurrentUserResponse, IPage } from "types";
// fetch-keys // fetch-keys
import { import { ALL_PAGES_LIST, FAVORITE_PAGES_LIST, MY_PAGES_LIST, RECENT_PAGES_LIST } from "constants/fetch-keys";
ALL_PAGES_LIST,
FAVORITE_PAGES_LIST,
MY_PAGES_LIST,
RECENT_PAGES_LIST,
} from "constants/fetch-keys";
type TConfirmPageDeletionProps = { type TConfirmPageDeletionProps = {
isOpen: boolean; isOpen: boolean;
@ -31,12 +26,7 @@ type TConfirmPageDeletionProps = {
user: ICurrentUserResponse | undefined; user: ICurrentUserResponse | undefined;
}; };
export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = ({ export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = ({ isOpen, setIsOpen, data, user }) => {
isOpen,
setIsOpen,
data,
user,
}) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter(); 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="px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start"> <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"> <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 <ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
className="h-6 w-6 text-red-600"
aria-hidden="true"
/>
</div> </div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<Dialog.Title <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Delete Page Delete Page
</Dialog.Title> </Dialog.Title>
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-custom-text-200"> <p className="text-sm text-custom-text-200">
Are you sure you want to delete Page-{" "} Are you sure you want to delete Page-{" "}
<span className="break-words font-medium text-custom-text-100"> <span className="break-words font-medium text-custom-text-100">{data?.name}</span>? All of the
{data?.name} data related to the page will be permanently removed. This action cannot be undone.
</span>
? All of the data related to the page will be permanently removed. This
action cannot be undone.
</p> </p>
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
// components // components
import { PagesView } from "components/pages"; import { PagesView } from "components/pages";
// types // types

View File

@ -3,7 +3,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
// components // components
import { PagesView } from "components/pages"; import { PagesView } from "components/pages";
// types // types
@ -18,8 +18,7 @@ export const FavoritePagesList: React.FC<TPagesListProps> = ({ viewType }) => {
const { data: pages } = useSWR( const { data: pages } = useSWR(
workspaceSlug && projectId ? FAVORITE_PAGES_LIST(projectId as string) : null, workspaceSlug && projectId ? FAVORITE_PAGES_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => ? () => pagesService.getPagesWithParams(workspaceSlug as string, projectId as string, "favorite")
pagesService.getPagesWithParams(workspaceSlug as string, projectId as string, "favorite")
: null : null
); );

View File

@ -3,7 +3,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
// components // components
import { PagesView } from "components/pages"; import { PagesView } from "components/pages";
// types // types
@ -18,12 +18,7 @@ export const MyPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
const { data: pages } = useSWR( const { data: pages } = useSWR(
workspaceSlug && projectId ? MY_PAGES_LIST(projectId as string) : null, workspaceSlug && projectId ? MY_PAGES_LIST(projectId as string) : null,
workspaceSlug && projectId 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 : null
); );

View File

@ -3,7 +3,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
// components // components
import { PagesView } from "components/pages"; import { PagesView } from "components/pages";
// types // types
@ -18,12 +18,7 @@ export const OtherPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
const { data: pages } = useSWR( const { data: pages } = useSWR(
workspaceSlug && projectId ? OTHER_PAGES_LIST(projectId as string) : null, workspaceSlug && projectId ? OTHER_PAGES_LIST(projectId as string) : null,
workspaceSlug && projectId 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 : null
); );

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
// components // components
import { PagesView } from "components/pages"; import { PagesView } from "components/pages";
// ui // ui
@ -28,9 +28,7 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
const { data: pages } = useSWR( const { data: pages } = useSWR(
workspaceSlug && projectId ? RECENT_PAGES_LIST(projectId as string) : null, workspaceSlug && projectId ? RECENT_PAGES_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => pagesService.getRecentPages(workspaceSlug as string, projectId as string) : null
? () => pagesService.getRecentPages(workspaceSlug as string, projectId as string)
: null
); );
const isEmpty = pages && Object.keys(pages).every((key) => pages[key].length === 0); const isEmpty = pages && Object.keys(pages).every((key) => pages[key].length === 0);
@ -44,9 +42,7 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
return ( return (
<div key={key} className="h-full overflow-hidden"> <div key={key} className="h-full overflow-hidden">
<h2 className="text-xl font-semibold capitalize mb-2"> <h2 className="text-xl font-semibold capitalize mb-2">{replaceUnderscoreIfSnakeCase(key)}</h2>
{replaceUnderscoreIfSnakeCase(key)}
</h2>
<PagesView pages={pages[key as keyof RecentPagesResponse]} viewType={viewType} /> <PagesView pages={pages[key as keyof RecentPagesResponse]} viewType={viewType} />
</div> </div>
); );

View File

@ -4,18 +4,13 @@ import useSWR, { mutate } from "swr";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
import projectService from "services/project.service"; import projectService from "services/project.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth"; import useUserAuth from "hooks/use-user-auth";
// components // components
import { import { CreateUpdatePageModal, DeletePageModal, SinglePageDetailedItem, SinglePageListItem } from "components/pages";
CreateUpdatePageModal,
DeletePageModal,
SinglePageDetailedItem,
SinglePageListItem,
} from "components/pages";
// ui // ui
import { EmptyState, Loader } from "components/ui"; import { EmptyState, Loader } from "components/ui";
// icons // icons
@ -91,11 +86,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
}), }),
false false
); );
mutate<IPage[]>( mutate<IPage[]>(FAVORITE_PAGES_LIST(projectId.toString()), (prevData) => [page, ...(prevData ?? [])], false);
FAVORITE_PAGES_LIST(projectId.toString()),
(prevData) => [page, ...(prevData ?? [])],
false
);
pagesService pagesService
.addPageToFavorites(workspaceSlug.toString(), projectId.toString(), { .addPageToFavorites(workspaceSlug.toString(), projectId.toString(), {
@ -185,11 +176,9 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
false false
); );
pagesService pagesService.patchPage(workspaceSlug.toString(), projectId.toString(), page.id, formData, user).then(() => {
.patchPage(workspaceSlug.toString(), projectId.toString(), page.id, formData, user) mutate(RECENT_PAGES_LIST(projectId.toString()));
.then(() => { });
mutate(RECENT_PAGES_LIST(projectId.toString()));
});
}; };
return ( return (

View File

@ -10,8 +10,8 @@ import { useForm } from "react-hook-form";
// react-beautiful-dnd // react-beautiful-dnd
import { Draggable } from "react-beautiful-dnd"; import { Draggable } from "react-beautiful-dnd";
// services // services
import pagesService from "services/pages.service"; import pagesService from "services/page.service";
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import aiService from "services/ai.service"; import aiService from "services/ai.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -48,13 +48,7 @@ type Props = {
user: ICurrentUserResponse | undefined; user: ICurrentUserResponse | undefined;
}; };
export const SinglePageBlock: React.FC<Props> = ({ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, showBlockDetails, index, user }) => {
block,
projectDetails,
showBlockDetails,
index,
user,
}) => {
const [isSyncing, setIsSyncing] = useState(false); const [isSyncing, setIsSyncing] = useState(false);
const [createBlockForm, setCreateBlockForm] = useState(false); const [createBlockForm, setCreateBlockForm] = useState(false);
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false); const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
@ -131,13 +125,7 @@ export const SinglePageBlock: React.FC<Props> = ({
if (!workspaceSlug || !projectId || !pageId) return; if (!workspaceSlug || !projectId || !pageId) return;
await pagesService await pagesService
.convertPageBlockToIssue( .convertPageBlockToIssue(workspaceSlug as string, projectId as string, pageId as string, block.id, user)
workspaceSlug as string,
projectId as string,
pageId as string,
block.id,
user
)
.then((res: IIssue) => { .then((res: IIssue) => {
mutate<IPageBlock[]>( mutate<IPageBlock[]>(
PAGE_BLOCKS_LIST(pageId as string), PAGE_BLOCKS_LIST(pageId as string),
@ -175,13 +163,7 @@ export const SinglePageBlock: React.FC<Props> = ({
); );
await pagesService await pagesService
.deletePageBlock( .deletePageBlock(workspaceSlug as string, projectId as string, pageId as string, block.id, user)
workspaceSlug as string,
projectId as string,
pageId as string,
block.id,
user
)
.catch(() => { .catch(() => {
setToastAlert({ setToastAlert({
type: "error", type: "error",
@ -221,8 +203,7 @@ export const SinglePageBlock: React.FC<Props> = ({
setToastAlert({ setToastAlert({
type: "error", type: "error",
title: "Error!", title: "Error!",
message: message: "You have reached the maximum number of requests of 50 requests per month per user.",
"You have reached the maximum number of requests of 50 requests per month per user.",
}); });
else else
setToastAlert({ setToastAlert({
@ -283,12 +264,9 @@ export const SinglePageBlock: React.FC<Props> = ({
}; };
const handleCopyText = () => { const handleCopyText = () => {
const originURL = const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
copyTextToClipboard( copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${block.issue}`).then(() => {
`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${block.issue}`
).then(() => {
setToastAlert({ setToastAlert({
type: "success", type: "success",
title: "Link Copied!", title: "Link Copied!",
@ -343,11 +321,7 @@ export const SinglePageBlock: React.FC<Props> = ({
> >
{block.issue && block.sync && ( {block.issue && block.sync && (
<div className="flex flex-shrink-0 cursor-default items-center gap-1 rounded py-1 px-1.5 text-xs"> <div className="flex flex-shrink-0 cursor-default items-center gap-1 rounded py-1 px-1.5 text-xs">
{isSyncing ? ( {isSyncing ? <ArrowPathIcon className="h-3 w-3 animate-spin" /> : <CheckIcon className="h-3 w-3" />}
<ArrowPathIcon className="h-3 w-3 animate-spin" />
) : (
<CheckIcon className="h-3 w-3" />
)}
{isSyncing ? "Syncing..." : "Synced"} {isSyncing ? "Syncing..." : "Synced"}
</div> </div>
)} )}
@ -431,9 +405,7 @@ export const SinglePageBlock: React.FC<Props> = ({
<div className="flex items-center"> <div className="flex items-center">
{block.issue && ( {block.issue && (
<div className="mr-1.5 flex"> <div className="mr-1.5 flex">
<Link <Link href={`/${workspaceSlug}/projects/${projectId}/issues/${block.issue}`}>
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"> <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" /> <LayerDiagonalIcon height="16" width="16" />
{projectDetails?.identifier}-{block.issue_detail?.sequence_id} {projectDetails?.identifier}-{block.issue_detail?.sequence_id}

View File

@ -7,7 +7,7 @@ import useSWR from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { DropResult } from "react-beautiful-dnd"; import { DropResult } from "react-beautiful-dnd";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import userService from "services/user.service"; import userService from "services/user.service";
// hooks // hooks
import useProfileIssues from "hooks/use-profile-issues"; import useProfileIssues from "hooks/use-profile-issues";
@ -31,9 +31,7 @@ export const ProfileIssuesView = () => {
// update issue modal // update issue modal
const [editIssueModal, setEditIssueModal] = useState(false); const [editIssueModal, setEditIssueModal] = useState(false);
const [issueToEdit, setIssueToEdit] = useState< const [issueToEdit, setIssueToEdit] = useState<(IIssue & { actionType: "edit" | "delete" }) | undefined>(undefined);
(IIssue & { actionType: "edit" | "delete" }) | undefined
>(undefined);
// delete issue modal // delete issue modal
const [deleteIssueModal, setDeleteIssueModal] = useState(false); const [deleteIssueModal, setDeleteIssueModal] = useState(false);
@ -60,19 +58,14 @@ export const ProfileIssuesView = () => {
} = useProfileIssues(workspaceSlug?.toString(), userId?.toString()); } = useProfileIssues(workspaceSlug?.toString(), userId?.toString());
const { data: profileData } = useSWR( const { data: profileData } = useSWR(
workspaceSlug && userId ? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) : null,
workspaceSlug && userId workspaceSlug && userId
? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) ? () => userService.getUserProfileProjectsSegregation(workspaceSlug.toString(), userId.toString())
: null,
workspaceSlug && userId
? () =>
userService.getUserProfileProjectsSegregation(workspaceSlug.toString(), userId.toString())
: null : null
); );
const { data: labels } = useSWR( const { data: labels } = useSWR(
workspaceSlug && (filters?.labels ?? []).length > 0 workspaceSlug && (filters?.labels ?? []).length > 0 ? WORKSPACE_LABELS(workspaceSlug.toString()) : null,
? WORKSPACE_LABELS(workspaceSlug.toString())
: null,
workspaceSlug && (filters?.labels ?? []).length > 0 workspaceSlug && (filters?.labels ?? []).length > 0
? () => issuesService.getWorkspaceLabels(workspaceSlug.toString()) ? () => issuesService.getWorkspaceLabels(workspaceSlug.toString())
: null : null
@ -90,13 +83,7 @@ export const ProfileIssuesView = () => {
async (result: DropResult) => { async (result: DropResult) => {
setTrashBox(false); setTrashBox(false);
if ( if (!result.destination || !workspaceSlug || !groupedIssues || displayFilters?.group_by !== "priority") return;
!result.destination ||
!workspaceSlug ||
!groupedIssues ||
displayFilters?.group_by !== "priority"
)
return;
const { source, destination } = result; const { source, destination } = result;
@ -125,10 +112,7 @@ export const ProfileIssuesView = () => {
return { return {
...prevData, ...prevData,
[sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"), [sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
[destinationGroup]: orderArrayBy( [destinationGroup]: orderArrayBy(destinationGroupArray, displayFilters.order_by ?? "-created_at"),
destinationGroupArray,
displayFilters.order_by ?? "-created_at"
),
}; };
}, false); }, false);
@ -218,15 +202,11 @@ export const ProfileIssuesView = () => {
(key) => filtersToDisplay[key as keyof IIssueFilterOptions] === null (key) => filtersToDisplay[key as keyof IIssueFilterOptions] === null
); );
const areFiltersApplied = const areFiltersApplied =
Object.keys(filtersToDisplay).length > 0 && Object.keys(filtersToDisplay).length > 0 && nullFilters.length !== Object.keys(filtersToDisplay).length;
nullFilters.length !== Object.keys(filtersToDisplay).length;
const isSubscribedIssuesRoute = router.pathname.includes("subscribed"); const isSubscribedIssuesRoute = router.pathname.includes("subscribed");
const isMySubscribedIssues = const isMySubscribedIssues =
(filters.subscriber && (filters.subscriber && filters.subscriber.length > 0 && router.pathname.includes("my-issues")) ?? false;
filters.subscriber.length > 0 &&
router.pathname.includes("my-issues")) ??
false;
const disableAddIssueOption = isSubscribedIssuesRoute || isMySubscribedIssues; const disableAddIssueOption = isSubscribedIssuesRoute || isMySubscribedIssues;

View File

@ -6,21 +6,14 @@ import { Controller, useForm } from "react-hook-form";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// ui components // ui components
import { import { ToggleSwitch, PrimaryButton, SecondaryButton, Icon, DangerButton, Loader } from "components/ui";
ToggleSwitch,
PrimaryButton,
SecondaryButton,
Icon,
DangerButton,
Loader,
} from "components/ui";
import { CustomPopover } from "./popover"; import { CustomPopover } from "./popover";
// mobx react lite // mobx react lite
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
// mobx store // mobx store
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
import { RootStore } from "store/root"; import { RootStore } from "store/root";
import { IProjectPublishSettings, TProjectPublishViews } from "store/project-publish"; import { IProjectPublishSettings, TProjectPublishViews } from "store/project_publish";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useProjectDetails from "hooks/use-project-details"; 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; let plane_deploy_url = process.env.NEXT_PUBLIC_DEPLOY_URL;
if (typeof window !== 'undefined' && !plane_deploy_url) { if (typeof window !== "undefined" && !plane_deploy_url) {
plane_deploy_url= window.location.protocol + "//" + window.location.host + "/spaces"; plane_deploy_url = window.location.protocol + "//" + window.location.host + "/spaces";
} }
const router = useRouter(); 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 // prefill form with the saved settings if the project is already published
useEffect(() => { useEffect(() => {
if ( if (projectPublish.projectPublishSettings && projectPublish.projectPublishSettings !== "not-initialized") {
projectPublish.projectPublishSettings &&
projectPublish.projectPublishSettings !== "not-initialized"
) {
let userBoards: TProjectPublishViews[] = []; let userBoards: TProjectPublishViews[] = [];
if (projectPublish.projectPublishSettings?.views) { if (projectPublish.projectPublishSettings?.views) {
@ -143,11 +133,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
projectPublish.project_id !== null && projectPublish.project_id !== null &&
projectPublish?.projectPublishSettings === "not-initialized" projectPublish?.projectPublishSettings === "not-initialized"
) { ) {
projectPublish.getProjectSettingsAsync( projectPublish.getProjectSettingsAsync(workspaceSlug.toString(), projectPublish.project_id, null);
workspaceSlug.toString(),
projectPublish.project_id,
null
);
} }
}, [workspaceSlug, projectPublish, projectPublish.projectPublishModal]); }, [workspaceSlug, projectPublish, projectPublish.projectPublishModal]);
@ -202,12 +188,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
setIsUnpublishing(true); setIsUnpublishing(true);
projectPublish projectPublish
.unPublishProject( .unPublishProject(workspaceSlug.toString(), projectPublish.project_id as string, publishId, null)
workspaceSlug.toString(),
projectPublish.project_id as string,
publishId,
null
)
.then((res) => { .then((res) => {
mutateProjectDetails(); mutateProjectDetails();
@ -269,11 +250,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
// check if an update is required or not // check if an update is required or not
const checkIfUpdateIsRequired = () => { const checkIfUpdateIsRequired = () => {
if ( if (!projectPublish.projectPublishSettings || projectPublish.projectPublishSettings === "not-initialized") return;
!projectPublish.projectPublishSettings ||
projectPublish.projectPublishSettings === "not-initialized"
)
return;
const currentSettings = projectPublish.projectPublishSettings as IProjectPublishSettings; const currentSettings = projectPublish.projectPublishSettings as IProjectPublishSettings;
const newSettings = getValues(); const newSettings = getValues();
@ -289,8 +266,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
let viewCheckFlag = 0; let viewCheckFlag = 0;
viewOptions.forEach((option) => { viewOptions.forEach((option) => {
if (currentSettings.views[option.key] !== newSettings.views.includes(option.key)) if (currentSettings.views[option.key] !== newSettings.views.includes(option.key)) viewCheckFlag++;
viewCheckFlag++;
}); });
if (viewCheckFlag !== 0) { if (viewCheckFlag !== 0) {
@ -416,9 +392,7 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
}} }}
> >
<div className="text-sm">{option.label}</div> <div className="text-sm">{option.label}</div>
<div <div className={`w-[18px] h-[18px] relative flex justify-center items-center`}>
className={`w-[18px] h-[18px] relative flex justify-center items-center`}
>
{value.length > 0 && value.includes(option.key) && ( {value.length > 0 && value.includes(option.key) && (
<Icon iconName="done" className="!text-lg" /> <Icon iconName="done" className="!text-lg" />
)} )}

View File

@ -11,7 +11,7 @@ import { TwitterPicker } from "react-color";
// headless ui // headless ui
import { Dialog, Popover, Transition } from "@headlessui/react"; import { Dialog, Popover, Transition } from "@headlessui/react";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // 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"> <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)}> <form onSubmit={handleSubmit(onSubmit)}>
<div> <div>
<Dialog.Title <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Create State Create State
</Dialog.Title> </Dialog.Title>
<div className="mt-2 space-y-3"> <div className="mt-2 space-y-3">
@ -215,10 +212,7 @@ export const CreateStateModal: React.FC<Props> = ({ isOpen, projectId, handleClo
name="color" name="color"
control={control} control={control}
render={({ field: { value, onChange } }) => ( render={({ field: { value, onChange } }) => (
<TwitterPicker <TwitterPicker color={value} onChange={(value) => onChange(value.hex)} />
color={value}
onChange={(value) => onChange(value.hex)}
/>
)} )}
/> />
</Popover.Panel> </Popover.Panel>

View File

@ -11,7 +11,7 @@ import { TwitterPicker } from "react-color";
// headless ui // headless ui
import { Popover, Transition } from "@headlessui/react"; import { Popover, Transition } from "@headlessui/react";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // ui
@ -39,13 +39,7 @@ const defaultValues: Partial<IState> = {
group: "backlog", group: "backlog",
}; };
export const CreateUpdateStateInline: React.FC<Props> = ({ export const CreateUpdateStateInline: React.FC<Props> = ({ data, onClose, selectedGroup, user, groupLength }) => {
data,
onClose,
selectedGroup,
user,
groupLength,
}) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
@ -153,8 +147,7 @@ export const CreateUpdateStateInline: React.FC<Props> = ({
setToastAlert({ setToastAlert({
type: "error", type: "error",
title: "Error!", title: "Error!",
message: message: "Another state exists with the same name. Please try again with another name.",
"Another state exists with the same name. Please try again with another name.",
}); });
else else
setToastAlert({ setToastAlert({
@ -230,9 +223,7 @@ export const CreateUpdateStateInline: React.FC<Props> = ({
name="group" name="group"
control={control} control={control}
render={({ field: { value, onChange } }) => ( render={({ field: { value, onChange } }) => (
<Tooltip <Tooltip tooltipContent={groupLength === 1 ? "Cannot have an empty group." : "Choose State"}>
tooltipContent={groupLength === 1 ? "Cannot have an empty group." : "Choose State"}
>
<div> <div>
<CustomSelect <CustomSelect
disabled={groupLength === 1} disabled={groupLength === 1}

View File

@ -9,7 +9,7 @@ import { Dialog, Transition } from "@headlessui/react";
// icons // icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// services // services
import stateServices from "services/state.service"; import stateServices from "services/project_state.service";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// ui // 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="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="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"> <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 <ExclamationTriangleIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
className="h-6 w-6 text-red-600"
aria-hidden="true"
/>
</div> </div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<Dialog.Title <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Delete State Delete State
</Dialog.Title> </Dialog.Title>
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-custom-text-200"> <p className="text-sm text-custom-text-200">
Are you sure you want to delete state-{" "} Are you sure you want to delete state-{" "}
<span className="font-medium text-custom-text-100">{data?.name}</span>? <span className="font-medium text-custom-text-100">{data?.name}</span>? All of the data
All of the data related to the state will be permanently removed. This related to the state will be permanently removed. This action cannot be undone.
action cannot be undone.
</p> </p>
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import { mutate } from "swr"; import { mutate } from "swr";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// ui // ui
import { Tooltip } from "components/ui"; import { Tooltip } from "components/ui";
// icons // icons
@ -58,11 +58,7 @@ export const SingleState: React.FC<Props> = ({
})); }));
newStatesList = orderArrayBy(newStatesList, "sequence", "ascending"); newStatesList = orderArrayBy(newStatesList, "sequence", "ascending");
mutate( mutate(STATES_LIST(projectId as string), orderStateGroups(groupBy(newStatesList, "group")), false);
STATES_LIST(projectId as string),
orderStateGroups(groupBy(newStatesList, "group")),
false
);
if (currentDefaultState) if (currentDefaultState)
stateService stateService
@ -131,11 +127,7 @@ export const SingleState: React.FC<Props> = ({
})); }));
newStatesList = orderArrayBy(newStatesList, "sequence", "ascending"); newStatesList = orderArrayBy(newStatesList, "sequence", "ascending");
mutate( mutate(STATES_LIST(projectId as string), orderStateGroups(groupBy(newStatesList, "group")), false);
STATES_LIST(projectId as string),
orderStateGroups(groupBy(newStatesList, "group")),
false
);
stateService stateService
.patchState( .patchState(
@ -216,26 +208,14 @@ export const SingleState: React.FC<Props> = ({
> >
{state.default ? ( {state.default ? (
<Tooltip tooltipContent="Cannot delete the default state."> <Tooltip tooltipContent="Cannot delete the default state.">
<X <X className={`h-4 w-4 ${groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"}`} />
className={`h-4 w-4 ${
groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"
}`}
/>
</Tooltip> </Tooltip>
) : groupLength === 1 ? ( ) : groupLength === 1 ? (
<Tooltip tooltipContent="Cannot have an empty group."> <Tooltip tooltipContent="Cannot have an empty group.">
<X <X className={`h-4 w-4 ${groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"}`} />
className={`h-4 w-4 ${
groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"
}`}
/>
</Tooltip> </Tooltip>
) : ( ) : (
<X <X className={`h-4 w-4 ${groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"}`} />
className={`h-4 w-4 ${
groupLength < 1 ? "text-custom-sidebar-text-400" : "text-red-500"
}`}
/>
)} )}
</button> </button>
</div> </div>

View File

@ -7,7 +7,7 @@ import useSWR from "swr";
// react-hook-form // react-hook-form
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// hooks // hooks
import useProjectMembers from "hooks/use-project-members"; import useProjectMembers from "hooks/use-project-members";
// components // components
@ -20,7 +20,7 @@ import { checkIfArraysHaveSameElements } from "helpers/array.helper";
import { getStatesList } from "helpers/state.helper"; import { getStatesList } from "helpers/state.helper";
// types // types
import { IQuery, IView } from "types"; import { IQuery, IView } from "types";
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// fetch-keys // fetch-keys
import { PROJECT_ISSUE_LABELS, STATES_LIST } from "constants/fetch-keys"; import { PROJECT_ISSUE_LABELS, STATES_LIST } from "constants/fetch-keys";
@ -37,13 +37,7 @@ const defaultValues: Partial<IView> = {
description: "", description: "",
}; };
export const ViewForm: React.FC<Props> = ({ export const ViewForm: React.FC<Props> = ({ handleFormSubmit, handleClose, status, data, preLoadedData }) => {
handleFormSubmit,
handleClose,
status,
data,
preLoadedData,
}) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
@ -60,9 +54,7 @@ export const ViewForm: React.FC<Props> = ({
const filters = watch("query"); const filters = watch("query");
const { data: stateGroups } = useSWR( const { data: stateGroups } = useSWR(
workspaceSlug && projectId && (filters?.state ?? []).length > 0 workspaceSlug && projectId && (filters?.state ?? []).length > 0 ? STATES_LIST(projectId as string) : null,
? STATES_LIST(projectId as string)
: null,
workspaceSlug && (filters?.state ?? []).length > 0 workspaceSlug && (filters?.state ?? []).length > 0
? () => stateService.getStates(workspaceSlug as string, projectId as string) ? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null : null
@ -117,9 +109,7 @@ export const ViewForm: React.FC<Props> = ({
return ( return (
<form onSubmit={handleSubmit(handleCreateUpdateView)}> <form onSubmit={handleSubmit(handleCreateUpdateView)}>
<div className="space-y-5"> <div className="space-y-5">
<h3 className="text-lg font-medium leading-6 text-custom-text-100"> <h3 className="text-lg font-medium leading-6 text-custom-text-100">{status ? "Update" : "Create"} View</h3>
{status ? "Update" : "Create"} View
</h3>
<div className="space-y-3"> <div className="space-y-3">
<div> <div>
<Input <Input
@ -157,10 +147,7 @@ export const ViewForm: React.FC<Props> = ({
const key = option.key as keyof typeof filters; const key = option.key as keyof typeof filters;
if (key === "start_date" || key === "target_date") { if (key === "start_date" || key === "target_date") {
const valueExists = checkIfArraysHaveSameElements( const valueExists = checkIfArraysHaveSameElements(filters?.[key] ?? [], option.value);
filters?.[key] ?? [],
option.value
);
setValue("query", { setValue("query", {
...filters, ...filters,

View File

@ -5,9 +5,9 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
import projectService from "services/project.service"; import projectService from "services/project.service";
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// components // components
import { DateFilterModal } from "components/core"; import { DateFilterModal } from "components/core";
// ui // ui
@ -32,12 +32,7 @@ type Props = {
height?: "sm" | "md" | "rg" | "lg"; height?: "sm" | "md" | "rg" | "lg";
}; };
export const SelectFilters: React.FC<Props> = ({ export const SelectFilters: React.FC<Props> = ({ filters, onSelect, direction = "right", height = "md" }) => {
filters,
onSelect,
direction = "right",
height = "md",
}) => {
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
const [dateFilterType, setDateFilterType] = useState<{ const [dateFilterType, setDateFilterType] = useState<{
title: string; title: string;
@ -52,9 +47,7 @@ export const SelectFilters: React.FC<Props> = ({
const { data: states } = useSWR( const { data: states } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
); );
const statesList = getStatesList(states); const statesList = getStatesList(states);

View File

@ -11,7 +11,7 @@ import { mutate } from "swr";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// fetch keys // fetch keys
import { ISSUE_DETAILS } from "constants/fetch-keys"; import { ISSUE_DETAILS } from "constants/fetch-keys";
@ -75,12 +75,7 @@ export const CreateUpdateLinkForm: React.FC<Props> = (props) => {
if (!data) if (!data)
await issuesService await issuesService
.createIssueLink( .createIssueLink(workspaceSlug.toString(), projectId.toString(), issueId.toString(), payload)
workspaceSlug.toString(),
projectId.toString(),
issueId.toString(),
payload
)
.then(() => { .then(() => {
onSuccess(); onSuccess();
mutate(ISSUE_DETAILS(issueId.toString())); mutate(ISSUE_DETAILS(issueId.toString()));
@ -110,20 +105,10 @@ export const CreateUpdateLinkForm: React.FC<Props> = (props) => {
: l : l
); );
mutate( mutate(ISSUE_DETAILS(issueId.toString()), (prevData) => ({ ...prevData, issue_link: updatedLinks }), false);
ISSUE_DETAILS(issueId.toString()),
(prevData) => ({ ...prevData, issue_link: updatedLinks }),
false
);
await issuesService await issuesService
.updateIssueLink( .updateIssueLink(workspaceSlug.toString(), projectId.toString(), issueId.toString(), data!.id, payload)
workspaceSlug.toString(),
projectId.toString(),
issueId.toString(),
data!.id,
payload
)
.then(() => { .then(() => {
onSuccess(); onSuccess();
mutate(ISSUE_DETAILS(issueId.toString())); mutate(ISSUE_DETAILS(issueId.toString()));
@ -172,13 +157,7 @@ export const CreateUpdateLinkForm: React.FC<Props> = (props) => {
loading={isSubmitting} loading={isSubmitting}
className="w-full !py-2 text-custom-text-300 !text-base flex items-center justify-center" className="w-full !py-2 text-custom-text-300 !text-base flex items-center justify-center"
> >
{data {data ? (isSubmitting ? "Updating Link..." : "Update Link") : isSubmitting ? "Adding Link..." : "Add Link"}
? isSubmitting
? "Updating Link..."
: "Update Link"
: isSubmitting
? "Adding Link..."
: "Add Link"}
</PrimaryButton> </PrimaryButton>
</div> </div>
</form> </form>

View File

@ -11,7 +11,7 @@ import useSWR, { mutate } from "swr";
import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys"; import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
@ -45,12 +45,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
const { data: issueActivities, mutate: mutateIssueActivity } = useSWR( const { data: issueActivities, mutate: mutateIssueActivity } = useSWR(
workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY(issueId.toString()) : null, workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY(issueId.toString()) : null,
workspaceSlug && projectId && issueId workspaceSlug && projectId && issueId
? () => ? () => issuesService.getIssueActivities(workspaceSlug.toString(), projectId.toString(), issueId.toString())
issuesService.getIssueActivities(
workspaceSlug.toString(),
projectId.toString(),
issueId.toString()
)
: null : null
); );
@ -58,14 +53,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
if (!workspaceSlug || !projectId || !issueId) return; if (!workspaceSlug || !projectId || !issueId) return;
await issuesService await issuesService
.patchIssueComment( .patchIssueComment(workspaceSlug as string, projectId as string, issueId as string, comment.id, comment, user)
workspaceSlug as string,
projectId as string,
issueId as string,
comment.id,
comment,
user
)
.then(() => mutateIssueActivity()); .then(() => mutateIssueActivity());
}; };
@ -75,13 +63,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false); mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
await issuesService await issuesService
.deleteIssueComment( .deleteIssueComment(workspaceSlug as string, projectId as string, issueId as string, commentId, user)
workspaceSlug as string,
projectId as string,
issueId as string,
commentId,
user
)
.then(() => mutateIssueActivity()); .then(() => mutateIssueActivity());
}; };
@ -89,13 +71,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
if (!workspaceSlug || !issueDetails) return; if (!workspaceSlug || !issueDetails) return;
await issuesService await issuesService
.createIssueComment( .createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData, user)
workspaceSlug.toString(),
issueDetails.project,
issueDetails.id,
formData,
user
)
.then(() => { .then(() => {
mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id)); mutate(PROJECT_ISSUES_ACTIVITY(issueDetails.id));
}) })
@ -118,11 +94,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
<ul role="list" className="-mb-4"> <ul role="list" className="-mb-4">
{issueActivities?.map((activityItem, index) => { {issueActivities?.map((activityItem, index) => {
// determines what type of action is performed // determines what type of action is performed
const message = activityItem.field ? ( const message = activityItem.field ? <ActivityMessage activity={activityItem} /> : "created the issue.";
<ActivityMessage activity={activityItem} />
) : (
"created the issue."
);
if ("field" in activityItem && activityItem.field !== "updated_by") { if ("field" in activityItem && activityItem.field !== "updated_by") {
return ( 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"> <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.field ? (
activityItem.new_value === "restore" ? ( activityItem.new_value === "restore" ? (
<Icon <Icon iconName="history" className="text-sm text-custom-text-200" />
iconName="history"
className="text-sm text-custom-text-200"
/>
) : ( ) : (
<ActivityIcon activity={activityItem} /> <ActivityIcon activity={activityItem} />
) )
) : activityItem.actor_detail.avatar && ) : activityItem.actor_detail.avatar && activityItem.actor_detail.avatar !== "" ? (
activityItem.actor_detail.avatar !== "" ? (
<img <img
src={activityItem.actor_detail.avatar} src={activityItem.actor_detail.avatar}
alt={activityItem.actor_detail.display_name} alt={activityItem.actor_detail.display_name}
@ -172,13 +140,10 @@ export const IssueActivity: React.FC<Props> = (props) => {
</div> </div>
<div className="min-w-0 flex-1 py-3"> <div className="min-w-0 flex-1 py-3">
<div className="text-xs text-custom-text-200 break-words"> <div className="text-xs text-custom-text-200 break-words">
{activityItem.field === "archived_at" && {activityItem.field === "archived_at" && activityItem.new_value !== "restore" ? (
activityItem.new_value !== "restore" ? (
<span className="text-gray font-medium">Plane</span> <span className="text-gray font-medium">Plane</span>
) : activityItem.actor_detail.is_bot ? ( ) : activityItem.actor_detail.is_bot ? (
<span className="text-gray font-medium"> <span className="text-gray font-medium">{activityItem.actor_detail.first_name} Bot</span>
{activityItem.actor_detail.first_name} Bot
</span>
) : ( ) : (
<button <button
type="button" type="button"
@ -190,10 +155,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
: activityItem.actor_detail.display_name} : activityItem.actor_detail.display_name}
</button> </button>
)}{" "} )}{" "}
{message}{" "} {message} <span className="whitespace-nowrap">{timeAgo(activityItem.created_at)}</span>
<span className="whitespace-nowrap">
{timeAgo(activityItem.created_at)}
</span>
</div> </div>
</div> </div>
</div> </div>
@ -217,10 +179,7 @@ export const IssueActivity: React.FC<Props> = (props) => {
<AddComment <AddComment
onSubmit={handleAddComment} onSubmit={handleAddComment}
disabled={ disabled={
!allowed || !allowed || !issueDetails || issueDetails.state === "closed" || issueDetails.state === "archived"
!issueDetails ||
issueDetails.state === "closed" ||
issueDetails.state === "archived"
} }
/> />
</div> </div>

View File

@ -9,7 +9,7 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// react dropzone // react dropzone
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
@ -61,12 +61,7 @@ export const IssueAttachments: React.FC<Props> = (props) => {
setIsLoading(true); setIsLoading(true);
issuesService issuesService
.uploadIssueAttachment( .uploadIssueAttachment(workspaceSlug as string, projectId as string, issueId as string, formData)
workspaceSlug as string,
projectId as string,
issueId as string,
formData
)
.then((res) => { .then((res) => {
mutate<IIssueAttachment[]>( mutate<IIssueAttachment[]>(
ISSUE_ATTACHMENTS(issueId as string), ISSUE_ATTACHMENTS(issueId as string),
@ -109,12 +104,7 @@ export const IssueAttachments: React.FC<Props> = (props) => {
const { data: attachments } = useSWR<IIssueAttachment[]>( const { data: attachments } = useSWR<IIssueAttachment[]>(
workspaceSlug && projectId && issueId ? ISSUE_ATTACHMENTS(issueId as string) : null, workspaceSlug && projectId && issueId ? ISSUE_ATTACHMENTS(issueId as string) : null,
workspaceSlug && projectId && issueId workspaceSlug && projectId && issueId
? () => ? () => issuesService.getIssueAttachment(workspaceSlug.toString(), projectId.toString(), issueId.toString())
issuesService.getIssueAttachment(
workspaceSlug.toString(),
projectId.toString(),
issueId.toString()
)
: null : null
); );

View File

@ -9,7 +9,7 @@ import { useRouter } from "next/router";
import { mutate } from "swr"; import { mutate } from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// icons // icons
// import { LinkIcon, PlusIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline"; // import { LinkIcon, PlusIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline";

View File

@ -11,7 +11,7 @@ import { mutate } from "swr";
import { Control, Controller, useWatch } from "react-hook-form"; import { Control, Controller, useWatch } from "react-hook-form";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
@ -105,10 +105,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
control={control} control={control}
name="state" name="state"
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<StateSelect <StateSelect value={value} onChange={(val: string) => submitChanges({ state: val })} />
value={value}
onChange={(val: string) => submitChanges({ state: val })}
/>
)} )}
/> />
</div> </div>
@ -117,13 +114,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
<div className="mb-[6px]"> <div className="mb-[6px]">
<div className="border border-custom-border-200 rounded-[4px] p-2 flex justify-between items-center"> <div className="border border-custom-border-200 rounded-[4px] p-2 flex justify-between items-center">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<svg <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path <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" 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" fill="#A3A3A3"
@ -137,10 +128,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
control={control} control={control}
name="priority" name="priority"
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<PrioritySelect <PrioritySelect value={value} onChange={(val) => submitChanges({ priority: val })} />
value={value}
onChange={(val) => submitChanges({ priority: val })}
/>
)} )}
/> />
</div> </div>
@ -157,10 +145,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
control={control} control={control}
name="assignees_list" name="assignees_list"
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<AssigneeSelect <AssigneeSelect value={value} onChange={(val: string) => submitChanges({ assignees_list: [val] })} />
value={value}
onChange={(val: string) => submitChanges({ assignees_list: [val] })}
/>
)} )}
/> />
</div> </div>
@ -180,10 +165,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
control={control} control={control}
name="estimate_point" name="estimate_point"
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<EstimateSelect <EstimateSelect value={value} onChange={(val) => submitChanges({ estimate_point: val })} />
value={value}
onChange={(val) => submitChanges({ estimate_point: val })}
/>
)} )}
/> />
</div> </div>
@ -201,10 +183,7 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
control={control} control={control}
name="parent" name="parent"
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<ParentSelect <ParentSelect value={value} onChange={(val) => submitChanges({ parent: val })} />
value={value}
onChange={(val) => submitChanges({ parent: val })}
/>
)} )}
/> />
</div> </div>
@ -224,22 +203,16 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
if (!user || !workspaceSlug || !projectId || !issueId) return; if (!user || !workspaceSlug || !projectId || !issueId) return;
issuesService issuesService
.createIssueRelation( .createIssueRelation(workspaceSlug as string, projectId as string, issueId as string, user, {
workspaceSlug as string, related_list: [
projectId as string, ...val.map((issue: any) => ({
issueId as string, issue: issue.blocker_issue_detail.id,
user, relation_type: "blocked_by" as const,
{ related_issue: issueId as string,
related_list: [ related_issue_detail: issue.blocker_issue_detail,
...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) => { .then((response) => {
handleMutation({ handleMutation({
issue_relations: [ issue_relations: [
@ -315,22 +288,16 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
if (!user || !workspaceSlug || !projectId || !issueId) return; if (!user || !workspaceSlug || !projectId || !issueId) return;
issuesService issuesService
.createIssueRelation( .createIssueRelation(workspaceSlug as string, projectId as string, issueId as string, user, {
workspaceSlug as string, related_list: [
projectId as string, ...val.map((issue: any) => ({
issueId as string, issue: issue.blocked_issue_detail.id,
user, relation_type: "blocked_by" as const,
{ related_issue: issueId as string,
related_list: [ related_issue_detail: issue.blocked_issue_detail,
...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) => { .then((response) => {
handleMutation({ handleMutation({
related_issues: [ related_issues: [
@ -429,13 +396,8 @@ export const IssuePropertiesDetail: React.FC<Props> = (props) => {
onClick={() => setIsViewAllOpen((prev) => !prev)} onClick={() => setIsViewAllOpen((prev) => !prev)}
className="w-full flex justify-center items-center gap-1 !py-2" className="w-full flex justify-center items-center gap-1 !py-2"
> >
<span className="text-base text-custom-primary-100"> <span className="text-base text-custom-primary-100">{isViewAllOpen ? "View less" : "View all"}</span>
{isViewAllOpen ? "View less" : "View all"} <ChevronDown size={16} className={`ml-1 text-custom-primary-100 ${isViewAllOpen ? "-rotate-180" : ""}`} />
</span>
<ChevronDown
size={16}
className={`ml-1 text-custom-primary-100 ${isViewAllOpen ? "-rotate-180" : ""}`}
/>
</SecondaryButton> </SecondaryButton>
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// fetch key // fetch key
import { ISSUE_DETAILS } from "constants/fetch-keys"; import { ISSUE_DETAILS } from "constants/fetch-keys";
@ -37,8 +37,7 @@ export const ParentSelect: React.FC<Props> = (props) => {
const { data: issueDetails } = useSWR( const { data: issueDetails } = useSWR(
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId.toString()) : null, workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId.toString()) : null,
workspaceSlug && projectId && issueId workspaceSlug && projectId && issueId
? () => ? () => issuesService.retrieve(workspaceSlug.toString(), projectId.toString(), issueId.toString())
issuesService.retrieve(workspaceSlug.toString(), projectId.toString(), issueId.toString())
: null : null
); );

View File

@ -11,7 +11,7 @@ import useSWR from "swr";
import { ChevronDown } from "lucide-react"; import { ChevronDown } from "lucide-react";
// services // services
import stateService from "services/state.service"; import stateService from "services/project_state.service";
// fetch key // fetch key
import { STATES_LIST } from "constants/fetch-keys"; import { STATES_LIST } from "constants/fetch-keys";
@ -39,9 +39,7 @@ export const StateSelect: React.FC<Props> = (props) => {
const { data: stateGroups } = useSWR( const { data: stateGroups } = useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
workspaceSlug && projectId workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null
? () => stateService.getStates(workspaceSlug as string, projectId as string)
: null
); );
const states = getStatesList(stateGroups); const states = getStatesList(stateGroups);

View File

@ -11,7 +11,7 @@ import useSWR, { mutate } from "swr";
import { X } from "lucide-react"; import { X } from "lucide-react";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// fetch key // fetch key
import { SUB_ISSUES } from "constants/fetch-keys"; import { SUB_ISSUES } from "constants/fetch-keys";
@ -41,8 +41,7 @@ export const SubIssueList: React.FC<Props> = (props) => {
const { data: subIssuesResponse } = useSWR( const { data: subIssuesResponse } = useSWR(
workspaceSlug && issueDetails ? SUB_ISSUES(issueDetails.id) : null, workspaceSlug && issueDetails ? SUB_ISSUES(issueDetails.id) : null,
workspaceSlug && issueDetails workspaceSlug && issueDetails
? () => ? () => issuesService.subIssues(workspaceSlug as string, issueDetails.project, issueDetails.id)
issuesService.subIssues(workspaceSlug as string, issueDetails.project, issueDetails.id)
: null : null
); );

View File

@ -1,7 +1,7 @@
import useSWR from "swr"; import useSWR from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
// fetch-keys // fetch-keys
@ -28,8 +28,7 @@ const useGanttChartIssues = (workspaceSlug: string | undefined, projectId: strin
const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR( const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR(
workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId, params) : null, workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId, params) : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => ? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
: null : null
); );

View File

@ -1,7 +1,7 @@
import useSWR from "swr"; import useSWR from "swr";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
// hooks // hooks
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
// fetch-keys // fetch-keys

View File

@ -7,7 +7,7 @@ import useSWR from "swr";
// contexts // contexts
import { issueViewContext } from "contexts/issue-view.context"; import { issueViewContext } from "contexts/issue-view.context";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issue.service";
import cyclesService from "services/cycles.service"; import cyclesService from "services/cycles.service";
import modulesService from "services/modules.service"; import modulesService from "services/modules.service";
// types // types
@ -45,19 +45,14 @@ const useCalendarIssuesView = () => {
}; };
const { data: projectCalendarIssues, mutate: mutateProjectCalendarIssues } = useSWR( const { data: projectCalendarIssues, mutate: mutateProjectCalendarIssues } = useSWR(
workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params) : null,
workspaceSlug && projectId workspaceSlug && projectId
? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params) ? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
: null,
workspaceSlug && projectId
? () =>
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
: null : null
); );
const { data: cycleCalendarIssues, mutate: mutateCycleCalendarIssues } = useSWR( const { data: cycleCalendarIssues, mutate: mutateCycleCalendarIssues } = useSWR(
workspaceSlug && projectId && cycleId workspaceSlug && projectId && cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params) : null,
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params)
: null,
workspaceSlug && projectId && cycleId workspaceSlug && projectId && cycleId
? () => ? () =>
cyclesService.getCycleIssuesWithParams( cyclesService.getCycleIssuesWithParams(
@ -70,9 +65,7 @@ const useCalendarIssuesView = () => {
); );
const { data: moduleCalendarIssues, mutate: mutateModuleCalendarIssues } = useSWR( const { data: moduleCalendarIssues, mutate: mutateModuleCalendarIssues } = useSWR(
workspaceSlug && projectId && moduleId workspaceSlug && projectId && moduleId ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) : null,
? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params)
: null,
workspaceSlug && projectId && moduleId workspaceSlug && projectId && moduleId
? () => ? () =>
modulesService.getModuleIssuesWithParams( modulesService.getModuleIssuesWithParams(
@ -87,8 +80,7 @@ const useCalendarIssuesView = () => {
const { data: viewCalendarIssues, mutate: mutateViewCalendarIssues } = useSWR( const { data: viewCalendarIssues, mutate: mutateViewCalendarIssues } = useSWR(
workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null, workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null,
workspaceSlug && projectId && viewId && params workspaceSlug && projectId && viewId && params
? () => ? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
: null : null
); );

View File

@ -4,7 +4,7 @@ import useSWR from "swr";
import { COMMENT_REACTION_LIST } from "constants/fetch-keys"; import { COMMENT_REACTION_LIST } from "constants/fetch-keys";
// services // services
import reactionService from "services/reaction.service"; import reactionService from "services/issue_reaction.service";
// helpers // helpers
import { groupReactions } from "helpers/emoji.helper"; import { groupReactions } from "helpers/emoji.helper";
@ -69,8 +69,7 @@ const useCommentReaction = (
if (!workspaceSlug || !projectId || !commendId) return; if (!workspaceSlug || !projectId || !commendId) return;
mutateCommentReactions( mutateCommentReactions(
(prevData: any) => (prevData: any) => prevData?.filter((r: any) => r.actor !== user?.user?.id || r.reaction !== reaction) || [],
prevData?.filter((r: any) => r.actor !== user?.user?.id || r.reaction !== reaction) || [],
false false
); );

View File

@ -3,7 +3,7 @@ import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// services // services
import estimatesService from "services/estimates.service"; import estimatesService from "services/project_estimates.service";
// hooks // hooks
import useProjectDetails from "hooks/use-project-details"; import useProjectDetails from "hooks/use-project-details";
// helpers // helpers

Some files were not shown because too many files have changed in this diff Show More