diff --git a/src/components/ProjectModal.tsx b/src/components/ProjectModal.tsx new file mode 100644 index 00000000..cc88d5e7 --- /dev/null +++ b/src/components/ProjectModal.tsx @@ -0,0 +1,87 @@ +import React from "react"; +import { + Dialog, + DialogTitle, + DialogContent, + TextField, + DialogActions, + Button, +} from "@material-ui/core"; + +interface IProps { + open: boolean; + title: string; + submitButtonText: string; + projectState: [ + { + id: string; + name: string; + mainBranchName: string; + }, + React.Dispatch< + React.SetStateAction<{ + id: string; + name: string; + mainBranchName: string; + }> + > + ]; + onSubmit: () => void; + onCancel: () => void; +} + +export const ProjectModal: React.FunctionComponent = ({ + open, + title, + submitButtonText, + projectState, + onSubmit, + onCancel, +}) => { + const [project, setProject] = projectState; + + return ( + + {title} + + + setProject({ + ...project, + name: event.target.value, + }) + } + /> + + setProject({ + ...project, + mainBranchName: event.target.value, + }) + } + /> + + + + + + + ); +}; diff --git a/src/contexts/project.context.tsx b/src/contexts/project.context.tsx index bf3fa04c..1f6fe568 100644 --- a/src/contexts/project.context.tsx +++ b/src/contexts/project.context.tsx @@ -18,12 +18,22 @@ interface ICreateAction { payload: Project; } +interface IUpdateAction { + type: "update"; + payload: Project; +} + interface IDeleteAction { type: "delete"; payload: string; } -type IAction = IRequestAction | IGetction | ICreateAction | IDeleteAction; +type IAction = + | IRequestAction + | IGetction + | ICreateAction + | IDeleteAction + | IUpdateAction; type Dispatch = (action: IAction) => void; type State = { projectList: Project[] }; @@ -51,6 +61,16 @@ function projectReducer(state: State, action: IAction): State { ...state, projectList: [action.payload, ...state.projectList], }; + case "update": + return { + ...state, + projectList: state.projectList.map((p) => { + if (p.id === action.payload.id) { + return action.payload; + } + return p; + }), + }; case "delete": return { ...state, @@ -109,7 +129,10 @@ async function getProjectList(dispatch: Dispatch) { }); } -async function createProject(dispatch: Dispatch, project: { name: string }) { +async function createProject( + dispatch: Dispatch, + project: { name: string; mainBranchName: string } +) { dispatch({ type: "request" }); projectsService @@ -122,6 +145,22 @@ async function createProject(dispatch: Dispatch, project: { name: string }) { }); } +async function updateProject( + dispatch: Dispatch, + project: { id: string; name: string; mainBranchName: string } +) { + dispatch({ type: "request" }); + + projectsService + .update(project) + .then((project: Project) => { + dispatch({ type: "update", payload: project }); + }) + .catch((error) => { + console.log(error.toString()); + }); +} + async function deleteProject(dispatch: Dispatch, id: string) { dispatch({ type: "request" }); @@ -142,6 +181,7 @@ export { useProjectState, useProjectDispatch, createProject, + updateProject, getProjectList, deleteProject, }; diff --git a/src/pages/ProjectListPage.tsx b/src/pages/ProjectListPage.tsx index 99b08f55..77396378 100644 --- a/src/pages/ProjectListPage.tsx +++ b/src/pages/ProjectListPage.tsx @@ -23,11 +23,13 @@ import { getProjectList, deleteProject, createProject, + updateProject, } from "../contexts"; import { Link } from "react-router-dom"; -import { Delete, Add } from "@material-ui/icons"; +import { Delete, Add, Edit } from "@material-ui/icons"; import { routes } from "../constants"; import { formatDateTime } from "../_helpers/format.helper"; +import { ProjectModal } from "../components/ProjectModal"; const ProjectsListPage = () => { const theme = useTheme(); @@ -35,18 +37,27 @@ const ProjectsListPage = () => { const projectDispatch = useProjectDispatch(); const [createDialogOpen, setCreateDialogOpen] = React.useState(false); - const [newProjectName, setNewProjectName] = React.useState(""); + const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false); + const [project, setProject] = React.useState<{ + id: string; + name: string; + mainBranchName: string; + }>({ + id: "", + name: "", + mainBranchName: "", + }); useEffect(() => { getProjectList(projectDispatch); }, [projectDispatch]); - const handleCreateClickOpen = () => { - setCreateDialogOpen(true); + const toggleCreateDialogOpen = () => { + setCreateDialogOpen(!createDialogOpen); }; - const handleCreateClose = () => { - setCreateDialogOpen(false); + const toggleUpdateDialogOpen = () => { + setUpdateDialogOpen(!updateDialogOpen); }; return ( @@ -66,49 +77,44 @@ const ProjectsListPage = () => { { + toggleCreateDialogOpen(); + setProject({ + id: "", + name: "", + mainBranchName: "", + }); + }} > - - Create Project - - setNewProjectName(event.target.value)} - /> - - - - - - + title={"Create Project"} + submitButtonText={"Create"} + onCancel={toggleCreateDialogOpen} + projectState={[project, setProject]} + onSubmit={() => + createProject(projectDispatch, project).then((project) => { + toggleCreateDialogOpen(); + }) + } + /> + + + updateProject(projectDispatch, project).then((project) => { + toggleUpdateDialogOpen(); + }) + } + /> {projectState.projectList.map((project) => ( @@ -117,14 +123,12 @@ const ProjectsListPage = () => { Key: {project.id} Name: {project.name} Main branch: {project.mainBranchName} - Created: {formatDateTime(project.createdAt)} + + Created: {formatDateTime(project.createdAt)} + - ) => { - deleteProject(projectDispatch, project.id); + toggleUpdateDialogOpen(); + setProject(project); }} - style={{ - marginLeft: "auto", + > + + + ) => { + deleteProject(projectDispatch, project.id); }} > diff --git a/src/services/projects.service.ts b/src/services/projects.service.ts index 170287a8..f4446849 100644 --- a/src/services/projects.service.ts +++ b/src/services/projects.service.ts @@ -6,6 +6,7 @@ export const projectsService = { getAll, remove, create, + update, }; function getAll(): Promise { @@ -28,7 +29,10 @@ function remove(id: string): Promise { ); } -function create(project: { name: string }): Promise { +function create(project: { + name: string; + mainBranchName: string; +}): Promise { const requestOptions = { method: "POST", headers: { "Content-Type": "application/json", ...authHeader() }, @@ -37,3 +41,17 @@ function create(project: { name: string }): Promise { return fetch(`${API_URL}/projects`, requestOptions).then(handleResponse); } + +function update(project: { + id: string; + name: string; + mainBranchName: string; +}): Promise { + const requestOptions = { + method: "PUT", + headers: { "Content-Type": "application/json", ...authHeader() }, + body: JSON.stringify(project), + }; + + return fetch(`${API_URL}/projects`, requestOptions).then(handleResponse); +}