Add gzipped download endpoint
This commit is contained in:
@@ -66,6 +66,7 @@ export class WorkItem extends React.Component {
|
|||||||
},
|
},
|
||||||
photos: {
|
photos: {
|
||||||
isValid: (r, v) => v && v.length > 0,
|
isValid: (r, v) => v && v.length > 0,
|
||||||
|
initValue: [],
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
isValid: (r, v) => v !== "",
|
isValid: (r, v) => v !== "",
|
||||||
@@ -278,6 +279,7 @@ export class WorkItem extends React.Component {
|
|||||||
height: 400,
|
height: 400,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
}}
|
}}
|
||||||
|
showsUserLocation
|
||||||
showsBuildings={false}
|
showsBuildings={false}
|
||||||
showsTraffic={false}
|
showsTraffic={false}
|
||||||
showsIndoors={false}
|
showsIndoors={false}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export const dotify = (s) => {
|
|||||||
export const regionContainingPoints = (points, inset) => {
|
export const regionContainingPoints = (points, inset) => {
|
||||||
let minX, maxX, minY, maxY
|
let minX, maxX, minY, maxY
|
||||||
|
|
||||||
if (!points) {
|
if (!points || points.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ export class ActivityRoutes {
|
|||||||
passport.authenticate("bearer", { session: false }),
|
passport.authenticate("bearer", { session: false }),
|
||||||
catchAll(this.deleteActivity)
|
catchAll(this.deleteActivity)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app
|
||||||
|
.route("/activities/all")
|
||||||
|
.delete(
|
||||||
|
passport.authenticate("bearer", { session: false }),
|
||||||
|
catchAll(this.deleteAllActivities)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async listActivities(req, res, next) {
|
async listActivities(req, res, next) {
|
||||||
@@ -156,4 +163,12 @@ export class ActivityRoutes {
|
|||||||
|
|
||||||
res.json({})
|
res.json({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteAllActivities(req, res, next) {
|
||||||
|
const Activity = this.db.Activity
|
||||||
|
|
||||||
|
await Activity.remove({})
|
||||||
|
|
||||||
|
res.json({})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import passport from "passport"
|
import passport from "passport"
|
||||||
import createError from "http-errors"
|
import createError from "http-errors"
|
||||||
import autobind from "autobind-decorator"
|
import autobind from "autobind-decorator"
|
||||||
|
import zlib from "zlib"
|
||||||
|
import { Readable } from "stream"
|
||||||
import { catchAll } from "."
|
import { catchAll } from "."
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
@@ -38,6 +40,13 @@ export class TeamRoutes {
|
|||||||
passport.authenticate("bearer", { session: false }),
|
passport.authenticate("bearer", { session: false }),
|
||||||
catchAll(this.deleteTeam)
|
catchAll(this.deleteTeam)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app
|
||||||
|
.route("/teams/status")
|
||||||
|
.get(
|
||||||
|
passport.authenticate("bearer", { session: false }),
|
||||||
|
catchAll(this.getTeamStatus)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async listTeams(req, res, next) {
|
async listTeams(req, res, next) {
|
||||||
@@ -146,4 +155,32 @@ export class TeamRoutes {
|
|||||||
|
|
||||||
res.json({})
|
res.json({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTeamStatus(req, res, next) {
|
||||||
|
const Team = this.db.Team
|
||||||
|
const Activity = this.db.Activity
|
||||||
|
let teams = await Team.find({}).exec()
|
||||||
|
|
||||||
|
teams = teams.map((doc) => doc.toObject({ versionKey: false }))
|
||||||
|
|
||||||
|
for (let team of teams) {
|
||||||
|
let activities = await Activity.find({ team: team._id }).exec()
|
||||||
|
|
||||||
|
team.activities = activities.map((doc) =>
|
||||||
|
doc.toObject({ versionKey: false })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const gzip = zlib.createGzip()
|
||||||
|
let readable = new Readable()
|
||||||
|
|
||||||
|
readable.push(JSON.stringify(teams, null, " "))
|
||||||
|
readable.push(null)
|
||||||
|
|
||||||
|
res.writeHead(200, {
|
||||||
|
"Content-Type": "text/html",
|
||||||
|
"Content-Encoding": "gzip",
|
||||||
|
})
|
||||||
|
readable.pipe(gzip).pipe(res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,13 @@ export class WorkItemRoutes {
|
|||||||
passport.authenticate("bearer", { session: false }),
|
passport.authenticate("bearer", { session: false }),
|
||||||
catchAll(this.deleteWorkItem)
|
catchAll(this.deleteWorkItem)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app
|
||||||
|
.route("/workitems/all")
|
||||||
|
.delete(
|
||||||
|
passport.authenticate("bearer", { session: false }),
|
||||||
|
catchAll(this.deleteAllWorkItems)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async listWorkItems(req, res, next) {
|
async listWorkItems(req, res, next) {
|
||||||
@@ -189,4 +196,14 @@ export class WorkItemRoutes {
|
|||||||
|
|
||||||
res.json({})
|
res.json({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteAllWorkItems(req, res, next) {
|
||||||
|
const Activity = this.db.Activity
|
||||||
|
const WorkItem = this.db.WorkItem
|
||||||
|
|
||||||
|
await Activity.remove({})
|
||||||
|
await WorkItem.remove({})
|
||||||
|
|
||||||
|
res.json({})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export let activitySchema = new Schema(
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
notes: { type: String, required: true },
|
notes: { type: String, required: true },
|
||||||
when: { type: Date, required: true },
|
|
||||||
fromStreetNumber: Number,
|
fromStreetNumber: Number,
|
||||||
toStreetNumber: Number,
|
toStreetNumber: Number,
|
||||||
photos: [Schema.Types.ObjectId],
|
photos: [Schema.Types.ObjectId],
|
||||||
@@ -25,5 +24,5 @@ export let activitySchema = new Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
activitySchema.methods.toClient = function() {
|
activitySchema.methods.toClient = function() {
|
||||||
return this.toObject()
|
return this.toObject({ versionKey: false })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
import { Schema } from 'mongoose'
|
import { Schema } from "mongoose"
|
||||||
|
|
||||||
export let teamSchema = new Schema({
|
export let teamSchema = new Schema(
|
||||||
name: { type: String },
|
{
|
||||||
}, { timestamps: true, id: false })
|
name: { type: String },
|
||||||
|
start: { type: Date },
|
||||||
|
stop: { type: Date },
|
||||||
|
},
|
||||||
|
{ timestamps: true, id: false }
|
||||||
|
)
|
||||||
|
|
||||||
teamSchema.methods.toClient = function() {
|
teamSchema.methods.toClient = function() {
|
||||||
return this.toObject()
|
return this.toObject({ versionKey: false })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
import EventEmitter from 'eventemitter3'
|
import EventEmitter from "eventemitter3"
|
||||||
import io from 'socket.io-client'
|
import io from "socket.io-client"
|
||||||
import autobind from 'autobind-decorator'
|
import autobind from "autobind-decorator"
|
||||||
|
|
||||||
const authTokenName = 'AuthToken'
|
const authTokenName = "AuthToken"
|
||||||
|
|
||||||
class NetworkError extends Error {
|
class NetworkError extends Error {
|
||||||
constructor(message) {
|
constructor(message) {
|
||||||
super(message)
|
super(message)
|
||||||
this.name = this.constructor.name
|
this.name = this.constructor.name
|
||||||
if (typeof Error.captureStackTrace === 'function') {
|
if (typeof Error.captureStackTrace === "function") {
|
||||||
Error.captureStackTrace(this, this.constructor)
|
Error.captureStackTrace(this, this.constructor)
|
||||||
} else {
|
} else {
|
||||||
this.stack = (new Error(message)).stack
|
this.stack = new Error(message).stack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class APIError extends Error {
|
class APIError extends Error {
|
||||||
constructor(status, message) {
|
constructor(status, message) {
|
||||||
super(message || '')
|
super(message || "")
|
||||||
this.status = status || 500
|
this.status = status || 500
|
||||||
this.name = this.constructor.name
|
this.name = this.constructor.name
|
||||||
if (typeof Error.captureStackTrace === 'function') {
|
if (typeof Error.captureStackTrace === "function") {
|
||||||
Error.captureStackTrace(this, this.constructor)
|
Error.captureStackTrace(this, this.constructor)
|
||||||
} else {
|
} else {
|
||||||
this.stack = (new Error(message)).stack
|
this.stack = new Error(message).stack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,49 +35,51 @@ class API extends EventEmitter {
|
|||||||
super()
|
super()
|
||||||
this.user = null
|
this.user = null
|
||||||
|
|
||||||
let token = localStorage.getItem(authTokenName) || sessionStorage.getItem(authTokenName)
|
let token =
|
||||||
|
localStorage.getItem(authTokenName) ||
|
||||||
|
sessionStorage.getItem(authTokenName)
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
this.token = token
|
this.token = token
|
||||||
this.user = { pending: true }
|
this.user = { pending: true }
|
||||||
|
|
||||||
this.who()
|
this.who()
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
this.user = user
|
this.user = user
|
||||||
this.connectSocket()
|
this.connectSocket()
|
||||||
this.emit('login')
|
this.emit("login")
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
localStorage.removeItem(authTokenName)
|
localStorage.removeItem(authTokenName)
|
||||||
sessionStorage.removeItem(authTokenName)
|
sessionStorage.removeItem(authTokenName)
|
||||||
this.token = null
|
this.token = null
|
||||||
this.user = null
|
this.user = null
|
||||||
this.socket = null
|
this.socket = null
|
||||||
this.emit('logout')
|
this.emit("logout")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectSocket() {
|
connectSocket() {
|
||||||
this.socket = io(window.location.origin, {
|
this.socket = io(window.location.origin, {
|
||||||
path: '/api/socketio',
|
path: "/api/socketio",
|
||||||
query: {
|
query: {
|
||||||
auth_token: this.token
|
auth_token: this.token,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
this.socket.on('disconnect', (reason) => {
|
this.socket.on("disconnect", (reason) => {
|
||||||
// Could happen if the auth_token is bad
|
// Could happen if the auth_token is bad
|
||||||
this.socket = null
|
this.socket = null
|
||||||
})
|
})
|
||||||
this.socket.on('notify', (message) => {
|
this.socket.on("notify", (message) => {
|
||||||
const { eventName, eventData } = message
|
const { eventName, eventData } = message
|
||||||
|
|
||||||
// Filter the few massages that affect our cached user data to avoid a server round trip
|
// Filter the few massages that affect our cached user data to avoid a server round trip
|
||||||
switch (eventName) {
|
switch (eventName) {
|
||||||
case 'newThumbnailImage':
|
case "newThumbnailImage":
|
||||||
this.user.thumbnailImageId = eventData.imageId
|
this.user.thumbnailImageId = eventData.imageId
|
||||||
break
|
break
|
||||||
case 'newProfileImage':
|
case "newProfileImage":
|
||||||
this.user.imageId = eventData.imageId
|
this.user.imageId = eventData.imageId
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
@@ -102,22 +104,27 @@ class API extends EventEmitter {
|
|||||||
|
|
||||||
makeImageUrl(id, size) {
|
makeImageUrl(id, size) {
|
||||||
if (id) {
|
if (id) {
|
||||||
return '/api/assets/' + id + '?access_token=' + this.token
|
return "/api/assets/" + id + "?access_token=" + this.token
|
||||||
} else if (size && size.width && size.height) {
|
} else if (size && size.width && size.height) {
|
||||||
return `/api/placeholders/${size.width}x${size.height}?access_token=${this.token}`
|
return `/api/placeholders/${size.width}x${size.height}?access_token=${
|
||||||
|
this.token
|
||||||
|
}`
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeAssetUrl(id) {
|
makeAssetUrl(id) {
|
||||||
return id ? '/api/assets/' + id + '?access_token=' + this.token : null
|
return id ? "/api/assets/" + id + "?access_token=" + this.token : null
|
||||||
}
|
}
|
||||||
|
|
||||||
static makeParams(params) {
|
static makeParams(params) {
|
||||||
return params ? '?' + Object.keys(params).map((key) => (
|
return params
|
||||||
[key, params[key]].map(encodeURIComponent).join('=')
|
? "?" +
|
||||||
)).join('&') : ''
|
Object.keys(params)
|
||||||
|
.map((key) => [key, params[key]].map(encodeURIComponent).join("="))
|
||||||
|
.join("&")
|
||||||
|
: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
request(method, path, requestBody, requestOptions) {
|
request(method, path, requestBody, requestOptions) {
|
||||||
@@ -125,82 +132,90 @@ class API extends EventEmitter {
|
|||||||
var promise = new Promise((resolve, reject) => {
|
var promise = new Promise((resolve, reject) => {
|
||||||
let fetchOptions = {
|
let fetchOptions = {
|
||||||
method: method,
|
method: method,
|
||||||
mode: 'cors',
|
mode: "cors",
|
||||||
cache: 'no-store'
|
cache: "no-store",
|
||||||
}
|
}
|
||||||
let headers = new Headers()
|
let headers = new Headers()
|
||||||
if (this.token) {
|
if (this.token) {
|
||||||
headers.set('Authorization', 'Bearer ' + this.token)
|
headers.set("Authorization", "Bearer " + this.token)
|
||||||
}
|
}
|
||||||
if (method === 'POST' || method === 'PUT') {
|
if (method === "POST" || method === "PUT") {
|
||||||
if (requestOptions.binary) {
|
if (requestOptions.binary) {
|
||||||
headers.set('Content-Type', 'application/octet-stream')
|
headers.set("Content-Type", "application/octet-stream")
|
||||||
headers.set('Content-Length', requestOptions.binary.length)
|
headers.set("Content-Length", requestOptions.binary.length)
|
||||||
headers.set('Range', 'byte ' + requestOptions.binary.offset)
|
headers.set("Range", "byte " + requestOptions.binary.offset)
|
||||||
fetchOptions.body = requestBody
|
fetchOptions.body = requestBody
|
||||||
} else {
|
} else {
|
||||||
headers.set('Content-Type', 'application/json')
|
headers.set("Content-Type", "application/json")
|
||||||
fetchOptions.body = JSON.stringify(requestBody)
|
fetchOptions.body = JSON.stringify(requestBody)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchOptions.headers = headers
|
fetchOptions.headers = headers
|
||||||
fetch('/api' + path, fetchOptions).then((res) => {
|
fetch("/api" + path, fetchOptions)
|
||||||
return Promise.all([ Promise.resolve(res), (requestOptions.binary && method === 'GET') ? res.blob() : res.json() ])
|
.then((res) => {
|
||||||
}).then((arr) => {
|
return Promise.all([
|
||||||
let [ res, responseBody ] = arr
|
Promise.resolve(res),
|
||||||
if (res.ok) {
|
requestOptions.binary && method === "GET" ? res.blob() : res.json(),
|
||||||
if (requestOptions.wantHeaders) {
|
])
|
||||||
resolve({ body: responseBody, headers: res.headers })
|
})
|
||||||
|
.then((arr) => {
|
||||||
|
let [res, responseBody] = arr
|
||||||
|
if (res.ok) {
|
||||||
|
if (requestOptions.wantHeaders) {
|
||||||
|
resolve({ body: responseBody, headers: res.headers })
|
||||||
|
} else {
|
||||||
|
resolve(responseBody)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
resolve(responseBody)
|
reject(new APIError(res.status, responseBody.message))
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
reject(new APIError(res.status, responseBody.message))
|
.catch((error) => {
|
||||||
}
|
reject(new NetworkError(error.message))
|
||||||
}).catch((error) => {
|
})
|
||||||
reject(new NetworkError(error.message))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
|
|
||||||
post(path, requestBody, options) {
|
post(path, requestBody, options) {
|
||||||
return this.request('POST', path, requestBody, options)
|
return this.request("POST", path, requestBody, options)
|
||||||
}
|
}
|
||||||
put(path, requestBody, options) {
|
put(path, requestBody, options) {
|
||||||
return this.request('PUT', path, requestBody, options)
|
return this.request("PUT", path, requestBody, options)
|
||||||
}
|
}
|
||||||
get(path, options) {
|
get(path, options) {
|
||||||
return this.request('GET', path, options)
|
return this.request("GET", path, options)
|
||||||
}
|
}
|
||||||
delete(path, options) {
|
delete(path, options) {
|
||||||
return this.request('DELETE', path, options)
|
return this.request("DELETE", path, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
login(email, password, remember) {
|
login(email, password, remember) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.post('/auth/login', { email, password }, { wantHeaders: true }).then((response) => {
|
this.post("/auth/login", { email, password }, { wantHeaders: true })
|
||||||
// Save bearer token for later use
|
.then((response) => {
|
||||||
const authValue = response.headers.get('Authorization')
|
// Save bearer token for later use
|
||||||
const [ scheme, token ] = authValue.split(' ')
|
const authValue = response.headers.get("Authorization")
|
||||||
|
const [scheme, token] = authValue.split(" ")
|
||||||
|
|
||||||
if (scheme !== 'Bearer' || !token) {
|
if (scheme !== "Bearer" || !token) {
|
||||||
reject(new APIError('Unexpected Authorization scheme or token'))
|
reject(new APIError("Unexpected Authorization scheme or token"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
localStorage.setItem(authTokenName, token)
|
localStorage.setItem(authTokenName, token)
|
||||||
} else {
|
} else {
|
||||||
sessionStorage.setItem(authTokenName, token)
|
sessionStorage.setItem(authTokenName, token)
|
||||||
}
|
}
|
||||||
this.token = token
|
this.token = token
|
||||||
this.user = response.body
|
this.user = response.body
|
||||||
this.connectSocket()
|
this.connectSocket()
|
||||||
this.emit('login')
|
this.emit("login")
|
||||||
resolve(response.body)
|
resolve(response.body)
|
||||||
}).catch((err) => {
|
})
|
||||||
reject(err)
|
.catch((err) => {
|
||||||
})
|
reject(err)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
logout() {
|
logout() {
|
||||||
@@ -211,82 +226,91 @@ class API extends EventEmitter {
|
|||||||
this.token = null
|
this.token = null
|
||||||
this.user = null
|
this.user = null
|
||||||
this.disconnectSocket()
|
this.disconnectSocket()
|
||||||
this.emit('logout')
|
this.emit("logout")
|
||||||
}
|
}
|
||||||
return this.delete('/auth/login').then(cb, cb)
|
return this.delete("/auth/login").then(cb, cb)
|
||||||
}
|
}
|
||||||
who() {
|
who() {
|
||||||
return this.get('/auth/who')
|
return this.get("/auth/who")
|
||||||
}
|
}
|
||||||
confirmEmail(emailToken) {
|
confirmEmail(emailToken) {
|
||||||
return this.post('/auth/email/confirm/', { emailToken })
|
return this.post("/auth/email/confirm/", { emailToken })
|
||||||
}
|
}
|
||||||
sendConfirmEmail(emails) {
|
sendConfirmEmail(emails) {
|
||||||
return this.post('/auth/email/send', emails)
|
return this.post("/auth/email/send", emails)
|
||||||
}
|
}
|
||||||
changePassword(passwords) {
|
changePassword(passwords) {
|
||||||
return this.post('/auth/password/change', passwords)
|
return this.post("/auth/password/change", passwords)
|
||||||
}
|
}
|
||||||
sendResetPassword(email) {
|
sendResetPassword(email) {
|
||||||
return this.post('/auth/password/send', { email })
|
return this.post("/auth/password/send", { email })
|
||||||
}
|
}
|
||||||
confirmResetPassword(passwordToken) {
|
confirmResetPassword(passwordToken) {
|
||||||
return this.post('/auth/password/confirm', { passwordToken })
|
return this.post("/auth/password/confirm", { passwordToken })
|
||||||
}
|
}
|
||||||
resetPassword(passwords) {
|
resetPassword(passwords) {
|
||||||
return this.post('/auth/password/reset', passwords)
|
return this.post("/auth/password/reset", passwords)
|
||||||
}
|
}
|
||||||
|
|
||||||
getUser(id) {
|
getUser(id) {
|
||||||
return this.get('/users/' + id)
|
return this.get("/users/" + id)
|
||||||
}
|
}
|
||||||
listUsers() {
|
listUsers() {
|
||||||
return this.get('/users')
|
return this.get("/users")
|
||||||
}
|
}
|
||||||
listUsersForTeam(teamId) {
|
listUsersForTeam(teamId) {
|
||||||
return this.get(`/users?team=${teamId}`)
|
return this.get(`/users?team=${teamId}`)
|
||||||
}
|
}
|
||||||
createUser(user) {
|
createUser(user) {
|
||||||
return this.post('/users', user)
|
return this.post("/users", user)
|
||||||
}
|
}
|
||||||
updateUser(user) {
|
updateUser(user) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.put('/users', user).then((user) => {
|
this.put("/users", user)
|
||||||
// If we just updated ourselves, update the internal cached copy
|
.then((user) => {
|
||||||
if (user._id === this.user._id) {
|
// If we just updated ourselves, update the internal cached copy
|
||||||
this.user = user
|
if (user._id === this.user._id) {
|
||||||
this.emit('login')
|
this.user = user
|
||||||
}
|
this.emit("login")
|
||||||
resolve(user)
|
}
|
||||||
}).catch((reason) => {
|
resolve(user)
|
||||||
reject(reason)
|
})
|
||||||
})
|
.catch((reason) => {
|
||||||
|
reject(reason)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
deleteUser(id) {
|
deleteUser(id) {
|
||||||
return this.delete('/users/' + id)
|
return this.delete("/users/" + id)
|
||||||
}
|
}
|
||||||
enterRoom(roomName) {
|
enterRoom(roomName) {
|
||||||
return this.put('/users/enter-room/' + (roomName || ''))
|
return this.put("/users/enter-room/" + (roomName || ""))
|
||||||
}
|
}
|
||||||
leaveRoom() {
|
leaveRoom() {
|
||||||
return this.put('/users/leave-room')
|
return this.put("/users/leave-room")
|
||||||
}
|
}
|
||||||
|
|
||||||
getTeam(id) {
|
getTeam(id) {
|
||||||
return this.get('/teams/' + id)
|
return this.get("/teams/" + id)
|
||||||
}
|
}
|
||||||
listTeams() {
|
listTeams() {
|
||||||
return this.get('/teams')
|
return this.get("/teams")
|
||||||
}
|
}
|
||||||
createTeam(team) {
|
createTeam(team) {
|
||||||
return this.post('/teams', team)
|
return this.post("/teams", team)
|
||||||
}
|
}
|
||||||
updateTeam(team) {
|
updateTeam(team) {
|
||||||
return this.put('/teams', team)
|
return this.put("/teams", team)
|
||||||
}
|
}
|
||||||
deleteTeam(id) {
|
deleteTeam(id) {
|
||||||
return this.delete('/teams/' + id)
|
return this.delete("/teams/" + id)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAllActivities() {
|
||||||
|
return this.delete("/activities/all")
|
||||||
|
}
|
||||||
|
deleteAllWorkItems() {
|
||||||
|
return this.delete("/workitems/all")
|
||||||
}
|
}
|
||||||
|
|
||||||
upload(file, progressCallback) {
|
upload(file, progressCallback) {
|
||||||
@@ -302,36 +326,40 @@ class API extends EventEmitter {
|
|||||||
const buffer = e.target.result
|
const buffer = e.target.result
|
||||||
const bytesRead = buffer.byteLength
|
const bytesRead = buffer.byteLength
|
||||||
|
|
||||||
this.post('/assets/upload/' + uploadId, buffer, {
|
this.post("/assets/upload/" + uploadId, buffer, {
|
||||||
binary: { offset: chunk * chunkSize, length: bytesRead }
|
binary: { offset: chunk * chunkSize, length: bytesRead },
|
||||||
}).then((uploadData) => {
|
|
||||||
chunk++
|
|
||||||
if (!progressCallback(uploadData)) {
|
|
||||||
return Promise.reject(new Error('Upload was canceled'))
|
|
||||||
}
|
|
||||||
if (chunk < numberOfChunks) {
|
|
||||||
let start = chunk * chunkSize
|
|
||||||
let end = Math.min(fileSize, start + chunkSize)
|
|
||||||
reader.readAsArrayBuffer(file.slice(start, end))
|
|
||||||
} else {
|
|
||||||
resolve(uploadData)
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
reject(err)
|
|
||||||
})
|
})
|
||||||
|
.then((uploadData) => {
|
||||||
|
chunk++
|
||||||
|
if (!progressCallback(uploadData)) {
|
||||||
|
return Promise.reject(new Error("Upload was canceled"))
|
||||||
|
}
|
||||||
|
if (chunk < numberOfChunks) {
|
||||||
|
let start = chunk * chunkSize
|
||||||
|
let end = Math.min(fileSize, start + chunkSize)
|
||||||
|
reader.readAsArrayBuffer(file.slice(start, end))
|
||||||
|
} else {
|
||||||
|
resolve(uploadData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.post('/assets/upload', {
|
this.post("/assets/upload", {
|
||||||
fileName: file.name,
|
fileName: file.name,
|
||||||
fileSize,
|
fileSize,
|
||||||
contentType: file.type,
|
contentType: file.type,
|
||||||
numberOfChunks
|
numberOfChunks,
|
||||||
}).then((uploadData) => {
|
|
||||||
uploadId = uploadData.uploadId
|
|
||||||
reader.readAsArrayBuffer(file.slice(0, chunkSize))
|
|
||||||
}).catch((err) => {
|
|
||||||
reject(err)
|
|
||||||
})
|
})
|
||||||
|
.then((uploadData) => {
|
||||||
|
uploadId = uploadData.uploadId
|
||||||
|
reader.readAsArrayBuffer(file.slice(0, chunkSize))
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { Component, Fragment } from 'react'
|
import React, { Component, Fragment } from "react"
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from "prop-types"
|
||||||
import { Box, Image, Column, Row, Button } from 'ui'
|
import { Box, Image, Column, Row, Button } from "ui"
|
||||||
import { MessageModal, WaitModal } from '../Modal'
|
import { MessageModal, WaitModal, YesNoMessageModal } from "../Modal"
|
||||||
// import { api } from 'src/API'
|
import { sizeInfo, colorInfo } from "ui/style"
|
||||||
import { sizeInfo, colorInfo } from 'ui/style'
|
import headerLogo from "images/deighton.png"
|
||||||
import headerLogo from 'images/deighton.png'
|
import autobind from "autobind-decorator"
|
||||||
|
import { api } from "../API"
|
||||||
|
|
||||||
export class System extends Component {
|
export class System extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -16,19 +17,113 @@ export class System extends Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
messageModal: null,
|
messageModal: null,
|
||||||
waitModal: null,
|
waitModal: null,
|
||||||
|
yesNoModal: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(props) {
|
componentDidMount(props) {
|
||||||
this.props.changeTitle('System')
|
this.props.changeTitle("System")
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.props.changeTitle('')
|
this.props.changeTitle("")
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleDeleteActivities() {
|
||||||
|
this.setState({
|
||||||
|
yesNoModal: {
|
||||||
|
question:
|
||||||
|
"Are you sure you want to delete all activities in the system?",
|
||||||
|
onDismiss: this.handleDeleteActivitiesDismiss,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleDeleteActivitiesDismiss(yes) {
|
||||||
|
if (yes) {
|
||||||
|
this.setState({ waitModal: { message: "Deleting All Activities..." } })
|
||||||
|
api
|
||||||
|
.deleteAllActivities()
|
||||||
|
.then(() => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: "thumb",
|
||||||
|
message: "All logged activities have been deleted",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: "hand",
|
||||||
|
message: "Unable to request delete activities.",
|
||||||
|
detail: error.message,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
yesNoModal: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleDeleteWorkItems() {
|
||||||
|
this.setState({
|
||||||
|
yesNoModal: {
|
||||||
|
question:
|
||||||
|
"Are you sure you want to delete all work items & activities in the system?",
|
||||||
|
onDismiss: this.handleDeleteWorkItemsDismiss,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleDeleteWorkItemsDismiss(yes) {
|
||||||
|
if (yes) {
|
||||||
|
this.setState({
|
||||||
|
waitModal: { message: "Deleting All Work Items & Activities..." },
|
||||||
|
})
|
||||||
|
api
|
||||||
|
.deleteAllWorkItems()
|
||||||
|
.then(() => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: "thumb",
|
||||||
|
message: "All work items and logged activities have been deleted",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: "hand",
|
||||||
|
message: "Unable to delete work items and activities.",
|
||||||
|
detail: error.message,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
yesNoModal: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleMessageModalDismiss() {
|
||||||
|
this.setState({ messageModal: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { messageModal, waitModal } = this.state
|
const { messageModal, yesNoModal, waitModal } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@@ -38,14 +133,22 @@ export class System extends Component {
|
|||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
<Row.Item width={sizeInfo.formRowSpacing} />
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
<Row.Item width={sizeInfo.modalWidth}>
|
<Row.Item width={sizeInfo.modalWidth}>
|
||||||
<Box border={{ width: sizeInfo.headerBorderWidth, color: colorInfo.headerBorder }} radius={sizeInfo.formBoxRadius}>
|
<Box
|
||||||
|
border={{
|
||||||
|
width: sizeInfo.headerBorderWidth,
|
||||||
|
color: colorInfo.headerBorder,
|
||||||
|
}}
|
||||||
|
radius={sizeInfo.formBoxRadius}>
|
||||||
<Column>
|
<Column>
|
||||||
<Column.Item minHeight={sizeInfo.formColumnSpacing} />
|
<Column.Item minHeight={sizeInfo.formColumnSpacing} />
|
||||||
<Column.Item>
|
<Column.Item>
|
||||||
<Row>
|
<Row>
|
||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
<Row.Item>
|
<Row.Item>
|
||||||
<Image source={headerLogo} width={sizeInfo.loginLogoWidth} />
|
<Image
|
||||||
|
source={headerLogo}
|
||||||
|
width={sizeInfo.loginLogoWidth}
|
||||||
|
/>
|
||||||
</Row.Item>
|
</Row.Item>
|
||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
</Row>
|
</Row>
|
||||||
@@ -55,7 +158,11 @@ export class System extends Component {
|
|||||||
<Row>
|
<Row>
|
||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
<Row.Item>
|
<Row.Item>
|
||||||
<Button text='Delete All Activities' width={sizeInfo.buttonWideWidth} />
|
<Button
|
||||||
|
text="Delete All Activities"
|
||||||
|
width={sizeInfo.buttonWideWidth}
|
||||||
|
onClick={this.handleDeleteActivities}
|
||||||
|
/>
|
||||||
</Row.Item>
|
</Row.Item>
|
||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
</Row>
|
</Row>
|
||||||
@@ -65,7 +172,11 @@ export class System extends Component {
|
|||||||
<Row>
|
<Row>
|
||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
<Row.Item>
|
<Row.Item>
|
||||||
<Button text='Delete All Work Items' width={sizeInfo.buttonWideWidth} />
|
<Button
|
||||||
|
text="Delete All Work Items"
|
||||||
|
width={sizeInfo.buttonWideWidth}
|
||||||
|
onClick={this.handleDeleteWorkItems}
|
||||||
|
/>
|
||||||
</Row.Item>
|
</Row.Item>
|
||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
</Row>
|
</Row>
|
||||||
@@ -75,7 +186,10 @@ export class System extends Component {
|
|||||||
<Row>
|
<Row>
|
||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
<Row.Item>
|
<Row.Item>
|
||||||
<Button text='Delete All Teams' width={sizeInfo.buttonWideWidth} />
|
<Button
|
||||||
|
text="Download Team Data"
|
||||||
|
width={sizeInfo.buttonWideWidth}
|
||||||
|
/>
|
||||||
</Row.Item>
|
</Row.Item>
|
||||||
<Row.Item grow />
|
<Row.Item grow />
|
||||||
</Row>
|
</Row>
|
||||||
@@ -88,15 +202,24 @@ export class System extends Component {
|
|||||||
</Row>
|
</Row>
|
||||||
</Column.Item>
|
</Column.Item>
|
||||||
<Column.Item grow>
|
<Column.Item grow>
|
||||||
|
<YesNoMessageModal
|
||||||
|
open={!!yesNoModal}
|
||||||
|
question={yesNoModal ? yesNoModal.question : ""}
|
||||||
|
onDismiss={yesNoModal && yesNoModal.onDismiss}
|
||||||
|
/>
|
||||||
|
|
||||||
<MessageModal
|
<MessageModal
|
||||||
open={!!messageModal}
|
open={!!messageModal}
|
||||||
icon={messageModal ? messageModal.icon : ''}
|
icon={messageModal ? messageModal.icon : ""}
|
||||||
message={messageModal ? messageModal.message : ''}
|
message={messageModal ? messageModal.message : ""}
|
||||||
detail={messageModal ? messageModal.title : ''}
|
detail={messageModal ? messageModal.title : ""}
|
||||||
onDismiss={this.handleMessageModalDismiss} />
|
onDismiss={this.handleMessageModalDismiss}
|
||||||
|
/>
|
||||||
|
|
||||||
<WaitModal active={!!waitModal}
|
<WaitModal
|
||||||
message={waitModal ? waitModal.message : ''} />
|
active={!!waitModal}
|
||||||
|
message={waitModal ? waitModal.message : ""}
|
||||||
|
/>
|
||||||
</Column.Item>
|
</Column.Item>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user