@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.
Files changed (93) hide show
  1. package/.env.development +4 -0
  2. package/.env.example +3 -0
  3. package/.eslintrc.json +25 -0
  4. package/babel.config.js +5 -0
  5. package/dist/dracul-media-frontend.es.js +16238 -0
  6. package/dist/dracul-media-frontend.umd.js +586 -0
  7. package/dist/media-frontend.css +1 -0
  8. package/docs-en.md +45 -0
  9. package/docs-es.md +45 -0
  10. package/package.json +56 -0
  11. package/readme.md +36 -0
  12. package/src/components/CsvWebViewer/CsvWebViewer.vue +81 -0
  13. package/src/components/CsvWebViewer/index.ts +4 -0
  14. package/src/components/FileUpload/FileUpload.vue +94 -0
  15. package/src/components/FileUpload/index.ts +4 -0
  16. package/src/components/FileUploadButton/FileUploadButton.vue +127 -0
  17. package/src/components/FileUploadButton/index.ts +4 -0
  18. package/src/components/FileUploadExpiration/FileUploadExpiration.vue +274 -0
  19. package/src/components/FileUploadExpiration/index.ts +4 -0
  20. package/src/components/FileUploadExpress/FileUploadExpress.vue +208 -0
  21. package/src/components/FileUploadExpress/index.ts +4 -0
  22. package/src/components/FileView/FileView.vue +336 -0
  23. package/src/components/FileView/index.ts +4 -0
  24. package/src/components/GroupsShow/GroupsShow.vue +40 -0
  25. package/src/components/GroupsShow/index.ts +4 -0
  26. package/src/components/MediaField/MediaField.vue +62 -0
  27. package/src/components/MediaField/index.ts +4 -0
  28. package/src/components/PdfWebViewer/PdfWebViewer.vue +81 -0
  29. package/src/components/PdfWebViewer/index.ts +4 -0
  30. package/src/components/UsersShow/UsersShow.vue +39 -0
  31. package/src/components/UsersShow/index.ts +4 -0
  32. package/src/components/XlsxWebViewer/XlsxWebViewer.vue +70 -0
  33. package/src/components/XlsxWebViewer/index.ts +4 -0
  34. package/src/helpers/redeableBytes.ts +9 -0
  35. package/src/i18n/index.ts +22 -0
  36. package/src/i18n/messages/DocMessages.ts +31 -0
  37. package/src/i18n/messages/ExtraMessages.ts +29 -0
  38. package/src/i18n/messages/FileMessages.ts +223 -0
  39. package/src/i18n/messages/UserStorageMessages.ts +145 -0
  40. package/src/i18n/permissions/FilePermissionMessages.ts +50 -0
  41. package/src/i18n/permissions/OldPermissionMessages.ts +59 -0
  42. package/src/i18n/permissions/UserStoragePermissionMessages.ts +40 -0
  43. package/src/index.ts +70 -0
  44. package/src/mixins/readableBytesMixin.ts +9 -0
  45. package/src/pages/FileManagementPage/FileCreate/FileCreate.vue +108 -0
  46. package/src/pages/FileManagementPage/FileCreate/index.ts +3 -0
  47. package/src/pages/FileManagementPage/FileCrud/FileCrud.vue +133 -0
  48. package/src/pages/FileManagementPage/FileCrud/index.ts +4 -0
  49. package/src/pages/FileManagementPage/FileDelete/FileDelete.vue +61 -0
  50. package/src/pages/FileManagementPage/FileDelete/index.ts +3 -0
  51. package/src/pages/FileManagementPage/FileFilters/FileFilters.vue +150 -0
  52. package/src/pages/FileManagementPage/FileFilters/index.ts +3 -0
  53. package/src/pages/FileManagementPage/FileForm/FileForm.vue +184 -0
  54. package/src/pages/FileManagementPage/FileForm/UserCombobox.vue +66 -0
  55. package/src/pages/FileManagementPage/FileForm/index.ts +3 -0
  56. package/src/pages/FileManagementPage/FileList/FileEditButton.vue +410 -0
  57. package/src/pages/FileManagementPage/FileList/FileList.vue +178 -0
  58. package/src/pages/FileManagementPage/FileList/index.ts +4 -0
  59. package/src/pages/FileManagementPage/FileShow/FileShow.vue +23 -0
  60. package/src/pages/FileManagementPage/FileShow/FileShowData.vue +35 -0
  61. package/src/pages/FileManagementPage/FileShow/index.ts +3 -0
  62. package/src/pages/FileManagementPage/FileUpdate/FileUpdate.vue +107 -0
  63. package/src/pages/FileManagementPage/FileUpdate/index.ts +4 -0
  64. package/src/pages/FileManagementPage/index.vue +20 -0
  65. package/src/pages/MediaDocPage/MediaDocCard.vue +35 -0
  66. package/src/pages/MediaDocPage/MediaDocPage.vue +78 -0
  67. package/src/pages/UserStoragePage/UserStorage.vue +311 -0
  68. package/src/pages/UserStoragePage/UserStorageForm/UserStorageForm.vue +172 -0
  69. package/src/pages/UserStoragePage/UserStorageUpdate/UserStorageUpdate.vue +91 -0
  70. package/src/pages/UserStoragePage/index.vue +14 -0
  71. package/src/providers/FileMetricsProvider.ts +47 -0
  72. package/src/providers/FileProvider.ts +60 -0
  73. package/src/providers/UploadProvider.ts +32 -0
  74. package/src/providers/UserStorageProvider.ts +47 -0
  75. package/src/providers/gql/almacenamientoPorUsuario.graphql +10 -0
  76. package/src/providers/gql/cantidadArchivosPorUsuario.graphql +10 -0
  77. package/src/providers/gql/fetchMediaVariables.graphql +6 -0
  78. package/src/providers/gql/fileCreate.graphql +27 -0
  79. package/src/providers/gql/fileDelete.graphql +7 -0
  80. package/src/providers/gql/fileFetch.graphql +29 -0
  81. package/src/providers/gql/fileFind.graphql +29 -0
  82. package/src/providers/gql/fileGlobalMetrics.graphql +6 -0
  83. package/src/providers/gql/filePaginate.graphql +38 -0
  84. package/src/providers/gql/fileUpdate.graphql +29 -0
  85. package/src/providers/gql/fileUpload.graphql +29 -0
  86. package/src/providers/gql/fileUploadAnonymous.graphql +25 -0
  87. package/src/providers/gql/fileUserMetrics.graphql +9 -0
  88. package/src/providers/gql/userStorageFetch.graphql +17 -0
  89. package/src/providers/gql/userStorageFindByUser.graphql +17 -0
  90. package/src/providers/gql/userStorageUpdate.graphql +31 -0
  91. package/src/routes/index.ts +32 -0
  92. package/vite.config.ts +65 -0
  93. 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,3 @@
1
+ import FileShow from './FileShow.vue'
2
+ export {FileShow}
3
+ export default FileShow
@@ -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,4 @@
1
+ import FileUpdate from './FileUpdate.vue'
2
+
3
+ export {FileUpdate}
4
+ export default FileUpdate
@@ -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>