fix: bug and auth fixes (#1224)

* fix: sign in and invitation page fixes

* fix: project and workspace services track event fix

* fix: user onboarding complete track event fix

* fix: issue track event fix

* fix: partial property , issue comment and mark as done issue track event fix

* fix: bulk delete , move to cycle or module and issue label track event fix

* fix: state , cycle and module track event fix

* fix: pages and block track event fix

* fix: integration , estimate , importer , analytics and gpt track event fix

* fix: view track event fix

* fix: build fix

* fix: build fix
This commit is contained in:
Anmol Singh Bhatia 2023-06-06 21:36:00 +05:30 committed by GitHub
parent c127353281
commit 6f2a38ad66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
117 changed files with 1319 additions and 494 deletions

View File

@ -18,7 +18,7 @@ import { Loader, PrimaryButton } from "components/ui";
// helpers
import { convertResponseToBarGraphData } from "helpers/analytics.helper";
// types
import { IAnalyticsParams, IAnalyticsResponse } from "types";
import { IAnalyticsParams, IAnalyticsResponse, ICurrentUserResponse } from "types";
// fetch-keys
import { ANALYTICS } from "constants/fetch-keys";
@ -29,6 +29,7 @@ type Props = {
control: Control<IAnalyticsParams, any>;
setValue: UseFormSetValue<IAnalyticsParams>;
fullScreen: boolean;
user: ICurrentUserResponse | undefined;
};
export const CustomAnalytics: React.FC<Props> = ({
@ -38,6 +39,7 @@ export const CustomAnalytics: React.FC<Props> = ({
control,
setValue,
fullScreen,
user,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -124,6 +126,7 @@ export const CustomAnalytics: React.FC<Props> = ({
params={params}
fullScreen={fullScreen}
isProjectLevel={isProjectLevel}
user={user}
/>
</div>
);

View File

@ -27,6 +27,7 @@ import { renderShortDate } from "helpers/date-time.helper";
import {
IAnalyticsParams,
IAnalyticsResponse,
ICurrentUserResponse,
IExportAnalyticsFormData,
IProject,
IWorkspace,
@ -41,6 +42,7 @@ type Props = {
params: IAnalyticsParams;
fullScreen: boolean;
isProjectLevel: boolean;
user: ICurrentUserResponse | undefined;
};
export const AnalyticsSidebar: React.FC<Props> = ({
@ -48,6 +50,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
params,
fullScreen,
isProjectLevel = false,
user,
}) => {
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
@ -138,7 +141,8 @@ export const AnalyticsSidebar: React.FC<Props> = ({
? "MODULE_ANALYTICS_EXPORT"
: projectId
? "PROJECT_ANALYTICS_EXPORT"
: "WORKSPACE_ANALYTICS_EXPORT"
: "WORKSPACE_ANALYTICS_EXPORT",
user
);
};

View File

@ -26,6 +26,7 @@ import {
import { IAnalyticsParams, IWorkspace } from "types";
// fetch-keys
import { ANALYTICS, CYCLE_DETAILS, MODULE_DETAILS, PROJECT_DETAILS } from "constants/fetch-keys";
import useUserAuth from "hooks/use-user-auth";
type Props = {
isOpen: boolean;
@ -47,6 +48,8 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
const { user } = useUserAuth();
const { control, watch, setValue } = useForm<IAnalyticsParams>({ defaultValues });
const params: IAnalyticsParams = {
@ -136,7 +139,8 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
trackEventServices.trackAnalyticsEvent(
eventPayload,
cycleId ? `CYCLE_${eventType}` : moduleId ? `MODULE_${eventType}` : `PROJECT_${eventType}`
cycleId ? `CYCLE_${eventType}` : moduleId ? `MODULE_${eventType}` : `PROJECT_${eventType}`,
user
);
};
@ -210,6 +214,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
control={control}
setValue={setValue}
fullScreen={fullScreen}
user={user}
/>
</Tab.Panel>
</Tab.Panels>

View File

@ -7,7 +7,7 @@ import { Command } from "cmdk";
// services
import issuesService from "services/issues.service";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
// constants
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY, PROJECT_MEMBERS } from "constants/fetch-keys";
// icons
@ -18,9 +18,10 @@ import { Avatar } from "components/ui";
type Props = {
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
issue: IIssue;
user: ICurrentUserResponse | undefined;
};
export const ChangeIssueAssignee: React.FC<Props> = ({ setIsPaletteOpen, issue }) => {
export const ChangeIssueAssignee: React.FC<Props> = ({ setIsPaletteOpen, issue, user }) => {
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
@ -71,7 +72,7 @@ export const ChangeIssueAssignee: React.FC<Props> = ({ setIsPaletteOpen, issue }
const payload = { ...formData };
await issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload)
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload, user)
.then(() => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
})

View File

@ -7,7 +7,7 @@ import { Command } from "cmdk";
// services
import issuesService from "services/issues.service";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
// constants
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
import { PRIORITIES } from "constants/project";
@ -17,9 +17,10 @@ import { CheckIcon, getPriorityIcon } from "components/icons";
type Props = {
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
issue: IIssue;
user: ICurrentUserResponse;
};
export const ChangeIssuePriority: React.FC<Props> = ({ setIsPaletteOpen, issue }) => {
export const ChangeIssuePriority: React.FC<Props> = ({ setIsPaletteOpen, issue, user }) => {
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
@ -42,7 +43,7 @@ export const ChangeIssuePriority: React.FC<Props> = ({ setIsPaletteOpen, issue }
const payload = { ...formData };
await issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload)
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload, user)
.then(() => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
})

View File

@ -12,7 +12,7 @@ import { getStatesList } from "helpers/state.helper";
import issuesService from "services/issues.service";
import stateService from "services/state.service";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
// fetch keys
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY, STATES_LIST } from "constants/fetch-keys";
// icons
@ -21,9 +21,10 @@ import { CheckIcon, getStateGroupIcon } from "components/icons";
type Props = {
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
issue: IIssue;
user: ICurrentUserResponse | undefined;
};
export const ChangeIssueState: React.FC<Props> = ({ setIsPaletteOpen, issue }) => {
export const ChangeIssueState: React.FC<Props> = ({ setIsPaletteOpen, issue, user }) => {
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
@ -53,7 +54,7 @@ export const ChangeIssueState: React.FC<Props> = ({ setIsPaletteOpen, issue }) =
const payload = { ...formData };
await issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload)
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload, user)
.then(() => {
mutateIssueDetails();
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));

View File

@ -136,7 +136,7 @@ export const CommandPalette: React.FC = () => {
const payload = { ...formData };
await issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload)
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload, user)
.then(() => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
mutate(ISSUE_DETAILS(issueId as string));
@ -330,25 +330,33 @@ export const CommandPalette: React.FC = () => {
<>
<ShortcutsModal isOpen={isShortcutsModalOpen} setIsOpen={setIsShortcutsModalOpen} />
{workspaceSlug && (
<CreateProjectModal isOpen={isProjectModalOpen} setIsOpen={setIsProjectModalOpen} />
<CreateProjectModal
isOpen={isProjectModalOpen}
setIsOpen={setIsProjectModalOpen}
user={user}
/>
)}
{projectId && (
<>
<CreateUpdateCycleModal
isOpen={isCreateCycleModalOpen}
handleClose={() => setIsCreateCycleModalOpen(false)}
user={user}
/>
<CreateUpdateModuleModal
isOpen={isCreateModuleModalOpen}
setIsOpen={setIsCreateModuleModalOpen}
user={user}
/>
<CreateUpdateViewModal
handleClose={() => setIsCreateViewModalOpen(false)}
isOpen={isCreateViewModalOpen}
user={user}
/>
<CreateUpdatePageModal
isOpen={isCreateUpdatePageModalOpen}
handleClose={() => setIsCreateUpdatePageModalOpen(false)}
user={user}
/>
</>
)}
@ -357,6 +365,7 @@ export const CommandPalette: React.FC = () => {
handleClose={() => setDeleteIssueModal(false)}
isOpen={deleteIssueModal}
data={issueDetails}
user={user}
/>
)}
@ -367,6 +376,7 @@ export const CommandPalette: React.FC = () => {
<BulkDeleteIssuesModal
isOpen={isBulkDeleteIssuesModalOpen}
setIsOpen={setIsBulkDeleteIssuesModalOpen}
user={user}
/>
<Transition.Root
show={isPaletteOpen}
@ -854,6 +864,7 @@ export const CommandPalette: React.FC = () => {
<ChangeIssueState
issue={issueDetails}
setIsPaletteOpen={setIsPaletteOpen}
user={user}
/>
</>
)}
@ -861,12 +872,14 @@ export const CommandPalette: React.FC = () => {
<ChangeIssuePriority
issue={issueDetails}
setIsPaletteOpen={setIsPaletteOpen}
user={user}
/>
)}
{page === "change-issue-assignee" && issueDetails && (
<ChangeIssueAssignee
issue={issueDetails}
setIsPaletteOpen={setIsPaletteOpen}
user={user}
/>
)}
{page === "change-interface-theme" && (

View File

@ -5,7 +5,7 @@ import { SingleBoard } from "components/core/board-view/single-board";
// helpers
import { addSpaceIfCamelCase } from "helpers/string.helper";
// types
import { IIssue, IState, UserAuth } from "types";
import { ICurrentUserResponse, IIssue, IState, UserAuth } from "types";
import { getStateGroupIcon } from "components/icons";
type Props = {
@ -19,6 +19,7 @@ type Props = {
handleTrashBox: (isDragging: boolean) => void;
removeIssue: ((bridgeId: string, issueId: string) => void) | null;
isCompleted?: boolean;
user: ICurrentUserResponse | undefined;
userAuth: UserAuth;
};
@ -33,6 +34,7 @@ export const AllBoards: React.FC<Props> = ({
handleTrashBox,
removeIssue,
isCompleted = false,
user,
userAuth,
}) => {
const {
@ -65,6 +67,7 @@ export const AllBoards: React.FC<Props> = ({
handleTrashBox={handleTrashBox}
removeIssue={removeIssue}
isCompleted={isCompleted}
user={user}
userAuth={userAuth}
/>
);

View File

@ -17,7 +17,7 @@ import { PlusIcon } from "@heroicons/react/24/outline";
// helpers
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
// types
import { IIssue, IState, UserAuth } from "types";
import { ICurrentUserResponse, IIssue, IState, UserAuth } from "types";
type Props = {
type?: "issue" | "cycle" | "module";
@ -31,6 +31,7 @@ type Props = {
handleTrashBox: (isDragging: boolean) => void;
removeIssue: ((bridgeId: string, issueId: string) => void) | null;
isCompleted?: boolean;
user: ICurrentUserResponse | undefined;
userAuth: UserAuth;
};
@ -46,6 +47,7 @@ export const SingleBoard: React.FC<Props> = ({
handleTrashBox,
removeIssue,
isCompleted = false,
user,
userAuth,
}) => {
// collapse/expand
@ -129,6 +131,7 @@ export const SingleBoard: React.FC<Props> = ({
removeIssue(issue.bridge_id, issue.id);
}}
isCompleted={isCompleted}
user={user}
userAuth={userAuth}
/>
)}

View File

@ -44,7 +44,7 @@ import { LayerDiagonalIcon } from "components/icons";
import { handleIssuesMutation } from "constants/issue";
import { copyTextToClipboard, truncateText } from "helpers/string.helper";
// types
import { IIssue, Properties, TIssueGroupByOptions, UserAuth } from "types";
import { ICurrentUserResponse, IIssue, Properties, TIssueGroupByOptions, UserAuth } from "types";
// fetch-keys
import {
CYCLE_DETAILS,
@ -69,6 +69,7 @@ type Props = {
handleDeleteIssue: (issue: IIssue) => void;
handleTrashBox: (isDragging: boolean) => void;
isCompleted?: boolean;
user: ICurrentUserResponse | undefined;
userAuth: UserAuth;
};
@ -87,6 +88,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
handleDeleteIssue,
handleTrashBox,
isCompleted = false,
user,
userAuth,
}) => {
// context menu
@ -170,7 +172,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
}
issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData)
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData, user)
.then(() => {
if (cycleId) {
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
@ -342,6 +344,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
user={user}
selfPositioned
/>
)}
@ -350,6 +353,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
user={user}
selfPositioned
/>
)}
@ -357,6 +361,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -384,6 +389,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
tooltipPosition="left"
user={user}
selfPositioned
/>
)}
@ -392,6 +398,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
user={user}
selfPositioned
/>
)}

View File

@ -18,7 +18,7 @@ import { DangerButton, SecondaryButton } from "components/ui";
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import { LayerDiagonalIcon } from "components/icons";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
// fetch keys
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
@ -29,9 +29,10 @@ type FormInput = {
type Props = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
user: ICurrentUserResponse | undefined;
};
export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user }) => {
const [query, setQuery] = useState("");
const router = useRouter();
@ -91,9 +92,14 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
if (workspaceSlug && projectId) {
await issuesServices
.bulkDeleteIssues(workspaceSlug as string, projectId as string, {
issue_ids: data.delete_issue_ids,
})
.bulkDeleteIssues(
workspaceSlug as string,
projectId as string,
{
issue_ids: data.delete_issue_ids,
},
user
)
.then((res) => {
setToastAlert({
title: "Success",

View File

@ -24,7 +24,7 @@ import {
formatDate,
} from "helpers/calendar.helper";
// types
import { ICalendarRange, IIssue, UserAuth } from "types";
import { ICalendarRange, ICurrentUserResponse, IIssue, UserAuth } from "types";
// fetch-keys
import {
CYCLE_ISSUES_WITH_PARAMS,
@ -38,6 +38,7 @@ type Props = {
handleDeleteIssue: (issue: IIssue) => void;
addIssueToDate: (date: string) => void;
isCompleted: boolean;
user: ICurrentUserResponse | undefined;
userAuth: UserAuth;
};
@ -46,6 +47,7 @@ export const CalendarView: React.FC<Props> = ({
handleDeleteIssue,
addIssueToDate,
isCompleted = false,
user,
userAuth,
}) => {
const [showWeekEnds, setShowWeekEnds] = useState(false);
@ -134,9 +136,15 @@ export const CalendarView: React.FC<Props> = ({
);
issuesService
.patchIssue(workspaceSlug as string, projectId as string, draggableId, {
target_date: destination?.droppableId,
})
.patchIssue(
workspaceSlug as string,
projectId as string,
draggableId,
{
target_date: destination?.droppableId,
},
user
)
.then(() => mutate(fetchKey));
};
@ -219,6 +227,7 @@ export const CalendarView: React.FC<Props> = ({
addIssueToDate={addIssueToDate}
isMonthlyView={isMonthlyView}
showWeekEnds={showWeekEnds}
user={user}
isNotAllowed={isNotAllowed}
/>
))}

View File

@ -10,7 +10,7 @@ import { PlusSmallIcon } from "@heroicons/react/24/outline";
// helper
import { formatDate } from "helpers/calendar.helper";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
type Props = {
handleEditIssue: (issue: IIssue) => void;
@ -23,6 +23,7 @@ type Props = {
addIssueToDate: (date: string) => void;
isMonthlyView: boolean;
showWeekEnds: boolean;
user: ICurrentUserResponse | undefined;
isNotAllowed: boolean;
};
@ -34,6 +35,7 @@ export const SingleCalendarDate: React.FC<Props> = ({
addIssueToDate,
isMonthlyView,
showWeekEnds,
user,
isNotAllowed,
}) => {
const [showAllIssues, setShowAllIssues] = useState(false);
@ -72,6 +74,7 @@ export const SingleCalendarDate: React.FC<Props> = ({
issue={issue}
handleEditIssue={handleEditIssue}
handleDeleteIssue={handleDeleteIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}

View File

@ -28,7 +28,7 @@ import { LayerDiagonalIcon } from "components/icons";
// helper
import { copyTextToClipboard, truncateText } from "helpers/string.helper";
// type
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
// fetch-keys
import {
CYCLE_ISSUES_WITH_PARAMS,
@ -44,6 +44,7 @@ type Props = {
provided: DraggableProvided;
snapshot: DraggableStateSnapshot;
issue: IIssue;
user: ICurrentUserResponse | undefined;
isNotAllowed: boolean;
};
@ -54,6 +55,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
provided,
snapshot,
issue,
user,
isNotAllowed,
}) => {
const router = useRouter();
@ -95,7 +97,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
);
issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, formData)
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, formData, user)
.then(() => {
mutate(fetchKey);
})
@ -183,6 +185,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
position="left"
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -192,6 +195,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
partialUpdateIssue={partialUpdateIssue}
position="left"
isNotAllowed={isNotAllowed}
user={user}
/>
)}
@ -199,6 +203,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -227,6 +232,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
position="left"
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -235,6 +241,7 @@ export const SingleCalendarIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
position="left"
user={user}
isNotAllowed={isNotAllowed}
/>
)}

View File

@ -10,6 +10,7 @@ import aiService from "services/ai.service";
import trackEventServices from "services/track-event.service";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// ui
import { Input, PrimaryButton, SecondaryButton } from "components/ui";
@ -60,6 +61,8 @@ export const GptAssistantModal: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug } = router.query;
const { user } = useUserAuth();
const editorRef = useRef<any>(null);
const { setToastAlert } = useToast();
@ -97,10 +100,15 @@ export const GptAssistantModal: React.FC<Props> = ({
}
await aiService
.createGptTask(workspaceSlug as string, projectId as string, {
prompt: content && content !== "" ? content : htmlContent ?? "",
task: formData.task,
})
.createGptTask(
workspaceSlug as string,
projectId as string,
{
prompt: content && content !== "" ? content : htmlContent ?? "",
task: formData.task,
},
user
)
.then((res) => {
setResponse(res.response_html);
setFocus("task");
@ -190,10 +198,15 @@ export const GptAssistantModal: React.FC<Props> = ({
if (block)
trackEventServices.trackUseGPTResponseEvent(
block,
"USE_GPT_RESPONSE_IN_PAGE_BLOCK"
"USE_GPT_RESPONSE_IN_PAGE_BLOCK",
user
);
else if (issue)
trackEventServices.trackUseGPTResponseEvent(issue, "USE_GPT_RESPONSE_IN_ISSUE");
trackEventServices.trackUseGPTResponseEvent(
issue,
"USE_GPT_RESPONSE_IN_ISSUE",
user
);
}}
>
Use this response

View File

@ -17,6 +17,7 @@ import { useProjectMyMembership } from "contexts/project-member.context";
// hooks
import useToast from "hooks/use-toast";
import useIssuesView from "hooks/use-issues-view";
import useUserAuth from "hooks/use-user-auth";
// components
import { AllLists, AllBoards, FilterList, CalendarView, GanttChartView } from "components/core";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
@ -89,6 +90,8 @@ export const IssuesView: React.FC<Props> = ({
const { memberRole } = useProjectMyMembership();
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const {
@ -220,11 +223,17 @@ export const IssuesView: React.FC<Props> = ({
// patch request
issuesService
.patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, {
priority: draggedItem.priority,
state: draggedItem.state,
sort_order: draggedItem.sort_order,
})
.patchIssue(
workspaceSlug as string,
projectId as string,
draggedItem.id,
{
priority: draggedItem.priority,
state: draggedItem.state,
sort_order: draggedItem.sort_order,
},
user
)
.then((response) => {
const sourceStateBeforeDrag = states.find((state) => state.name === source.droppableId);
@ -232,14 +241,17 @@ export const IssuesView: React.FC<Props> = ({
sourceStateBeforeDrag?.group !== "completed" &&
response?.state_detail?.group === "completed"
)
trackEventServices.trackIssueMarkedAsDoneEvent({
workspaceSlug,
workspaceId: draggedItem.workspace,
projectName: draggedItem.project_detail.name,
projectIdentifier: draggedItem.project_detail.identifier,
projectId,
issueId: draggedItem.id,
});
trackEventServices.trackIssueMarkedAsDoneEvent(
{
workspaceSlug,
workspaceId: draggedItem.workspace,
projectName: draggedItem.project_detail.name,
projectIdentifier: draggedItem.project_detail.identifier,
projectId,
issueId: draggedItem.id,
},
user
);
if (cycleId) {
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
@ -419,6 +431,7 @@ export const IssuesView: React.FC<Props> = ({
isOpen={createViewModal !== null}
handleClose={() => setCreateViewModal(null)}
preLoadedData={createViewModal}
user={user}
/>
<CreateUpdateIssueModal
isOpen={createIssueModal && preloadedData?.actionType === "createIssue"}
@ -437,6 +450,7 @@ export const IssuesView: React.FC<Props> = ({
handleClose={() => setDeleteIssueModal(false)}
isOpen={deleteIssueModal}
data={issueToDelete}
user={user}
/>
<TransferIssuesModal
handleClose={() => setTransferIssuesModal(false)}
@ -508,6 +522,7 @@ export const IssuesView: React.FC<Props> = ({
: null
}
isCompleted={isCompleted}
user={user}
userAuth={memberRole}
/>
) : issueView === "kanban" ? (
@ -528,6 +543,7 @@ export const IssuesView: React.FC<Props> = ({
: null
}
isCompleted={isCompleted}
user={user}
userAuth={memberRole}
/>
) : issueView === "calendar" ? (
@ -536,6 +552,7 @@ export const IssuesView: React.FC<Props> = ({
handleDeleteIssue={handleDeleteIssue}
addIssueToDate={addIssueToDate}
isCompleted={isCompleted}
user={user}
userAuth={memberRole}
/>
) : (

View File

@ -3,7 +3,7 @@ import useIssuesView from "hooks/use-issues-view";
// components
import { SingleList } from "components/core/list-view/single-list";
// types
import { IIssue, IState, UserAuth } from "types";
import { ICurrentUserResponse, IIssue, IState, UserAuth } from "types";
// types
type Props = {
@ -16,6 +16,7 @@ type Props = {
openIssuesListModal?: (() => void) | null;
removeIssue: ((bridgeId: string, issueId: string) => void) | null;
isCompleted?: boolean;
user: ICurrentUserResponse | undefined;
userAuth: UserAuth;
};
@ -29,6 +30,7 @@ export const AllLists: React.FC<Props> = ({
handleDeleteIssue,
removeIssue,
isCompleted = false,
user,
userAuth,
}) => {
const { groupedByIssues, groupByProperty: selectedGroup, showEmptyGroups } = useIssuesView();
@ -58,6 +60,7 @@ export const AllLists: React.FC<Props> = ({
openIssuesListModal={type !== "issue" ? openIssuesListModal : null}
removeIssue={removeIssue}
isCompleted={isCompleted}
user={user}
userAuth={userAuth}
/>
);

View File

@ -36,7 +36,7 @@ import { LayerDiagonalIcon } from "components/icons";
import { copyTextToClipboard, truncateText } from "helpers/string.helper";
import { handleIssuesMutation } from "constants/issue";
// types
import { IIssue, Properties, UserAuth } from "types";
import { ICurrentUserResponse, IIssue, Properties, UserAuth } from "types";
// fetch-keys
import {
CYCLE_DETAILS,
@ -57,6 +57,7 @@ type Props = {
removeIssue?: (() => void) | null;
handleDeleteIssue: (issue: IIssue) => void;
isCompleted?: boolean;
user: ICurrentUserResponse | undefined;
userAuth: UserAuth;
};
@ -71,6 +72,7 @@ export const SingleListIssue: React.FC<Props> = ({
groupTitle,
handleDeleteIssue,
isCompleted = false,
user,
userAuth,
}) => {
// context menu
@ -141,7 +143,7 @@ export const SingleListIssue: React.FC<Props> = ({
);
issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData)
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData, user)
.then(() => {
if (cycleId) {
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
@ -241,6 +243,7 @@ export const SingleListIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
position="right"
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -249,6 +252,7 @@ export const SingleListIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
position="right"
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -256,6 +260,7 @@ export const SingleListIssue: React.FC<Props> = ({
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -284,6 +289,7 @@ export const SingleListIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
position="right"
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -292,6 +298,7 @@ export const SingleListIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
position="right"
user={user}
isNotAllowed={isNotAllowed}
/>
)}

View File

@ -19,7 +19,14 @@ import { getPriorityIcon, getStateGroupIcon } from "components/icons";
// helpers
import { addSpaceIfCamelCase } from "helpers/string.helper";
// types
import { IIssue, IIssueLabels, IState, TIssueGroupByOptions, UserAuth } from "types";
import {
ICurrentUserResponse,
IIssue,
IIssueLabels,
IState,
TIssueGroupByOptions,
UserAuth,
} from "types";
// fetch-keys
import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS } from "constants/fetch-keys";
@ -39,6 +46,7 @@ type Props = {
openIssuesListModal?: (() => void) | null;
removeIssue: ((bridgeId: string, issueId: string) => void) | null;
isCompleted?: boolean;
user: ICurrentUserResponse | undefined;
userAuth: UserAuth;
};
@ -56,6 +64,7 @@ export const SingleList: React.FC<Props> = ({
openIssuesListModal,
removeIssue,
isCompleted = false,
user,
userAuth,
}) => {
const router = useRouter();
@ -208,6 +217,7 @@ export const SingleList: React.FC<Props> = ({
removeIssue(issue.bridge_id, issue.id);
}}
isCompleted={isCompleted}
user={user}
userAuth={userAuth}
/>
))

View File

@ -8,6 +8,7 @@ import { mutate } from "swr";
import cyclesService from "services/cycles.service";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// components
import {
CreateUpdateCycleModal,
@ -48,6 +49,7 @@ export const CyclesView: React.FC<Props> = ({ cycles, viewType }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const handleEditCycle = (cycle: ICycle) => {
@ -158,11 +160,13 @@ export const CyclesView: React.FC<Props> = ({ cycles, viewType }) => {
isOpen={createUpdateCycleModal}
handleClose={() => setCreateUpdateCycleModal(false)}
data={selectedCycleToUpdate}
user={user}
/>
<DeleteCycleModal
isOpen={deleteCycleModal}
setIsOpen={setDeleteCycleModal}
data={selectedCycleToDelete}
user={user}
/>
{cycles ? (
cycles.length > 0 ? (

View File

@ -14,11 +14,12 @@ import { DangerButton, SecondaryButton } from "components/ui";
// icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// types
import type { ICycle } from "types";
import type { ICurrentUserResponse, ICycle } from "types";
type TConfirmCycleDeletionProps = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
data?: ICycle | null;
user: ICurrentUserResponse | undefined;
};
// fetch-keys
import {
@ -34,6 +35,7 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
isOpen,
setIsOpen,
data,
user,
}) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
@ -53,7 +55,7 @@ export const DeleteCycleModal: React.FC<TConfirmCycleDeletionProps> = ({
setIsDeleteLoading(true);
await cycleService
.deleteCycle(workspaceSlug as string, data.project, data.id)
.deleteCycle(workspaceSlug as string, data.project, data.id, user)
.then(() => {
const cycleType = getDateRangeStatus(data.start_date, data.end_date);
const fetchKey =

View File

@ -15,7 +15,7 @@ import { CycleForm } from "components/cycles";
// helper
import { getDateRangeStatus, isDateGreaterThanToday } from "helpers/date-time.helper";
// types
import type { ICycle } from "types";
import type { ICurrentUserResponse, ICycle } from "types";
// fetch keys
import {
COMPLETED_CYCLES_LIST,
@ -30,12 +30,14 @@ type CycleModalProps = {
isOpen: boolean;
handleClose: () => void;
data?: ICycle | null;
user: ICurrentUserResponse | undefined;
};
export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
isOpen,
handleClose,
data,
user,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -46,7 +48,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
if (!workspaceSlug || !projectId) return;
await cycleService
.createCycle(workspaceSlug.toString(), projectId.toString(), payload)
.createCycle(workspaceSlug.toString(), projectId.toString(), payload, user)
.then((res) => {
switch (getDateRangeStatus(res.start_date, res.end_date)) {
case "completed":
@ -84,7 +86,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
if (!workspaceSlug || !projectId) return;
await cycleService
.updateCycle(workspaceSlug.toString(), projectId.toString(), cycleId, payload)
.updateCycle(workspaceSlug.toString(), projectId.toString(), cycleId, payload, user)
.then((res) => {
switch (getDateRangeStatus(data?.start_date, data?.end_date)) {
case "completed":

View File

@ -4,6 +4,7 @@ import { useRouter } from "next/router";
import useSWR from "swr";
import useUserAuth from "hooks/use-user-auth";
// headless ui
import { Listbox, Transition } from "@headlessui/react";
// icons
@ -35,6 +36,8 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
const router = useRouter();
const { workspaceSlug } = router.query;
const { user } = useUserAuth();
const { data: cycles } = useSWR(
workspaceSlug && projectId ? CYCLES_LIST(projectId) : null,
workspaceSlug && projectId
@ -54,7 +57,11 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({
return (
<>
<CreateUpdateCycleModal isOpen={isCycleModalActive} handleClose={closeCycleModal} />
<CreateUpdateCycleModal
isOpen={isCycleModalActive}
handleClose={closeCycleModal}
user={user}
/>
<Listbox as="div" className="relative" value={value} onChange={onChange} multiple={multiple}>
{({ open }) => (
<>

View File

@ -39,7 +39,7 @@ import {
renderShortDate,
} from "helpers/date-time.helper";
// types
import { ICycle, IIssue } from "types";
import { ICurrentUserResponse, ICycle, IIssue } from "types";
// fetch-keys
import { CYCLE_DETAILS, CYCLE_ISSUES } from "constants/fetch-keys";
@ -48,6 +48,7 @@ type Props = {
isOpen: boolean;
cycleStatus: string;
isCompleted: boolean;
user: ICurrentUserResponse | undefined;
};
export const CycleDetailsSidebar: React.FC<Props> = ({
@ -55,6 +56,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
isOpen,
cycleStatus,
isCompleted,
user,
}) => {
const [cycleDeleteModal, setCycleDeleteModal] = useState(false);
@ -94,7 +96,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
);
cyclesService
.patchCycle(workspaceSlug as string, projectId as string, cycleId as string, data)
.patchCycle(workspaceSlug as string, projectId as string, cycleId as string, data, user)
.then(() => mutate(CYCLE_DETAILS(cycleId as string)))
.catch((e) => console.log(e));
};
@ -294,7 +296,12 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
return (
<>
<DeleteCycleModal isOpen={cycleDeleteModal} setIsOpen={setCycleDeleteModal} data={cycle} />
<DeleteCycleModal
isOpen={cycleDeleteModal}
setIsOpen={setCycleDeleteModal}
data={cycle}
user={user}
/>
<div
className={`fixed top-[66px] ${
isOpen ? "right-0" : "-right-[24rem]"

View File

@ -17,7 +17,7 @@ import { Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
// helpers
import { checkDuplicates } from "helpers/array.helper";
// types
import { IEstimate, IEstimateFormData } from "types";
import { ICurrentUserResponse, IEstimate, IEstimateFormData } from "types";
// fetch-keys
import { ESTIMATES_LIST, ESTIMATE_DETAILS } from "constants/fetch-keys";
@ -25,6 +25,7 @@ type Props = {
isOpen: boolean;
handleClose: () => void;
data?: IEstimate;
user: ICurrentUserResponse | undefined;
};
type FormValues = {
@ -49,7 +50,7 @@ const defaultValues: Partial<FormValues> = {
value6: "",
};
export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data, isOpen }) => {
export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data, isOpen, user }) => {
const {
register,
formState: { isSubmitting },
@ -73,7 +74,7 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
if (!workspaceSlug || !projectId) return;
await estimatesService
.createEstimate(workspaceSlug as string, projectId as string, payload)
.createEstimate(workspaceSlug as string, projectId as string, payload, user)
.then(() => {
mutate(ESTIMATES_LIST(projectId as string));
onClose();
@ -118,7 +119,13 @@ export const CreateUpdateEstimateModal: React.FC<Props> = ({ handleClose, data,
);
await estimatesService
.patchEstimate(workspaceSlug as string, projectId as string, data?.id as string, payload)
.patchEstimate(
workspaceSlug as string,
projectId as string,
data?.id as string,
payload,
user
)
.then(() => {
mutate(ESTIMATES_LIST(projectId.toString()));
mutate(ESTIMATE_DETAILS(data.id));

View File

@ -16,15 +16,17 @@ import { PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
// helpers
import { orderArrayBy } from "helpers/array.helper";
// types
import { IEstimate } from "types";
import { ICurrentUserResponse, IEstimate } from "types";
type Props = {
user: ICurrentUserResponse | undefined;
estimate: IEstimate;
editEstimate: (estimate: IEstimate) => void;
handleEstimateDelete: (estimateId: string) => void;
};
export const SingleEstimate: React.FC<Props> = ({
user,
estimate,
editEstimate,
handleEstimateDelete,
@ -52,7 +54,7 @@ export const SingleEstimate: React.FC<Props> = ({
}, false);
await projectService
.updateProject(workspaceSlug as string, projectId as string, payload)
.updateProject(workspaceSlug as string, projectId as string, payload, user)
.catch(() => {
setToastAlert({
type: "error",

View File

@ -15,7 +15,7 @@ import { DangerButton, Input, SecondaryButton } from "components/ui";
// icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// types
import { IImporterService } from "types";
import { ICurrentUserResponse, IImporterService } from "types";
// fetch-keys
import { IMPORTER_SERVICES_LIST } from "constants/fetch-keys";
@ -23,9 +23,10 @@ type Props = {
isOpen: boolean;
handleClose: () => void;
data: IImporterService | null;
user: ICurrentUserResponse | undefined;
};
export const DeleteImportModal: React.FC<Props> = ({ isOpen, handleClose, data }) => {
export const DeleteImportModal: React.FC<Props> = ({ isOpen, handleClose, data, user }) => {
const [deleteLoading, setDeleteLoading] = useState(false);
const [confirmDeleteImport, setConfirmDeleteImport] = useState(false);
@ -45,7 +46,7 @@ export const DeleteImportModal: React.FC<Props> = ({ isOpen, handleClose, data }
false
);
IntegrationService.deleteImporterService(workspaceSlug as string, data.service, data.id)
IntegrationService.deleteImporterService(workspaceSlug as string, data.service, data.id, user)
.catch(() =>
setToastAlert({
type: "error",

View File

@ -27,7 +27,7 @@ import { ArrowLeftIcon, ListBulletIcon } from "@heroicons/react/24/outline";
// images
import GithubLogo from "public/services/github.png";
// types
import { IGithubRepoCollaborator, IGithubServiceImportFormData } from "types";
import { ICurrentUserResponse, IGithubRepoCollaborator, IGithubServiceImportFormData } from "types";
// fetch-keys
import {
APP_INTEGRATIONS,
@ -89,7 +89,11 @@ const integrationWorkflowData = [
},
];
export const GithubImporterRoot = () => {
type Props = {
user: ICurrentUserResponse | undefined;
};
export const GithubImporterRoot: React.FC<Props> = ({ user }) => {
const [currentStep, setCurrentStep] = useState<IIntegrationData>({
state: "import-configure",
});
@ -157,7 +161,7 @@ export const GithubImporterRoot = () => {
project_id: formData.project,
};
await GithubIntegrationService.createGithubServiceImport(workspaceSlug as string, payload)
await GithubIntegrationService.createGithubServiceImport(workspaceSlug as string, payload, user)
.then(() => {
router.push(`/${workspaceSlug}/settings/import-export`);
mutate(IMPORTER_SERVICES_LIST(workspaceSlug as string));

View File

@ -6,6 +6,8 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
// hooks
import useUserAuth from "hooks/use-user-auth";
// services
import IntegrationService from "services/integration";
// components
@ -35,6 +37,8 @@ const IntegrationGuide = () => {
const router = useRouter();
const { workspaceSlug, provider } = router.query;
const { user } = useUserAuth();
const { data: importerServices } = useSWR(
workspaceSlug ? IMPORTER_SERVICES_LIST(workspaceSlug as string) : null,
workspaceSlug ? () => IntegrationService.getImporterServicesList(workspaceSlug as string) : null
@ -51,6 +55,7 @@ const IntegrationGuide = () => {
isOpen={deleteImportModal}
handleClose={() => setDeleteImportModal(false)}
data={importToDelete}
user={user}
/>
<div className="h-full space-y-2">
{!provider && (
@ -156,8 +161,8 @@ const IntegrationGuide = () => {
</>
)}
{provider && provider === "github" && <GithubImporterRoot />}
{provider && provider === "jira" && <JiraImporterRoot />}
{provider && provider === "github" && <GithubImporterRoot user={user} />}
{provider && provider === "jira" && <JiraImporterRoot user={user} />}
</div>
</>
);

View File

@ -35,7 +35,7 @@ import {
import JiraLogo from "public/services/jira.png";
import { IJiraImporterForm } from "types";
import { ICurrentUserResponse, IJiraImporterForm } from "types";
const integrationWorkflowData: Array<{
title: string;
@ -64,7 +64,11 @@ const integrationWorkflowData: Array<{
},
];
export const JiraImporterRoot = () => {
type Props = {
user: ICurrentUserResponse | undefined;
};
export const JiraImporterRoot: React.FC<Props> = ({ user }) => {
const [currentStep, setCurrentStep] = useState<IJiraIntegrationData>({
state: "import-configure",
});
@ -85,7 +89,7 @@ export const JiraImporterRoot = () => {
if (!workspaceSlug) return;
await jiraImporterService
.createJiraImporter(workspaceSlug.toString(), data)
.createJiraImporter(workspaceSlug.toString(), data, user)
.then(() => {
mutate(IMPORTER_SERVICES_LIST(workspaceSlug.toString()));
router.push(`/${workspaceSlug}/settings/import-export`);

View File

@ -27,7 +27,7 @@ import { Loader } from "components/ui";
import { renderShortNumericDateFormat, timeAgo } from "helpers/date-time.helper";
import { addSpaceIfCamelCase } from "helpers/string.helper";
// types
import { IIssueComment, IIssueLabels } from "types";
import { ICurrentUserResponse, IIssueComment, IIssueLabels } from "types";
import { PROJECT_ISSUES_ACTIVITY, PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
import useEstimateOption from "hooks/use-estimate-option";
@ -110,7 +110,11 @@ const activityDetails: {
},
};
export const IssueActivitySection: React.FC = () => {
type Props = {
user: ICurrentUserResponse | undefined;
};
export const IssueActivitySection: React.FC<Props> = ({ user }) => {
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
@ -143,7 +147,8 @@ export const IssueActivitySection: React.FC = () => {
projectId as string,
issueId as string,
comment.id,
comment
comment,
user
)
.then((res) => {
mutateIssueActivities();
@ -160,7 +165,8 @@ export const IssueActivitySection: React.FC = () => {
workspaceSlug as string,
projectId as string,
issueId as string,
commentId
commentId,
user
)
.then(() => mutateIssueActivities());
};

View File

@ -14,7 +14,7 @@ import useToast from "hooks/use-toast";
// ui
import { Loader, SecondaryButton } from "components/ui";
// types
import type { IIssueComment } from "types";
import type { ICurrentUserResponse, IIssueComment } from "types";
// fetch-keys
import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
@ -40,7 +40,11 @@ const defaultValues: Partial<IIssueComment> = {
comment_html: "",
};
export const AddComment: React.FC = () => {
type Props = {
user: ICurrentUserResponse | undefined;
};
export const AddComment: React.FC<Props> = ({ user }) => {
const {
handleSubmit,
control,
@ -67,7 +71,13 @@ export const AddComment: React.FC = () => {
)
return;
await issuesServices
.createIssueComment(workspaceSlug as string, projectId as string, issueId as string, formData)
.createIssueComment(
workspaceSlug as string,
projectId as string,
issueId as string,
formData,
user
)
.then(() => {
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
reset(defaultValues);

View File

@ -17,7 +17,7 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// ui
import { SecondaryButton, DangerButton } from "components/ui";
// types
import type { IIssue } from "types";
import type { ICurrentUserResponse, IIssue } from "types";
// fetch-keys
import {
CYCLE_ISSUES_WITH_PARAMS,
@ -30,9 +30,10 @@ type Props = {
isOpen: boolean;
handleClose: () => void;
data: IIssue | null;
user: ICurrentUserResponse | undefined;
};
export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data }) => {
export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data, user }) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter();
@ -57,7 +58,7 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
if (!workspaceSlug || !projectId || !data) return;
await issueServices
.deleteIssue(workspaceSlug as string, projectId as string, data.id)
.deleteIssue(workspaceSlug as string, projectId as string, data.id, user)
.then(() => {
if (issueView === "calendar") {
const calendarFetchKey = cycleId

View File

@ -39,7 +39,7 @@ import { SparklesIcon, XMarkIcon } from "@heroicons/react/24/outline";
// helpers
import { cosineSimilarity } from "helpers/string.helper";
// types
import type { IIssue } from "types";
import type { ICurrentUserResponse, IIssue } from "types";
// rich-text-editor
const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor"), {
ssr: false,
@ -91,6 +91,7 @@ export interface IssueFormProps {
setCreateMore: React.Dispatch<React.SetStateAction<boolean>>;
handleClose: () => void;
status: boolean;
user: ICurrentUserResponse | undefined;
}
export const IssueForm: FC<IssueFormProps> = ({
@ -103,6 +104,7 @@ export const IssueForm: FC<IssueFormProps> = ({
setCreateMore,
handleClose,
status,
user,
}) => {
// states
const [mostSimilarIssue, setMostSimilarIssue] = useState<IIssue | undefined>();
@ -177,10 +179,15 @@ export const IssueForm: FC<IssueFormProps> = ({
setIAmFeelingLucky(true);
aiService
.createGptTask(workspaceSlug as string, projectId as string, {
prompt: issueName,
task: "Generate a proper description for this issue in context of a project management software.",
})
.createGptTask(
workspaceSlug as string,
projectId as string,
{
prompt: issueName,
task: "Generate a proper description for this issue in context of a project management software.",
},
user
)
.then((res) => {
if (res.response === "")
setToastAlert({
@ -227,12 +234,18 @@ export const IssueForm: FC<IssueFormProps> = ({
isOpen={stateModal}
handleClose={() => setStateModal(false)}
projectId={projectId}
user={user}
/>
<CreateUpdateCycleModal
isOpen={cycleModal}
handleClose={() => setCycleModal(false)}
user={user}
/>
<CreateUpdateCycleModal isOpen={cycleModal} handleClose={() => setCycleModal(false)} />
<CreateLabelModal
isOpen={labelModal}
handleClose={() => setLabelModal(false)}
projectId={projectId}
user={user}
/>
</>
)}

View File

@ -102,9 +102,15 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
if (!workspaceSlug || !projectId) return;
await issuesService
.addIssueToCycle(workspaceSlug as string, activeProject ?? "", cycleId, {
issues: [issueId],
})
.addIssueToCycle(
workspaceSlug as string,
activeProject ?? "",
cycleId,
{
issues: [issueId],
},
user
)
.then(() => {
if (cycleId) {
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId, params));
@ -117,9 +123,15 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
if (!workspaceSlug || !projectId) return;
await modulesService
.addIssuesToModule(workspaceSlug as string, activeProject ?? "", moduleId as string, {
issues: [issueId],
})
.addIssuesToModule(
workspaceSlug as string,
activeProject ?? "",
moduleId as string,
{
issues: [issueId],
},
user
)
.then(() => {
if (moduleId) {
mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
@ -148,7 +160,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
if (!workspaceSlug) return;
await issuesService
.createIssues(workspaceSlug as string, activeProject ?? "", payload)
.createIssues(workspaceSlug as string, activeProject ?? "", payload, user)
.then(async (res) => {
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle);
@ -180,7 +192,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
const updateIssue = async (payload: Partial<IIssue>) => {
await issuesService
.updateIssue(workspaceSlug as string, activeProject ?? "", data?.id ?? "", payload)
.updateIssue(workspaceSlug as string, activeProject ?? "", data?.id ?? "", payload, user)
.then((res) => {
if (isUpdatingSingleIssue) {
mutate<IIssue>(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false);
@ -261,6 +273,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
projectId={activeProject ?? ""}
setActiveProject={setActiveProject}
status={data ? true : false}
user={user}
/>
</Dialog.Panel>
</Transition.Child>

View File

@ -7,6 +7,7 @@ import { mutate } from "swr";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// services
import issuesService from "services/issues.service";
// components
@ -37,6 +38,9 @@ type Props = {
export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const partialUpdateIssue = useCallback(
@ -55,7 +59,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
);
issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData)
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData, user)
.then((res) => {
mutate(USER_ISSUE(workspaceSlug as string));
})
@ -110,6 +114,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
<ViewPrioritySelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -117,6 +122,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
<ViewStateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}
@ -124,6 +130,7 @@ export const MyIssuesListItem: React.FC<Props> = ({ issue, properties, projectId
<ViewDueDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}

View File

@ -12,6 +12,7 @@ import { TwitterPicker } from "react-color";
import { Popover, Listbox, Transition } from "@headlessui/react";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// services
import issuesService from "services/issues.service";
import modulesService from "services/modules.service";
@ -76,6 +77,8 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
const { user } = useUserAuth();
const { memberRole } = useProjectMyMembership();
const { setToastAlert } = useToast();
@ -110,7 +113,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
const handleNewLabel = (formData: any) => {
if (!workspaceSlug || !projectId || isSubmitting) return;
issuesService
.createIssueLabel(workspaceSlug as string, projectId as string, formData)
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user)
.then((res) => {
reset(defaultValues);
issueLabelMutate((prevData) => [...(prevData ?? []), res], false);
@ -124,9 +127,15 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
if (!workspaceSlug || !projectId || !issueDetail) return;
issuesService
.addIssueToCycle(workspaceSlug as string, projectId as string, cycleDetails.id, {
issues: [issueDetail.id],
})
.addIssueToCycle(
workspaceSlug as string,
projectId as string,
cycleDetails.id,
{
issues: [issueDetail.id],
},
user
)
.then((res) => {
mutate(ISSUE_DETAILS(issueId as string));
});
@ -139,9 +148,15 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
if (!workspaceSlug || !projectId || !issueDetail) return;
modulesService
.addIssuesToModule(workspaceSlug as string, projectId as string, moduleDetail.id, {
issues: [issueDetail.id],
})
.addIssuesToModule(
workspaceSlug as string,
projectId as string,
moduleDetail.id,
{
issues: [issueDetail.id],
},
user
)
.then((res) => {
mutate(ISSUE_DETAILS(issueId as string));
});
@ -228,6 +243,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
handleClose={() => setDeleteIssueModal(false)}
isOpen={deleteIssueModal}
data={issueDetail ?? null}
user={user}
/>
<div className="sticky top-5 w-full divide-y-2 divide-brand-base">
<div className="flex items-center justify-between pb-3">

View File

@ -21,15 +21,16 @@ import { ChevronRightIcon, PlusIcon, XMarkIcon } from "@heroicons/react/24/outli
// helpers
import { orderArrayBy } from "helpers/array.helper";
// types
import { IIssue, ISubIssueResponse } from "types";
import { ICurrentUserResponse, IIssue, ISubIssueResponse } from "types";
// fetch-keys
import { PROJECT_ISSUES_LIST, SUB_ISSUES } from "constants/fetch-keys";
type Props = {
parentIssue: IIssue;
user: ICurrentUserResponse | undefined;
};
export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
export const SubIssuesList: FC<Props> = ({ parentIssue, user }) => {
// states
const [createIssueModal, setCreateIssueModal] = useState(false);
const [subIssuesListModal, setSubIssuesListModal] = useState(false);
@ -134,7 +135,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
);
issuesService
.patchIssue(workspaceSlug.toString(), projectId.toString(), issueId, { parent: null })
.patchIssue(workspaceSlug.toString(), projectId.toString(), issueId, { parent: null }, user)
.then((res) => {
mutate(SUB_ISSUES(parentIssue.id ?? ""));

View File

@ -12,7 +12,7 @@ import { AssigneesList, Avatar, CustomSearchSelect, Tooltip } from "components/u
// icons
import { UserGroupIcon } from "@heroicons/react/24/outline";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
@ -22,6 +22,7 @@ type Props = {
position?: "left" | "right";
selfPositioned?: boolean;
tooltipPosition?: "left" | "right";
user: ICurrentUserResponse | undefined;
isNotAllowed: boolean;
};
@ -31,6 +32,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
position = "left",
selfPositioned = false,
tooltipPosition = "right",
user,
isNotAllowed,
}) => {
const router = useRouter();
@ -83,7 +85,8 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
projectName: issue.project_detail.name,
issueId: issue.id,
},
"ISSUE_PROPERTY_UPDATE_ASSIGNEE"
"ISSUE_PROPERTY_UPDATE_ASSIGNEE",
user
);
}}
options={options}

View File

@ -7,15 +7,21 @@ import { findHowManyDaysLeft } from "helpers/date-time.helper";
// services
import trackEventServices from "services/track-event.service";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
type Props = {
issue: IIssue;
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
user: ICurrentUserResponse | undefined;
isNotAllowed: boolean;
};
export const ViewDueDateSelect: React.FC<Props> = ({ issue, partialUpdateIssue, isNotAllowed }) => {
export const ViewDueDateSelect: React.FC<Props> = ({
issue,
partialUpdateIssue,
user,
isNotAllowed,
}) => {
const router = useRouter();
const { workspaceSlug } = router.query;
@ -51,7 +57,8 @@ export const ViewDueDateSelect: React.FC<Props> = ({ issue, partialUpdateIssue,
projectName: issue.project_detail.name,
issueId: issue.id,
},
"ISSUE_PROPERTY_UPDATE_DUE_DATE"
"ISSUE_PROPERTY_UPDATE_DUE_DATE",
user
);
}}
className={issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"}

View File

@ -11,13 +11,14 @@ import { CustomSelect, Tooltip } from "components/ui";
// icons
import { PlayIcon } from "@heroicons/react/24/outline";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
type Props = {
issue: IIssue;
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
position?: "left" | "right";
selfPositioned?: boolean;
user: ICurrentUserResponse | undefined;
isNotAllowed: boolean;
};
@ -26,6 +27,7 @@ export const ViewEstimateSelect: React.FC<Props> = ({
partialUpdateIssue,
position = "left",
selfPositioned = false,
user,
isNotAllowed,
}) => {
const router = useRouter();
@ -51,7 +53,8 @@ export const ViewEstimateSelect: React.FC<Props> = ({
projectName: issue.project_detail.name,
issueId: issue.id,
},
"ISSUE_PROPERTY_UPDATE_ESTIMATE"
"ISSUE_PROPERTY_UPDATE_ESTIMATE",
user
);
}}
label={

View File

@ -7,7 +7,7 @@ import { CustomSelect, Tooltip } from "components/ui";
// icons
import { getPriorityIcon } from "components/icons/priority-icon";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
// constants
import { PRIORITIES } from "constants/project";
// services
@ -18,6 +18,7 @@ type Props = {
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
position?: "left" | "right";
selfPositioned?: boolean;
user: ICurrentUserResponse | undefined;
isNotAllowed: boolean;
};
@ -26,6 +27,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
partialUpdateIssue,
position = "left",
selfPositioned = false,
user,
isNotAllowed,
}) => {
const router = useRouter();
@ -45,7 +47,8 @@ export const ViewPrioritySelect: React.FC<Props> = ({
projectName: issue.project_detail.name,
issueId: issue.id,
},
"ISSUE_PROPERTY_UPDATE_PRIORITY"
"ISSUE_PROPERTY_UPDATE_PRIORITY",
user
);
}}
maxHeight="md"

View File

@ -13,7 +13,7 @@ import { getStateGroupIcon } from "components/icons";
import { addSpaceIfCamelCase } from "helpers/string.helper";
import { getStatesList } from "helpers/state.helper";
// types
import { IIssue } from "types";
import { ICurrentUserResponse, IIssue } from "types";
// fetch-keys
import { STATES_LIST } from "constants/fetch-keys";
@ -22,6 +22,7 @@ type Props = {
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
position?: "left" | "right";
selfPositioned?: boolean;
user: ICurrentUserResponse | undefined;
isNotAllowed: boolean;
};
@ -30,6 +31,7 @@ export const ViewStateSelect: React.FC<Props> = ({
partialUpdateIssue,
position = "left",
selfPositioned = false,
user,
isNotAllowed,
}) => {
const router = useRouter();
@ -77,21 +79,25 @@ export const ViewStateSelect: React.FC<Props> = ({
projectName: issue.project_detail.name,
issueId: issue.id,
},
"ISSUE_PROPERTY_UPDATE_STATE"
"ISSUE_PROPERTY_UPDATE_STATE",
user
);
const oldState = states.find((s) => s.id === issue.state);
const newState = states.find((s) => s.id === data);
if (oldState?.group !== "completed" && newState?.group !== "completed") {
trackEventServices.trackIssueMarkedAsDoneEvent({
workspaceSlug: issue.workspace_detail.slug,
workspaceId: issue.workspace_detail.id,
projectId: issue.project_detail.id,
projectIdentifier: issue.project_detail.identifier,
projectName: issue.project_detail.name,
issueId: issue.id,
});
trackEventServices.trackIssueMarkedAsDoneEvent(
{
workspaceSlug: issue.workspace_detail.slug,
workspaceId: issue.workspace_detail.id,
projectId: issue.project_detail.id,
projectIdentifier: issue.project_detail.identifier,
projectName: issue.project_detail.name,
issueId: issue.id,
},
user
);
}
}}
options={options}

View File

@ -17,7 +17,7 @@ import { Input, PrimaryButton, SecondaryButton } from "components/ui";
// icons
import { ChevronDownIcon } from "@heroicons/react/24/outline";
// types
import type { IIssueLabels, IState } from "types";
import type { ICurrentUserResponse, IIssueLabels, IState } from "types";
// constants
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
@ -26,6 +26,7 @@ type Props = {
isOpen: boolean;
projectId: string;
handleClose: () => void;
user: ICurrentUserResponse | undefined;
};
const defaultValues: Partial<IState> = {
@ -33,7 +34,7 @@ const defaultValues: Partial<IState> = {
color: "#858E96",
};
export const CreateLabelModal: React.FC<Props> = ({ isOpen, projectId, handleClose }) => {
export const CreateLabelModal: React.FC<Props> = ({ isOpen, projectId, handleClose, user }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
@ -57,7 +58,7 @@ export const CreateLabelModal: React.FC<Props> = ({ isOpen, projectId, handleClo
if (!workspaceSlug) return;
await issuesService
.createIssueLabel(workspaceSlug as string, projectId as string, formData)
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user)
.then((res) => {
mutate<IIssueLabels[]>(
PROJECT_ISSUE_LABELS(projectId),

View File

@ -6,6 +6,8 @@ import { mutate } from "swr";
// react-hook-form
import { Controller, SubmitHandler, useForm } from "react-hook-form";
// hooks
import useUserAuth from "hooks/use-user-auth";
// react-color
import { TwitterPicker } from "react-color";
// headless ui
@ -42,6 +44,8 @@ export const CreateUpdateLabelInline = forwardRef<Ref, Props>(function CreateUpd
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const {
handleSubmit,
control,
@ -58,7 +62,7 @@ export const CreateUpdateLabelInline = forwardRef<Ref, Props>(function CreateUpd
if (!workspaceSlug || !projectId || isSubmitting) return;
await issuesService
.createIssueLabel(workspaceSlug as string, projectId as string, formData)
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user)
.then((res) => {
mutate<IIssueLabels[]>(
PROJECT_ISSUE_LABELS(projectId as string),
@ -78,7 +82,8 @@ export const CreateUpdateLabelInline = forwardRef<Ref, Props>(function CreateUpd
workspaceSlug as string,
projectId as string,
labelToUpdate?.id ?? "",
formData
formData,
user
)
.then(() => {
reset(defaultValues);

View File

@ -15,7 +15,7 @@ import useToast from "hooks/use-toast";
// ui
import { DangerButton, SecondaryButton } from "components/ui";
// types
import type { IIssueLabels } from "types";
import type { ICurrentUserResponse, IIssueLabels } from "types";
// fetch-keys
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
@ -23,9 +23,10 @@ type Props = {
isOpen: boolean;
onClose: () => void;
data: IIssueLabels | null;
user: ICurrentUserResponse | undefined;
};
export const DeleteLabelModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
export const DeleteLabelModal: React.FC<Props> = ({ isOpen, onClose, data, user }) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter();
@ -50,7 +51,7 @@ export const DeleteLabelModal: React.FC<Props> = ({ isOpen, onClose, data }) =>
);
await issuesService
.deleteIssueLabel(workspaceSlug.toString(), projectId.toString(), data.id)
.deleteIssueLabel(workspaceSlug.toString(), projectId.toString(), data.id, user)
.then(() => handleClose())
.catch(() => {
setIsDeleteLoading(false);

View File

@ -11,7 +11,7 @@ import { RectangleStackIcon, MagnifyingGlassIcon } from "@heroicons/react/24/out
// services
import issuesService from "services/issues.service";
// types
import { IIssueLabels } from "types";
import { ICurrentUserResponse, IIssueLabels } from "types";
// constants
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
@ -19,9 +19,10 @@ type Props = {
isOpen: boolean;
handleClose: () => void;
parent: IIssueLabels | undefined;
user: ICurrentUserResponse | undefined;
};
export const LabelsListModal: React.FC<Props> = ({ isOpen, handleClose, parent }) => {
export const LabelsListModal: React.FC<Props> = ({ isOpen, handleClose, parent, user }) => {
const [query, setQuery] = useState("");
const router = useRouter();
@ -58,9 +59,15 @@ export const LabelsListModal: React.FC<Props> = ({ isOpen, handleClose, parent }
);
await issuesService
.patchIssueLabel(workspaceSlug as string, projectId as string, label.id, {
parent: parent?.id ?? "",
})
.patchIssueLabel(
workspaceSlug as string,
projectId as string,
label.id,
{
parent: parent?.id ?? "",
},
user
)
.then(() => mutate());
};

View File

@ -20,7 +20,7 @@ import {
TrashIcon,
} from "@heroicons/react/24/outline";
// types
import { IIssueLabels } from "types";
import { ICurrentUserResponse, IIssueLabels } from "types";
// fetch-keys
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
@ -30,6 +30,7 @@ type Props = {
addLabelToGroup: (parentLabel: IIssueLabels) => void;
editLabel: (label: IIssueLabels) => void;
handleLabelDelete: () => void;
user: ICurrentUserResponse | undefined;
};
export const SingleLabelGroup: React.FC<Props> = ({
@ -38,6 +39,7 @@ export const SingleLabelGroup: React.FC<Props> = ({
addLabelToGroup,
editLabel,
handleLabelDelete,
user,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -57,9 +59,15 @@ export const SingleLabelGroup: React.FC<Props> = ({
);
issuesService
.patchIssueLabel(workspaceSlug as string, projectId as string, label.id, {
parent: null,
})
.patchIssueLabel(
workspaceSlug as string,
projectId as string,
label.id,
{
parent: null,
},
user
)
.then(() => {
mutate(PROJECT_ISSUE_LABELS(projectId as string));
});

View File

@ -15,7 +15,7 @@ import { SecondaryButton, DangerButton } from "components/ui";
// icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// types
import type { IModule } from "types";
import type { ICurrentUserResponse, IModule } from "types";
// fetch-keys
import { MODULE_LIST } from "constants/fetch-keys";
@ -23,9 +23,10 @@ type Props = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
data?: IModule;
user: ICurrentUserResponse | undefined;
};
export const DeleteModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data }) => {
export const DeleteModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, user }) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter();
@ -50,7 +51,7 @@ export const DeleteModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data })
);
await modulesService
.deleteModule(workspaceSlug as string, projectId as string, data.id)
.deleteModule(workspaceSlug as string, projectId as string, data.id, user)
.then(() => {
if (moduleId) router.push(`/${workspaceSlug}/projects/${data.project}/modules`);
handleClose();

View File

@ -15,7 +15,7 @@ import modulesService from "services/modules.service";
// hooks
import useToast from "hooks/use-toast";
// types
import type { IModule } from "types";
import type { ICurrentUserResponse, IModule } from "types";
// fetch-keys
import { MODULE_LIST } from "constants/fetch-keys";
@ -23,6 +23,7 @@ type Props = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
data?: IModule;
user: ICurrentUserResponse | undefined;
};
const defaultValues: Partial<IModule> = {
@ -33,7 +34,7 @@ const defaultValues: Partial<IModule> = {
members_list: [],
};
export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data }) => {
export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, user }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -50,7 +51,7 @@ export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, da
const createModule = async (payload: Partial<IModule>) => {
await modulesService
.createModule(workspaceSlug as string, projectId as string, payload)
.createModule(workspaceSlug as string, projectId as string, payload, user)
.then(() => {
mutate(MODULE_LIST(projectId as string));
handleClose();
@ -72,7 +73,7 @@ export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, da
const updateModule = async (payload: Partial<IModule>) => {
await modulesService
.updateModule(workspaceSlug as string, projectId as string, data?.id ?? "", payload)
.updateModule(workspaceSlug as string, projectId as string, data?.id ?? "", payload, user)
.then((res) => {
mutate<IModule[]>(
MODULE_LIST(projectId as string),

View File

@ -37,7 +37,7 @@ import { LinkIcon } from "@heroicons/react/20/solid";
import { renderDateFormat, renderShortDate } from "helpers/date-time.helper";
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
// types
import { IIssue, IModule, ModuleLink } from "types";
import { ICurrentUserResponse, IIssue, IModule, ModuleLink } from "types";
// fetch-keys
import { MODULE_DETAILS } from "constants/fetch-keys";
// constant
@ -56,9 +56,16 @@ type Props = {
module?: IModule;
isOpen: boolean;
moduleIssues?: IIssue[];
user: ICurrentUserResponse | undefined;
};
export const ModuleDetailsSidebar: React.FC<Props> = ({ issues, module, isOpen, moduleIssues }) => {
export const ModuleDetailsSidebar: React.FC<Props> = ({
issues,
module,
isOpen,
moduleIssues,
user,
}) => {
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
const [moduleLinkModal, setModuleLinkModal] = useState(false);
@ -86,7 +93,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({ issues, module, isOpen,
);
modulesService
.patchModule(workspaceSlug as string, projectId as string, moduleId as string, data)
.patchModule(workspaceSlug as string, projectId as string, moduleId as string, data, user)
.then(() => mutate(MODULE_DETAILS(moduleId as string)))
.catch((e) => console.log(e));
};
@ -181,6 +188,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({ issues, module, isOpen,
isOpen={moduleDeleteModal}
setIsOpen={setModuleDeleteModal}
data={module}
user={user}
/>
<div
className={`fixed top-[66px] ${

View File

@ -27,16 +27,17 @@ import { CalendarMonthIcon, TargetIcon } from "components/icons";
import { copyTextToClipboard, truncateText } from "helpers/string.helper";
import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
// types
import { IModule } from "types";
import { ICurrentUserResponse, IModule } from "types";
// fetch-key
import { MODULE_LIST } from "constants/fetch-keys";
type Props = {
module: IModule;
handleEditModule: () => void;
user: ICurrentUserResponse | undefined;
};
export const SingleModuleCard: React.FC<Props> = ({ module, handleEditModule }) => {
export const SingleModuleCard: React.FC<Props> = ({ module, handleEditModule, user }) => {
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
const router = useRouter();
@ -128,6 +129,7 @@ export const SingleModuleCard: React.FC<Props> = ({ module, handleEditModule })
isOpen={moduleDeleteModal}
setIsOpen={setModuleDeleteModal}
data={module}
user={user}
/>
<div className="flex flex-col divide-y divide-brand-base overflow-hidden rounded-[10px] border border-brand-base bg-brand-base text-xs">
<div className="p-4">

View File

@ -2,16 +2,17 @@
import { useForm } from "react-hook-form";
import useToast from "hooks/use-toast";
import workspaceService from "services/workspace.service";
import { IUser } from "types";
import { ICurrentUserResponse, IUser } from "types";
// ui components
import { MultiInput, PrimaryButton, SecondaryButton } from "components/ui";
type Props = {
setStep: React.Dispatch<React.SetStateAction<number>>;
workspace: any;
user: ICurrentUserResponse | undefined;
};
export const InviteMembers: React.FC<Props> = ({ setStep, workspace }) => {
export const InviteMembers: React.FC<Props> = ({ setStep, workspace, user }) => {
const { setToastAlert } = useToast();
const {
@ -23,7 +24,7 @@ export const InviteMembers: React.FC<Props> = ({ setStep, workspace }) => {
const onSubmit = async (formData: IUser) => {
await workspaceService
.inviteWorkspace(workspace.slug, formData)
.inviteWorkspace(workspace.slug, formData, user)
.then(() => {
setToastAlert({
type: "success",

View File

@ -9,7 +9,7 @@ import { Tab } from "@headlessui/react";
// services
import workspaceService from "services/workspace.service";
// types
import { IWorkspaceMemberInvitation } from "types";
import { ICurrentUserResponse, IWorkspaceMemberInvitation } from "types";
// fetch-keys
import { USER_WORKSPACE_INVITATIONS } from "constants/fetch-keys";
// constants
@ -21,9 +21,10 @@ import { getFirstCharacters, truncateText } from "helpers/string.helper";
type Props = {
setStep: React.Dispatch<React.SetStateAction<number>>;
setWorkspace: React.Dispatch<React.SetStateAction<any>>;
user: ICurrentUserResponse | undefined;
};
export const Workspace: React.FC<Props> = ({ setStep, setWorkspace }) => {
export const Workspace: React.FC<Props> = ({ setStep, setWorkspace, user }) => {
const [isJoiningWorkspaces, setIsJoiningWorkspaces] = useState(false);
const [invitationsRespond, setInvitationsRespond] = useState<string[]>([]);
const [defaultValues, setDefaultValues] = useState({
@ -98,7 +99,7 @@ export const Workspace: React.FC<Props> = ({ setStep, setWorkspace }) => {
>
<Tab.List as="div" className="flex flex-col gap-3 px-7 pt-7 pb-3.5">
<div className="flex flex-col gap-2 justify-center">
<h3 className="text-base font-semibold text-brand-base">Workspaces</h3>
<h3 className="text-base font-semibold text-brand-base">Workspace</h3>
<p className="text-sm text-brand-secondary">
Create or join the workspace to get started with Plane.
</p>
@ -161,7 +162,10 @@ export const Workspace: React.FC<Props> = ({ setStep, setWorkspace }) => {
{truncateText(invitation.workspace.name, 30)}
</div>
<p className="text-sm text-brand-secondary">
Invited by {invitation.workspace.owner.first_name}
Invited by{" "}
{invitation.created_by_detail
? invitation.created_by_detail.first_name
: invitation.workspace.owner.first_name}
</p>
</div>
<div className="flex-shrink-0 self-center">
@ -237,6 +241,7 @@ export const Workspace: React.FC<Props> = ({ setStep, setWorkspace }) => {
}}
defaultValues={defaultValues}
setDefaultValues={setDefaultValues}
user={user}
/>
</Tab.Panel>
</Tab.Panels>

View File

@ -16,7 +16,7 @@ import useToast from "hooks/use-toast";
// ui
import { TextArea } from "components/ui";
// types
import { IPageBlock } from "types";
import { ICurrentUserResponse, IPageBlock } from "types";
// fetch-keys
import { PAGE_BLOCKS_LIST } from "constants/fetch-keys";
@ -24,7 +24,11 @@ const defaultValues = {
name: "",
};
export const CreateBlock = () => {
type Props = {
user: ICurrentUserResponse | undefined;
};
export const CreateBlock: React.FC<Props> = ({ user }) => {
const [blockTitle, setBlockTitle] = useState("");
const router = useRouter();
@ -49,9 +53,15 @@ export const CreateBlock = () => {
if (!workspaceSlug || !projectId || !pageId) return;
await pagesService
.createPageBlock(workspaceSlug as string, projectId as string, pageId as string, {
name: watch("name"),
})
.createPageBlock(
workspaceSlug as string,
projectId as string,
pageId as string,
{
name: watch("name"),
},
user
)
.then((res) => {
mutate<IPageBlock[]>(
PAGE_BLOCKS_LIST(pageId as string),

View File

@ -20,7 +20,7 @@ import { GptAssistantModal } from "components/core";
// ui
import { Loader, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
// types
import { IPageBlock } from "types";
import { ICurrentUserResponse, IPageBlock } from "types";
// fetch-keys
import { PAGE_BLOCKS_LIST } from "constants/fetch-keys";
@ -30,6 +30,7 @@ type Props = {
handleAiAssistance?: (response: string) => void;
setIsSyncing?: React.Dispatch<React.SetStateAction<boolean>>;
focus?: keyof IPageBlock;
user: ICurrentUserResponse | undefined;
};
const defaultValues = {
@ -61,6 +62,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
handleAiAssistance,
setIsSyncing,
focus,
user,
}) => {
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
const [gptAssistantModal, setGptAssistantModal] = useState(false);
@ -96,11 +98,17 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
if (!workspaceSlug || !projectId || !pageId) return;
await pagesService
.createPageBlock(workspaceSlug as string, projectId as string, pageId as string, {
name: formData.name,
description: formData.description ?? "",
description_html: formData.description_html ?? "<p></p>",
})
.createPageBlock(
workspaceSlug as string,
projectId as string,
pageId as string,
{
name: formData.name,
description: formData.description ?? "",
description_html: formData.description_html ?? "<p></p>",
},
user
)
.then((res) => {
mutate<IPageBlock[]>(
PAGE_BLOCKS_LIST(pageId as string),
@ -139,21 +147,34 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
);
await pagesService
.patchPageBlock(workspaceSlug as string, projectId as string, pageId as string, data.id, {
name: formData.name,
description: formData.description,
description_html: formData.description_html,
})
.patchPageBlock(
workspaceSlug as string,
projectId as string,
pageId as string,
data.id,
{
name: formData.name,
description: formData.description,
description_html: formData.description_html,
},
user
)
.then((res) => {
mutate(PAGE_BLOCKS_LIST(pageId as string));
editorRef.current?.setEditorValue(res.description_html);
if (data.issue && data.sync)
issuesService
.patchIssue(workspaceSlug as string, projectId as string, data.issue, {
name: res.name,
description: res.description,
description_html: res.description_html,
})
.patchIssue(
workspaceSlug as string,
projectId as string,
data.issue,
{
name: res.name,
description: res.description,
description_html: res.description_html,
},
user
)
.finally(() => {
if (setIsSyncing) setIsSyncing(false);
});
@ -169,10 +190,15 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
setIAmFeelingLucky(true);
aiService
.createGptTask(workspaceSlug as string, projectId as string, {
prompt: watch("name"),
task: "Generate a proper description for this issue in context of a project management software.",
})
.createGptTask(
workspaceSlug as string,
projectId as string,
{
prompt: watch("name"),
task: "Generate a proper description for this issue in context of a project management software.",
},
user
)
.then((res) => {
if (res.response === "")
setToastAlert({

View File

@ -13,7 +13,7 @@ import useToast from "hooks/use-toast";
// components
import { PageForm } from "./page-form";
// types
import { IPage } from "types";
import { ICurrentUserResponse, IPage } from "types";
// fetch-keys
import {
ALL_PAGES_LIST,
@ -26,9 +26,10 @@ type Props = {
isOpen: boolean;
handleClose: () => void;
data?: IPage | null;
user: ICurrentUserResponse | undefined;
};
export const CreateUpdatePageModal: React.FC<Props> = ({ isOpen, handleClose, data }) => {
export const CreateUpdatePageModal: React.FC<Props> = ({ isOpen, handleClose, data, user }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -40,7 +41,7 @@ export const CreateUpdatePageModal: React.FC<Props> = ({ isOpen, handleClose, da
const createPage = async (payload: IPage) => {
await pagesService
.createPage(workspaceSlug as string, projectId as string, payload)
.createPage(workspaceSlug as string, projectId as string, payload, user)
.then((res) => {
mutate(RECENT_PAGES_LIST(projectId as string));
mutate<IPage[]>(
@ -82,7 +83,7 @@ export const CreateUpdatePageModal: React.FC<Props> = ({ isOpen, handleClose, da
const updatePage = async (payload: IPage) => {
await pagesService
.patchPage(workspaceSlug as string, projectId as string, data?.id ?? "", payload)
.patchPage(workspaceSlug as string, projectId as string, data?.id ?? "", payload, user)
.then((res) => {
mutate(RECENT_PAGES_LIST(projectId as string));
mutate<IPage[]>(

View File

@ -15,7 +15,7 @@ import { DangerButton, SecondaryButton } from "components/ui";
// icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// types
import type { IPage } from "types";
import type { ICurrentUserResponse, IPage } from "types";
// fetch-keys
import {
ALL_PAGES_LIST,
@ -28,12 +28,14 @@ type TConfirmPageDeletionProps = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
data?: IPage | null;
user: ICurrentUserResponse | undefined;
};
export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = ({
isOpen,
setIsOpen,
data,
user,
}) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
@ -52,7 +54,7 @@ export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = ({
if (!data || !workspaceSlug || !projectId) return;
await pagesService
.deletePage(workspaceSlug as string, data.project, data.id)
.deletePage(workspaceSlug as string, data.project, data.id, user)
.then(() => {
mutate(RECENT_PAGES_LIST(projectId as string));
mutate<IPage[]>(

View File

@ -8,6 +8,7 @@ import pagesService from "services/pages.service";
import projectService from "services/project.service";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// components
import {
CreateUpdatePageModal,
@ -44,6 +45,8 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const { data: people } = useSWR(
@ -181,7 +184,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
);
pagesService
.patchPage(workspaceSlug.toString(), projectId.toString(), page.id, formData)
.patchPage(workspaceSlug.toString(), projectId.toString(), page.id, formData, user)
.then(() => {
mutate(RECENT_PAGES_LIST(projectId.toString()));
});
@ -193,11 +196,13 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
isOpen={createUpdatePageModal}
handleClose={() => setCreateUpdatePageModal(false)}
data={selectedPageToUpdate}
user={user}
/>
<DeletePageModal
isOpen={deletePageModal}
setIsOpen={setDeletePageModal}
data={selectedPageToDelete}
user={user}
/>
{pages ? (
<div className="space-y-4 h-full overflow-y-auto">

View File

@ -35,7 +35,7 @@ import {
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
import { IIssue, IPageBlock, IProject } from "types";
import { ICurrentUserResponse, IIssue, IPageBlock, IProject } from "types";
// fetch-keys
import { PAGE_BLOCKS_LIST } from "constants/fetch-keys";
@ -43,9 +43,10 @@ type Props = {
block: IPageBlock;
projectDetails: IProject | undefined;
index: number;
user: ICurrentUserResponse | undefined;
};
export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index }) => {
export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index, user }) => {
const [isSyncing, setIsSyncing] = useState(false);
const [createBlockForm, setCreateBlockForm] = useState(false);
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
@ -87,20 +88,33 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
);
await pagesService
.patchPageBlock(workspaceSlug as string, projectId as string, pageId as string, block.id, {
name: formData.name,
description: formData.description,
description_html: formData.description_html,
})
.patchPageBlock(
workspaceSlug as string,
projectId as string,
pageId as string,
block.id,
{
name: formData.name,
description: formData.description,
description_html: formData.description_html,
},
user
)
.then((res) => {
mutate(PAGE_BLOCKS_LIST(pageId as string));
if (block.issue && block.sync)
issuesService
.patchIssue(workspaceSlug as string, projectId as string, block.issue, {
name: res.name,
description: res.description,
description_html: res.description_html,
})
.patchIssue(
workspaceSlug as string,
projectId as string,
block.issue,
{
name: res.name,
description: res.description,
description_html: res.description_html,
},
user
)
.finally(() => setIsSyncing(false));
});
};
@ -113,7 +127,8 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
workspaceSlug as string,
projectId as string,
pageId as string,
block.id
block.id,
user
)
.then((res: IIssue) => {
mutate<IPageBlock[]>(
@ -152,7 +167,13 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
);
await pagesService
.deletePageBlock(workspaceSlug as string, projectId as string, pageId as string, block.id)
.deletePageBlock(
workspaceSlug as string,
projectId as string,
pageId as string,
block.id,
user
)
.catch(() => {
setToastAlert({
type: "error",
@ -168,10 +189,15 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
setIAmFeelingLucky(true);
aiService
.createGptTask(workspaceSlug as string, projectId as string, {
prompt: block.name,
task: "Generate a proper description for this issue in context of a project management software.",
})
.createGptTask(
workspaceSlug as string,
projectId as string,
{
prompt: block.name,
task: "Generate a proper description for this issue in context of a project management software.",
},
user
)
.then((res) => {
if (res.response === "")
setToastAlert({
@ -243,7 +269,8 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
block.id,
{
sync: !block.sync,
}
},
user
);
};
@ -281,6 +308,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
data={block}
setIsSyncing={setIsSyncing}
focus="name"
user={user}
/>
</div>
) : (

View File

@ -24,7 +24,7 @@ import EmojiIconPicker from "components/emoji-icon-picker";
// helpers
import { getRandomEmoji } from "helpers/common.helper";
// types
import { IProject } from "types";
import { ICurrentUserResponse, IProject } from "types";
// fetch-keys
import { PROJECTS_LIST, WORKSPACE_MEMBERS_ME } from "constants/fetch-keys";
// constants
@ -33,6 +33,7 @@ import { NETWORK_CHOICES } from "constants/project";
type Props = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
user: ICurrentUserResponse | undefined;
};
const defaultValues: Partial<IProject> = {
@ -63,7 +64,7 @@ const IsGuestCondition: React.FC<{
};
export const CreateProjectModal: React.FC<Props> = (props) => {
const { isOpen, setIsOpen } = props;
const { isOpen, setIsOpen, user } = props;
const [isChangeIdentifierRequired, setIsChangeIdentifierRequired] = useState(true);
@ -120,7 +121,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
else payload.emoji = formData.emoji_and_icon;
await projectServices
.createProject(workspaceSlug as string, payload)
.createProject(workspaceSlug as string, payload, user)
.then((res) => {
mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string),

View File

@ -13,7 +13,7 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// ui
import { DangerButton, Input, SecondaryButton } from "components/ui";
// types
import type { IProject, IWorkspace } from "types";
import type { ICurrentUserResponse, IProject, IWorkspace } from "types";
// fetch-keys
import { PROJECTS_LIST } from "constants/fetch-keys";
@ -22,6 +22,7 @@ type TConfirmProjectDeletionProps = {
onClose: () => void;
onSuccess?: () => void;
data: IProject | null;
user: ICurrentUserResponse | undefined;
};
export const DeleteProjectModal: React.FC<TConfirmProjectDeletionProps> = ({
@ -29,6 +30,7 @@ export const DeleteProjectModal: React.FC<TConfirmProjectDeletionProps> = ({
data,
onClose,
onSuccess,
user,
}) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [confirmProjectName, setConfirmProjectName] = useState("");
@ -65,7 +67,7 @@ export const DeleteProjectModal: React.FC<TConfirmProjectDeletionProps> = ({
setIsDeleteLoading(true);
if (!data || !workspaceSlug || !canDelete) return;
await projectService
.deleteProject(workspaceSlug, data.id)
.deleteProject(workspaceSlug, data.id, user)
.then(() => {
handleClose();
mutate<IProject[]>(PROJECTS_LIST(workspaceSlug), (prevData) =>

View File

@ -15,7 +15,7 @@ import useToast from "hooks/use-toast";
import projectService from "services/project.service";
import workspaceService from "services/workspace.service";
// types
import { IProjectMemberInvitation } from "types";
import { ICurrentUserResponse, IProjectMemberInvitation } from "types";
// fetch-keys
import { PROJECT_INVITATIONS, WORKSPACE_MEMBERS } from "constants/fetch-keys";
// constants
@ -25,6 +25,7 @@ type Props = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
members: any[];
user: ICurrentUserResponse | undefined;
};
type ProjectMember = IProjectMemberInvitation & {
@ -40,7 +41,7 @@ const defaultValues: Partial<ProjectMember> = {
user_id: "",
};
const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, members }) => {
const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, members, user }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -70,7 +71,7 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
const onSubmit = async (formData: ProjectMember) => {
if (!workspaceSlug || !projectId || isSubmitting) return;
await projectService
.inviteProject(workspaceSlug as string, projectId as string, formData)
.inviteProject(workspaceSlug as string, projectId as string, formData, user)
.then((response) => {
setIsOpen(false);
mutate<any[]>(

View File

@ -9,6 +9,7 @@ import { PlusIcon } from "@heroicons/react/24/outline";
// hooks
import useToast from "hooks/use-toast";
import useTheme from "hooks/use-theme";
import useUserAuth from "hooks/use-user-auth";
// services
import projectService from "services/project.service";
// components
@ -29,6 +30,9 @@ export const ProjectSidebarList: FC = () => {
// router
const router = useRouter();
const { workspaceSlug } = router.query;
const { user } = useUserAuth();
// states
const [isCreateProjectModal, setCreateProjectModal] = useState(false);
// theme
@ -136,11 +140,16 @@ export const ProjectSidebarList: FC = () => {
return (
<>
<CreateProjectModal isOpen={isCreateProjectModal} setIsOpen={setCreateProjectModal} />
<CreateProjectModal
isOpen={isCreateProjectModal}
setIsOpen={setCreateProjectModal}
user={user}
/>
<DeleteProjectModal
isOpen={deleteProjectModal}
onClose={() => setDeleteProjectModal(false)}
data={projectToDelete}
user={user}
/>
<div className="mt-2.5 h-full overflow-y-auto border-t border-brand-base bg-brand-sidebar pt-2.5">
{favoriteProjects && favoriteProjects.length > 0 && (

View File

@ -19,7 +19,7 @@ import { CustomSelect, Input, PrimaryButton, SecondaryButton, TextArea } from "c
// icons
import { ChevronDownIcon } from "@heroicons/react/24/outline";
// types
import type { IState, IStateResponse } from "types";
import type { ICurrentUserResponse, IState, IStateResponse } from "types";
// fetch keys
import { STATES_LIST } from "constants/fetch-keys";
// constants
@ -30,6 +30,7 @@ type Props = {
isOpen: boolean;
projectId: string;
handleClose: () => void;
user: ICurrentUserResponse | undefined;
};
const defaultValues: Partial<IState> = {
@ -39,7 +40,7 @@ const defaultValues: Partial<IState> = {
group: "backlog",
};
export const CreateStateModal: React.FC<Props> = ({ isOpen, projectId, handleClose }) => {
export const CreateStateModal: React.FC<Props> = ({ isOpen, projectId, handleClose, user }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
@ -69,7 +70,7 @@ export const CreateStateModal: React.FC<Props> = ({ isOpen, projectId, handleClo
};
await stateService
.createState(workspaceSlug as string, projectId, payload)
.createState(workspaceSlug as string, projectId, payload, user)
.then((res) => {
mutate<IStateResponse>(
STATES_LIST(projectId.toString()),

View File

@ -17,7 +17,7 @@ import useToast from "hooks/use-toast";
// ui
import { CustomSelect, Input, PrimaryButton, SecondaryButton } from "components/ui";
// types
import type { IState, IStateResponse } from "types";
import type { ICurrentUserResponse, IState, IStateResponse } from "types";
// fetch-keys
import { STATES_LIST } from "constants/fetch-keys";
// constants
@ -27,6 +27,7 @@ type Props = {
data: IState | null;
onClose: () => void;
selectedGroup: StateGroup | null;
user: ICurrentUserResponse | undefined;
};
export type StateGroup = "backlog" | "unstarted" | "started" | "completed" | "cancelled" | null;
@ -37,7 +38,12 @@ const defaultValues: Partial<IState> = {
group: "backlog",
};
export const CreateUpdateStateInline: React.FC<Props> = ({ data, onClose, selectedGroup }) => {
export const CreateUpdateStateInline: React.FC<Props> = ({
data,
onClose,
selectedGroup,
user,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -83,7 +89,7 @@ export const CreateUpdateStateInline: React.FC<Props> = ({ data, onClose, select
if (!data) {
await stateService
.createState(workspaceSlug.toString(), projectId.toString(), { ...payload })
.createState(workspaceSlug.toString(), projectId.toString(), { ...payload }, user)
.then((res) => {
mutate<IStateResponse>(
STATES_LIST(projectId.toString()),
@ -121,9 +127,15 @@ export const CreateUpdateStateInline: React.FC<Props> = ({ data, onClose, select
});
} else {
await stateService
.updateState(workspaceSlug.toString(), projectId.toString(), data.id, {
...payload,
})
.updateState(
workspaceSlug.toString(),
projectId.toString(),
data.id,
{
...payload,
},
user
)
.then(() => {
mutate(STATES_LIST(projectId.toString()));
handleClose();

View File

@ -15,7 +15,7 @@ import useToast from "hooks/use-toast";
// ui
import { DangerButton, SecondaryButton } from "components/ui";
// types
import type { IState, IStateResponse } from "types";
import type { ICurrentUserResponse, IState, IStateResponse } from "types";
// fetch-keys
import { STATES_LIST } from "constants/fetch-keys";
@ -23,9 +23,10 @@ type Props = {
isOpen: boolean;
onClose: () => void;
data: IState | null;
user: ICurrentUserResponse | undefined;
};
export const DeleteStateModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
export const DeleteStateModal: React.FC<Props> = ({ isOpen, onClose, data, user }) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter();
@ -44,7 +45,7 @@ export const DeleteStateModal: React.FC<Props> = ({ isOpen, onClose, data }) =>
setIsDeleteLoading(true);
await stateServices
.deleteState(workspaceSlug as string, data.project, data.id)
.deleteState(workspaceSlug as string, data.project, data.id, user)
.then(() => {
mutate<IStateResponse>(
STATES_LIST(data.project),

View File

@ -21,7 +21,7 @@ import { addSpaceIfCamelCase } from "helpers/string.helper";
import { groupBy, orderArrayBy } from "helpers/array.helper";
import { orderStateGroups } from "helpers/state.helper";
// types
import { IState } from "types";
import { ICurrentUserResponse, IState } from "types";
// fetch-keys
import { STATES_LIST } from "constants/fetch-keys";
@ -31,6 +31,7 @@ type Props = {
statesList: IState[];
handleEditState: () => void;
handleDeleteState: () => void;
user: ICurrentUserResponse | undefined;
};
export const SingleState: React.FC<Props> = ({
@ -39,6 +40,7 @@ export const SingleState: React.FC<Props> = ({
statesList,
handleEditState,
handleDeleteState,
user,
}) => {
const [isSubmitting, setIsSubmitting] = useState(false);
@ -67,14 +69,26 @@ export const SingleState: React.FC<Props> = ({
if (currentDefaultState)
stateService
.patchState(workspaceSlug as string, projectId as string, currentDefaultState?.id ?? "", {
default: false,
})
.patchState(
workspaceSlug as string,
projectId as string,
currentDefaultState?.id ?? "",
{
default: false,
},
user
)
.then(() => {
stateService
.patchState(workspaceSlug as string, projectId as string, state.id, {
default: true,
})
.patchState(
workspaceSlug as string,
projectId as string,
state.id,
{
default: true,
},
user
)
.then(() => {
mutate(STATES_LIST(projectId as string));
setIsSubmitting(false);
@ -85,9 +99,15 @@ export const SingleState: React.FC<Props> = ({
});
else
stateService
.patchState(workspaceSlug as string, projectId as string, state.id, {
default: true,
})
.patchState(
workspaceSlug as string,
projectId as string,
state.id,
{
default: true,
},
user
)
.then(() => {
mutate(STATES_LIST(projectId as string));
setIsSubmitting(false);
@ -121,9 +141,15 @@ export const SingleState: React.FC<Props> = ({
);
stateService
.patchState(workspaceSlug as string, projectId as string, state.id, {
sequence: newSequence,
})
.patchState(
workspaceSlug as string,
projectId as string,
state.id,
{
sequence: newSequence,
},
user
)
.then((res) => {
console.log(res);
mutate(STATES_LIST(projectId as string));

View File

@ -15,7 +15,7 @@ import { DangerButton, SecondaryButton } from "components/ui";
// icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// types
import type { IView } from "types";
import type { ICurrentUserResponse, IView } from "types";
// fetch-keys
import { VIEWS_LIST } from "constants/fetch-keys";
@ -23,9 +23,10 @@ type Props = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
data: IView | null;
user: ICurrentUserResponse | undefined;
};
export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, setIsOpen }) => {
export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, setIsOpen, user }) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter();
@ -43,7 +44,7 @@ export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, setIsOpen }) =>
if (!workspaceSlug || !data || !projectId) return;
await viewsService
.deleteView(workspaceSlug as string, projectId as string, data.id)
.deleteView(workspaceSlug as string, projectId as string, data.id, user)
.then(() => {
mutate<IView[]>(VIEWS_LIST(projectId as string), (views) =>
views?.filter((view) => view.id !== data.id)

View File

@ -13,7 +13,7 @@ import useToast from "hooks/use-toast";
// components
import { ViewForm } from "components/views";
// types
import { IView } from "types";
import { ICurrentUserResponse, IView } from "types";
// fetch-keys
import { VIEWS_LIST } from "constants/fetch-keys";
@ -22,6 +22,7 @@ type Props = {
handleClose: () => void;
data?: IView | null;
preLoadedData?: Partial<IView> | null;
user: ICurrentUserResponse | undefined;
};
export const CreateUpdateViewModal: React.FC<Props> = ({
@ -29,6 +30,7 @@ export const CreateUpdateViewModal: React.FC<Props> = ({
handleClose,
data,
preLoadedData,
user,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -45,7 +47,7 @@ export const CreateUpdateViewModal: React.FC<Props> = ({
query_data: payload.query,
};
await viewsService
.createView(workspaceSlug as string, projectId as string, payload)
.createView(workspaceSlug as string, projectId as string, payload, user)
.then(() => {
mutate(VIEWS_LIST(projectId as string));
handleClose();
@ -71,7 +73,7 @@ export const CreateUpdateViewModal: React.FC<Props> = ({
query_data: payload.query,
};
await viewsService
.updateView(workspaceSlug as string, projectId as string, data?.id ?? "", payloadData)
.updateView(workspaceSlug as string, projectId as string, data?.id ?? "", payloadData, user)
.then((res) => {
mutate<IView[]>(
VIEWS_LIST(projectId as string),

View File

@ -11,7 +11,7 @@ import useToast from "hooks/use-toast";
// ui
import { CustomSelect, Input, PrimaryButton } from "components/ui";
// types
import { IWorkspace } from "types";
import { ICurrentUserResponse, IWorkspace } from "types";
// fetch-keys
import { USER_WORKSPACES } from "constants/fetch-keys";
// constants
@ -25,6 +25,7 @@ type Props = {
company_size: number | null;
};
setDefaultValues: Dispatch<SetStateAction<any>>;
user: ICurrentUserResponse | undefined;
};
const restrictedUrls = [
@ -44,6 +45,7 @@ export const CreateWorkspaceForm: React.FC<Props> = ({
onSubmit,
defaultValues,
setDefaultValues,
user,
}) => {
const [slugError, setSlugError] = useState(false);
const [invalidSlug, setInvalidSlug] = useState(false);
@ -66,7 +68,7 @@ export const CreateWorkspaceForm: React.FC<Props> = ({
if (res.status === true && !restrictedUrls.includes(formData.slug)) {
setSlugError(false);
await workspaceService
.createWorkspace(formData)
.createWorkspace(formData, user)
.then((res) => {
setToastAlert({
type: "success",

View File

@ -15,7 +15,7 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// ui
import { DangerButton, Input, SecondaryButton } from "components/ui";
// types
import type { IWorkspace } from "types";
import type { ICurrentUserResponse, IWorkspace } from "types";
// fetch-keys
import { USER_WORKSPACES } from "constants/fetch-keys";
@ -23,9 +23,10 @@ type Props = {
isOpen: boolean;
data: IWorkspace | null;
onClose: () => void;
user: ICurrentUserResponse | undefined;
};
export const DeleteWorkspaceModal: React.FC<Props> = ({ isOpen, data, onClose }) => {
export const DeleteWorkspaceModal: React.FC<Props> = ({ isOpen, data, onClose, user }) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [confirmWorkspaceName, setConfirmWorkspaceName] = useState("");
@ -57,7 +58,7 @@ export const DeleteWorkspaceModal: React.FC<Props> = ({ isOpen, data, onClose })
setIsDeleteLoading(true);
if (!data || !canDelete) return;
await workspaceService
.deleteWorkspace(data.slug)
.deleteWorkspace(data.slug, user)
.then(() => {
handleClose();
router.push("/");

View File

@ -10,7 +10,7 @@ import { CustomSelect, Input, PrimaryButton, SecondaryButton } from "components/
// hooks
import useToast from "hooks/use-toast";
// types
import { IWorkspaceMemberInvitation } from "types";
import { ICurrentUserResponse, IWorkspaceMemberInvitation } from "types";
// fetch keys
import { WORKSPACE_INVITATIONS } from "constants/fetch-keys";
// constants
@ -21,6 +21,7 @@ type Props = {
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
workspace_slug: string;
members: any[];
user: ICurrentUserResponse | undefined;
};
const defaultValues: Partial<IWorkspaceMemberInvitation> = {
@ -33,6 +34,7 @@ const SendWorkspaceInvitationModal: React.FC<Props> = ({
setIsOpen,
workspace_slug,
members,
user,
}) => {
const { setToastAlert } = useToast();
@ -54,7 +56,7 @@ const SendWorkspaceInvitationModal: React.FC<Props> = ({
const onSubmit = async (formData: IWorkspaceMemberInvitation) => {
await workspaceService
.inviteWorkspace(workspace_slug, { emails: [formData] })
.inviteWorkspace(workspace_slug, { emails: [formData] }, user)
.then((res) => {
setIsOpen(false);
handleClose();
@ -101,7 +103,10 @@ const SendWorkspaceInvitationModal: React.FC<Props> = ({
<form onSubmit={handleSubmit(onSubmit)}>
<div className="space-y-5">
<div>
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-brand-base">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-brand-base"
>
Members
</Dialog.Title>
<p className="text-sm text-brand-secondary">

View File

@ -46,7 +46,10 @@ const SingleInvitation: React.FC<Props> = ({
<div className="min-w-0 flex-1">
<div className="text-sm font-medium">{truncateText(invitation.workspace.name, 30)}</div>
<p className="text-sm text-brand-secondary">
Invited by {invitation.workspace.owner.first_name}
Invited by{" "}
{invitation.created_by_detail
? invitation.created_by_detail.first_name
: invitation.workspace.owner.first_name}
</p>
</div>
<div className="flex-shrink-0 self-center">

View File

@ -18,6 +18,7 @@ import {
IProjectMember,
TIssueGroupByOptions,
TIssueOrderByOptions,
ICurrentUserResponse,
} from "types";
// fetch-keys
import {
@ -26,6 +27,7 @@ import {
USER_PROJECT_VIEW,
VIEW_DETAILS,
} from "constants/fetch-keys";
import useUserAuth from "hooks/use-user-auth";
export const issueViewContext = createContext<ContextType>({} as ContextType);
@ -212,33 +214,54 @@ const saveCycleFilters = async (
workspaceSlug: string,
projectId: string,
cycleId: string,
state: any
state: any,
user: ICurrentUserResponse | undefined
) => {
await cyclesService.patchCycle(workspaceSlug, projectId, cycleId, {
...state,
});
await cyclesService.patchCycle(
workspaceSlug,
projectId,
cycleId,
{
...state,
},
user
);
};
const saveModuleFilters = async (
workspaceSlug: string,
projectId: string,
moduleId: string,
state: any
state: any,
user: ICurrentUserResponse | undefined
) => {
await modulesService.patchModule(workspaceSlug, projectId, moduleId, {
...state,
});
await modulesService.patchModule(
workspaceSlug,
projectId,
moduleId,
{
...state,
},
user
);
};
const saveViewFilters = async (
workspaceSlug: string,
projectId: string,
viewId: string,
state: any
state: any,
user: ICurrentUserResponse | undefined
) => {
await viewsService.patchView(workspaceSlug, projectId, viewId, {
...state,
});
await viewsService.patchView(
workspaceSlug,
projectId,
viewId,
{
...state,
},
user
);
};
const setNewDefault = async (workspaceSlug: string, projectId: string, state: any) => {
@ -267,6 +290,8 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const { user } = useUserAuth();
const { data: myViewProps, mutate: mutateMyViewProps } = useSWR(
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId as string) : null,
workspaceSlug && projectId
@ -505,14 +530,20 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
};
}, false);
saveCycleFilters(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), {
view_props: {
filters: {
...state.filters,
...property,
saveCycleFilters(
workspaceSlug.toString(),
projectId.toString(),
cycleId.toString(),
{
view_props: {
filters: {
...state.filters,
...property,
},
},
},
});
user
);
} else if (moduleId) {
mutateModuleDetails((prevData: any) => {
if (!prevData) return prevData;
@ -528,14 +559,20 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
};
}, false);
saveModuleFilters(workspaceSlug.toString(), projectId.toString(), moduleId.toString(), {
view_props: {
filters: {
...state.filters,
...property,
saveModuleFilters(
workspaceSlug.toString(),
projectId.toString(),
moduleId.toString(),
{
view_props: {
filters: {
...state.filters,
...property,
},
},
},
});
user
);
} else if (viewId) {
mutateViewDetails((prevData: any) => {
if (!prevData) return prevData;
@ -548,12 +585,18 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
};
}, false);
if (saveToServer)
saveViewFilters(workspaceSlug as string, projectId as string, viewId as string, {
query_data: {
...state.filters,
...property,
saveViewFilters(
workspaceSlug as string,
projectId as string,
viewId as string,
{
query_data: {
...state.filters,
...property,
},
},
});
user
);
} else {
mutateMyViewProps((prevData) => {
if (!prevData) return prevData;

View File

@ -6,6 +6,8 @@ import useSWR from "swr";
// react-hook-form
import { useForm } from "react-hook-form";
// hooks
import useUserAuth from "hooks/use-user-auth";
// headless ui
import { Tab } from "@headlessui/react";
// services
@ -35,6 +37,8 @@ const Analytics = () => {
const router = useRouter();
const { workspaceSlug } = router.query;
const { user } = useUserAuth();
const { control, watch, setValue } = useForm<IAnalyticsParams>({ defaultValues });
const params: IAnalyticsParams = {
@ -59,7 +63,7 @@ const Analytics = () => {
? "WORKSPACE_SCOPE_AND_DEMAND_ANALYTICS"
: "WORKSPACE_CUSTOM_ANALYTICS";
trackEventServices.trackAnalyticsEvent(eventPayload, eventType);
trackEventServices.trackAnalyticsEvent(eventPayload, eventType, user);
};
useEffect(() => {
@ -67,7 +71,8 @@ const Analytics = () => {
trackEventServices.trackAnalyticsEvent(
{ workspaceSlug: workspaceSlug?.toString() },
"WORKSPACE_SCOPE_AND_DEMAND_ANALYTICS"
"WORKSPACE_SCOPE_AND_DEMAND_ANALYTICS",
user
);
}, [workspaceSlug]);
@ -119,6 +124,7 @@ const Analytics = () => {
params={params}
control={control}
setValue={setValue}
user={user}
fullScreen
/>
</Tab.Panel>

View File

@ -19,6 +19,7 @@ import cycleServices from "services/cycles.service";
import projectService from "services/project.service";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// components
import { AnalyticsProjectModal } from "components/analytics";
// ui
@ -44,6 +45,8 @@ const SingleCycle: React.FC = () => {
const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query;
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const { data: activeProject } = useSWR(
@ -94,7 +97,7 @@ const SingleCycle: React.FC = () => {
if (!workspaceSlug || !projectId) return;
await issuesService
.addIssueToCycle(workspaceSlug as string, projectId as string, cycleId as string, data)
.addIssueToCycle(workspaceSlug as string, projectId as string, cycleId as string, data, user)
.then(() => {
mutate(CYCLE_ISSUES(cycleId as string));
})
@ -185,6 +188,7 @@ const SingleCycle: React.FC = () => {
cycle={cycleDetails}
isOpen={cycleSidebar}
isCompleted={cycleStatus === "completed" ?? false}
user={user}
/>
</ProjectAuthorizationWrapper>
</IssueViewContextProvider>

View File

@ -8,6 +8,7 @@ import useSWR from "swr";
import { Tab } from "@headlessui/react";
// hooks
import useLocalStorage from "hooks/use-local-storage";
import useUserAuth from "hooks/use-user-auth";
// services
import cycleService from "services/cycles.service";
import projectService from "services/project.service";
@ -62,6 +63,8 @@ const ProjectCycles: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { data: activeProject } = useSWR(
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
workspaceSlug && projectId
@ -110,6 +113,7 @@ const ProjectCycles: NextPage = () => {
isOpen={createUpdateCycleModal}
handleClose={() => setCreateUpdateCycleModal(false)}
data={selectedCycle}
user={user}
/>
<div className="space-y-5 p-8 h-full flex flex-col overflow-hidden">
<div className="flex gap-4 justify-between">

View File

@ -7,6 +7,8 @@ import useSWR, { mutate } from "swr";
// react-hook-form
import { useForm } from "react-hook-form";
// hooks
import useUserAuth from "hooks/use-user-auth";
// services
import issuesService from "services/issues.service";
// layouts
@ -50,6 +52,8 @@ const IssueDetailsPage: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
const { user } = useUserAuth();
const { data: issueDetails, mutate: mutateIssueDetails } = useSWR<IIssue | undefined>(
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
workspaceSlug && projectId && issueId
@ -92,7 +96,7 @@ const IssueDetailsPage: NextPage = () => {
const payload = { ...formData };
await issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload)
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload, user)
.then((res) => {
mutateIssueDetails();
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
@ -192,7 +196,7 @@ const IssueDetailsPage: NextPage = () => {
) : null}
<IssueDescriptionForm issue={issueDetails} handleFormSubmit={submitChanges} />
<div className="mt-2 space-y-2">
<SubIssuesList parentIssue={issueDetails} />
<SubIssuesList parentIssue={issueDetails} user={user} />
</div>
</div>
<div className="flex flex-col gap-3 py-3">
@ -204,8 +208,8 @@ const IssueDetailsPage: NextPage = () => {
</div>
<div className="space-y-5 pt-3">
<h3 className="text-lg text-brand-base">Comments/Activity</h3>
<IssueActivitySection />
<AddComment />
<IssueActivitySection user={user} />
<AddComment user={user} />
</div>
</div>
<div className="basis-1/3 space-y-5 border-l border-brand-base p-5">

View File

@ -17,6 +17,7 @@ import modulesService from "services/modules.service";
import issuesService from "services/issues.service";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// layouts
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
// contexts
@ -49,6 +50,8 @@ const SingleModule: React.FC = () => {
const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query;
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const { data: issues } = useSWR(
@ -95,7 +98,13 @@ const SingleModule: React.FC = () => {
if (!workspaceSlug || !projectId) return;
await modulesService
.addIssuesToModule(workspaceSlug as string, projectId as string, moduleId as string, data)
.addIssuesToModule(
workspaceSlug as string,
projectId as string,
moduleId as string,
data,
user
)
.then(() => mutate(MODULE_ISSUES(moduleId as string)))
.catch(() =>
setToastAlert({
@ -186,6 +195,7 @@ const SingleModule: React.FC = () => {
module={moduleDetails}
isOpen={moduleSidebar}
moduleIssues={moduleIssues}
user={user}
/>
</ProjectAuthorizationWrapper>
</IssueViewContextProvider>

View File

@ -6,6 +6,8 @@ import useSWR from "swr";
// layouts
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
// hooks
import useUserAuth from "hooks/use-user-auth";
// services
import projectService from "services/project.service";
import modulesService from "services/modules.service";
@ -37,6 +39,8 @@ const ProjectModules: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { data: activeProject } = useSWR(
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
workspaceSlug && projectId
@ -89,6 +93,7 @@ const ProjectModules: NextPage = () => {
isOpen={createUpdateModule}
setIsOpen={setCreateUpdateModule}
data={selectedModule}
user={user}
/>
{modules ? (
modules.length > 0 ? (
@ -126,6 +131,7 @@ const ProjectModules: NextPage = () => {
key={module.id}
module={module}
handleEditModule={() => handleEditModule(module)}
user={user}
/>
))}
</div>

View File

@ -116,7 +116,7 @@ const SinglePage: NextPage = () => {
if (!formData.name || formData.name.length === 0 || formData.name === "") return;
await pagesService
.patchPage(workspaceSlug as string, projectId as string, pageId as string, formData)
.patchPage(workspaceSlug as string, projectId as string, pageId as string, formData, user)
.then(() => {
mutate<IPage>(
PAGE_DETAILS(pageId as string),
@ -143,7 +143,7 @@ const SinglePage: NextPage = () => {
);
await pagesService
.patchPage(workspaceSlug as string, projectId as string, pageId as string, formData)
.patchPage(workspaceSlug as string, projectId as string, pageId as string, formData, user)
.then(() => {
mutate(PAGE_DETAILS(pageId as string));
});
@ -237,7 +237,8 @@ const SinglePage: NextPage = () => {
result.draggableId,
{
sort_order: newSortOrder,
}
},
user
);
};
@ -529,6 +530,7 @@ const SinglePage: NextPage = () => {
block={block}
projectDetails={projectDetails}
index={index}
user={user}
/>
))}
{provided.placeholder}
@ -542,6 +544,7 @@ const SinglePage: NextPage = () => {
<CreateUpdateBlockInline
handleClose={() => setCreateBlockForm(false)}
focus="name"
user={user}
/>
</div>
)}
@ -550,6 +553,7 @@ const SinglePage: NextPage = () => {
isOpen={labelModal}
handleClose={() => setLabelModal(false)}
projectId={projectId}
user={user}
/>
)}
</>
@ -562,7 +566,7 @@ const SinglePage: NextPage = () => {
</div>
</div>
<div>
<CreateBlock />
<CreateBlock user={user} />
</div>
</div>
) : (

View File

@ -27,6 +27,7 @@ import { TPageViewProps } from "types";
import type { NextPage } from "next";
// fetch-keys
import { PROJECT_DETAILS } from "constants/fetch-keys";
import useUserAuth from "hooks/use-user-auth";
const AllPagesList = dynamic<TPagesListProps>(
() => import("components/pages").then((a) => a.AllPagesList),
@ -66,6 +67,8 @@ const ProjectPages: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { storedValue: pageTab, setValue: setPageTab } = useLocalStorage("pageTab", "Recent");
const { data: projectDetails } = useSWR(
@ -98,6 +101,7 @@ const ProjectPages: NextPage = () => {
<CreateUpdatePageModal
isOpen={createUpdatePageModal}
handleClose={() => setCreateUpdatePageModal(false)}
user={user}
/>
<ProjectAuthorizationWrapper
breadcrumbs={

View File

@ -13,6 +13,7 @@ import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
import projectService from "services/project.service";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// components
import { SettingsHeader } from "components/project";
// ui
@ -35,6 +36,8 @@ const ControlSettings: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { data: projectDetails } = useSWR<IProject>(
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
workspaceSlug && projectId
@ -65,7 +68,7 @@ const ControlSettings: NextPage = () => {
};
await projectService
.updateProject(workspaceSlug as string, projectId as string, payload)
.updateProject(workspaceSlug as string, projectId as string, payload, user)
.then((res) => {
mutate(PROJECT_DETAILS(projectId as string));
mutate(PROJECTS_LIST(workspaceSlug as string));

View File

@ -16,6 +16,7 @@ import { CreateUpdateEstimateModal, SingleEstimate } from "components/estimates"
import { SettingsHeader } from "components/project";
//hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// ui
import { EmptyState, Loader, SecondaryButton } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
@ -37,6 +38,8 @@ const EstimatesSettings: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const { projectDetails } = useProjectDetails();
@ -63,7 +66,7 @@ const EstimatesSettings: NextPage = () => {
);
estimatesService
.deleteEstimate(workspaceSlug as string, projectId as string, estimateId)
.deleteEstimate(workspaceSlug as string, projectId as string, estimateId, user)
.catch(() => {
setToastAlert({
type: "error",
@ -87,7 +90,7 @@ const EstimatesSettings: NextPage = () => {
);
projectService
.updateProject(workspaceSlug as string, projectId as string, { estimate: null })
.updateProject(workspaceSlug as string, projectId as string, { estimate: null }, user)
.catch(() =>
setToastAlert({
type: "error",
@ -106,6 +109,7 @@ const EstimatesSettings: NextPage = () => {
setEstimateFormOpen(false);
setEstimateToUpdate(undefined);
}}
user={user}
/>
<ProjectAuthorizationWrapper
breadcrumbs={
@ -149,6 +153,7 @@ const EstimatesSettings: NextPage = () => {
estimate={estimate}
editEstimate={(estimate) => editEstimate(estimate)}
handleEstimateDelete={(estimateId) => removeEstimate(estimateId)}
user={user}
/>
))}
</section>

View File

@ -11,6 +11,7 @@ import trackEventServices, { MiscellaneousEventType } from "services/track-event
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// components
import { SettingsHeader } from "components/project";
// ui
@ -75,6 +76,8 @@ const FeaturesSettings: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const { data: projectDetails } = useSWR(
@ -134,7 +137,7 @@ const FeaturesSettings: NextPage = () => {
});
await projectService
.updateProject(workspaceSlug as string, projectId as string, formData)
.updateProject(workspaceSlug as string, projectId as string, formData, user)
.then(() => {
mutate(
projectDetails.is_favorite
@ -194,7 +197,8 @@ const FeaturesSettings: NextPage = () => {
},
!projectDetails?.[feature.property as keyof IProject]
? getEventType(feature.title, true)
: getEventType(feature.title, false)
: getEventType(feature.title, false),
user
);
handleSubmit({
[feature.property]: !projectDetails?.[feature.property as keyof IProject],

View File

@ -17,6 +17,7 @@ import { ImagePickerPopover } from "components/core";
import EmojiIconPicker from "components/emoji-icon-picker";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// ui
import {
Input,
@ -45,6 +46,8 @@ const defaultValues: Partial<IProject> = {
const GeneralSettings: NextPage = () => {
const [selectProject, setSelectedProject] = useState<string | null>(null);
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const router = useRouter();
@ -83,7 +86,7 @@ const GeneralSettings: NextPage = () => {
if (!workspaceSlug || !projectDetails) return;
await projectService
.updateProject(workspaceSlug as string, projectDetails.id, payload)
.updateProject(workspaceSlug as string, projectDetails.id, payload, user)
.then((res) => {
mutate<IProject>(
PROJECT_DETAILS(projectDetails.id),
@ -154,6 +157,7 @@ const GeneralSettings: NextPage = () => {
onSuccess={() => {
router.push(`/${workspaceSlug}/projects`);
}}
user={user}
/>
<form onSubmit={handleSubmit(onSubmit)} className="p-8">
<SettingsHeader />

View File

@ -4,6 +4,8 @@ import { useRouter } from "next/router";
import useSWR from "swr";
// hooks
import useUserAuth from "hooks/use-user-auth";
// services
import projectService from "services/project.service";
import issuesService from "services/issues.service";
@ -47,6 +49,8 @@ const LabelsSettings: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const scrollToRef = useRef<HTMLDivElement>(null);
const { data: projectDetails } = useSWR(
@ -85,11 +89,13 @@ const LabelsSettings: NextPage = () => {
isOpen={labelsListModal}
handleClose={() => setLabelsListModal(false)}
parent={parentLabel}
user={user}
/>
<DeleteLabelModal
isOpen={!!selectDeleteLabel}
data={selectDeleteLabel ?? null}
onClose={() => setSelectDeleteLabel(null)}
user={user}
/>
<ProjectAuthorizationWrapper
breadcrumbs={
@ -160,6 +166,7 @@ const LabelsSettings: NextPage = () => {
});
}}
handleLabelDelete={() => setSelectDeleteLabel(label)}
user={user}
/>
);
})

View File

@ -135,6 +135,7 @@ const MembersSettings: NextPage = () => {
isOpen={inviteModal}
setIsOpen={setInviteModal}
members={members}
user={user}
/>
<ProjectAuthorizationWrapper
breadcrumbs={

View File

@ -8,6 +8,7 @@ import useSWR from "swr";
import stateService from "services/state.service";
// hooks
import useProjectDetails from "hooks/use-project-details";
import useUserAuth from "hooks/use-user-auth";
// layouts
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
// components
@ -38,6 +39,8 @@ const StatesSettings: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { projectDetails } = useProjectDetails();
const { data: states } = useSWR(
@ -55,6 +58,7 @@ const StatesSettings: NextPage = () => {
isOpen={!!selectDeleteState}
data={statesList?.find((s) => s.id === selectDeleteState) ?? null}
onClose={() => setSelectDeleteState(null)}
user={user}
/>
<ProjectAuthorizationWrapper
breadcrumbs={
@ -100,6 +104,7 @@ const StatesSettings: NextPage = () => {
}}
data={null}
selectedGroup={key as keyof StateGroup}
user={user}
/>
)}
{orderedStateGroups[key].map((state, index) =>
@ -111,6 +116,7 @@ const StatesSettings: NextPage = () => {
statesList={statesList}
handleEditState={() => setSelectedState(state.id)}
handleDeleteState={() => setSelectDeleteState(state.id)}
user={user}
/>
) : (
<div
@ -126,6 +132,7 @@ const StatesSettings: NextPage = () => {
statesList?.find((state) => state.id === selectedState) ?? null
}
selectedGroup={key as keyof StateGroup}
user={user}
/>
</div>
)

View File

@ -4,6 +4,8 @@ import { useRouter } from "next/router";
import useSWR from "swr";
// hooks
import useUserAuth from "hooks/use-user-auth";
// services
import viewsService from "services/views.service";
import projectService from "services/project.service";
@ -34,6 +36,8 @@ const ProjectViews: NextPage = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth();
const { data: activeProject } = useSWR(
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
workspaceSlug && projectId
@ -86,11 +90,13 @@ const ProjectViews: NextPage = () => {
isOpen={createUpdateViewModal}
handleClose={() => setCreateUpdateViewModal(false)}
data={selectedViewToUpdate}
user={user}
/>
<DeleteViewModal
isOpen={deleteViewModal}
data={selectedViewToDelete}
setIsOpen={setDeleteViewModal}
user={user}
/>
{views ? (
views.length > 0 ? (

View File

@ -9,6 +9,7 @@ import projectService from "services/project.service";
// hooks
import useProjects from "hooks/use-projects";
import useWorkspaces from "hooks/use-workspaces";
import useUserAuth from "hooks/use-user-auth";
// layouts
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
// components
@ -30,6 +31,8 @@ const ProjectsPage: NextPage = () => {
// router
const router = useRouter();
const { workspaceSlug } = router.query;
const { user } = useUserAuth();
// context data
const { activeWorkspace } = useWorkspaces();
const { projects } = useProjects();
@ -81,6 +84,7 @@ const ProjectsPage: NextPage = () => {
isOpen={!!deleteProject}
onClose={() => setDeleteProject(null)}
data={projects?.find((item) => item.id === deleteProject) ?? null}
user={user}
/>
{projects ? (
<div className="p-8">

View File

@ -12,6 +12,7 @@ import workspaceService from "services/workspace.service";
import fileService from "services/file.service";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// layouts
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
import SettingsNavbar from "layouts/settings-navbar";
@ -49,6 +50,8 @@ const WorkspaceSettings: NextPage = () => {
const router = useRouter();
const { workspaceSlug } = router.query;
const { user } = useUserAuth();
const { setToastAlert } = useToast();
const { data: activeWorkspace } = useSWR(
@ -82,7 +85,7 @@ const WorkspaceSettings: NextPage = () => {
};
await workspaceService
.updateWorkspace(activeWorkspace.slug, payload)
.updateWorkspace(activeWorkspace.slug, payload, user)
.then((res) => {
mutate<IWorkspace[]>(USER_WORKSPACES, (prevData) =>
prevData?.map((workspace) => (workspace.id === res.id ? res : workspace))
@ -114,7 +117,7 @@ const WorkspaceSettings: NextPage = () => {
fileService.deleteFile(asset).then(() => {
workspaceService
.updateWorkspace(activeWorkspace.slug, { logo: "" })
.updateWorkspace(activeWorkspace.slug, { logo: "" }, user)
.then((res) => {
setToastAlert({
type: "success",
@ -169,6 +172,7 @@ const WorkspaceSettings: NextPage = () => {
setIsOpen(false);
}}
data={activeWorkspace ?? null}
user={user}
/>
<div className="p-8">
<SettingsHeader />

View File

@ -135,6 +135,7 @@ const MembersSettings: NextPage = () => {
setIsOpen={setInviteModal}
workspace_slug={workspaceSlug as string}
members={members}
user={user}
/>
<WorkspaceAuthorizationLayout
breadcrumbs={

View File

@ -10,28 +10,12 @@ const jitsu = createClient({
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { eventName, extra } = req.body;
const { eventName, user, extra } = req.body;
if (!eventName) {
return res.status(400).json({ message: "Bad request" });
}
const cookie = convertCookieStringToObject(req.headers.cookie);
const accessToken = cookie?.accessToken;
if (!accessToken) return res.status(401).json({ message: "Unauthorized" });
const user = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/users/me/`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
})
.then((res) => res.json())
.then((data) => data.user)
.catch(() => res.status(401).json({ message: "Unauthorized" }));
if (!user) return res.status(401).json({ message: "Unauthorized" });
// TODO: cache user info

View File

@ -47,6 +47,7 @@ const CreateWorkspace: NextPage = () => {
defaultValues={defaultValues}
setDefaultValues={() => {}}
onSubmit={(res) => router.push(`/${res.slug}`)}
user={user}
/>
</div>
</div>

View File

@ -95,21 +95,21 @@ const HomePage: NextPage = () => {
<Spinner />
</div>
) : (
<div className="flex h-screen w-full items-center justify-center overflow-auto bg-gray-50">
<div className="flex h-screen w-full items-center justify-center overflow-auto">
<div className="flex min-h-full w-full flex-col justify-center py-12 px-6 lg:px-8">
<div className="flex flex-col gap-10 sm:mx-auto sm:w-full sm:max-w-md">
<div className="flex flex-col items-center justify-center gap-10">
<Image src={Logo} height={80} width={80} alt="Plane Web Logo" />
<div className="text-center text-xl font-medium text-black">
<div className="text-center text-xl font-medium text-brand-base">
Sign In to your Plane Account
</div>
</div>
<div className="flex flex-col rounded-[10px] bg-white shadow-md">
<div className="flex flex-col rounded-[10px] bg-brand-base shadow-md">
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
<>
<EmailCodeForm />
<div className="flex flex-col gap-3 py-5 px-5 border-t items-center justify-center border-gray-300 ">
<div className="flex flex-col items-center justify-center gap-3 border-t border-brand-base py-5 px-5">
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
<GithubLoginButton handleSignIn={handleGithubSignIn} />
</div>

View File

@ -124,7 +124,7 @@ const OnBoard: NextPage = () => {
</div>
</div>
) : workspaces && workspaces.length > 0 ? (
<div className="flex flex-col gap-y-3">
<div className="flex flex-col w-full overflow-auto gap-y-3">
<h2 className="mb-4 text-xl font-medium">Your workspaces</h2>
{workspaces.map((workspace) => (
<Link key={workspace.id} href={workspace.slug}>

View File

@ -49,18 +49,18 @@ const MagicSignIn: NextPage = () => {
return (
<DefaultLayout>
<div className="flex h-screen w-full items-center justify-center overflow-auto bg-gray-50">
<div className="h-screen w-full overflow-auto bg-brand-surface-1">
{isSigningIn ? (
<div className="flex h-full w-full flex-col items-center justify-center gap-y-2">
<h2 className="text-4xl">Signing you in...</h2>
<p className="text-sm text-gray-600">
<p className="text-sm text-brand-secondary">
Please wait while we are preparing your take off.
</p>
</div>
) : errorSigningIn ? (
<div className="flex h-full w-full flex-col items-center justify-center gap-y-2">
<h2 className="text-4xl">Error</h2>
<p className="text-sm text-gray-600">
<p className="text-sm text-brand-secondary">
{errorSigningIn}.
<span
className="cursor-pointer underline"
@ -90,7 +90,7 @@ const MagicSignIn: NextPage = () => {
) : (
<div className="flex h-full w-full flex-col items-center justify-center gap-y-2">
<h2 className="text-4xl">Success</h2>
<p className="text-sm text-gray-600">Redirecting you to the app...</p>
<p className="text-sm text-brand-secondary">Redirecting you to the app...</p>
</div>
)}
</div>

View File

@ -53,9 +53,9 @@ const Onboarding: NextPage = () => {
{step === 1 ? (
<UserDetails user={user} setStep={setStep} setUserRole={setUserRole} />
) : step === 2 ? (
<Workspace setStep={setStep} setWorkspace={setWorkspace} />
<Workspace setStep={setStep} setWorkspace={setWorkspace} user={user} />
) : (
<InviteMembers setStep={setStep} workspace={workspace} />
<InviteMembers setStep={setStep} workspace={workspace} user={user} />
)}
</div>
) : (
@ -80,7 +80,7 @@ const Onboarding: NextPage = () => {
onClick={() => {
if (step === 8) {
userService
.updateUserOnBoard({ userRole })
.updateUserOnBoard({ userRole }, user)
.then(async () => {
mutate<ICurrentUserResponse>(
CURRENT_USER,

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