Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.

Commit 5a54f6d

Browse files
authored
Feature/ff 429 create new folder (#170)
* FF-430 add create new Folder api function * FF-433 add Modal for create new folder * update snapshots * change be url * add prevent default to event handler * update snapshots
1 parent a3a962d commit 5a54f6d

File tree

8 files changed

+465
-279
lines changed

8 files changed

+465
-279
lines changed

src/__tests__/__snapshots__/storybook.test.ts.snap

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,6 @@ exports[`Storyshots Filesystem default 1`] = `
226226
Download
227227
</a>
228228
</span>
229-
<button
230-
className="btn btn-primary"
231-
disabled={false}
232-
type="button"
233-
>
234-
New Folder
235-
</button>
236229
</span>
237230
</div>
238231
<div
@@ -333,7 +326,6 @@ exports[`Storyshots Filesystem default 1`] = `
333326
<div
334327
className="text-center col"
335328
>
336-
337329
Nothing to see here.
338330
</div>
339331
</div>

src/background/api/filesystem.ts

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
import {FsEntity} from "./filesystemTypes";
2-
import {filesystemPath, hostname} from "./api";
3-
import Axios, {AxiosResponse} from "axios";
4-
import {constants} from "../constants";
1+
import { FsEntity } from "./filesystemTypes";
2+
import { filesystemPath, hostname } from "./api";
3+
import Axios, { AxiosError, AxiosResponse } from "axios";
4+
import { constants } from "../constants";
55

66
import store from "../redux/store";
7-
import {ApiAction, ApiActionStatus, ApiActionType} from "../redux/actions/apiActionsTypes";
8-
import {addApiAction, changeStatus, nextFsEntity} from "../redux/actions/apiActions";
9-
import {addToContents, removeFromContents} from "../redux/actions/filesystem";
10-
import {EditableFileWithPreflightInfo, PreflightEntity} from "../../components/pages/filesytem/upload/preflightTypes";
11-
import {isFsEntityInFolder} from "../methods/filesystem";
12-
7+
import {
8+
ApiAction,
9+
ApiActionStatus,
10+
ApiActionType
11+
} from "../redux/actions/apiActionsTypes";
12+
import {
13+
addApiAction,
14+
changeStatus,
15+
nextFsEntity
16+
} from "../redux/actions/apiActions";
17+
import { addToContents, removeFromContents } from "../redux/actions/filesystem";
18+
import {
19+
EditableFileWithPreflightInfo,
20+
PreflightEntity
21+
} from "../../components/pages/filesytem/upload/preflightTypes";
22+
import { isFsEntityInFolder } from "../methods/filesystem";
1323

1424
const fhHostname = constants.url.FH_URL;
1525

1626
export const getFolderContents = (path: string) => {
17-
console.log("[Get folder content", path)
27+
console.log("[Get folder content", path);
1828
return new Promise<AxiosResponse<FsEntity[]>>((resolve, reject) => {
1929
let config = {
2030
headers: {
@@ -24,9 +34,8 @@ export const getFolderContents = (path: string) => {
2434
Axios.get<FsEntity[]>(hostname + filesystemPath + "contents", config)
2535
.then((response: AxiosResponse<FsEntity[]>) => resolve(response))
2636
.catch((error) => reject(error));
27-
})
28-
}
29-
37+
});
38+
};
3039

3140
export const uploadPreflight = (
3241
files: File[] | EditableFileWithPreflightInfo[],
@@ -43,7 +52,8 @@ export const uploadPreflight = (
4352
}));
4453
return new Promise<PreflightEntity[]>((resolve, reject) => {
4554
Axios.post<PreflightEntity[]>(
46-
hostname + filesystemPath + parentFolderID + "/upload/preflight", postData
55+
hostname + filesystemPath + parentFolderID + "/upload/preflight",
56+
postData
4757
)
4858
.then((response: AxiosResponse<PreflightEntity[]>) => {
4959
resolve(response.data);
@@ -52,8 +62,15 @@ export const uploadPreflight = (
5262
});
5363
};
5464

55-
export const uploadFiles = (files: File[] | EditableFileWithPreflightInfo[], parentFolderID: string) => {
56-
console.log("[API filesystem] uploading files to folderID", parentFolderID , files);
65+
export const uploadFiles = (
66+
files: File[] | EditableFileWithPreflightInfo[],
67+
parentFolderID: string
68+
) => {
69+
console.log(
70+
"[API filesystem] uploading files to folderID",
71+
parentFolderID,
72+
files
73+
);
5774
const apiCall = (file: File | EditableFileWithPreflightInfo) => {
5875
return new Promise((resolve, reject) => {
5976
let formData = new FormData();
@@ -78,9 +95,10 @@ export const uploadFiles = (files: File[] | EditableFileWithPreflightInfo[], par
7895
.then((response: AxiosResponse<[FsEntity]>) => {
7996
const currentPath = store.getState().filesystem.currentPath;
8097

81-
const fsEntityToShow = response.data.find((fsEntity: FsEntity) =>
82-
isFsEntityInFolder(fsEntity, currentPath)
83-
)
98+
const fsEntityToShow = response.data.find(
99+
(fsEntity: FsEntity) =>
100+
isFsEntityInFolder(fsEntity, currentPath)
101+
);
84102

85103
if (fsEntityToShow) {
86104
store.dispatch(addToContents(fsEntityToShow));
@@ -93,15 +111,16 @@ export const uploadFiles = (files: File[] | EditableFileWithPreflightInfo[], par
93111
handleMultipleApiActions(files, apiCall, ApiActionType.UPLOAD);
94112
};
95113

96-
97114
export const deleteFsEntities = (files: FsEntity[]) => {
98115
const apiCall = (fsEntity: FsEntity) => {
99116
return new Promise((resolve, reject) => {
100117
Axios.delete<FsEntity[]>(
101118
fhHostname + "/delete/" + fsEntity.fileSystemId
102119
)
103120
.then((response: AxiosResponse<FsEntity[]>) => {
104-
response.data.forEach((e) => store.dispatch(removeFromContents(e)));
121+
response.data.forEach((e) =>
122+
store.dispatch(removeFromContents(e))
123+
);
105124
resolve(response);
106125
})
107126
.catch((error) => reject(error));
@@ -110,6 +129,21 @@ export const deleteFsEntities = (files: FsEntity[]) => {
110129
handleMultipleApiActions(files, apiCall, ApiActionType.DELETE);
111130
};
112131

132+
export const createNewFolder = (
133+
folderName: string,
134+
parentFolderID: string
135+
): Promise<AxiosResponse<FsEntity>> => {
136+
const body = { name: folderName };
137+
138+
return new Promise((resolve, reject) => {
139+
Axios.post<FsEntity>(
140+
hostname + filesystemPath + parentFolderID + "/folder/create",
141+
body
142+
)
143+
.then((response: AxiosResponse<FsEntity>) => resolve(response))
144+
.catch((error: AxiosError) => reject(error));
145+
});
146+
};
113147

114148
function handleMultipleApiActions<Type extends File | FsEntity>(
115149
items: Type[],
@@ -134,7 +168,9 @@ function handleMultipleApiActions<Type extends File | FsEntity>(
134168
// get the info from the store
135169
apiAction = store
136170
.getState()
137-
.apiActions.actions.find((a: ApiAction) => a.key === apiAction?.key);
171+
.apiActions.actions.find(
172+
(a: ApiAction) => a.key === apiAction?.key
173+
);
138174

139175
if (
140176
!apiAction ||
@@ -150,7 +186,10 @@ function handleMultipleApiActions<Type extends File | FsEntity>(
150186

151187
if (currentIndex === apiAction.totalAmount) {
152188
store.dispatch(
153-
changeStatus({key: apiAction.key, status: ApiActionStatus.FINISHED})
189+
changeStatus({
190+
key: apiAction.key,
191+
status: ApiActionStatus.FINISHED
192+
})
154193
);
155194
return;
156195
} else {
@@ -166,7 +205,9 @@ function handleMultipleApiActions<Type extends File | FsEntity>(
166205

167206
action(items[currentIndex])
168207
.then((response) => {
169-
console.log("[API filesystem] handleMultipleApiActions next iteration");
208+
console.log(
209+
"[API filesystem] handleMultipleApiActions next iteration"
210+
);
170211
handleMultipleApiActions(items, action, type, apiAction);
171212
})
172213
.catch((error) => {

src/background/constants.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
11
interface constantsdef {
2-
url: { API_URL: string
3-
FH_URL: string};
2+
url: { API_URL: string; FH_URL: string };
43
}
54

65
const prod: constantsdef = {
7-
url: {
8-
API_URL: window.location.origin + "/api",
9-
FH_URL: window.location.origin + "/data"
10-
}
6+
url: {
7+
API_URL: window.location.origin + "/api",
8+
FH_URL: window.location.origin + "/data"
9+
}
1110
};
1211

1312
const dev: constantsdef = {
14-
url: {
15-
API_URL: "https://demo.filefighter.de/api",
16-
// API_URL: "http://localhost:8080",
17-
//API_URL: "http://localhost/api",
18-
//FH_URL: "http://localhost:5000/data"
19-
//FH_URL: "http://localhost/data"
20-
FH_URL: "https://demo.filefighter.de/data"
21-
22-
}
13+
url: {
14+
API_URL: "https://demo.filefighter.de/api",
15+
// API_URL: "http://localhost:8080",
16+
//API_URL: "http://localhost/api",
17+
//FH_URL: "http://localhost:5000/data"
18+
//FH_URL: "http://localhost/data"
19+
FH_URL: "https://demo.filefighter.de/data"
20+
}
2321
};
2422
export const constants = process.env.NODE_ENV === "development" ? dev : prod;
2523

26-
2724
export const MIN_PASSWORD_LENGTH = 8;
2825
export const MAX_PASSWORD_LENGTH = 20;
2926
export const DEFAULT_ALERT_DURATION = 3500;

src/components/pages/filesytem/FileList.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,10 @@ function FileList(props: Props): ReactElement {
223223
<div className="overflow-auto flex-grow-1">
224224
{/*Table Body*/}
225225
<Row className="m-0">
226-
{error ? (
226+
{error && !filesAndFolders.length ? (
227227
<Col className={"text-center"}> {error}</Col>
228228
) : filesAndFolders?.length === 0 ? (
229229
<Col className={"text-center"}>
230-
{" "}
231230
Nothing to see here.
232231
</Col>
233232
) : (

src/components/pages/filesytem/ToolbarActions.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { connect, ConnectedProps } from "react-redux";
55
import { deleteFsEntities } from "../../../background/api/filesystem";
66
import { constants } from "../../../background/constants";
77
import { FsEntity } from "../../../background/api/filesystemTypes";
8+
import { NewFolder } from "./upload/NewFolder";
89

910
const mapState = (state: SystemState) => ({
1011
selectedFsEntities: state.filesystem.selectedFsEntities
@@ -34,7 +35,7 @@ function ToolbarActions(props: Props): ReactElement | null {
3435
href={constants.url.FH_URL + "/download?ids=" + props.selectedFsEntities.map((e: FsEntity) => e.fileSystemId.toString())}>Download</Button>
3536
</span>
3637
</Fade>
37-
<Button>New Folder</Button>
38+
<NewFolder/>
3839
</span>
3940
);
4041
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { useSelector } from "react-redux";
2+
import React, { ReactElement, useState } from "react";
3+
import { RootState } from "../../../../background/redux/store";
4+
import { Button } from "react-bootstrap";
5+
import { Modal } from "react-bootstrap";
6+
import { NewFolderModalContent } from "./NewFolderModalContent";
7+
8+
function NewFolder(): ReactElement {
9+
const currentFsItemId = useSelector(
10+
(state: RootState) => state.filesystem.currentFsItemId
11+
);
12+
const [showModal, setShowModal] = useState(false);
13+
const handleClose = () => setShowModal(false);
14+
const handleShow = () => setShowModal(true);
15+
16+
if (currentFsItemId === "-1") {
17+
return <></>;
18+
}
19+
20+
return (
21+
<>
22+
<Button onClick={handleShow}>New Folder</Button>
23+
<Modal
24+
show={showModal}
25+
onHide={handleClose}
26+
contentClassName={"bg-body"}
27+
>
28+
<NewFolderModalContent
29+
handleClose={handleClose}
30+
currentFsItemId={currentFsItemId}
31+
/>
32+
</Modal>
33+
</>
34+
);
35+
}
36+
37+
export { NewFolder };
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Button, Form, Modal, Row } from "react-bootstrap";
2+
import React, { FormEvent, useState } from "react";
3+
import { createNewFolder } from "../../../../background/api/filesystem";
4+
import { isFileNameValid } from "../../../../background/methods/filesystem";
5+
import { useDispatch } from "react-redux";
6+
import { addToContents } from "../../../../background/redux/actions/filesystem";
7+
8+
interface Props {
9+
handleClose: () => void;
10+
currentFsItemId: string;
11+
}
12+
13+
function NewFolderModalContent({ handleClose, currentFsItemId }: Props) {
14+
const dispatch = useDispatch();
15+
const [folderName, setFolderName] = useState("");
16+
const [error, setError] = useState("");
17+
18+
function handleApply(event: FormEvent) {
19+
event.preventDefault();
20+
console.log("[NEW FOLDER ]", folderName);
21+
if (!isFileNameValid(folderName)) {
22+
setError("The name is not a valid foldername.");
23+
return;
24+
}
25+
26+
createNewFolder(folderName, currentFsItemId)
27+
.then((response) => {
28+
dispatch(addToContents(response.data));
29+
setError("");
30+
handleClose();
31+
})
32+
.catch((error) =>
33+
setError(
34+
error.response?.data.message ?? "Something went wrong :("
35+
)
36+
);
37+
}
38+
39+
return (
40+
<Form onSubmit={handleApply}>
41+
<Modal.Header closeButton>
42+
<Modal.Title>Create new Directory</Modal.Title>
43+
</Modal.Header>
44+
<Modal.Body>
45+
<Form.Group controlId="formFolderName">
46+
<Form.Label>Folder name</Form.Label>
47+
<Form.Control
48+
type="text"
49+
placeholder="e.g. My Cat Pictures"
50+
value={folderName}
51+
onChange={(event) => setFolderName(event.target.value)}
52+
/>
53+
</Form.Group>
54+
{error && <p className="text-danger">{error}</p>}
55+
</Modal.Body>
56+
57+
<Modal.Footer>
58+
<Row className="w-100 justify-content-between">
59+
<Button variant="secondary" onClick={handleClose}>
60+
Abort
61+
</Button>
62+
<Button variant="primary" onClick={handleApply}>
63+
Creat Folder
64+
</Button>
65+
</Row>
66+
</Modal.Footer>
67+
</Form>
68+
);
69+
}
70+
71+
export { NewFolderModalContent };

0 commit comments

Comments
 (0)