init from origin + update
This commit is contained in:
22
server/routers/task-boss/auth.js
Normal file
22
server/routers/task-boss/auth.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const hash = require('pbkdf2-password')()
|
||||
|
||||
const users = [{
|
||||
salt: '1iKffJhuhGpQGx7JjiGc+ShnorhVcJDtEqTTBQjX7tnuMxfdN3nMpYpcxY0MZSPSCCGP+9l4swhit4E/ZGgVZA==',
|
||||
hash: 'tQ0V8293708V2/sjyVG6yMSAlizKmTezTpoLxBc0hpfxEOImRNRGQbEx3HuGrowE4HYjzdshJbu52E5rsDE0Hfrmy1unoL/8tjeWGRF04d2sF1lhUrpxt3v1pf3du0rhR2PLTL5d2BWEtT3pSStiWeetw/zdZRDNMo9PVQqnjX0=',
|
||||
login: 'task-boss',
|
||||
id: '1',
|
||||
}]
|
||||
|
||||
function authenticate(login, pass, fn) {
|
||||
let user = users.find((u) => u.login === login)
|
||||
if (!user) return fn(new Error('невозможно найти пользователя'))
|
||||
hash({
|
||||
password: pass, salt: user.salt,
|
||||
}, (err, pass, salt, hash) => {
|
||||
if (err) return fn(err)
|
||||
if (hash === user.hash) return fn(null, user)
|
||||
fn(new Error('неверный пароль'))
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = authenticate
|
||||
5
server/routers/task-boss/constants.js
Normal file
5
server/routers/task-boss/constants.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const JWT_SECRET_KEY = 'hefiowaehfipwehafcpoweHPOCHopcvhophvnpx dsl;sad sl;dn lsdn lsd'
|
||||
|
||||
module.exports = {
|
||||
JWT_SECRET_KEY,
|
||||
}
|
||||
288
server/routers/task-boss/controllers.js
Normal file
288
server/routers/task-boss/controllers.js
Normal file
@@ -0,0 +1,288 @@
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
|
||||
const hash = require('pbkdf2-password')()
|
||||
|
||||
const { getDB } = require('../../utils/mongo')
|
||||
// eslint-disable-next-line import/order
|
||||
const ObjectId = require('mongodb').ObjectID
|
||||
|
||||
let db = null
|
||||
|
||||
const connect = async () => {
|
||||
db = await getDB('task-boss')
|
||||
}
|
||||
|
||||
const _idToId = (data) => {
|
||||
const { _id, ...rest } = data
|
||||
|
||||
return {
|
||||
id: _id,
|
||||
...rest,
|
||||
}
|
||||
}
|
||||
|
||||
const DEPTS_COLLECTION = 'depts4'
|
||||
const PEOPLES_COLLECTION = 'peoples4'
|
||||
const USERS_COLL = 'users3'
|
||||
|
||||
const getDepts = async () => {
|
||||
if (db === null) throw new Error('no db connection')
|
||||
|
||||
const deptsCollection = db.collection(DEPTS_COLLECTION)
|
||||
|
||||
let responseData = null
|
||||
|
||||
try {
|
||||
responseData = await deptsCollection.aggregate([{
|
||||
$lookup: {
|
||||
from: 'peoples',
|
||||
let: {
|
||||
id_fio_resp: '$idFio',
|
||||
},
|
||||
pipeline: [
|
||||
{
|
||||
$match:
|
||||
{
|
||||
$expr:
|
||||
{
|
||||
$eq: ['$id', '$$id_fio_resp'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
id: '$_id',
|
||||
_id: 0,
|
||||
family: 1,
|
||||
name: 1,
|
||||
secondName: 1,
|
||||
email: 1,
|
||||
phone: 1,
|
||||
},
|
||||
}],
|
||||
as: 'responsible',
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: '$responsible',
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
id: '$_id',
|
||||
_id: 0,
|
||||
parentId: '$parentDept',
|
||||
name: 1,
|
||||
qty: {
|
||||
$size: '$employees',
|
||||
},
|
||||
responsible: '$responsible',
|
||||
notes: '$note',
|
||||
tasks: {
|
||||
inWork: '0',
|
||||
newTasks: '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
]).toArray()
|
||||
} catch (e) {
|
||||
console.log(e.message)
|
||||
}
|
||||
|
||||
if (!responseData || responseData?.length === 0) {
|
||||
responseData = require('./stubs/depts.json').body
|
||||
}
|
||||
|
||||
return responseData
|
||||
}
|
||||
|
||||
const getDeptData = async (deptId) => {
|
||||
if (db === null) throw new Error('no db connection')
|
||||
|
||||
const deptsCollection = db.collection(DEPTS_COLLECTION)
|
||||
const data = await deptsCollection.aggregate([
|
||||
{
|
||||
$match: {
|
||||
_id: new ObjectId(deptId),
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: 'peoples',
|
||||
pipeline: [{
|
||||
$project: {
|
||||
id: '$_id',
|
||||
_id: 0,
|
||||
family: 1,
|
||||
name: 1,
|
||||
secondName: 1,
|
||||
email: 1,
|
||||
phone: 1,
|
||||
},
|
||||
}],
|
||||
localField: 'idFio',
|
||||
foreignField: 'id',
|
||||
as: 'responsible',
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: '$responsible',
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
id: '$_id',
|
||||
_id: 0,
|
||||
parentId: '$parentDept',
|
||||
name: 1,
|
||||
qty: {
|
||||
$size: '$employees',
|
||||
},
|
||||
responsible: '$responsible',
|
||||
notes: '$note',
|
||||
tasks: {
|
||||
inWork: '0',
|
||||
newTasks: '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
]).toArray()
|
||||
|
||||
if (data.length === 0) {
|
||||
throw new Error('No data')
|
||||
}
|
||||
|
||||
return _idToId(data[0])
|
||||
}
|
||||
|
||||
const dropDocuments = async (id) => {
|
||||
try {
|
||||
const deptsCollection = db.collection(DEPTS_COLLECTION)
|
||||
|
||||
const data = await deptsCollection.find({
|
||||
parentDept: id,
|
||||
}).toArray()
|
||||
|
||||
data.forEach((element) => {
|
||||
dropDocuments(element._id)
|
||||
})
|
||||
|
||||
deptsCollection.deleteOne({
|
||||
_id: new ObjectId(id),
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteDeptById = async (body) => {
|
||||
if (db === null) throw new Error('no db connection');
|
||||
try {
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (let deptId in body) {
|
||||
dropDocuments(deptId)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
const getPeoplesData = async () => {
|
||||
if (db === null) throw new Error('no db connection')
|
||||
|
||||
const peoplesCollection = db.collection(PEOPLES_COLLECTION)
|
||||
const data = await peoplesCollection.find().toArray()
|
||||
|
||||
if (data.length === 0) {
|
||||
const newData = require('./stubs/peoples/success.json').data
|
||||
|
||||
peoplesCollection.insertMany(newData)
|
||||
|
||||
return _idToId(newData)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const createDept = async ({ form, peoples }) => {
|
||||
if (db === null) throw new Error('no db connection')
|
||||
|
||||
const deptsCollection = db.collection(DEPTS_COLLECTION)
|
||||
|
||||
const data = await deptsCollection.find({
|
||||
name: form.name,
|
||||
}).toArray()
|
||||
|
||||
if (data.length > 0) throw new Error('duplication of stirng')
|
||||
|
||||
const employees = []
|
||||
peoples.forEach((item) => employees.push({
|
||||
id: item.id,
|
||||
}))
|
||||
const dataToInsert = {
|
||||
...form, employees,
|
||||
}
|
||||
deptsCollection.insertMany([dataToInsert])
|
||||
|
||||
return dataToInsert
|
||||
}
|
||||
const authenticate = async (login, pass, fn) => {
|
||||
if (db === null) return fn(new Error('no db connection'))
|
||||
const usersCollection = db.collection(USERS_COLL)
|
||||
const users = await usersCollection.find({
|
||||
login,
|
||||
}).toArray()
|
||||
if (!users.length) return fn(new Error('невозможно найти пользователя'))
|
||||
const [user] = users
|
||||
|
||||
hash({
|
||||
password: pass, salt: user.salt,
|
||||
}, (err, pass, salt, hash) => {
|
||||
if (err) return fn(err)
|
||||
if (hash === user.hash) return fn(null, user)
|
||||
fn(new Error('неверный пароль'))
|
||||
})
|
||||
}
|
||||
|
||||
const setUsers = async (dataUsers) => {
|
||||
if (db === null) throw new Error('no db connection')
|
||||
|
||||
const usersCollection = db.collection(USERS_COLL)
|
||||
const data = await usersCollection.find({
|
||||
$or: [{
|
||||
login: dataUsers.login,
|
||||
}, {
|
||||
mail: dataUsers.mail,
|
||||
}],
|
||||
}).toArray()
|
||||
|
||||
if (data.length === 0) {
|
||||
hash({
|
||||
password: dataUsers.password, saltLength: 15,
|
||||
}, (err, pass, salt, hash) => {
|
||||
const users = {
|
||||
}
|
||||
const { login, password, mail } = {
|
||||
...dataUsers,
|
||||
}
|
||||
users.login = login
|
||||
users.name = login
|
||||
users.mail = mail
|
||||
users.hash = hash
|
||||
users.salt = salt
|
||||
usersCollection.insertMany([users])
|
||||
})
|
||||
|
||||
return dataUsers
|
||||
}
|
||||
if (data.length !== 0) throw new Error('Почта или логин уже существует')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
connect,
|
||||
getDepts,
|
||||
getDeptData,
|
||||
getPeoplesData,
|
||||
createDept,
|
||||
setUsers,
|
||||
deleteDeptById,
|
||||
authenticate,
|
||||
}
|
||||
162
server/routers/task-boss/index.js
Normal file
162
server/routers/task-boss/index.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const router = require('express').Router()
|
||||
const { promisify } = require('util')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const md5 = require('crypto-js/md5')
|
||||
|
||||
const { JWT_SECRET_KEY } = require('./constants')
|
||||
const { getResponse } = require('../../utils/common')
|
||||
const { connect, getPeoplesData, createDept, getDepts, getDeptData, deleteDeptById, setUsers, authenticate } = require('./controllers')
|
||||
|
||||
connect()
|
||||
|
||||
router.get('/depts', async (req, res) => {
|
||||
const errors = []
|
||||
const depts = await getDepts().catch((e) => errors.push(e.message))
|
||||
res.send(getResponse(errors, depts))
|
||||
})
|
||||
|
||||
router.get('/peoples', async (req, res) => {
|
||||
const errors = []
|
||||
const peoplesData = await getPeoplesData().catch((e) => errors.push(e.message))
|
||||
res.send(getResponse(errors, peoplesData))
|
||||
})
|
||||
|
||||
router.post('/depts/create', async (req, res) => {
|
||||
const errors = []
|
||||
const deptData = await createDept(req.body.data).catch((e) => errors.push(e.message))
|
||||
res.send(getResponse(errors, deptData))
|
||||
})
|
||||
|
||||
router.get('/dept/:id', async (req, res) => {
|
||||
const errors = []
|
||||
const data = await getDeptData(req.params.id)
|
||||
res.send(getResponse(errors, data))
|
||||
})
|
||||
|
||||
router.post('/dept/delete', async (req, res) => {
|
||||
const errors = []
|
||||
await deleteDeptById(req.body)
|
||||
res.send(getResponse(errors, 'success'))
|
||||
});
|
||||
|
||||
router.post('/login', async (req, res, next) => {
|
||||
const auth = promisify(authenticate)
|
||||
try {
|
||||
console.log(req.body.login)
|
||||
console.log(req.body.password)
|
||||
const { hash, salt, ...user } = await auth(req.body.login, req.body.password)
|
||||
|
||||
const token = jwt.sign({
|
||||
user,
|
||||
}, JWT_SECRET_KEY)
|
||||
req.session.regenerate(() => {
|
||||
req.session.user = user
|
||||
res.send({
|
||||
success: true,
|
||||
body: {
|
||||
...user,
|
||||
token,
|
||||
},
|
||||
errors: [],
|
||||
warnings: [],
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/tasks', (req, resp) => {
|
||||
resp.send(require('./stubs/tasks/tasks.json'))
|
||||
})
|
||||
|
||||
router.post('/tasks/create', (req, resp) => {
|
||||
try {
|
||||
const { dept } = req.body
|
||||
const rawTasksData = fs.readFileSync(path.resolve(__dirname, './stubs/tasks/tasks.json'))
|
||||
const tasksData = JSON.parse(rawTasksData)
|
||||
const deptTasksData = tasksData.body.filter((data) => data.label === dept)
|
||||
|
||||
const task = {
|
||||
id: md5(new Date()).toString(),
|
||||
number: 100,
|
||||
task: req.body.task,
|
||||
status: 'open',
|
||||
priority: req.body.priority,
|
||||
performer: req.body.performer,
|
||||
deadline: new Date(req.body.deadline).getTime(),
|
||||
lastChanged: new Date().getTime(),
|
||||
description: req.body.description,
|
||||
}
|
||||
|
||||
if (!deptTasksData.length) {
|
||||
const newDeptTasksData = {
|
||||
label: req.body.dept,
|
||||
data: [task],
|
||||
}
|
||||
tasksData.body.push(newDeptTasksData)
|
||||
} else {
|
||||
tasksData.body.map((data) => {
|
||||
if (data.label === dept) {
|
||||
return data.data.push(task)
|
||||
}
|
||||
return data
|
||||
})
|
||||
}
|
||||
fs.writeFileSync(path.resolve(__dirname, './stubs/tasks/tasks.json'), JSON.stringify(tasksData))
|
||||
resp.send({
|
||||
success: true,
|
||||
body: task,
|
||||
errors: [],
|
||||
warnings: [],
|
||||
})
|
||||
} catch (e) {
|
||||
resp.send('./stubs/error.json')
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/tasks/delete', (req, resp) => {
|
||||
resp.send(require('./stubs/tasks/delete.json'))
|
||||
})
|
||||
|
||||
router.post('/tasks/edit', (req, resp) => {
|
||||
resp.send({
|
||||
success: true,
|
||||
body: req.body,
|
||||
errors: [],
|
||||
warnings: [],
|
||||
})
|
||||
})
|
||||
|
||||
router.post('/sign-up', async (req, res, next) => {
|
||||
try {
|
||||
const { hash, salt, ...user } = await setUsers({
|
||||
login: req.body.login,
|
||||
password: req.body.password,
|
||||
mail: req.body.mail,
|
||||
})
|
||||
|
||||
const token = jwt.sign({
|
||||
user,
|
||||
}, JWT_SECRET_KEY)
|
||||
req.session.regenerate(() => {
|
||||
req.session.user = user
|
||||
|
||||
res.send({
|
||||
success: true,
|
||||
body: {
|
||||
...user,
|
||||
token,
|
||||
},
|
||||
errors: [],
|
||||
warnings: [],
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
119
server/routers/task-boss/stubs/depts.json
Normal file
119
server/routers/task-boss/stubs/depts.json
Normal file
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"body": [{
|
||||
"id": "001",
|
||||
"parentId": "0",
|
||||
"name": "Головной",
|
||||
"qty": 56,
|
||||
"responsible": {
|
||||
"id": 1,
|
||||
"family": "Иванов",
|
||||
"name": "Иван",
|
||||
"secondName": "Иванович",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
},
|
||||
"tasks": {
|
||||
"inWork": 12,
|
||||
"newTasks": 54
|
||||
},
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "002",
|
||||
"parentId": "0",
|
||||
"name": "Цех",
|
||||
"qty": 52,
|
||||
"responsible": {
|
||||
"id": 2,
|
||||
"family": "Буйнов",
|
||||
"name": "Александр",
|
||||
"secondName": "Александрович",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
},
|
||||
"tasks": {
|
||||
"inWork": 67,
|
||||
"newTasks": 12
|
||||
},
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "003",
|
||||
"parentId": "002",
|
||||
"name": "Токарка",
|
||||
"qty": 6,
|
||||
"responsible": {
|
||||
"id": 3,
|
||||
"family": "Петров",
|
||||
"name": "Иван",
|
||||
"secondName": "Иванович",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
},
|
||||
"tasks": {
|
||||
"inWork": 22,
|
||||
"newTasks": 2
|
||||
},
|
||||
"notes": "1 этаж. красный сектор"
|
||||
},
|
||||
{
|
||||
"id": "004",
|
||||
"parentId": "002",
|
||||
"name": "Слесарка",
|
||||
"qty": 34,
|
||||
"responsible": {
|
||||
"id": 3,
|
||||
"family": "Петров",
|
||||
"name": "Иван",
|
||||
"secondName": "Иванович",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
},
|
||||
"tasks": {
|
||||
"inWork": 34,
|
||||
"newTasks": 10
|
||||
},
|
||||
"notes": "1 этаж. синий сектор"
|
||||
},
|
||||
{
|
||||
"id": "005",
|
||||
"parentId": "002",
|
||||
"name": "Отрезной",
|
||||
"qty": 12,
|
||||
"responsible": {
|
||||
"id": 4,
|
||||
"family": "Сидоров",
|
||||
"name": "Сергей",
|
||||
"secondName": "Викторович",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
},
|
||||
"tasks": {
|
||||
"inWork": 11,
|
||||
"newTasks": 0
|
||||
},
|
||||
"notes": "1 этаж. оранжевый сектор"
|
||||
},
|
||||
{
|
||||
"id": "006",
|
||||
"parentId": "0",
|
||||
"name": "Снабжение",
|
||||
"qty": 1,
|
||||
"responsible": {
|
||||
"id": 5,
|
||||
"family": "Зыков",
|
||||
"name": "Александр",
|
||||
"secondName": "Геннадьевич",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
},
|
||||
"tasks": {
|
||||
"inWork": 2,
|
||||
"newTasks": 0
|
||||
},
|
||||
"notes": ""
|
||||
}
|
||||
],
|
||||
"errors": [],
|
||||
"warnings": []
|
||||
}
|
||||
6
server/routers/task-boss/stubs/error.json
Normal file
6
server/routers/task-boss/stubs/error.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"success": true,
|
||||
"body": null,
|
||||
"errors": [],
|
||||
"warnings": []
|
||||
}
|
||||
30
server/routers/task-boss/stubs/peoples/success.json
Normal file
30
server/routers/task-boss/stubs/peoples/success.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"family": "Иванов",
|
||||
"name": "Иван",
|
||||
"secondName": "Иванович",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"family": "Буйнов",
|
||||
"name": "Александр",
|
||||
"secondName": "Александрович",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"family": "Зыков",
|
||||
"name": "Александр",
|
||||
"secondName": "Григорьевич",
|
||||
"phone": "",
|
||||
"email": ""
|
||||
}
|
||||
],
|
||||
"errors": [],
|
||||
"warnings": []
|
||||
}
|
||||
8
server/routers/task-boss/stubs/tasks/delete.json
Normal file
8
server/routers/task-boss/stubs/tasks/delete.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"success": true,
|
||||
"body": {
|
||||
"id": "1"
|
||||
},
|
||||
"errors": [],
|
||||
"warnings": []
|
||||
}
|
||||
165
server/routers/task-boss/stubs/tasks/tasks.json
Normal file
165
server/routers/task-boss/stubs/tasks/tasks.json
Normal file
@@ -0,0 +1,165 @@
|
||||
{
|
||||
"success": true,
|
||||
"body": [{
|
||||
"label": "Головной",
|
||||
"data": [{
|
||||
"id": "1",
|
||||
"number": 10,
|
||||
"task": "Установить Microsoft Office",
|
||||
"status": "inProgress",
|
||||
"priority": "middle",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "2",
|
||||
"number": 11,
|
||||
"task": "Замена картриджа",
|
||||
"status": "open",
|
||||
"priority": "high",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "3",
|
||||
"number": 12,
|
||||
"task": "Установка второго монитора",
|
||||
"status": "inProgress",
|
||||
"priority": "low",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "4",
|
||||
"number": 13,
|
||||
"task": "Замена клавиатуры",
|
||||
"status": "inProgress",
|
||||
"priority": "high",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1629534740538,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "5",
|
||||
"number": 14,
|
||||
"task": "Закончилась подписка на ПО",
|
||||
"status": "needInfo",
|
||||
"priority": "middle",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}]
|
||||
}, {
|
||||
"label": "Цех",
|
||||
"data": [{
|
||||
"id": "6",
|
||||
"number": 20,
|
||||
"task": "Установить Microsoft Office",
|
||||
"status": "inProgress",
|
||||
"priority": "middle",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "7",
|
||||
"number": 21,
|
||||
"task": "Замена картриджа",
|
||||
"status": "open",
|
||||
"priority": "high",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "8",
|
||||
"number": 22,
|
||||
"task": "Установка второго монитора",
|
||||
"status": "inProgress",
|
||||
"priority": "low",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "9",
|
||||
"number": 23,
|
||||
"task": "Замена клавиатуры",
|
||||
"status": "inProgress",
|
||||
"priority": "high",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1629534740538,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "10",
|
||||
"number": 24,
|
||||
"task": "Закончилась подписка на ПО",
|
||||
"status": "needInfo",
|
||||
"priority": "middle",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}]
|
||||
}, {
|
||||
"label": "Токарка",
|
||||
"data": [{
|
||||
"id": "11",
|
||||
"number": 30,
|
||||
"task": "Установить Microsoft Office",
|
||||
"status": "inProgress",
|
||||
"priority": "middle",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "12",
|
||||
"number": 31,
|
||||
"task": "Замена картриджа",
|
||||
"status": "open",
|
||||
"priority": "high",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "13",
|
||||
"number": 32,
|
||||
"task": "Установка второго монитора",
|
||||
"status": "inProgress",
|
||||
"priority": "low",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "14",
|
||||
"number": 33,
|
||||
"task": "Замена клавиатуры",
|
||||
"status": "inProgress",
|
||||
"priority": "high",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1629534740538,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}, {
|
||||
"id": "15",
|
||||
"number": 34,
|
||||
"task": "Закончилась подписка на ПО",
|
||||
"status": "needInfo",
|
||||
"priority": "middle",
|
||||
"performer": "Иванов Иван Иванович",
|
||||
"deadline": 1640927340000,
|
||||
"lastChanged": 1629534740538,
|
||||
"description": "Описание"
|
||||
}]
|
||||
}],
|
||||
"errors": [],
|
||||
"warnings": []
|
||||
}
|
||||
Reference in New Issue
Block a user