delete project

This commit is contained in:
Takeshi Kimata
2024-02-18 11:06:02 +09:00
parent d752b4a3b0
commit a44c50df29
5 changed files with 133 additions and 27 deletions

View File

@@ -26,8 +26,10 @@ app.use("/", indexRoute);
// "/projects" // "/projects"
const projectsIndexRoute = require('./routes/projects/index')(sequelize); const projectsIndexRoute = require('./routes/projects/index')(sequelize);
const projectsNewRoute = require('./routes/projects/new')(sequelize); const projectsNewRoute = require('./routes/projects/new')(sequelize);
const projectsDeleteRoute = require('./routes/projects/delete')(sequelize);
app.use('/projects', projectsIndexRoute); app.use('/projects', projectsIndexRoute);
app.use('/projects', projectsNewRoute); app.use('/projects', projectsNewRoute);
app.use('/projects', projectsDeleteRoute);
// "/folders" // "/folders"
const foldersIndexRoute = require('./routes/folders/index')(sequelize); const foldersIndexRoute = require('./routes/folders/index')(sequelize);

View File

@@ -0,0 +1,29 @@
const express = require("express");
const router = express.Router();
const defineProject = require("../../models/projects");
const { DataTypes } = require("sequelize");
module.exports = function (sequelize) {
const Project = defineProject(sequelize, DataTypes);
router.delete("/:projectId", async (req, res) => {
const projectId = req.params.projectId;
try {
const project = await Project.findOne({
where: {
id: projectId,
},
});
if (!project) {
return res.status(404).send("Project not found");
}
await project.destroy();
res.status(204).send();
} catch (error) {
console.error(error);
res.status(500).send("Internal Server Error");
}
});
return router;
};

View File

@@ -79,6 +79,27 @@ async function createProject(name, detail) {
} }
} }
async function deleteProject(projectId: number) {
const fetchOptions = {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
};
const url = `${apiServer}/projects/${projectId}`;
try {
const response = await fetch(url, fetchOptions);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
} catch (error) {
console.error("Error deleting project:", error);
throw error;
}
}
export default function ProjectsPage() { export default function ProjectsPage() {
// projects // projects
const [projects, setProjects] = useState([]); const [projects, setProjects] = useState([]);
@@ -144,6 +165,15 @@ export default function ProjectsPage() {
} }
}; };
const onDeleteClicked = async (projectId: number) => {
try {
await deleteProject(projectId);
setProjects(projects.filter((project) => project.id !== projectId));
} catch (error) {
console.error("Error deleting project:", error);
}
};
return ( return (
<div className="container mx-auto max-w-7xl pt-16 px-6 flex-grow"> <div className="container mx-auto max-w-7xl pt-16 px-6 flex-grow">
<div className="flex h-full items-center"> <div className="flex h-full items-center">
@@ -157,15 +187,20 @@ export default function ProjectsPage() {
{projects.map((project, index) => ( {projects.map((project, index) => (
<ProjectCard <ProjectCard
key={index} key={index}
projectName={project.name} project={project}
projectDetail={project.detail} onDeleteClicked={onDeleteClicked}
/> />
))} ))}
</div> </div>
<Modal isOpen={isCreateDialogOpen}> <Modal
isOpen={isCreateDialogOpen}
onOpenChange={() => {
setIsCreateDialogOpen(false);
}}
>
<ModalContent> <ModalContent>
<ModalHeader className="flex flex-col gap-1">Modal Title</ModalHeader> <ModalHeader className="flex flex-col gap-1">Project</ModalHeader>
<ModalBody> <ModalBody>
<Input <Input
type="text" type="text"
@@ -194,7 +229,7 @@ export default function ProjectsPage() {
/> />
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button color="danger" variant="light" onPress={closeModal}> <Button variant="light" onPress={closeModal}>
Close Close
</Button> </Button>
<Button color="primary" onPress={onCreateClicked}> <Button color="primary" onPress={onCreateClicked}>

View File

@@ -1,34 +1,55 @@
import React from "react"; import React from "react";
import { import {
Link,
Button,
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem,
Card, Card,
CardHeader, CardHeader,
CardBody, CardBody,
Divider, Divider,
Image,
} from "@nextui-org/react"; } from "@nextui-org/react";
import NextLink from "next/link"; import { DotsIcon } from "@/components/icons";
export function ProjectCard({ projectName, projectDetail }) { export function ProjectCard({ project, onDeleteClicked }) {
return ( return (
<NextLink href={`/projects/${1}/home`}> <Card className="w-[250px]">
<Card className="w-[250px] bg-white hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-800"> <CardHeader className="flex gap-3 h-[50px] justify-between text-ellipsis overflow-hidden">
<CardHeader className="flex gap-3"> <div className="flex gap-5">
<Image <div className="flex flex-col gap-1 items-start justify-center">
alt="nextui logo" <Link href={`/projects/${project.id}/home`}>{project.name}</Link>
height={40}
radius="sm"
src="https://avatars.githubusercontent.com/u/86160567?s=200&v=4"
width={40}
/>
<div className="flex flex-col">
<p className="text-md">{projectName}</p>
</div> </div>
</CardHeader> </div>
<Divider /> <Dropdown>
<CardBody> <DropdownTrigger>
<p>{projectDetail}</p> <Button
</CardBody> isIconOnly
</Card> variant="light"
</NextLink> size="sm"
className="text-transparent hover:text-inherit"
>
<DotsIcon size={16} />
</Button>
</DropdownTrigger>
<DropdownMenu aria-label="Static Actions">
<DropdownItem key="edit">Edit project</DropdownItem>
<DropdownItem
key="delete"
className="text-danger"
color="danger"
onClick={() => onDeleteClicked(project.id)}
>
Delete project
</DropdownItem>
</DropdownMenu>
</Dropdown>
</CardHeader>
<Divider />
<CardBody className="h-[50px] text-ellipsis overflow-hidden">
<p>{project.detail}</p>
</CardBody>
</Card>
); );
} }

View File

@@ -284,3 +284,22 @@ export const FolderIcon: React.FC<IconSvgProps> = ({
</svg> </svg>
); );
}; };
export const DotsIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => {
return (
<svg
viewBox="0 0 24 24"
fill="currentColor"
height={size || height}
width={size || height}
{...props}
>
<path d="M12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 12c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />
</svg>
);
};