Skip to content

Commit 84ddd09

Browse files
authored
Merge pull request #25 from Visual-Regression-Tracker/71-project-edit
71 project edit
2 parents 6a9e745 + 3490621 commit 84ddd09

File tree

4 files changed

+209
-55
lines changed

4 files changed

+209
-55
lines changed

src/components/ProjectModal.tsx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import React from "react";
2+
import {
3+
Dialog,
4+
DialogTitle,
5+
DialogContent,
6+
TextField,
7+
DialogActions,
8+
Button,
9+
} from "@material-ui/core";
10+
11+
interface IProps {
12+
open: boolean;
13+
title: string;
14+
submitButtonText: string;
15+
projectState: [
16+
{
17+
id: string;
18+
name: string;
19+
mainBranchName: string;
20+
},
21+
React.Dispatch<
22+
React.SetStateAction<{
23+
id: string;
24+
name: string;
25+
mainBranchName: string;
26+
}>
27+
>
28+
];
29+
onSubmit: () => void;
30+
onCancel: () => void;
31+
}
32+
33+
export const ProjectModal: React.FunctionComponent<IProps> = ({
34+
open,
35+
title,
36+
submitButtonText,
37+
projectState,
38+
onSubmit,
39+
onCancel,
40+
}) => {
41+
const [project, setProject] = projectState;
42+
43+
return (
44+
<Dialog open={open} onClose={onCancel} aria-labelledby="form-dialog-title">
45+
<DialogTitle id="form-dialog-title">{title}</DialogTitle>
46+
<DialogContent>
47+
<TextField
48+
autoFocus
49+
margin="dense"
50+
id="name"
51+
label="Project name"
52+
type="text"
53+
fullWidth
54+
value={project.name}
55+
onChange={(event) =>
56+
setProject({
57+
...project,
58+
name: event.target.value,
59+
})
60+
}
61+
/>
62+
<TextField
63+
margin="dense"
64+
id="branchName"
65+
label="Main branch"
66+
type="text"
67+
fullWidth
68+
value={project.mainBranchName}
69+
onChange={(event) =>
70+
setProject({
71+
...project,
72+
mainBranchName: event.target.value,
73+
})
74+
}
75+
/>
76+
</DialogContent>
77+
<DialogActions>
78+
<Button onClick={onCancel} color="primary">
79+
Cancel
80+
</Button>
81+
<Button onClick={onSubmit} color="primary">
82+
{submitButtonText}
83+
</Button>
84+
</DialogActions>
85+
</Dialog>
86+
);
87+
};

src/contexts/project.context.tsx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,22 @@ interface ICreateAction {
1818
payload: Project;
1919
}
2020

21+
interface IUpdateAction {
22+
type: "update";
23+
payload: Project;
24+
}
25+
2126
interface IDeleteAction {
2227
type: "delete";
2328
payload: string;
2429
}
2530

26-
type IAction = IRequestAction | IGetction | ICreateAction | IDeleteAction;
31+
type IAction =
32+
| IRequestAction
33+
| IGetction
34+
| ICreateAction
35+
| IDeleteAction
36+
| IUpdateAction;
2737

2838
type Dispatch = (action: IAction) => void;
2939
type State = { projectList: Project[] };
@@ -51,6 +61,16 @@ function projectReducer(state: State, action: IAction): State {
5161
...state,
5262
projectList: [action.payload, ...state.projectList],
5363
};
64+
case "update":
65+
return {
66+
...state,
67+
projectList: state.projectList.map((p) => {
68+
if (p.id === action.payload.id) {
69+
return action.payload;
70+
}
71+
return p;
72+
}),
73+
};
5474
case "delete":
5575
return {
5676
...state,
@@ -109,7 +129,10 @@ async function getProjectList(dispatch: Dispatch) {
109129
});
110130
}
111131

112-
async function createProject(dispatch: Dispatch, project: { name: string }) {
132+
async function createProject(
133+
dispatch: Dispatch,
134+
project: { name: string; mainBranchName: string }
135+
) {
113136
dispatch({ type: "request" });
114137

115138
projectsService
@@ -122,6 +145,22 @@ async function createProject(dispatch: Dispatch, project: { name: string }) {
122145
});
123146
}
124147

148+
async function updateProject(
149+
dispatch: Dispatch,
150+
project: { id: string; name: string; mainBranchName: string }
151+
) {
152+
dispatch({ type: "request" });
153+
154+
projectsService
155+
.update(project)
156+
.then((project: Project) => {
157+
dispatch({ type: "update", payload: project });
158+
})
159+
.catch((error) => {
160+
console.log(error.toString());
161+
});
162+
}
163+
125164
async function deleteProject(dispatch: Dispatch, id: string) {
126165
dispatch({ type: "request" });
127166

@@ -142,6 +181,7 @@ export {
142181
useProjectState,
143182
useProjectDispatch,
144183
createProject,
184+
updateProject,
145185
getProjectList,
146186
deleteProject,
147187
};

src/pages/ProjectListPage.tsx

Lines changed: 61 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,41 @@ import {
2323
getProjectList,
2424
deleteProject,
2525
createProject,
26+
updateProject,
2627
} from "../contexts";
2728
import { Link } from "react-router-dom";
28-
import { Delete, Add } from "@material-ui/icons";
29+
import { Delete, Add, Edit } from "@material-ui/icons";
2930
import { routes } from "../constants";
3031
import { formatDateTime } from "../_helpers/format.helper";
32+
import { ProjectModal } from "../components/ProjectModal";
3133

3234
const ProjectsListPage = () => {
3335
const theme = useTheme();
3436
const projectState = useProjectState();
3537
const projectDispatch = useProjectDispatch();
3638

3739
const [createDialogOpen, setCreateDialogOpen] = React.useState(false);
38-
const [newProjectName, setNewProjectName] = React.useState<string>("");
40+
const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false);
41+
const [project, setProject] = React.useState<{
42+
id: string;
43+
name: string;
44+
mainBranchName: string;
45+
}>({
46+
id: "",
47+
name: "",
48+
mainBranchName: "",
49+
});
3950

4051
useEffect(() => {
4152
getProjectList(projectDispatch);
4253
}, [projectDispatch]);
4354

44-
const handleCreateClickOpen = () => {
45-
setCreateDialogOpen(true);
55+
const toggleCreateDialogOpen = () => {
56+
setCreateDialogOpen(!createDialogOpen);
4657
};
4758

48-
const handleCreateClose = () => {
49-
setCreateDialogOpen(false);
59+
const toggleUpdateDialogOpen = () => {
60+
setUpdateDialogOpen(!updateDialogOpen);
5061
};
5162

5263
return (
@@ -66,49 +77,44 @@ const ProjectsListPage = () => {
6677
<Fab
6778
color="primary"
6879
aria-label="add"
69-
onClick={handleCreateClickOpen}
80+
onClick={() => {
81+
toggleCreateDialogOpen();
82+
setProject({
83+
id: "",
84+
name: "",
85+
mainBranchName: "",
86+
});
87+
}}
7088
>
7189
<Add />
7290
</Fab>
7391
</Box>
7492

75-
<Dialog
93+
<ProjectModal
7694
open={createDialogOpen}
77-
onClose={handleCreateClose}
78-
aria-labelledby="form-dialog-title"
79-
>
80-
<DialogTitle id="form-dialog-title">Create Project</DialogTitle>
81-
<DialogContent>
82-
<TextField
83-
autoFocus
84-
margin="dense"
85-
id="name"
86-
label="Project name"
87-
type="text"
88-
fullWidth
89-
value={newProjectName}
90-
onChange={(event) => setNewProjectName(event.target.value)}
91-
/>
92-
</DialogContent>
93-
<DialogActions>
94-
<Button onClick={handleCreateClose} color="primary">
95-
Cancel
96-
</Button>
97-
<Button
98-
onClick={() => {
99-
createProject(projectDispatch, {
100-
name: newProjectName,
101-
}).then((project) => {
102-
setNewProjectName("");
103-
handleCreateClose();
104-
});
105-
}}
106-
color="primary"
107-
>
108-
Create
109-
</Button>
110-
</DialogActions>
111-
</Dialog>
95+
title={"Create Project"}
96+
submitButtonText={"Create"}
97+
onCancel={toggleCreateDialogOpen}
98+
projectState={[project, setProject]}
99+
onSubmit={() =>
100+
createProject(projectDispatch, project).then((project) => {
101+
toggleCreateDialogOpen();
102+
})
103+
}
104+
/>
105+
106+
<ProjectModal
107+
open={updateDialogOpen}
108+
title={"Update Project"}
109+
submitButtonText={"Update"}
110+
onCancel={toggleUpdateDialogOpen}
111+
projectState={[project, setProject]}
112+
onSubmit={() =>
113+
updateProject(projectDispatch, project).then((project) => {
114+
toggleUpdateDialogOpen();
115+
})
116+
}
117+
/>
112118
</Grid>
113119
{projectState.projectList.map((project) => (
114120
<Grid item xs={4} key={project.id}>
@@ -117,14 +123,12 @@ const ProjectsListPage = () => {
117123
<Typography>Key: {project.id}</Typography>
118124
<Typography>Name: {project.name}</Typography>
119125
<Typography>Main branch: {project.mainBranchName}</Typography>
120-
<Typography>Created: {formatDateTime(project.createdAt)}</Typography>
126+
<Typography>
127+
Created: {formatDateTime(project.createdAt)}
128+
</Typography>
121129
</CardContent>
122130
<CardActions>
123-
<Button
124-
color="primary"
125-
component={Link}
126-
to={`${project.id}`}
127-
>
131+
<Button color="primary" component={Link} to={`${project.id}`}>
128132
Builds
129133
</Button>
130134
<Button
@@ -136,10 +140,15 @@ const ProjectsListPage = () => {
136140
</Button>
137141
<IconButton
138142
onClick={(event: React.MouseEvent<HTMLElement>) => {
139-
deleteProject(projectDispatch, project.id);
143+
toggleUpdateDialogOpen();
144+
setProject(project);
140145
}}
141-
style={{
142-
marginLeft: "auto",
146+
>
147+
<Edit />
148+
</IconButton>
149+
<IconButton
150+
onClick={(event: React.MouseEvent<HTMLElement>) => {
151+
deleteProject(projectDispatch, project.id);
143152
}}
144153
>
145154
<Delete />

src/services/projects.service.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const projectsService = {
66
getAll,
77
remove,
88
create,
9+
update,
910
};
1011

1112
function getAll(): Promise<Project[]> {
@@ -28,7 +29,10 @@ function remove(id: string): Promise<number> {
2829
);
2930
}
3031

31-
function create(project: { name: string }): Promise<Project> {
32+
function create(project: {
33+
name: string;
34+
mainBranchName: string;
35+
}): Promise<Project> {
3236
const requestOptions = {
3337
method: "POST",
3438
headers: { "Content-Type": "application/json", ...authHeader() },
@@ -37,3 +41,17 @@ function create(project: { name: string }): Promise<Project> {
3741

3842
return fetch(`${API_URL}/projects`, requestOptions).then(handleResponse);
3943
}
44+
45+
function update(project: {
46+
id: string;
47+
name: string;
48+
mainBranchName: string;
49+
}): Promise<Project> {
50+
const requestOptions = {
51+
method: "PUT",
52+
headers: { "Content-Type": "application/json", ...authHeader() },
53+
body: JSON.stringify(project),
54+
};
55+
56+
return fetch(`${API_URL}/projects`, requestOptions).then(handleResponse);
57+
}

0 commit comments

Comments
 (0)