forked from github/plane
feat: now user can edit view (#793)
* feat: now user can edit view * fix: build error
This commit is contained in:
parent
0f9812cf2c
commit
032ef831b2
@ -401,7 +401,7 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
<CreateUpdateViewModal
|
<CreateUpdateViewModal
|
||||||
isOpen={createViewModal !== null}
|
isOpen={createViewModal !== null}
|
||||||
handleClose={() => setCreateViewModal(null)}
|
handleClose={() => setCreateViewModal(null)}
|
||||||
preLoadedData={createViewModal}
|
data={createViewModal}
|
||||||
/>
|
/>
|
||||||
<CreateUpdateIssueModal
|
<CreateUpdateIssueModal
|
||||||
isOpen={createIssueModal && preloadedData?.actionType === "createIssue"}
|
isOpen={createIssueModal && preloadedData?.actionType === "createIssue"}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
@ -21,12 +21,11 @@ import { VIEWS_LIST } from "constants/fetch-keys";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
data: IView | null;
|
data: IView | null;
|
||||||
onClose: () => void;
|
|
||||||
onSuccess?: () => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, onClose, onSuccess }) => {
|
export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, setIsOpen }) => {
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -35,14 +34,14 @@ export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, onClose, onSucc
|
|||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
setIsOpen(false);
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
onClose();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeletion = async () => {
|
const handleDeletion = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
if (!workspaceSlug || !data || !projectId) return;
|
if (!workspaceSlug || !data || !projectId) return;
|
||||||
|
|
||||||
await viewsService
|
await viewsService
|
||||||
.deleteView(workspaceSlug as string, projectId as string, data.id)
|
.deleteView(workspaceSlug as string, projectId as string, data.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -50,8 +49,6 @@ export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, onClose, onSucc
|
|||||||
views?.filter((view) => view.id !== data.id)
|
views?.filter((view) => view.id !== data.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (onSuccess) onSuccess();
|
|
||||||
|
|
||||||
handleClose();
|
handleClose();
|
||||||
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
@ -66,6 +63,8 @@ export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, onClose, onSucc
|
|||||||
title: "Error!",
|
title: "Error!",
|
||||||
message: "View could not be deleted. Please try again.",
|
message: "View could not be deleted. Please try again.",
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -111,10 +110,9 @@ export const DeleteViewModal: React.FC<Props> = ({ isOpen, data, onClose, onSucc
|
|||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
Are you sure you want to delete view- {" "}
|
Are you sure you want to delete view-{" "}
|
||||||
<span className="font-bold">{data?.name}</span>
|
<span className="font-bold">{data?.name}</span>? All of the data related
|
||||||
? All of the data related to the view will be permanently removed.
|
to the view will be permanently removed. This action cannot be undone.
|
||||||
This action cannot be undone.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
|
|
||||||
import useSWR from "swr";
|
|
||||||
|
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
// ui
|
// ui
|
||||||
@ -11,11 +8,6 @@ import { Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
|
|||||||
import { FilterList } from "components/core";
|
import { FilterList } from "components/core";
|
||||||
// types
|
// types
|
||||||
import { IView } from "types";
|
import { IView } from "types";
|
||||||
// constant
|
|
||||||
import { STATE_LIST } from "constants/fetch-keys";
|
|
||||||
|
|
||||||
// services
|
|
||||||
import stateService from "services/state.service";
|
|
||||||
// components
|
// components
|
||||||
import { SelectFilters } from "components/views";
|
import { SelectFilters } from "components/views";
|
||||||
|
|
||||||
@ -23,8 +15,7 @@ type Props = {
|
|||||||
handleFormSubmit: (values: IView) => Promise<void>;
|
handleFormSubmit: (values: IView) => Promise<void>;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
data?: IView;
|
data?: IView | null;
|
||||||
preLoadedData?: Partial<IView> | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultValues: Partial<IView> = {
|
const defaultValues: Partial<IView> = {
|
||||||
@ -32,13 +23,7 @@ const defaultValues: Partial<IView> = {
|
|||||||
description: "",
|
description: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewForm: React.FC<Props> = ({
|
export const ViewForm: React.FC<Props> = ({ handleFormSubmit, handleClose, status, data }) => {
|
||||||
handleFormSubmit,
|
|
||||||
handleClose,
|
|
||||||
status,
|
|
||||||
data,
|
|
||||||
preLoadedData,
|
|
||||||
}) => {
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
@ -49,6 +34,7 @@ export const ViewForm: React.FC<Props> = ({
|
|||||||
} = useForm<IView>({
|
} = useForm<IView>({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
});
|
});
|
||||||
|
const filters = watch("query");
|
||||||
|
|
||||||
const handleCreateUpdateView = async (formData: IView) => {
|
const handleCreateUpdateView = async (formData: IView) => {
|
||||||
await handleFormSubmit(formData);
|
await handleFormSubmit(formData);
|
||||||
@ -66,13 +52,10 @@ export const ViewForm: React.FC<Props> = ({
|
|||||||
}, [data, reset]);
|
}, [data, reset]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reset({
|
if (status && data) {
|
||||||
...defaultValues,
|
setValue("query", data.query_data);
|
||||||
...preLoadedData,
|
}
|
||||||
});
|
}, [data, status, setValue]);
|
||||||
}, [preLoadedData, reset]);
|
|
||||||
|
|
||||||
const filters = watch("query");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(handleCreateUpdateView)}>
|
<form onSubmit={handleSubmit(handleCreateUpdateView)}>
|
||||||
@ -84,18 +67,19 @@ export const ViewForm: React.FC<Props> = ({
|
|||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
id="name"
|
id="name"
|
||||||
label="Name"
|
|
||||||
name="name"
|
name="name"
|
||||||
type="name"
|
type="name"
|
||||||
placeholder="Enter name"
|
placeholder="Title"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
mode="transparent"
|
||||||
|
className="resize-none text-xl"
|
||||||
error={errors.name}
|
error={errors.name}
|
||||||
register={register}
|
register={register}
|
||||||
validations={{
|
validations={{
|
||||||
required: "Name is required",
|
required: "Title is required",
|
||||||
maxLength: {
|
maxLength: {
|
||||||
value: 255,
|
value: 255,
|
||||||
message: "Name should be less than 255 characters",
|
message: "Title should be less than 255 characters",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -104,8 +88,9 @@ export const ViewForm: React.FC<Props> = ({
|
|||||||
<TextArea
|
<TextArea
|
||||||
id="description"
|
id="description"
|
||||||
name="description"
|
name="description"
|
||||||
label="Description"
|
placeholder="Description"
|
||||||
placeholder="Enter description"
|
className="h-32 resize-none text-sm"
|
||||||
|
mode="transparent"
|
||||||
error={errors.description}
|
error={errors.description}
|
||||||
register={register}
|
register={register}
|
||||||
/>
|
/>
|
||||||
|
@ -20,16 +20,10 @@ import { VIEWS_LIST } from "constants/fetch-keys";
|
|||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
data?: IView;
|
data?: IView | null;
|
||||||
preLoadedData?: Partial<IView> | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CreateUpdateViewModal: React.FC<Props> = ({
|
export const CreateUpdateViewModal: React.FC<Props> = ({ isOpen, handleClose, data }) => {
|
||||||
isOpen,
|
|
||||||
handleClose,
|
|
||||||
data,
|
|
||||||
preLoadedData,
|
|
||||||
}) => {
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
@ -66,14 +60,18 @@ export const CreateUpdateViewModal: React.FC<Props> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateView = async (payload: IView) => {
|
const updateView = async (payload: IView) => {
|
||||||
|
const payloadData = {
|
||||||
|
...payload,
|
||||||
|
query_data: payload.query,
|
||||||
|
};
|
||||||
await viewsService
|
await viewsService
|
||||||
.updateView(workspaceSlug as string, projectId as string, data?.id ?? "", payload)
|
.updateView(workspaceSlug as string, projectId as string, data?.id ?? "", payloadData)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
mutate<IView[]>(
|
mutate<IView[]>(
|
||||||
VIEWS_LIST(projectId as string),
|
VIEWS_LIST(projectId as string),
|
||||||
(prevData) =>
|
(prevData) =>
|
||||||
prevData?.map((p) => {
|
prevData?.map((p) => {
|
||||||
if (p.id === res.id) return { ...p, ...payload };
|
if (p.id === res.id) return { ...p, ...payloadData };
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}),
|
}),
|
||||||
@ -135,7 +133,6 @@ export const CreateUpdateViewModal: React.FC<Props> = ({
|
|||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
status={data ? true : false}
|
status={data ? true : false}
|
||||||
data={data}
|
data={data}
|
||||||
preLoadedData={preLoadedData}
|
|
||||||
/>
|
/>
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
@ -22,10 +22,11 @@ import { renderShortDate, renderShortTime } from "helpers/date-time.helper";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
view: IView;
|
view: IView;
|
||||||
setSelectedView: React.Dispatch<React.SetStateAction<IView | null>>;
|
handleEditView: () => void;
|
||||||
|
handleDeleteView: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SingleViewItem: React.FC<Props> = ({ view, setSelectedView }) => {
|
export const SingleViewItem: React.FC<Props> = ({ view, handleEditView, handleDeleteView }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
@ -134,23 +135,23 @@ export const SingleViewItem: React.FC<Props> = ({ view, setSelectedView }) => {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<CustomMenu width="auto" verticalEllipsis>
|
<CustomMenu width="auto" verticalEllipsis>
|
||||||
{/* <CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
onClick={(e: any) => {
|
onClick={(e: any) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
// handleEditView();
|
handleEditView();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="flex items-center justify-start gap-2">
|
<span className="flex items-center justify-start gap-2">
|
||||||
<PencilIcon className="h-3.5 w-3.5" />
|
<PencilIcon className="h-3.5 w-3.5" />
|
||||||
<span>Edit View</span>
|
<span>Edit View</span>
|
||||||
</span>
|
</span>
|
||||||
</CustomMenu.MenuItem> */}
|
</CustomMenu.MenuItem>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
onClick={(e: any) => {
|
onClick={(e: any) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSelectedView(view);
|
handleDeleteView();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="flex items-center justify-start gap-2">
|
<span className="flex items-center justify-start gap-2">
|
||||||
|
@ -25,8 +25,11 @@ import { IView } from "types";
|
|||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
|
|
||||||
const ProjectViews: NextPage = () => {
|
const ProjectViews: NextPage = () => {
|
||||||
const [isCreateViewModalOpen, setIsCreateViewModalOpen] = useState(false);
|
const [createUpdateViewModal, setCreateUpdateViewModal] = useState(false);
|
||||||
const [selectedView, setSelectedView] = useState<IView | null>(null);
|
const [selectedViewToUpdate, setSelectedViewToUpdate] = useState<IView | null>(null);
|
||||||
|
|
||||||
|
const [deleteViewModal, setDeleteViewModal] = useState(false);
|
||||||
|
const [selectedViewToDelete, setSelectedViewToDelete] = useState<IView | null>(null);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
@ -45,6 +48,16 @@ const ProjectViews: NextPage = () => {
|
|||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleEditView = (view: IView) => {
|
||||||
|
setSelectedViewToUpdate(view);
|
||||||
|
setCreateUpdateViewModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteView = (view: IView) => {
|
||||||
|
setSelectedViewToDelete(view);
|
||||||
|
setDeleteViewModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProjectAuthorizationWrapper
|
<ProjectAuthorizationWrapper
|
||||||
meta={{
|
meta={{
|
||||||
@ -73,14 +86,14 @@ const ProjectViews: NextPage = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CreateUpdateViewModal
|
<CreateUpdateViewModal
|
||||||
isOpen={isCreateViewModalOpen}
|
isOpen={createUpdateViewModal}
|
||||||
handleClose={() => setIsCreateViewModalOpen(false)}
|
handleClose={() => setCreateUpdateViewModal(false)}
|
||||||
|
data={selectedViewToUpdate}
|
||||||
/>
|
/>
|
||||||
<DeleteViewModal
|
<DeleteViewModal
|
||||||
isOpen={!!selectedView}
|
isOpen={deleteViewModal}
|
||||||
data={selectedView}
|
data={selectedViewToDelete}
|
||||||
onClose={() => setSelectedView(null)}
|
setIsOpen={setDeleteViewModal}
|
||||||
onSuccess={() => setSelectedView(null)}
|
|
||||||
/>
|
/>
|
||||||
{views ? (
|
{views ? (
|
||||||
views.length > 0 ? (
|
views.length > 0 ? (
|
||||||
@ -88,7 +101,12 @@ const ProjectViews: NextPage = () => {
|
|||||||
<h3 className="text-3xl font-semibold text-black">Views</h3>
|
<h3 className="text-3xl font-semibold text-black">Views</h3>
|
||||||
<ul role="list" className="divide-y">
|
<ul role="list" className="divide-y">
|
||||||
{views.map((view) => (
|
{views.map((view) => (
|
||||||
<SingleViewItem key={view.id} view={view} setSelectedView={setSelectedView} />
|
<SingleViewItem
|
||||||
|
key={view.id}
|
||||||
|
view={view}
|
||||||
|
handleEditView={() => handleEditView(view)}
|
||||||
|
handleDeleteView={() => handleDeleteView(view)}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user