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