mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: app dir build errors. (#4746)
This commit is contained in:
parent
f93803ace8
commit
c880e8b48c
@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
// components
|
||||
import { PageHead } from "@/components/core";
|
||||
|
@ -20,7 +20,7 @@ import { AuthenticationWrapper } from "@/lib/wrappers";
|
||||
// services
|
||||
import { WorkspaceService } from "@/services/workspace.service";
|
||||
|
||||
export enum EOnboardingSteps {
|
||||
enum EOnboardingSteps {
|
||||
PROFILE_SETUP = "PROFILE_SETUP",
|
||||
WORKSPACE_CREATE_OR_JOIN = "WORKSPACE_CREATE_OR_JOIN",
|
||||
INVITE_MEMBERS = "INVITE_MEMBERS",
|
||||
|
@ -33,8 +33,8 @@ const defaultValues: FormValues = {
|
||||
confirm_password: "",
|
||||
};
|
||||
|
||||
export const userService = new UserService();
|
||||
export const authService = new AuthService();
|
||||
const userService = new UserService();
|
||||
const authService = new AuthService();
|
||||
|
||||
const defaultShowPassword = {
|
||||
oldPassword: false,
|
||||
|
@ -1,291 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// types
|
||||
import { IEstimate, IEstimateFormData } from "@plane/types";
|
||||
// ui
|
||||
import { Button, Input, TextArea, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@/components/core";
|
||||
// helpers
|
||||
import { checkDuplicates } from "@/helpers/array.helper";
|
||||
// hooks
|
||||
import { useEstimate } from "@/hooks/store";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
data?: IEstimate;
|
||||
};
|
||||
|
||||
const defaultValues = {
|
||||
name: "",
|
||||
description: "",
|
||||
value1: "",
|
||||
value2: "",
|
||||
value3: "",
|
||||
value4: "",
|
||||
value5: "",
|
||||
value6: "",
|
||||
};
|
||||
|
||||
type FormValues = typeof defaultValues;
|
||||
|
||||
export const CreateUpdateEstimateModal: React.FC<Props> = observer((props) => {
|
||||
const { handleClose, data, isOpen } = props;
|
||||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const { createEstimate, updateEstimate } = useEstimate();
|
||||
// form info
|
||||
const {
|
||||
formState: { errors, isSubmitting },
|
||||
handleSubmit,
|
||||
control,
|
||||
reset,
|
||||
} = useForm<FormValues>({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const onClose = () => {
|
||||
handleClose();
|
||||
reset();
|
||||
};
|
||||
|
||||
const handleCreateEstimate = async (payload: IEstimateFormData) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await createEstimate(workspaceSlug.toString(), projectId.toString(), payload)
|
||||
.then(() => {
|
||||
onClose();
|
||||
})
|
||||
.catch((err) => {
|
||||
const error = err?.error;
|
||||
const errorString = Array.isArray(error) ? error[0] : error;
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message:
|
||||
errorString ?? err.status === 400
|
||||
? "Estimate with that name already exists. Please try again with another name."
|
||||
: "Estimate could not be created. Please try again.",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateEstimate = async (payload: IEstimateFormData) => {
|
||||
if (!workspaceSlug || !projectId || !data) return;
|
||||
|
||||
await updateEstimate(workspaceSlug.toString(), projectId.toString(), data.id, payload)
|
||||
.then(() => {
|
||||
onClose();
|
||||
})
|
||||
.catch((err) => {
|
||||
const error = err?.error;
|
||||
const errorString = Array.isArray(error) ? error[0] : error;
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: errorString ?? "Estimate could not be updated. Please try again.",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = async (formData: FormValues) => {
|
||||
if (!formData.name || formData.name === "") {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Estimate title cannot be empty.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
formData.value1 === "" ||
|
||||
formData.value2 === "" ||
|
||||
formData.value3 === "" ||
|
||||
formData.value4 === "" ||
|
||||
formData.value5 === "" ||
|
||||
formData.value6 === ""
|
||||
) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Estimate point cannot be empty.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
formData.value1.length > 20 ||
|
||||
formData.value2.length > 20 ||
|
||||
formData.value3.length > 20 ||
|
||||
formData.value4.length > 20 ||
|
||||
formData.value5.length > 20 ||
|
||||
formData.value6.length > 20
|
||||
) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Estimate point cannot have more than 20 characters.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
checkDuplicates([
|
||||
formData.value1,
|
||||
formData.value2,
|
||||
formData.value3,
|
||||
formData.value4,
|
||||
formData.value5,
|
||||
formData.value6,
|
||||
])
|
||||
) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Estimate points cannot have duplicate values.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: IEstimateFormData = {
|
||||
estimate: {
|
||||
name: formData.name,
|
||||
description: formData.description,
|
||||
},
|
||||
estimate_points: [],
|
||||
};
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const point = {
|
||||
key: i,
|
||||
value: formData[`value${i + 1}` as keyof FormValues],
|
||||
};
|
||||
|
||||
if (data)
|
||||
payload.estimate_points.push({
|
||||
id: data.points[i].id,
|
||||
...point,
|
||||
});
|
||||
else payload.estimate_points.push({ ...point });
|
||||
}
|
||||
|
||||
if (data) await handleUpdateEstimate(payload);
|
||||
else await handleCreateEstimate(payload);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data)
|
||||
reset({
|
||||
...defaultValues,
|
||||
...data,
|
||||
value1: data.points[0]?.value,
|
||||
value2: data.points[1]?.value,
|
||||
value3: data.points[2]?.value,
|
||||
value4: data.points[3]?.value,
|
||||
value5: data.points[4]?.value,
|
||||
value6: data.points[5]?.value,
|
||||
});
|
||||
else reset({ ...defaultValues });
|
||||
}, [data, reset]);
|
||||
|
||||
return (
|
||||
<ModalCore isOpen={isOpen} handleClose={handleClose} position={EModalPosition.TOP} width={EModalWidth.XXL}>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="space-y-5 p-5">
|
||||
<div className="text-xl font-medium text-custom-text-200">{data ? "Update" : "Create"} Estimate</div>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="name"
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
type="name"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.name)}
|
||||
placeholder="Title"
|
||||
className="w-full text-base"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Controller
|
||||
name="description"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<TextArea
|
||||
id="description"
|
||||
name="description"
|
||||
value={value}
|
||||
placeholder="Description"
|
||||
onChange={onChange}
|
||||
className="w-full text-base resize-none min-h-24"
|
||||
hasError={Boolean(errors?.description)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* list of all the points */}
|
||||
{/* since they are all the same, we can use a loop to render them */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{Array(6)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<div className="flex items-center" key={i}>
|
||||
<span className="flex h-full items-center rounded-lg bg-custom-background-80">
|
||||
<span className="rounded-lg px-2 text-sm text-custom-text-200">{i + 1}</span>
|
||||
<span className="rounded-r-lg bg-custom-background-100">
|
||||
<Controller
|
||||
control={control}
|
||||
name={`value${i + 1}` as keyof FormValues}
|
||||
rules={{
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: "Estimate point must at most be of 20 characters",
|
||||
},
|
||||
}}
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
ref={ref}
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
id={`value${i + 1}`}
|
||||
name={`value${i + 1}`}
|
||||
placeholder={`Point ${i + 1}`}
|
||||
className="w-full rounded-l-none"
|
||||
hasError={Boolean(errors[`value${i + 1}` as keyof FormValues])}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-5 py-4 flex items-center justify-end gap-2 border-t-[0.5px] border-custom-border-200">
|
||||
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" size="sm" type="submit" loading={isSubmitting}>
|
||||
{data ? (isSubmitting ? "Updating" : "Update Estimate") : isSubmitting ? "Creating" : "Create Estimate"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalCore>
|
||||
);
|
||||
});
|
@ -1,78 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// types
|
||||
import { IEstimate } from "@plane/types";
|
||||
// ui
|
||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { AlertModalCore } from "@/components/core";
|
||||
// hooks
|
||||
import { useEstimate } from "@/hooks/store";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
data: IEstimate | null;
|
||||
handleClose: () => void;
|
||||
};
|
||||
|
||||
export const DeleteEstimateModal: React.FC<Props> = observer((props) => {
|
||||
const { isOpen, handleClose, data } = props;
|
||||
// states
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const { deleteEstimate } = useEstimate();
|
||||
|
||||
const handleEstimateDelete = async () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
setIsDeleteLoading(true);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
||||
const estimateId = data?.id!;
|
||||
|
||||
await deleteEstimate(workspaceSlug.toString(), projectId.toString(), estimateId)
|
||||
.then(() => {
|
||||
handleClose();
|
||||
})
|
||||
.catch((err) => {
|
||||
const error = err?.error;
|
||||
const errorString = Array.isArray(error) ? error[0] : error;
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: errorString ?? "Estimate could not be deleted. Please try again",
|
||||
});
|
||||
})
|
||||
.finally(() => setIsDeleteLoading(false));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setIsDeleteLoading(false);
|
||||
}, [isOpen]);
|
||||
|
||||
const onClose = () => {
|
||||
setIsDeleteLoading(false);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertModalCore
|
||||
handleClose={onClose}
|
||||
handleSubmit={handleEstimateDelete}
|
||||
isSubmitting={isDeleteLoading}
|
||||
isOpen={isOpen}
|
||||
title="Delete Estimate"
|
||||
content={
|
||||
<>
|
||||
Are you sure you want to delete estimate-{" "}
|
||||
<span className="break-words font-medium text-custom-text-100">{data?.name}</span>
|
||||
{""}? All of the data related to the estiamte will be permanently removed. This action cannot be undone.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
@ -1,120 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { IEstimate } from "@plane/types";
|
||||
// store hooks
|
||||
import { Button, Loader, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { EmptyState } from "@/components/empty-state";
|
||||
import { CreateUpdateEstimateModal, DeleteEstimateModal, EstimateListItem } from "@/components/estimates";
|
||||
import { EmptyStateType } from "@/constants/empty-state";
|
||||
import { orderArrayBy } from "@/helpers/array.helper";
|
||||
import { useEstimate, useProject } from "@/hooks/store";
|
||||
// components
|
||||
// ui
|
||||
// types
|
||||
// helpers
|
||||
// constants
|
||||
|
||||
export const EstimatesList: React.FC = observer(() => {
|
||||
// states
|
||||
const [estimateFormOpen, setEstimateFormOpen] = useState(false);
|
||||
const [estimateToDelete, setEstimateToDelete] = useState<string | null>(null);
|
||||
const [estimateToUpdate, setEstimateToUpdate] = useState<IEstimate | undefined>();
|
||||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const { updateProject, currentProjectDetails } = useProject();
|
||||
const { projectEstimates, getProjectEstimateById } = useEstimate();
|
||||
|
||||
const editEstimate = (estimate: IEstimate) => {
|
||||
setEstimateFormOpen(true);
|
||||
// Order the points array by key before updating the estimate to update state
|
||||
setEstimateToUpdate({
|
||||
...estimate,
|
||||
points: orderArrayBy(estimate.points, "key"),
|
||||
});
|
||||
};
|
||||
|
||||
const disableEstimates = () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
updateProject(workspaceSlug.toString(), projectId.toString(), { estimate: null }).catch((err) => {
|
||||
const error = err?.error;
|
||||
const errorString = Array.isArray(error) ? error[0] : error;
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: errorString ?? "Estimate could not be disabled. Please try again",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateUpdateEstimateModal
|
||||
isOpen={estimateFormOpen}
|
||||
data={estimateToUpdate}
|
||||
handleClose={() => {
|
||||
setEstimateFormOpen(false);
|
||||
setEstimateToUpdate(undefined);
|
||||
}}
|
||||
/>
|
||||
|
||||
<DeleteEstimateModal
|
||||
isOpen={!!estimateToDelete}
|
||||
handleClose={() => setEstimateToDelete(null)}
|
||||
data={getProjectEstimateById(estimateToDelete!)}
|
||||
/>
|
||||
|
||||
<section className="flex items-center justify-between border-b border-custom-border-100 py-3.5">
|
||||
<h3 className="text-xl font-medium">Estimates</h3>
|
||||
<div className="col-span-12 space-y-5 sm:col-span-7">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
setEstimateFormOpen(true);
|
||||
setEstimateToUpdate(undefined);
|
||||
}}
|
||||
size="sm"
|
||||
>
|
||||
Add Estimate
|
||||
</Button>
|
||||
{currentProjectDetails?.estimate && (
|
||||
<Button variant="neutral-primary" onClick={disableEstimates} size="sm">
|
||||
Disable Estimates
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{projectEstimates ? (
|
||||
projectEstimates.length > 0 ? (
|
||||
<section className="h-full overflow-y-auto bg-custom-background-100">
|
||||
{projectEstimates.map((estimate) => (
|
||||
<EstimateListItem
|
||||
key={estimate.id}
|
||||
estimate={estimate}
|
||||
editEstimate={(estimate) => editEstimate(estimate)}
|
||||
deleteEstimate={(estimateId) => setEstimateToDelete(estimateId)}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
) : (
|
||||
<div className="h-full w-full py-8">
|
||||
<EmptyState type={EmptyStateType.PROJECT_SETTINGS_ESTIMATE} />
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<Loader className="mt-5 space-y-5">
|
||||
<Loader.Item height="40px" />
|
||||
<Loader.Item height="40px" />
|
||||
<Loader.Item height="40px" />
|
||||
<Loader.Item height="40px" />
|
||||
</Loader>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
@ -1,200 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { CalendarCheck2, CalendarClock } from "lucide-react";
|
||||
// types
|
||||
import { TBulkIssueProperties } from "@plane/types";
|
||||
// ui
|
||||
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { DateDropdown, MemberDropdown, PriorityDropdown, StateDropdown } from "@/components/dropdowns";
|
||||
import { IssueLabelSelect } from "@/components/issues/select";
|
||||
import { CreateLabelModal } from "@/components/labels";
|
||||
// constants
|
||||
import { EErrorCodes, ERROR_DETAILS } from "@/constants/errors";
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
// helpers
|
||||
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
// hooks
|
||||
import { useIssues } from "@/hooks/store";
|
||||
import { TSelectionHelper, TSelectionSnapshot } from "@/hooks/use-multiple-select";
|
||||
|
||||
type Props = {
|
||||
selectionHelpers: TSelectionHelper;
|
||||
snapshot: TSelectionSnapshot;
|
||||
};
|
||||
|
||||
const defaultValues: TBulkIssueProperties = {
|
||||
state_id: "",
|
||||
// @ts-expect-error priority should not be undefined, but it should be, in this case
|
||||
priority: undefined,
|
||||
assignee_ids: [],
|
||||
start_date: null,
|
||||
target_date: null,
|
||||
label_ids: [],
|
||||
};
|
||||
|
||||
export const IssueBulkOperationsProperties: React.FC<Props> = (props) => {
|
||||
const { snapshot } = props;
|
||||
// states
|
||||
const [createLabelModal, setCreateLabelModal] = useState(false);
|
||||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const {
|
||||
issues: { bulkUpdateProperties },
|
||||
} = useIssues(EIssuesStoreType.PROJECT);
|
||||
// form info
|
||||
const {
|
||||
control,
|
||||
formState: { dirtyFields, isDirty, isSubmitting },
|
||||
handleSubmit,
|
||||
reset,
|
||||
watch,
|
||||
} = useForm<TBulkIssueProperties>({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const handleBulkOperations = async (data: TBulkIssueProperties) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const payload: Partial<TBulkIssueProperties> = {};
|
||||
Object.keys(dirtyFields).forEach((key) => {
|
||||
const payloadKey = key as keyof typeof dirtyFields;
|
||||
// @ts-expect-error values might not match
|
||||
payload[payloadKey] = data[payloadKey];
|
||||
});
|
||||
|
||||
await bulkUpdateProperties(workspaceSlug.toString(), projectId.toString(), {
|
||||
issue_ids: snapshot.selectedEntityIds,
|
||||
properties: payload,
|
||||
})
|
||||
.then(() => {
|
||||
reset(defaultValues);
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorInfo = ERROR_DETAILS[error?.error_code as EErrorCodes] ?? undefined;
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: errorInfo?.title ?? "Error!",
|
||||
message: errorInfo?.message ?? "Something went wrong. Please try again.",
|
||||
});
|
||||
});
|
||||
};
|
||||
const isUpdateDisabled = !snapshot.isSelectionActive;
|
||||
|
||||
const startDate = watch("start_date");
|
||||
const targetDate = watch("target_date");
|
||||
|
||||
const minDate = getDate(startDate);
|
||||
minDate?.setDate(minDate.getDate());
|
||||
|
||||
const maxDate = getDate(targetDate);
|
||||
maxDate?.setDate(maxDate.getDate());
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(handleBulkOperations)} className="size-full flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Controller
|
||||
name="state_id"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<StateDropdown
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
projectId={projectId?.toString() ?? ""}
|
||||
buttonVariant="border-with-text"
|
||||
disabled={isUpdateDisabled}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="priority"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<PriorityDropdown
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
buttonVariant="border-with-text"
|
||||
buttonClassName="!text-custom-text-300"
|
||||
disabled={isUpdateDisabled}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="assignee_ids"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<MemberDropdown
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
buttonVariant={value?.length > 0 ? "transparent-without-text" : "border-with-text"}
|
||||
buttonClassName={value?.length > 0 ? "hover:bg-transparent" : ""}
|
||||
placeholder="Assignees"
|
||||
multiple
|
||||
disabled={isUpdateDisabled}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="start_date"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<DateDropdown
|
||||
value={value}
|
||||
onChange={(val) => onChange(val ? renderFormattedPayloadDate(val) : null)}
|
||||
buttonVariant="border-with-text"
|
||||
placeholder="Start date"
|
||||
icon={<CalendarClock className="size-3 flex-shrink-0" />}
|
||||
disabled={isUpdateDisabled}
|
||||
maxDate={maxDate ?? undefined}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="target_date"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<DateDropdown
|
||||
value={value}
|
||||
onChange={(val) => onChange(val ? renderFormattedPayloadDate(val) : null)}
|
||||
buttonVariant="border-with-text"
|
||||
placeholder="Due date"
|
||||
icon={<CalendarCheck2 className="size-3 flex-shrink-0" />}
|
||||
disabled={isUpdateDisabled}
|
||||
minDate={minDate ?? undefined}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{projectId && (
|
||||
<Controller
|
||||
name="label_ids"
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<>
|
||||
<CreateLabelModal
|
||||
isOpen={createLabelModal}
|
||||
handleClose={() => setCreateLabelModal(false)}
|
||||
projectId={projectId.toString()}
|
||||
onSuccess={(res) => onChange([...value, res.id])}
|
||||
/>
|
||||
<IssueLabelSelect
|
||||
value={value}
|
||||
projectId={projectId.toString()}
|
||||
onChange={onChange}
|
||||
setIsOpen={() => setCreateLabelModal(true)}
|
||||
buttonClassName="text-custom-text-300"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isDirty && (
|
||||
<Button type="submit" variant="primary" size="sm" className="py-1" loading={isSubmitting}>
|
||||
{isSubmitting ? "Updating" : "Update"}
|
||||
</Button>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
};
|
@ -1,68 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { FC, ReactNode } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import dynamic from "next/dynamic";
|
||||
// import Router from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
import NProgress from "nprogress";
|
||||
import { SWRConfig } from "swr";
|
||||
// ui
|
||||
import { Toast } from "@plane/ui";
|
||||
// constants
|
||||
import { SWR_CONFIG } from "@/constants/swr-config";
|
||||
//helpers
|
||||
import { resolveGeneralTheme } from "@/helpers/theme.helper";
|
||||
// hooks
|
||||
import { useInstance, useWorkspace, useUser } from "@/hooks/store";
|
||||
// wrappers
|
||||
import { InstanceWrapper } from "@/lib/wrappers";
|
||||
// dynamic imports
|
||||
const StoreWrapper = dynamic(() => import("@/lib/wrappers/store-wrapper"), { ssr: false });
|
||||
const PostHogProvider = dynamic(() => import("@/lib/posthog-provider"), { ssr: false });
|
||||
const CrispWrapper = dynamic(() => import("@/lib/wrappers/crisp-wrapper"), { ssr: false });
|
||||
// nprogress
|
||||
NProgress.configure({ showSpinner: false });
|
||||
// Router.events.on("routeChangeStart", NProgress.start);
|
||||
// Router.events.on("routeChangeError", NProgress.done);
|
||||
// Router.events.on("routeChangeComplete", NProgress.done);
|
||||
|
||||
export interface IAppProvider {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const AppProvider: FC<IAppProvider> = observer((props) => {
|
||||
const { children } = props;
|
||||
// store hooks
|
||||
const { config } = useInstance();
|
||||
const {
|
||||
data: currentUser,
|
||||
membership: { currentProjectRole, currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
// themes
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<>
|
||||
<InstanceWrapper>
|
||||
<StoreWrapper>
|
||||
<CrispWrapper user={currentUser}>
|
||||
<PostHogProvider
|
||||
user={currentUser}
|
||||
currentWorkspaceId={currentWorkspace?.id}
|
||||
workspaceRole={currentWorkspaceRole}
|
||||
projectRole={currentProjectRole}
|
||||
posthogAPIKey={config?.posthog_api_key || undefined}
|
||||
posthogHost={config?.posthog_host || undefined}
|
||||
>
|
||||
{/* TODO: Need to handle custom themes for toast */}
|
||||
<Toast theme={resolveGeneralTheme(resolvedTheme)} />
|
||||
<SWRConfig value={SWR_CONFIG}>{children}</SWRConfig>
|
||||
</PostHogProvider>
|
||||
</CrispWrapper>
|
||||
</StoreWrapper>
|
||||
</InstanceWrapper>
|
||||
</>
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue
Block a user