Skip to content

Commit 30163e9

Browse files
committed
Cancel job using job id
Closes #35
1 parent 3654e3f commit 30163e9

File tree

4 files changed

+132
-24
lines changed

4 files changed

+132
-24
lines changed

src/utils/API/endpoint.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,12 @@ const urls = {
110110
/**
111111
* Delete a config with config name.
112112
*/
113-
deleteConfig: "config/delete"
113+
deleteConfig: "config/delete",
114+
115+
/**
116+
* Stop a running job by job id
117+
*/
118+
stopJob: "job/stop",
114119

115120
};
116-
export default urls;
121+
export default urls;

src/utils/Tools.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,31 @@ export function changeSearchFilter(list, searchQuery = "") {
271271
export function isLocalRemoteName(remoteName) {
272272
return (remoteName && remoteName !== "" && remoteName[0] === "/");
273273
}
274+
275+
/**
276+
* Group the array items by the given key inside each object.
277+
*
278+
* @param xs{T} array of T type
279+
* @param keyGetter key to select from the T
280+
* @returns map{T} map with format {key: [...objects with same key]}
281+
*/
282+
283+
// export function groupByKey(xs, key) {
284+
// return xs.reduce(function(rv, x) {
285+
// (rv[x[key]] = rv[x[key]] || []).push(x);
286+
// return rv;
287+
// }, {});
288+
// }
289+
export function groupByKey(xs, keyGetter) {
290+
const map = new Map();
291+
xs.forEach((item) => {
292+
const key = keyGetter(item);
293+
const collection = map.get(key);
294+
if (!collection) {
295+
map.set(key, [item]);
296+
} else {
297+
collection.push(item);
298+
}
299+
});
300+
return map;
301+
}

src/views/Base/RunningJobs/RunningJobs.js

Lines changed: 96 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import React from 'react';
2-
import {Button, Card, CardBody, CardHeader, Col, Progress, Row} from "reactstrap";
3-
import {bytesToKB, formatBytes, secondsToStr} from "../../../utils/Tools";
1+
import React, {useState} from 'react';
2+
import {Button, Card, CardBody, CardHeader, Col, Collapse, Container, Progress, Row} from "reactstrap";
3+
import {bytesToKB, formatBytes, groupByKey, secondsToStr} from "../../../utils/Tools";
44
import * as PropTypes from "prop-types";
55
import {connect} from "react-redux";
66
import {Line} from "react-chartjs-2";
77
import {CustomTooltips} from "@coreui/coreui-plugin-chartjs-custom-tooltips";
8+
import axiosInstance from "../../../utils/API/API";
9+
import urls from "../../../utils/API/endpoint";
810

911
const options = {
1012
tooltips: {
@@ -47,27 +49,27 @@ function JobCard({job}) {
4749
}
4850

4951
function getCroppedName(name) {
50-
const leftChars = 30;
51-
const rightChars = 5;
52+
const leftChars = 30;
53+
const rightChars = 5;
5254

53-
if (name.length > leftChars) {
54-
const croppedName = name.substr(0, leftChars) + " ... " + name.substr(-rightChars);
55-
return croppedName;
56-
}
57-
return name;
55+
if (name.length > leftChars) {
56+
const croppedName = name.substr(0, leftChars) + " ... " + name.substr(-rightChars);
57+
return croppedName;
58+
}
59+
return name;
5860

5961
}
6062

6163
function JobCardRow({job}) {
6264
const {name, percentage, speed, size} = job;
6365
return (
6466
<React.Fragment>
65-
<Row className="runningJobs">
66-
{(size && speed) ? (
67+
<Row className="runningJobs">
68+
{(size && speed) ? (
6769

68-
<Col lg={12} className="itemName"> {getCroppedName(name)} {" "}
69-
({formatBytes(size)}) - {formatBytes(speed)}PS </Col>
70-
) : (
70+
<Col lg={12} className="itemName"> {getCroppedName(name)} {" "}
71+
({formatBytes(size)}) - {formatBytes(speed)}PS </Col>
72+
) : (
7173
<Col lg={12}>Calculating</Col>)}
7274

7375
</Row>
@@ -82,7 +84,7 @@ function JobCardRow({job}) {
8284
}
8385

8486
function GlobalStatus({stats}) {
85-
const {speed, bytes, checks, elapsedTime, deletes, errors, transfers} = stats;
87+
const {speed, bytes, checks, elapsedTime, deletes, errors, transfers, lastError} = stats;
8688
return (
8789
<Card>
8890
<CardHeader><strong>Global Stats</strong></CardHeader>
@@ -117,6 +119,11 @@ function GlobalStatus({stats}) {
117119
<td>Transfers:</td>
118120
<td>{transfers}</td>
119121
</tr>
122+
<tr>
123+
<td>Last Error:</td>
124+
<td>{lastError}</td>
125+
</tr>
126+
120127
</tbody>
121128
</table>
122129

@@ -129,17 +136,84 @@ function GlobalStatus({stats}) {
129136
function TransferringJobs({transferring}) {
130137
if (transferring !== undefined) {
131138
return transferring.map((item, idx) => {
132-
return (<JobCard key={item.name} job={item}/>);
139+
return (<JobCard key={item.name} job={item}/>);
133140
});
134141
}
135142
return null;
136143
}
137144

138145
function TransferringJobsRow({transferring}) {
139146
if (transferring !== undefined) {
140-
return transferring.map((item, idx) => {
141-
return (<JobCardRow key={item.name} job={item}/>);
147+
const grouped = groupByKey(transferring, job => job.group);
148+
console.log(grouped);
149+
150+
const array = [];
151+
152+
grouped.forEach((val, keys) => {
153+
console.log(val, keys);
154+
array.push (<JobGroup job={val} groupId={keys} key={keys}/>);
142155
});
156+
return array;
157+
158+
// return grouped.values().map((item, idx) => {
159+
// return (<JobCardRow key={item.name} job={item}/>);
160+
// });
161+
}
162+
return null;
163+
}
164+
165+
function JobGroup({job, groupId}) {
166+
const [showCollapse, setShowCollapse] = useState(false);
167+
const [cancelButtonEnabled, setCancelButtonEnabled] = useState(true);
168+
console.log(job);
169+
170+
const stopJob = (e, groupId) => {
171+
e.stopPropagation();
172+
if(groupId && groupId.indexOf('/') !== -1) {
173+
setCancelButtonEnabled(false);
174+
const jobid = groupId.split('/')[1];
175+
axiosInstance.post(urls.stopJob, {jobid, _async:true}).then(function (res) {
176+
console.log(res);
177+
}).catch(err => {
178+
console.error(err);
179+
})
180+
}
181+
};
182+
// setCancelButtonEnabled((groupId && groupId !== "undefined"));
183+
if(job) {
184+
return (
185+
<>
186+
{groupId &&
187+
<Card>
188+
189+
<CardHeader onClick={() => setShowCollapse(!showCollapse)}>
190+
<Container>
191+
<Row>
192+
<Col sm={10}>
193+
Transferring {job.length} file(s)
194+
</Col>
195+
<Col sm={2}>
196+
<Button color={"light"} disabled={!cancelButtonEnabled}
197+
onClick={(e) => stopJob(e, groupId)}
198+
className={"btn-outline-danger btn-pill"}><i
199+
className="fa fa-close fa-sm"/></Button>
200+
</Col>
201+
</Row>
202+
</Container>
203+
</CardHeader>
204+
<Collapse isOpen={showCollapse}>
205+
<CardBody>
206+
{
207+
job.map((item, idx) => {
208+
return (<JobCardRow key={item.name} job={item}/>);
209+
})
210+
}
211+
</CardBody>
212+
</Collapse>
213+
</Card>
214+
}
215+
</>
216+
);
143217
}
144218
return null;
145219
}
@@ -164,6 +238,8 @@ class RunningJobs extends React.Component {
164238

165239

166240

241+
242+
167243
render() {
168244
const {jobs, isConnected, lineChartData} = this.props;
169245
const {transferring} = jobs;
@@ -200,7 +276,6 @@ class RunningJobs extends React.Component {
200276
} else if (mode === "card") {
201277
if (isConnected) {
202278
return (
203-
204279
<TransferringJobsRow transferring={transferring}/>
205280
);
206281
} else {
@@ -218,7 +293,7 @@ class RunningJobs extends React.Component {
218293
</Button>
219294
</div>
220295
</CardHeader>
221-
<CardBody className={!this.state.isShowing ? "d-none" : "progress-modal-body"}>
296+
<CardBody className={!this.state.isShowing ? "d-none" : "progress-modal-body"} style={{overflowY: 'scroll'}}>
222297
<TransferringJobsRow transferring={transferring}/>
223298

224299
</CardBody>

src/views/Explorer/FilesView/FilesView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ class FilesView extends React.PureComponent {
243243
await axiosInstance.post(urls.deleteFile, data);
244244
this.updateHandler();
245245
toast.info(`${item.Name} deleted.`, {
246-
autoClose: false
246+
autoClose: true
247247
});
248248
}
249249
} catch (e) {

0 commit comments

Comments
 (0)