@testdracul/media-frontend 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.development +4 -0
- package/.env.example +3 -0
- package/.eslintrc.json +25 -0
- package/babel.config.js +5 -0
- package/dist/dracul-media-frontend.es.js +16238 -0
- package/dist/dracul-media-frontend.umd.js +586 -0
- package/dist/media-frontend.css +1 -0
- package/docs-en.md +45 -0
- package/docs-es.md +45 -0
- package/package.json +56 -0
- package/readme.md +36 -0
- package/src/components/CsvWebViewer/CsvWebViewer.vue +81 -0
- package/src/components/CsvWebViewer/index.ts +4 -0
- package/src/components/FileUpload/FileUpload.vue +94 -0
- package/src/components/FileUpload/index.ts +4 -0
- package/src/components/FileUploadButton/FileUploadButton.vue +127 -0
- package/src/components/FileUploadButton/index.ts +4 -0
- package/src/components/FileUploadExpiration/FileUploadExpiration.vue +274 -0
- package/src/components/FileUploadExpiration/index.ts +4 -0
- package/src/components/FileUploadExpress/FileUploadExpress.vue +208 -0
- package/src/components/FileUploadExpress/index.ts +4 -0
- package/src/components/FileView/FileView.vue +336 -0
- package/src/components/FileView/index.ts +4 -0
- package/src/components/GroupsShow/GroupsShow.vue +40 -0
- package/src/components/GroupsShow/index.ts +4 -0
- package/src/components/MediaField/MediaField.vue +62 -0
- package/src/components/MediaField/index.ts +4 -0
- package/src/components/PdfWebViewer/PdfWebViewer.vue +81 -0
- package/src/components/PdfWebViewer/index.ts +4 -0
- package/src/components/UsersShow/UsersShow.vue +39 -0
- package/src/components/UsersShow/index.ts +4 -0
- package/src/components/XlsxWebViewer/XlsxWebViewer.vue +70 -0
- package/src/components/XlsxWebViewer/index.ts +4 -0
- package/src/helpers/redeableBytes.ts +9 -0
- package/src/i18n/index.ts +22 -0
- package/src/i18n/messages/DocMessages.ts +31 -0
- package/src/i18n/messages/ExtraMessages.ts +29 -0
- package/src/i18n/messages/FileMessages.ts +223 -0
- package/src/i18n/messages/UserStorageMessages.ts +145 -0
- package/src/i18n/permissions/FilePermissionMessages.ts +50 -0
- package/src/i18n/permissions/OldPermissionMessages.ts +59 -0
- package/src/i18n/permissions/UserStoragePermissionMessages.ts +40 -0
- package/src/index.ts +70 -0
- package/src/mixins/readableBytesMixin.ts +9 -0
- package/src/pages/FileManagementPage/FileCreate/FileCreate.vue +108 -0
- package/src/pages/FileManagementPage/FileCreate/index.ts +3 -0
- package/src/pages/FileManagementPage/FileCrud/FileCrud.vue +133 -0
- package/src/pages/FileManagementPage/FileCrud/index.ts +4 -0
- package/src/pages/FileManagementPage/FileDelete/FileDelete.vue +61 -0
- package/src/pages/FileManagementPage/FileDelete/index.ts +3 -0
- package/src/pages/FileManagementPage/FileFilters/FileFilters.vue +150 -0
- package/src/pages/FileManagementPage/FileFilters/index.ts +3 -0
- package/src/pages/FileManagementPage/FileForm/FileForm.vue +184 -0
- package/src/pages/FileManagementPage/FileForm/UserCombobox.vue +66 -0
- package/src/pages/FileManagementPage/FileForm/index.ts +3 -0
- package/src/pages/FileManagementPage/FileList/FileEditButton.vue +410 -0
- package/src/pages/FileManagementPage/FileList/FileList.vue +178 -0
- package/src/pages/FileManagementPage/FileList/index.ts +4 -0
- package/src/pages/FileManagementPage/FileShow/FileShow.vue +23 -0
- package/src/pages/FileManagementPage/FileShow/FileShowData.vue +35 -0
- package/src/pages/FileManagementPage/FileShow/index.ts +3 -0
- package/src/pages/FileManagementPage/FileUpdate/FileUpdate.vue +107 -0
- package/src/pages/FileManagementPage/FileUpdate/index.ts +4 -0
- package/src/pages/FileManagementPage/index.vue +20 -0
- package/src/pages/MediaDocPage/MediaDocCard.vue +35 -0
- package/src/pages/MediaDocPage/MediaDocPage.vue +78 -0
- package/src/pages/UserStoragePage/UserStorage.vue +311 -0
- package/src/pages/UserStoragePage/UserStorageForm/UserStorageForm.vue +172 -0
- package/src/pages/UserStoragePage/UserStorageUpdate/UserStorageUpdate.vue +91 -0
- package/src/pages/UserStoragePage/index.vue +14 -0
- package/src/providers/FileMetricsProvider.ts +47 -0
- package/src/providers/FileProvider.ts +60 -0
- package/src/providers/UploadProvider.ts +32 -0
- package/src/providers/UserStorageProvider.ts +47 -0
- package/src/providers/gql/almacenamientoPorUsuario.graphql +10 -0
- package/src/providers/gql/cantidadArchivosPorUsuario.graphql +10 -0
- package/src/providers/gql/fetchMediaVariables.graphql +6 -0
- package/src/providers/gql/fileCreate.graphql +27 -0
- package/src/providers/gql/fileDelete.graphql +7 -0
- package/src/providers/gql/fileFetch.graphql +29 -0
- package/src/providers/gql/fileFind.graphql +29 -0
- package/src/providers/gql/fileGlobalMetrics.graphql +6 -0
- package/src/providers/gql/filePaginate.graphql +38 -0
- package/src/providers/gql/fileUpdate.graphql +29 -0
- package/src/providers/gql/fileUpload.graphql +29 -0
- package/src/providers/gql/fileUploadAnonymous.graphql +25 -0
- package/src/providers/gql/fileUserMetrics.graphql +9 -0
- package/src/providers/gql/userStorageFetch.graphql +17 -0
- package/src/providers/gql/userStorageFindByUser.graphql +17 -0
- package/src/providers/gql/userStorageUpdate.graphql +31 -0
- package/src/routes/index.ts +32 -0
- package/vite.config.ts +65 -0
- package/vue.config.js +22 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row>
|
|
3
|
+
<v-col cols="12" sm="6" md="4">
|
|
4
|
+
<v-list>
|
|
5
|
+
<show-field :model-value="item.filename" :label="$t('media.file.filename')" icon="mdi-text-box"/>
|
|
6
|
+
<show-field :model-value="item.absolutePath" :label="$t('media.file.absolutePath')" icon="mdi-folder"/>
|
|
7
|
+
<show-field :model-value="item.createdAt" :label="$t('media.file.createdAt')" icon="mdi-calendar"/>
|
|
8
|
+
</v-list>
|
|
9
|
+
</v-col>
|
|
10
|
+
|
|
11
|
+
<v-col cols="12" sm="6" md="4">
|
|
12
|
+
<v-list>
|
|
13
|
+
<show-field :model-value="item.extension" :label="$t('media.file.extension')" icon="mdi-file"/>
|
|
14
|
+
<show-field :model-value="item.size.toString()" :label="$t('media.file.size')" icon="mdi-image-size-select-actual"/>
|
|
15
|
+
<show-field :model-value="item.createdBy?.user?.name" :label="$t('media.file.createdBy')" icon="mdi-account-circle"/>
|
|
16
|
+
</v-list>
|
|
17
|
+
</v-col>
|
|
18
|
+
|
|
19
|
+
<v-col cols="12" sm="6" md="4">
|
|
20
|
+
<v-list>
|
|
21
|
+
<show-field :model-value="item.relativePath" :label="$t('media.file.relativePath')" icon="mdi-folder-open"/>
|
|
22
|
+
<show-field :model-value="item.url" :label="$t('media.file.url')" icon="mdi-link"/>
|
|
23
|
+
</v-list>
|
|
24
|
+
</v-col>
|
|
25
|
+
|
|
26
|
+
</v-row>
|
|
27
|
+
</template>
|
|
28
|
+
<script setup>
|
|
29
|
+
import {ShowField} from '@testdracul/common-frontend'
|
|
30
|
+
|
|
31
|
+
defineProps({
|
|
32
|
+
item: {type: Object, required: true}
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<crud-update
|
|
3
|
+
:open="open"
|
|
4
|
+
:loading="loading"
|
|
5
|
+
:title="title"
|
|
6
|
+
:error-message="errorMessage"
|
|
7
|
+
:disable-submit="!hasChanges"
|
|
8
|
+
@update="update"
|
|
9
|
+
@close="$emit('close')"
|
|
10
|
+
>
|
|
11
|
+
|
|
12
|
+
<v-card flat class="mb-3">
|
|
13
|
+
<v-card-text>
|
|
14
|
+
<file-form
|
|
15
|
+
updating
|
|
16
|
+
:old-file-extension="oldFileExtension"
|
|
17
|
+
ref="formRef"
|
|
18
|
+
v-model="form"
|
|
19
|
+
:input-errors="inputErrors"
|
|
20
|
+
@file-selected="handleFileSelected"
|
|
21
|
+
@save="update"
|
|
22
|
+
/>
|
|
23
|
+
</v-card-text>
|
|
24
|
+
</v-card>
|
|
25
|
+
|
|
26
|
+
</crud-update>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script setup>
|
|
30
|
+
import { ref, computed, onBeforeMount } from 'vue'
|
|
31
|
+
import FileProvider from "../../../providers/FileProvider";
|
|
32
|
+
import {CrudUpdate, ClientError} from '@testdracul/common-frontend'
|
|
33
|
+
import FileForm from "../FileForm";
|
|
34
|
+
|
|
35
|
+
const props = defineProps({
|
|
36
|
+
open: {type: Boolean, default: true},
|
|
37
|
+
item: {type: Object, required: true}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const emit = defineEmits(['close', 'itemUpdated'])
|
|
41
|
+
|
|
42
|
+
const title = ref('media.file.editing')
|
|
43
|
+
const errorMessage = ref('')
|
|
44
|
+
const inputErrors = ref({})
|
|
45
|
+
const loading = ref(false)
|
|
46
|
+
const form = ref({
|
|
47
|
+
id: props.item.id,
|
|
48
|
+
description: props.item.description,
|
|
49
|
+
tags: props.item.tags,
|
|
50
|
+
expirationDate: props.item.expirationDate,
|
|
51
|
+
isPublic: props.item.isPublic ? props.item.isPublic : false,
|
|
52
|
+
groups: props.item.groups ? props.item.groups : [],
|
|
53
|
+
users: props.item.users ? props.item.users : []
|
|
54
|
+
})
|
|
55
|
+
const file = ref(null)
|
|
56
|
+
const oldFileExtension = ref(props.item.extension)
|
|
57
|
+
const initialForm = ref(null)
|
|
58
|
+
const hasFileSelected = ref(false)
|
|
59
|
+
const formRef = ref(null)
|
|
60
|
+
|
|
61
|
+
const hasFormChanges = computed(() => {
|
|
62
|
+
return JSON.stringify(form.value) !== JSON.stringify(initialForm.value)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const hasChanges = computed(() => {
|
|
66
|
+
return hasFormChanges.value || hasFileSelected.value
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
onBeforeMount(() => {
|
|
70
|
+
initialForm.value = JSON.parse(JSON.stringify(form.value))
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const update = () => {
|
|
74
|
+
if (formRef.value?.validate()) {
|
|
75
|
+
loading.value = true
|
|
76
|
+
FileProvider.updateFile(form.value, file.value).then(r => {
|
|
77
|
+
if (r) {
|
|
78
|
+
emit('itemUpdated', r.data.fileUpdate)
|
|
79
|
+
emit('close')
|
|
80
|
+
}
|
|
81
|
+
}).catch(error => {
|
|
82
|
+
let clientError = new ClientError(error)
|
|
83
|
+
inputErrors.value = clientError.inputErrors
|
|
84
|
+
errorMessage.value = clientError.i18nMessage
|
|
85
|
+
}).finally(() => loading.value = false)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const handleFileSelected = (fileParam) => {
|
|
90
|
+
if (fileParam) {
|
|
91
|
+
const newFileExtension = '.' + fileParam.name.split('.').pop()
|
|
92
|
+
|
|
93
|
+
if (newFileExtension == oldFileExtension.value) {
|
|
94
|
+
file.value = fileParam
|
|
95
|
+
hasFileSelected.value = true
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
file.value = null
|
|
99
|
+
hasFileSelected.value = false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<style scoped>
|
|
105
|
+
|
|
106
|
+
</style>
|
|
107
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row>
|
|
3
|
+
<v-col cols="12">
|
|
4
|
+
<file-crud :topAddButton="true"/>
|
|
5
|
+
</v-col>
|
|
6
|
+
</v-row>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
import FileCrud from "./FileCrud";
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
name: "File",
|
|
14
|
+
components: {FileCrud}
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<style scoped>
|
|
19
|
+
|
|
20
|
+
</style>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="fill-height hover-card" @click="$router.push({name: 'MediaDocPage'})">
|
|
3
|
+
<v-card-item>
|
|
4
|
+
<template v-slot:prepend>
|
|
5
|
+
<v-icon color="info" size="x-large">mdi-folder-information</v-icon>
|
|
6
|
+
</template>
|
|
7
|
+
<v-card-title>{{ $t('media.doc.title') }}</v-card-title>
|
|
8
|
+
<v-card-subtitle>{{ $t('media.doc.subtitle') }}</v-card-subtitle>
|
|
9
|
+
</v-card-item>
|
|
10
|
+
<v-card-text>
|
|
11
|
+
Manual de usuario para la carga, organización y gestión de archivos multimedia.
|
|
12
|
+
</v-card-text>
|
|
13
|
+
<v-card-actions>
|
|
14
|
+
<v-spacer />
|
|
15
|
+
<v-btn variant="text" color="info">Ver Manual</v-btn>
|
|
16
|
+
</v-card-actions>
|
|
17
|
+
</v-card>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script>
|
|
21
|
+
export default {
|
|
22
|
+
name: "MediaDocCard"
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<style scoped>
|
|
27
|
+
.hover-card {
|
|
28
|
+
transition: transform 0.2s;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
.hover-card:hover {
|
|
32
|
+
transform: translateY(-4px);
|
|
33
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.1) !important;
|
|
34
|
+
}
|
|
35
|
+
</style>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<doc-layout
|
|
3
|
+
:title="$t('media.doc.title')"
|
|
4
|
+
icon="mdi-folder-information"
|
|
5
|
+
color="info"
|
|
6
|
+
swagger
|
|
7
|
+
:swagger-label="$t('media.doc.swagger')"
|
|
8
|
+
>
|
|
9
|
+
<template v-slot:overview>
|
|
10
|
+
<p>
|
|
11
|
+
La Gestión de Medios es su centro de control para archivos y documentos.
|
|
12
|
+
Aquí puede cargar imágenes, PDFs y otros archivos, organizarlos mediante etiquetas
|
|
13
|
+
y controlar quién puede verlos. El sistema se encarga automáticamente de optimizar
|
|
14
|
+
el espacio eliminando archivos temporales cuando ya no son necesarios.
|
|
15
|
+
</p>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<template v-slot:how-to-use>
|
|
19
|
+
<v-timeline side="end" align="start" density="compact">
|
|
20
|
+
<v-timeline-item dot-color="info" size="x-small">
|
|
21
|
+
<div class="font-weight-bold">Subir Archivos</div>
|
|
22
|
+
<div class="text-caption">Use el botón "Subir" en la gestión de archivos. Puede definir una fecha de expiración si el archivo es temporal.</div>
|
|
23
|
+
</v-timeline-item>
|
|
24
|
+
<v-timeline-item dot-color="info" size="x-small">
|
|
25
|
+
<div class="font-weight-bold">Gestionar Almacenamiento</div>
|
|
26
|
+
<div class="text-caption">Desde "Mi Almacenamiento" puede ver cuánto espacio está ocupando y sus límites permitidos.</div>
|
|
27
|
+
</v-timeline-item>
|
|
28
|
+
<v-timeline-item dot-color="info" size="x-small">
|
|
29
|
+
<div class="font-weight-bold">Compartir y Previsualizar</div>
|
|
30
|
+
<div class="text-caption">Haga clic en cualquier archivo para ver su contenido, obtener su enlace público o descargar una copia.</div>
|
|
31
|
+
</v-timeline-item>
|
|
32
|
+
</v-timeline>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<template v-slot:benefits>
|
|
36
|
+
<v-row no-gutters>
|
|
37
|
+
<v-col cols="12" sm="6">
|
|
38
|
+
<v-list-item title="Organización Inteligente" subtitle="Etiquete sus archivos para encontrarlos rápidamente." />
|
|
39
|
+
</v-col>
|
|
40
|
+
<v-col cols="12" sm="6">
|
|
41
|
+
<v-list-item title="Limpieza Automática" subtitle="Los archivos con fecha de expiración se borran solos al vencer." />
|
|
42
|
+
</v-col>
|
|
43
|
+
<v-col cols="12" sm="6">
|
|
44
|
+
<v-list-item title="Seguridad de Datos" subtitle="Solo usted y los usuarios autorizados pueden acceder a sus archivos privados." />
|
|
45
|
+
</v-col>
|
|
46
|
+
<v-col cols="12" sm="6">
|
|
47
|
+
<v-list-item title="Multi-dispositivo" subtitle="Acceda a sus documentos desde cualquier navegador o dispositivo." />
|
|
48
|
+
</v-col>
|
|
49
|
+
</v-row>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<template v-slot:faq>
|
|
53
|
+
<v-expansion-panels variant="accordion">
|
|
54
|
+
<v-expansion-panel
|
|
55
|
+
title="¿Qué pasa cuando un archivo expira?"
|
|
56
|
+
text="El sistema detecta automáticamente la fecha vencida y elimina el archivo físicamente para liberar espacio. Recibirá una notificación si el archivo era crítico."
|
|
57
|
+
/>
|
|
58
|
+
<v-expansion-panel
|
|
59
|
+
title="¿Hay un límite de tamaño?"
|
|
60
|
+
text="Sí, cada usuario tiene una cuota máxima asignada por el administrador. Puede verificar su estado en la sección de Almacenamiento."
|
|
61
|
+
/>
|
|
62
|
+
<v-expansion-panel
|
|
63
|
+
title="¿Quién puede ver mis archivos públicos?"
|
|
64
|
+
text="Cualquier persona que tenga el enlace directo, incluso si no ha iniciado sesión en el sistema."
|
|
65
|
+
/>
|
|
66
|
+
</v-expansion-panels>
|
|
67
|
+
</template>
|
|
68
|
+
</doc-layout>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<script>
|
|
72
|
+
import { DocLayout } from "@testdracul/common-frontend"
|
|
73
|
+
|
|
74
|
+
export default {
|
|
75
|
+
name: "MediaDocPage",
|
|
76
|
+
components: { DocLayout }
|
|
77
|
+
}
|
|
78
|
+
</script>
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<crud-layout :title="t('media.userStorage.title')" :subtitle="t('media.userStorage.subtitle')">
|
|
3
|
+
<template v-slot:list>
|
|
4
|
+
<v-expansion-panels class="mb-4">
|
|
5
|
+
<v-expansion-panel elevation="2">
|
|
6
|
+
<v-expansion-panel-title>
|
|
7
|
+
<v-icon icon="mdi-filter-variant" class="mr-2"></v-icon>
|
|
8
|
+
{{ t('media.userStorage.filters') }}
|
|
9
|
+
</v-expansion-panel-title>
|
|
10
|
+
<v-expansion-panel-text>
|
|
11
|
+
<v-row dense>
|
|
12
|
+
<v-col cols="12" sm="6" md="3">
|
|
13
|
+
<user-autocomplete
|
|
14
|
+
v-model="filters.user"
|
|
15
|
+
:label="t('media.userStorage.user')"
|
|
16
|
+
filter
|
|
17
|
+
></user-autocomplete>
|
|
18
|
+
</v-col>
|
|
19
|
+
<v-col cols="12" sm="6" md="3">
|
|
20
|
+
<v-select
|
|
21
|
+
v-model="filters.visibility"
|
|
22
|
+
:items="visibilityOptions"
|
|
23
|
+
:label="t('media.userStorage.filesPrivacy')"
|
|
24
|
+
variant="underlined"
|
|
25
|
+
density="compact"
|
|
26
|
+
clearable
|
|
27
|
+
prepend-inner-icon="mdi-eye"
|
|
28
|
+
></v-select>
|
|
29
|
+
</v-col>
|
|
30
|
+
<v-col cols="12" sm="6" md="3">
|
|
31
|
+
<v-text-field
|
|
32
|
+
v-model="filters.capacityMin"
|
|
33
|
+
type="number"
|
|
34
|
+
:label="t('media.userStorage.capacityMin')"
|
|
35
|
+
variant="underlined"
|
|
36
|
+
density="compact"
|
|
37
|
+
prepend-inner-icon="mdi-arrow-up-bold-circle-outline"
|
|
38
|
+
></v-text-field>
|
|
39
|
+
</v-col>
|
|
40
|
+
<v-col cols="12" sm="6" md="3">
|
|
41
|
+
<v-text-field
|
|
42
|
+
v-model="filters.capacityMax"
|
|
43
|
+
type="number"
|
|
44
|
+
:label="t('media.userStorage.capacityMax')"
|
|
45
|
+
variant="underlined"
|
|
46
|
+
density="compact"
|
|
47
|
+
prepend-inner-icon="mdi-arrow-down-bold-circle-outline"
|
|
48
|
+
></v-text-field>
|
|
49
|
+
</v-col>
|
|
50
|
+
<v-col cols="12" sm="6" md="3">
|
|
51
|
+
<v-text-field
|
|
52
|
+
v-model="filters.maxFileSizeMin"
|
|
53
|
+
type="number"
|
|
54
|
+
:label="t('media.userStorage.maxFileSizeMin')"
|
|
55
|
+
variant="underlined"
|
|
56
|
+
density="compact"
|
|
57
|
+
prepend-inner-icon="mdi-file-upload-outline"
|
|
58
|
+
></v-text-field>
|
|
59
|
+
</v-col>
|
|
60
|
+
<v-col cols="12" sm="6" md="3">
|
|
61
|
+
<v-text-field
|
|
62
|
+
v-model="filters.maxFileSizeMax"
|
|
63
|
+
type="number"
|
|
64
|
+
:label="t('media.userStorage.maxFileSizeMax')"
|
|
65
|
+
variant="underlined"
|
|
66
|
+
density="compact"
|
|
67
|
+
prepend-inner-icon="mdi-file-upload-outline"
|
|
68
|
+
></v-text-field>
|
|
69
|
+
</v-col>
|
|
70
|
+
<v-col cols="12" sm="6" md="3">
|
|
71
|
+
<v-text-field
|
|
72
|
+
v-model="filters.percentageMin"
|
|
73
|
+
type="number"
|
|
74
|
+
:label="t('media.userStorage.percentageMin')"
|
|
75
|
+
variant="underlined"
|
|
76
|
+
density="compact"
|
|
77
|
+
prepend-inner-icon="mdi-percent-outline"
|
|
78
|
+
></v-text-field>
|
|
79
|
+
</v-col>
|
|
80
|
+
<v-col cols="12" sm="6" md="3">
|
|
81
|
+
<v-text-field
|
|
82
|
+
v-model="filters.percentageMax"
|
|
83
|
+
type="number"
|
|
84
|
+
:label="t('media.userStorage.percentageMax')"
|
|
85
|
+
variant="underlined"
|
|
86
|
+
density="compact"
|
|
87
|
+
prepend-inner-icon="mdi-percent-outline"
|
|
88
|
+
></v-text-field>
|
|
89
|
+
</v-col>
|
|
90
|
+
<v-col cols="12" sm="6" md="3">
|
|
91
|
+
<v-text-field
|
|
92
|
+
v-model="filters.fileExpirationTimeMin"
|
|
93
|
+
type="number"
|
|
94
|
+
:label="t('media.userStorage.fileExpirationTimeMin')"
|
|
95
|
+
variant="underlined"
|
|
96
|
+
density="compact"
|
|
97
|
+
prepend-inner-icon="mdi-clock-outline"
|
|
98
|
+
></v-text-field>
|
|
99
|
+
</v-col>
|
|
100
|
+
<v-col cols="12" sm="6" md="3">
|
|
101
|
+
<v-text-field
|
|
102
|
+
v-model="filters.fileExpirationTimeMax"
|
|
103
|
+
type="number"
|
|
104
|
+
:label="t('media.userStorage.fileExpirationTimeMax')"
|
|
105
|
+
variant="underlined"
|
|
106
|
+
density="compact"
|
|
107
|
+
prepend-inner-icon="mdi-clock-outline"
|
|
108
|
+
></v-text-field>
|
|
109
|
+
</v-col>
|
|
110
|
+
</v-row>
|
|
111
|
+
<v-row dense>
|
|
112
|
+
<v-col cols="12" class="text-right">
|
|
113
|
+
<v-btn size="small" variant="text" color="secondary" @click="clearFilters">
|
|
114
|
+
{{ t("common.clearFilters") }}
|
|
115
|
+
</v-btn>
|
|
116
|
+
</v-col>
|
|
117
|
+
</v-row>
|
|
118
|
+
</v-expansion-panel-text>
|
|
119
|
+
</v-expansion-panel>
|
|
120
|
+
</v-expansion-panels>
|
|
121
|
+
|
|
122
|
+
<loading v-if="loadingPermissions"></loading>
|
|
123
|
+
<v-data-table
|
|
124
|
+
v-else
|
|
125
|
+
:headers="headers"
|
|
126
|
+
:items="filteredUsersList"
|
|
127
|
+
class="elevation-0"
|
|
128
|
+
density="compact"
|
|
129
|
+
hover
|
|
130
|
+
:items-per-page-options="[5, 10, 25, 50, 100]"
|
|
131
|
+
:items-per-page-text="t('common.itemsPerPageText')"
|
|
132
|
+
>
|
|
133
|
+
<template v-slot:top>
|
|
134
|
+
<v-toolbar
|
|
135
|
+
flat
|
|
136
|
+
color="transparent"
|
|
137
|
+
>
|
|
138
|
+
<v-toolbar-title>{{ t("media.userStorage.title2") }}</v-toolbar-title>
|
|
139
|
+
<v-spacer></v-spacer>
|
|
140
|
+
|
|
141
|
+
<user-storage-update v-if="!!userToUpdate"
|
|
142
|
+
:open="!!userToUpdate"
|
|
143
|
+
:user-storage-form="userToUpdate"
|
|
144
|
+
@close="userToUpdate=null"
|
|
145
|
+
@role-updated="onRoleUpdated"
|
|
146
|
+
/>
|
|
147
|
+
|
|
148
|
+
</v-toolbar>
|
|
149
|
+
</template>
|
|
150
|
+
<template v-slot:[`item.fileExpirationTime`]="{ item }">
|
|
151
|
+
{{ item.fileExpirationTime }} {{ t("media.userStorage.days") }}
|
|
152
|
+
</template>
|
|
153
|
+
<template v-slot:[`item.maxFileSize`]="{ item }">
|
|
154
|
+
{{ item.maxFileSize.toFixed(2) }} MB
|
|
155
|
+
</template>
|
|
156
|
+
<template v-slot:[`item.capacity`]="{ item }">
|
|
157
|
+
{{ item.usedSpace.toFixed(2) }}/{{ item.capacity }} MB
|
|
158
|
+
</template>
|
|
159
|
+
<template v-slot:[`item.usedSpace`]="{ item }">
|
|
160
|
+
{{ percentageUsedFormatted(item) }}
|
|
161
|
+
</template>
|
|
162
|
+
<template v-slot:[`item.filesPrivacy`]="{ item }">
|
|
163
|
+
{{ t('media.userStorage.privacy.' + item.filesPrivacy) }}
|
|
164
|
+
</template>
|
|
165
|
+
<template v-slot:[`item.actions`]="{ item }">
|
|
166
|
+
<v-btn
|
|
167
|
+
variant="outlined"
|
|
168
|
+
size="small"
|
|
169
|
+
color="secondary"
|
|
170
|
+
@click="editItem(item)"
|
|
171
|
+
>{{ t("media.userStorage.edit") }}
|
|
172
|
+
</v-btn>
|
|
173
|
+
</template>
|
|
174
|
+
</v-data-table>
|
|
175
|
+
<v-snackbar
|
|
176
|
+
v-model="snackbar"
|
|
177
|
+
:timeout="timeout"
|
|
178
|
+
:color="snackbarColor"
|
|
179
|
+
>
|
|
180
|
+
{{ t("media.userStorage.updated") }}
|
|
181
|
+
|
|
182
|
+
<template v-slot:actions>
|
|
183
|
+
<v-btn
|
|
184
|
+
color="white"
|
|
185
|
+
variant="text"
|
|
186
|
+
@click="snackbar = false"
|
|
187
|
+
>
|
|
188
|
+
X
|
|
189
|
+
</v-btn>
|
|
190
|
+
</template>
|
|
191
|
+
</v-snackbar>
|
|
192
|
+
</template>
|
|
193
|
+
</crud-layout>
|
|
194
|
+
</template>
|
|
195
|
+
|
|
196
|
+
<script setup>
|
|
197
|
+
import { ref, computed, onBeforeMount } from 'vue'
|
|
198
|
+
import { useI18n } from 'vue-i18n'
|
|
199
|
+
import {CrudLayout, Loading} from "@testdracul/common-frontend"
|
|
200
|
+
import {UserAutocomplete} from "@testdracul/user-frontend"
|
|
201
|
+
import UserStorageUpdate from './UserStorageUpdate/UserStorageUpdate.vue';
|
|
202
|
+
import userStorageProvider from '../../providers/UserStorageProvider'
|
|
203
|
+
|
|
204
|
+
const { t } = useI18n()
|
|
205
|
+
|
|
206
|
+
const initialFilters = {
|
|
207
|
+
user: null,
|
|
208
|
+
visibility: null,
|
|
209
|
+
capacityMin: null,
|
|
210
|
+
capacityMax: null,
|
|
211
|
+
maxFileSizeMin: null,
|
|
212
|
+
maxFileSizeMax: null,
|
|
213
|
+
percentageMin: null,
|
|
214
|
+
percentageMax: null,
|
|
215
|
+
fileExpirationTimeMin: null,
|
|
216
|
+
fileExpirationTimeMax: null
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const filters = ref({...initialFilters})
|
|
220
|
+
|
|
221
|
+
const usersList = ref([])
|
|
222
|
+
const loadingPermissions = ref(true)
|
|
223
|
+
const userToUpdate = ref(null)
|
|
224
|
+
// const flashMessage = ref(null)
|
|
225
|
+
const snackbar = ref(false)
|
|
226
|
+
const snackbarColor = ref("")
|
|
227
|
+
const timeout = ref(3000)
|
|
228
|
+
|
|
229
|
+
const visibilityOptions = computed(() => [
|
|
230
|
+
{ title: t('media.userStorage.privacy.public'), value: 'public' },
|
|
231
|
+
{ title: t('media.userStorage.privacy.private'), value: 'private' }
|
|
232
|
+
])
|
|
233
|
+
|
|
234
|
+
const headers = computed(() => [
|
|
235
|
+
{
|
|
236
|
+
title: t("media.userStorage.user"),
|
|
237
|
+
align: 'start',
|
|
238
|
+
sortable: false,
|
|
239
|
+
key: 'user.name',
|
|
240
|
+
},
|
|
241
|
+
{ title: t("media.userStorage.fileExpirationTime"), key: 'fileExpirationTime', align: 'center' },
|
|
242
|
+
{ title: t("media.userStorage.maxFileSize"), key: 'maxFileSize', align: 'center' },
|
|
243
|
+
{ title: t("media.userStorage.capacity"), key: 'capacity', align: 'center' },
|
|
244
|
+
{ title: t("media.userStorage.percentage"), key: 'usedSpace', align: 'center' },
|
|
245
|
+
{ title: t("media.userStorage.filesPrivacy"), key: 'filesPrivacy', align: 'center' },
|
|
246
|
+
{ title: t("media.userStorage.actions"), key: 'actions', sortable: false, align: 'center' },
|
|
247
|
+
])
|
|
248
|
+
|
|
249
|
+
const percentageUsed = (item) => {
|
|
250
|
+
return item.capacity > 0 ? parseFloat(item.usedSpace * 100 / item.capacity) : 0
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const percentageUsedFormatted = (item) => {
|
|
254
|
+
return item.capacity > 0 ? percentageUsed(item).toFixed(2) + "%" : "-"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const filteredUsersList = computed(() => {
|
|
258
|
+
return usersList.value.filter(u => {
|
|
259
|
+
const percentage = percentageUsed(u)
|
|
260
|
+
|
|
261
|
+
const matchesUser = !filters.value.user || u.user.id === filters.value.user
|
|
262
|
+
const matchesVisibility = !filters.value.visibility || u.filesPrivacy === filters.value.visibility
|
|
263
|
+
const matchesCapacityMin = !filters.value.capacityMin || u.capacity >= parseFloat(filters.value.capacityMin)
|
|
264
|
+
const matchesCapacityMax = !filters.value.capacityMax || u.capacity <= parseFloat(filters.value.capacityMax)
|
|
265
|
+
const matchesMaxFileSizeMin = !filters.value.maxFileSizeMin || u.maxFileSize >= parseFloat(filters.value.maxFileSizeMin)
|
|
266
|
+
const matchesMaxFileSizeMax = !filters.value.maxFileSizeMax || u.maxFileSize <= parseFloat(filters.value.maxFileSizeMax)
|
|
267
|
+
const matchesPercentageMin = !filters.value.percentageMin || percentage >= parseFloat(filters.value.percentageMin)
|
|
268
|
+
const matchesPercentageMax = !filters.value.percentageMax || percentage <= parseFloat(filters.value.percentageMax)
|
|
269
|
+
const matchesFileExpirationMin = !filters.value.fileExpirationTimeMin || u.fileExpirationTime >= parseInt(filters.value.fileExpirationTimeMin)
|
|
270
|
+
const matchesFileExpirationMax = !filters.value.fileExpirationTimeMax || u.fileExpirationTime <= parseInt(filters.value.fileExpirationTimeMax)
|
|
271
|
+
|
|
272
|
+
return matchesUser && matchesVisibility && matchesCapacityMin && matchesCapacityMax &&
|
|
273
|
+
matchesMaxFileSizeMin && matchesMaxFileSizeMax &&
|
|
274
|
+
matchesPercentageMin && matchesPercentageMax &&
|
|
275
|
+
matchesFileExpirationMin && matchesFileExpirationMax
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
const clearFilters = () => {
|
|
280
|
+
filters.value = {...initialFilters}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const load = () => {
|
|
284
|
+
usersList.value = []
|
|
285
|
+
loadingPermissions.value = true
|
|
286
|
+
userStorageProvider.fetchUserStorage()
|
|
287
|
+
.then(r => {
|
|
288
|
+
usersList.value = r.data.userStorageFetch
|
|
289
|
+
})
|
|
290
|
+
.catch(err => console.error(err))
|
|
291
|
+
.finally(() => loadingPermissions.value = false)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const onRoleUpdated = () => {
|
|
295
|
+
load()
|
|
296
|
+
snackbarColor.value = "success"
|
|
297
|
+
snackbar.value = true
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const editItem = (user) => {
|
|
301
|
+
userToUpdate.value = user
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
onBeforeMount(() => {
|
|
305
|
+
load()
|
|
306
|
+
})
|
|
307
|
+
</script>
|
|
308
|
+
|
|
309
|
+
<style scoped>
|
|
310
|
+
|
|
311
|
+
</style>
|