diff --git a/api/src/controllers/train.controller.js b/api/src/controllers/train.controller.js index a34c6612..0beb1b3d 100644 --- a/api/src/controllers/train.controller.js +++ b/api/src/controllers/train.controller.js @@ -8,24 +8,35 @@ const train = require('../util/train.util'); const filesystem = require('../util/fs.util'); const { jwt } = require('../util/auth.util'); const { BAD_REQUEST } = require('../constants/http-status'); -const { AUTH, STORAGE } = require('../constants'); +const { AUTH, STORAGE, UI } = require('../constants'); const { tryParseJSON } = require('../util/validators.util'); module.exports.get = async (req, res) => { + const limit = UI.PAGINATION.LIMIT; + const { page } = req.query; const token = AUTH ? jwt.sign({ route: 'storage' }) : null; const db = database.connect(); let files = req.query.name ? db .prepare( - 'SELECT id, name, filename, createdAt FROM file WHERE name = ? AND isActive = 1 ORDER BY name ASC, id DESC' + 'SELECT id, name, filename, createdAt FROM file WHERE name = ? AND isActive = 1 ORDER BY id DESC LIMIT ?,?' ) - .all(req.query.name) + .bind(req.query.name, limit * (page - 1), limit) + .all() : db .prepare( - 'SELECT id, name, filename, createdAt FROM file WHERE isActive = 1 ORDER BY name ASC, id DESC' + 'SELECT id, name, filename, createdAt FROM file WHERE isActive = 1 ORDER BY id DESC LIMIT ?,?' ) + .bind(limit * (page - 1), limit) .all(); + const [total] = req.query.name + ? db + .prepare('SELECT COUNT(*) count FROM file WHERE name = ? AND isActive = 1') + .bind(req.query.name) + .all() + : db.prepare('SELECT COUNT(*) count FROM file WHERE isActive = 1').all(); + files.forEach((file) => { file.results = []; const trainings = db.prepare('SELECT * FROM train WHERE fileId = ?').all(file.id); @@ -67,7 +78,7 @@ module.exports.get = async (req, res) => { return output; }) ); - res.send(files); + res.send({ limit, total: total.count, files }); }; module.exports.delete = async (req, res) => { diff --git a/api/src/routes/train.routes.js b/api/src/routes/train.routes.js index d1279fb6..62e16e03 100644 --- a/api/src/routes/train.routes.js +++ b/api/src/routes/train.routes.js @@ -1,12 +1,13 @@ const express = require('express'); const multer = require('multer'); -const { jwt } = require('../middlewares'); +const { jwt, expressValidator, validate } = require('../middlewares'); const controller = require('../controllers/train.controller'); +const { query } = expressValidator; const router = express.Router(); router - .get('/', jwt, controller.get) + .get('/', jwt, validate([query('page').default(1).isInt()]), controller.get) .patch('/:id', jwt, controller.patch) .get('/status', controller.status) .post('/add/:name', multer().array('files[]'), controller.add) diff --git a/frontend/src/views/Train.vue b/frontend/src/views/Train.vue index 8180e97e..8c77554e 100644 --- a/frontend/src/views/Train.vue +++ b/frontend/src/views/Train.vue @@ -10,29 +10,37 @@ ref="header" />
- -
-

Training in progress...

+ +
+

Training...

{{ name.name }} - {{ name.trained }}/{{ name.total }}
-
+

No files found

-
+
+
@@ -42,17 +50,25 @@ import Grid from '@/components/Grid.vue'; import Sleep from '@/util/sleep.util'; import ApiService from '@/services/api.service'; import Header from '@/components/Header.vue'; +import Pagination from '@/components/Pagination.vue'; export default { components: { Grid, Header, ProgressBar, + Pagination, }, data: () => ({ + pagination: { + total: 0, + page: 1, + temp: 1, + limit: 0, + }, loading: { - files: true, - status: true, + files: false, + status: false, }, folders: [], status: [], @@ -69,6 +85,9 @@ export default { toolbarHeight: Number, }, computed: { + isPaginationVisible() { + return this.pagination.total > this.pagination.limit; + }, areAllSelected() { return this.filtered.length > 0 && this.matches.selected.length === this.filtered.length; }, @@ -98,6 +117,26 @@ export default { this.emitter.on('realoadTrain', (...args) => this.init(...args)); this.emitter.on('toggleAsset', (...args) => this.selected(...args)); this.emitter.on('assetLoaded', (...args) => this.assetLoaded(...args)); + + this.emitter.on('paginate', (value) => { + this.pagination.temp = value; + this.clear(['source', 'selected', 'disabled', 'loaded']); + this.get().status(); + }); + }, + beforeUnmount() { + const emitters = [ + 'trainingFolder', + 'folders', + 'clearSelected', + 'realoadTrain', + 'toggleAsset', + 'assetLoaded', + 'paginate', + ]; + emitters.forEach((emitter) => { + this.emitter.off(emitter); + }); }, async mounted() { this.headerHeight = this.$refs.header.getHeight(); @@ -106,12 +145,16 @@ export default { watch: {}, methods: { async init() { + this.clear(['source', 'selected', 'disabled', 'loaded']); const promises = []; - this.status = []; - this.matches.selected = []; promises.push(this.get().status()); await Promise.all(promises); }, + clear(items) { + items.forEach((item) => { + this.matches[item] = []; + }); + }, get() { const $this = this; return { @@ -119,9 +162,20 @@ export default { try { $this.loading.files = true; const { data } = $this.trainingFolder - ? await ApiService.get(`train?name=${$this.trainingFolder}`) - : await ApiService.get('train'); - $this.matches.source = data; + ? await ApiService.get(`train?name=${$this.trainingFolder}`, { params: { page: $this.pagination.temp } }) + : await ApiService.get('train', { params: { page: $this.pagination.temp } }); + $this.matches.source = data.files; + $this.pagination.limit = data.limit; + $this.pagination.total = data.total; + + if (data.files.length) $this.pagination.page = $this.pagination.temp; + + if ($this.pagination.temp > 1 && !data.files.length) { + $this.pagination.temp -= 1; + await $this.get().files(); + return; + } + $this.loading.files = false; } catch (error) { $this.emitter.emit('error', error); @@ -282,34 +336,30 @@ export default { } } -.fixed { +.pagination { position: fixed; - top: $tool-bar-height; - z-index: 5; + left: 300px; + top: 100px; + z-index: 3; + top: 0; left: 50%; transform: translateX(-50%); width: 100%; - background: var(--surface-a); max-width: $max-width; + padding: 0.5rem 0; + background: var(--surface-b); +} - .name { - text-align: center; - white-space: nowrap; - font-size: 0.9rem; - @media only screen and (max-width: 576px) { - font-size: 0.75rem !important; - } - } - .status { - font-size: 0.8rem; - @media only screen and (max-width: 576px) { - font-size: 0.65rem !important; - } +.pagination-padding { + padding-top: 2rem; + @media only screen and (max-width: 576px) { + padding-top: 2.25rem; } } .progress-holder { width: 30%; + min-width: 300px; @media only screen and (max-width: 576px) { width: 70%; }