From 0feae64fd8f140bfb39f515633c114d90069c140 Mon Sep 17 00:00:00 2001 From: TomasHermanek Date: Thu, 5 Mar 2026 20:19:50 +0100 Subject: [PATCH 01/50] Feature/#84581/internal image (#251) Added: - Internal image flag support for asset licences (DamAssetLicenceExtended type, internalRule config, override/internal flag switches in asset metadata) - Two new job types: jobAssetFileReprocessInternalFlag and jobSynchronizeImageChanged with full create/detail UI --- package.json | 2 +- src/locales/en/coreDam/asset.json | 6 +- src/locales/en/coreDam/assetLicence.json | 8 +- src/locales/en/coreDam/job.json | 10 +- src/locales/sk/coreDam/asset.json | 6 +- src/locales/sk/coreDam/assetLicence.json | 8 +- src/locales/sk/coreDam/job.json | 10 +- .../coreDam/factory/AssetLicenceFactory.ts | 10 +- src/model/coreDam/factory/JobFactory.ts | 30 ++++- src/model/coreDam/filter/UserFilter.ts | 2 +- src/model/coreDam/type/AssetLicence.ts | 12 ++ src/model/coreDam/valueObject/JobResource.ts | 12 ++ src/services/api/coreDam/assetApi.ts | 13 +- src/services/api/coreDam/assetLicenceApi.ts | 12 +- src/services/api/coreDam/audioApi.ts | 2 +- src/services/api/coreDam/documentApi.ts | 2 +- src/services/api/coreDam/imageApi.ts | 2 +- src/stores/coreDam/assetDetailStore.ts | 8 ++ src/stores/coreDam/assetLicenceStore.ts | 6 +- src/types/coreDam/Job.ts | 23 +++- .../asset/components/AssetMetadata.vue | 34 +++++- .../components/AssetDetailSidebarMetadata.vue | 12 +- .../detail/composables/assetDetailActions.ts | 13 +- .../components/AssetListSidebarMetadata.vue | 9 +- .../components/AssetLicenceCreateButton.vue | 6 +- .../components/AssetLicenceDatatable.vue | 4 +- .../components/AssetLicenceDetail.vue | 32 ++++- .../components/AssetLicenceEditForm.vue | 38 +++++- .../composables/assetLicenceActions.ts | 17 ++- .../composables/assetLicenceValidation.ts | 4 +- src/views/coreDam/job/JobDetailView.vue | 8 ++ .../job/components/JobCreateButton.vue | 8 ++ ...eateFormAssetFileReprocessInternalFlag.vue | 114 ++++++++++++++++++ .../JobCreateFormSynchronizeImageChanged.vue | 114 ++++++++++++++++++ ...obDetailAssetFileReprocessInternalFlag.vue | 48 ++++++++ .../JobDetailSynchronizeImageChanged.vue | 48 ++++++++ yarn.lock | 10 +- 37 files changed, 651 insertions(+), 52 deletions(-) create mode 100644 src/model/coreDam/type/AssetLicence.ts create mode 100644 src/views/coreDam/job/components/JobCreateFormAssetFileReprocessInternalFlag.vue create mode 100644 src/views/coreDam/job/components/JobCreateFormSynchronizeImageChanged.vue create mode 100644 src/views/coreDam/job/components/JobDetailAssetFileReprocessInternalFlag.vue create mode 100644 src/views/coreDam/job/components/JobDetailSynchronizeImageChanged.vue diff --git a/package.json b/package.json index 987d588f..ed4ac14a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "cy:open": "CYPRESS_CACHE_FOLDER='node_modules/.cache/Cypress' yarn cypress open -C cypress/config/cypress.config.ts" }, "dependencies": { - "@anzusystems/common-admin": "1.47.0-beta.354", + "@anzusystems/common-admin": "1.47.0-beta.357", "@floating-ui/dom": "^1.7.5", "@mdi/font": "7.4.47", "@sentry/vue": "^10.38.0", diff --git a/src/locales/en/coreDam/asset.json b/src/locales/en/coreDam/asset.json index 2f4aa643..11c36833 100644 --- a/src/locales/en/coreDam/asset.json +++ b/src/locales/en/coreDam/asset.json @@ -160,7 +160,11 @@ "height": "Height", "ratio": "Ratio", "animated": "Animated", - "mainRoute": "Original" + "mainRoute": "Original", + "flags": { + "internal": "Internal", + "overrideInternal": "Override internal flag" + } } }, "roi": { diff --git a/src/locales/en/coreDam/assetLicence.json b/src/locales/en/coreDam/assetLicence.json index 62e07c85..8bf36481 100644 --- a/src/locales/en/coreDam/assetLicence.json +++ b/src/locales/en/coreDam/assetLicence.json @@ -3,7 +3,13 @@ "id": "ID", "name": "Name", "extSystem": "External System", - "extId": "External ID" + "extId": "External ID", + "internalRule": { + "active": "Internal rule active", + "markAsInternalSince": "Mark as internal since" + }, + "internalRuleAuthors": "Internal rule authors", + "internalRuleUsers": "Internal rule users" }, "filter": { "id": "ID", diff --git a/src/locales/en/coreDam/job.json b/src/locales/en/coreDam/job.json index a98c572a..1bd87a89 100644 --- a/src/locales/en/coreDam/job.json +++ b/src/locales/en/coreDam/job.json @@ -4,11 +4,17 @@ "fullSync": "Full synchronization", "authorId": "Author Id", "processAll": "Process all", - "licence": "Licence" + "licence": "Licence", + "targetLicenceId": "Target Licence Id", + "processFrom": "Process from", + "processUntil": "Process until", + "bulkSize": "Bulk size" }, "jobResource": { "jobPodcastSynchronizer": "Podcast synchronizer", "jobAuthorCurrentOptimize": "Author current optimize", - "jobImageCopy": "Image copy" + "jobImageCopy": "Image copy", + "jobAssetFileReprocessInternalFlag": "Asset file reprocess internal flag", + "jobSynchronizeImageChanged": "Synchronize image changed" } } diff --git a/src/locales/sk/coreDam/asset.json b/src/locales/sk/coreDam/asset.json index b4a4ceeb..1364cba5 100644 --- a/src/locales/sk/coreDam/asset.json +++ b/src/locales/sk/coreDam/asset.json @@ -161,7 +161,11 @@ "height": "Výška", "ratio": "Pomer strán", "animated": "Animované", - "mainRoute": "Originál" + "mainRoute": "Originál", + "flags": { + "internal": "Interný", + "overrideInternal": "Preťažiť príznak interný" + } } }, "roi": { diff --git a/src/locales/sk/coreDam/assetLicence.json b/src/locales/sk/coreDam/assetLicence.json index 68ee567e..2bab1957 100644 --- a/src/locales/sk/coreDam/assetLicence.json +++ b/src/locales/sk/coreDam/assetLicence.json @@ -3,7 +3,13 @@ "id": "ID", "name": "Meno", "extSystem": "Externý systém", - "extId": "Externé ID" + "extId": "Externé ID", + "internalRule": { + "active": "Aktívny processing interné", + "markAsInternalSince": "Procesuj príznak interné od" + }, + "internalRuleAuthors": "Interný autori", + "internalRuleUsers": "Používatelia uploadujúci interné obrázky" }, "filter": { "id": "ID", diff --git a/src/locales/sk/coreDam/job.json b/src/locales/sk/coreDam/job.json index 2f1b6a2c..cb493e36 100644 --- a/src/locales/sk/coreDam/job.json +++ b/src/locales/sk/coreDam/job.json @@ -4,11 +4,17 @@ "fullSync": "Plná synchronizácia", "authorId": "Autor ID", "processAll": "Spracuj všetko", - "licence": "Licencia Id" + "licence": "Licencia Id", + "targetLicenceId": "Cieľová licencia Id", + "processFrom": "Spracovať od", + "processUntil": "Spracovať do", + "bulkSize": "Veľkosť dávky" }, "jobResource": { "jobPodcastSynchronizer": "Podcastový synchronizátor", "jobAuthorCurrentOptimize": "Optimalizácia autorov", - "jobImageCopy": "Kopírovanie obrázkov" + "jobImageCopy": "Kopírovanie obrázkov", + "jobAssetFileReprocessInternalFlag": "Prepočet interného príznaku súborov", + "jobSynchronizeImageChanged": "Synchronizácia zmien obrázkov" } } diff --git a/src/model/coreDam/factory/AssetLicenceFactory.ts b/src/model/coreDam/factory/AssetLicenceFactory.ts index dc7edb53..6446575f 100644 --- a/src/model/coreDam/factory/AssetLicenceFactory.ts +++ b/src/model/coreDam/factory/AssetLicenceFactory.ts @@ -1,15 +1,21 @@ import { SYSTEM_CORE_DAM } from '@/model/systems' -import type { DamAssetLicence } from '@anzusystems/common-admin' import { dateTimeNow } from '@anzusystems/common-admin' import { ENTITY } from '@/services/api/coreDam/assetLicenceApi' +import type { DamAssetLicenceExtended } from '@/model/coreDam/type/AssetLicence' export function useAssetLicenceFactory() { - const createDefault = (): DamAssetLicence => { + const createDefault = (): DamAssetLicenceExtended => { return { id: 0, name: '', extSystem: null, extId: '', + internalRule: { + active: false, + markAsInternalSince: null, + }, + internalRuleAuthors: [], + internalRuleUsers: [], createdAt: dateTimeNow(), modifiedAt: dateTimeNow(), createdBy: 0, diff --git a/src/model/coreDam/factory/JobFactory.ts b/src/model/coreDam/factory/JobFactory.ts index d76ebe2e..57c3c94d 100644 --- a/src/model/coreDam/factory/JobFactory.ts +++ b/src/model/coreDam/factory/JobFactory.ts @@ -1,7 +1,17 @@ import { SYSTEM_CORE_DAM } from '@/model/systems' import { useCommonJobFactory } from '@anzusystems/common-admin' -import type { JobAuthorCurrentOptimize, JobPodcastSynchronizer } from '@/types/coreDam/Job' -import { JOB_AUTHOR_CURRENT_OPTIMIZE, JOB_RESOURCE_PODCAST_SYNCHRONIZER } from '@/model/coreDam/valueObject/JobResource' +import type { + JobAssetFileReprocessInternalFlag, + JobAuthorCurrentOptimize, + JobPodcastSynchronizer, + JobSynchronizeImageChanged, +} from '@/types/coreDam/Job' +import { + JOB_AUTHOR_CURRENT_OPTIMIZE, + JOB_RESOURCE_ASSET_FILE_REPROCESS_INTERNAL_FLAG, + JOB_RESOURCE_PODCAST_SYNCHRONIZER, + JOB_RESOURCE_SYNCHRONIZE_IMAGE_CHANGED, +} from '@/model/coreDam/valueObject/JobResource' export function useJobFactory() { const { createBase } = useCommonJobFactory() @@ -25,8 +35,24 @@ export function useJobFactory() { } } + const createAssetFileReprocessInternalFlag = (): JobAssetFileReprocessInternalFlag => { + return { + ...createBase(JOB_RESOURCE_ASSET_FILE_REPROCESS_INTERNAL_FLAG, SYSTEM_CORE_DAM), + ...{ targetLicenceId: 0, processFrom: null, processUntil: null, bulkSize: 500 }, + } + } + + const createSynchronizeImageChanged = (): JobSynchronizeImageChanged => { + return { + ...createBase(JOB_RESOURCE_SYNCHRONIZE_IMAGE_CHANGED, SYSTEM_CORE_DAM), + ...{ targetLicenceId: 0, processFrom: null, processUntil: null, bulkSize: 500 }, + } + } + return { createPodcastSynchronizer, createAuthorCurrentOptimize, + createAssetFileReprocessInternalFlag, + createSynchronizeImageChanged, } } diff --git a/src/model/coreDam/filter/UserFilter.ts b/src/model/coreDam/filter/UserFilter.ts index 88f5f881..cef75995 100644 --- a/src/model/coreDam/filter/UserFilter.ts +++ b/src/model/coreDam/filter/UserFilter.ts @@ -21,7 +21,7 @@ export function useUserListFilter() { export function useUserFilter() { return reactive({ email: { - ...makeFilter({ name: 'email', variant: 'startsWith' }), + ...makeFilter({ name: 'email', variant: 'contains' }), }, }) } diff --git a/src/model/coreDam/type/AssetLicence.ts b/src/model/coreDam/type/AssetLicence.ts new file mode 100644 index 00000000..fefaad0f --- /dev/null +++ b/src/model/coreDam/type/AssetLicence.ts @@ -0,0 +1,12 @@ +import type { DamAssetLicence, DatetimeUTCNullable, DocId, IntegerId } from '@anzusystems/common-admin' + +export interface AssetLicenceInternalRule { + active: boolean + markAsInternalSince: DatetimeUTCNullable +} + +export interface DamAssetLicenceExtended extends DamAssetLicence { + internalRule: AssetLicenceInternalRule + internalRuleAuthors: DocId[] + internalRuleUsers: IntegerId[] +} diff --git a/src/model/coreDam/valueObject/JobResource.ts b/src/model/coreDam/valueObject/JobResource.ts index 071f28a5..985b3c6d 100644 --- a/src/model/coreDam/valueObject/JobResource.ts +++ b/src/model/coreDam/valueObject/JobResource.ts @@ -4,11 +4,15 @@ import { useI18n } from 'vue-i18n' export const JOB_RESOURCE_PODCAST_SYNCHRONIZER = 'jobPodcastSynchronizer' export const JOB_AUTHOR_CURRENT_OPTIMIZE = 'jobAuthorCurrentOptimize' export const JOB_RESOURCE_IMAGE_COPY = 'jobImageCopy' +export const JOB_RESOURCE_ASSET_FILE_REPROCESS_INTERNAL_FLAG = 'jobAssetFileReprocessInternalFlag' +export const JOB_RESOURCE_SYNCHRONIZE_IMAGE_CHANGED = 'jobSynchronizeImageChanged' export type JobResource = | typeof JOB_RESOURCE_PODCAST_SYNCHRONIZER | typeof JOB_RESOURCE_IMAGE_COPY | typeof JOB_AUTHOR_CURRENT_OPTIMIZE + | typeof JOB_RESOURCE_ASSET_FILE_REPROCESS_INTERNAL_FLAG + | typeof JOB_RESOURCE_SYNCHRONIZE_IMAGE_CHANGED | JobBaseResource export function useJobResource() { @@ -27,5 +31,13 @@ export function useJobResource() { title: t('coreDam.job.jobResource.jobAuthorCurrentOptimize'), value: 'jobAuthorCurrentOptimize', }, + { + title: t('coreDam.job.jobResource.jobAssetFileReprocessInternalFlag'), + value: 'jobAssetFileReprocessInternalFlag', + }, + { + title: t('coreDam.job.jobResource.jobSynchronizeImageChanged'), + value: 'jobSynchronizeImageChanged', + }, ]) } diff --git a/src/services/api/coreDam/assetApi.ts b/src/services/api/coreDam/assetApi.ts index 28fc0878..36c9b048 100644 --- a/src/services/api/coreDam/assetApi.ts +++ b/src/services/api/coreDam/assetApi.ts @@ -43,6 +43,8 @@ export interface AssetMetadataBulkItem { described: boolean customData: AssetCustomData mainFileSingleUse: boolean | null + mainFileOverrideInternal: boolean + mainFileInternal: boolean } const BULK_METADATA_LIMIT = 10 @@ -125,6 +127,8 @@ const listItemsToMetadataBulkItems = (items: UploadQueueItem[]) => { described: true, customData: item.customData, mainFileSingleUse: item.mainFileSingleUse, + mainFileOverrideInternal: false, + mainFileInternal: false, }) } }) @@ -199,7 +203,12 @@ const handleMetadataValidationError = (error: Error, assetType: DamAssetTypeType showUnknownError() } -export const updateAssetMetadata = (asset: AssetDetailItemDto, mainFileSingleUse: boolean | null) => { +export const updateAssetMetadata = ( + asset: AssetDetailItemDto, + mainFileSingleUse: boolean | null, + mainFileOverrideInternal: boolean, + mainFileInternal: boolean, +) => { return new Promise((resolve, reject) => { const data: AssetMetadataBulkItem = { id: asset.id, @@ -208,6 +217,8 @@ export const updateAssetMetadata = (asset: AssetDetailItemDto, mainFileSingleUse described: true, customData: asset.metadata.customData, mainFileSingleUse: mainFileSingleUse, + mainFileOverrideInternal: mainFileOverrideInternal, + mainFileInternal: mainFileInternal, } damClient() .patch(END_POINT + '/metadata-bulk-update', JSON.stringify([data])) diff --git a/src/services/api/coreDam/assetLicenceApi.ts b/src/services/api/coreDam/assetLicenceApi.ts index 9c956e95..037217e1 100644 --- a/src/services/api/coreDam/assetLicenceApi.ts +++ b/src/services/api/coreDam/assetLicenceApi.ts @@ -1,16 +1,16 @@ import { damClient } from '@/services/api/clients/damClient' import { SYSTEM_CORE_DAM } from '@/model/systems' -import type { DamAssetLicence } from '@anzusystems/common-admin' import { apiCreateOne, apiFetchOne, apiUpdateOne } from '@anzusystems/common-admin' +import type { DamAssetLicenceExtended } from '@/model/coreDam/type/AssetLicence' const END_POINT = '/adm/v1/asset-licence' export const ENTITY = 'assetLicence' -export const createAssetLicence = (data: DamAssetLicence) => - apiCreateOne(damClient, data, END_POINT, {}, SYSTEM_CORE_DAM, ENTITY) +export const createAssetLicence = (data: DamAssetLicenceExtended) => + apiCreateOne(damClient, data, END_POINT, {}, SYSTEM_CORE_DAM, ENTITY) -export const updateAssetLicence = (id: number, data: DamAssetLicence) => - apiUpdateOne(damClient, data, END_POINT + '/:id', { id }, SYSTEM_CORE_DAM, ENTITY) +export const updateAssetLicence = (id: number, data: DamAssetLicenceExtended) => + apiUpdateOne(damClient, data, END_POINT + '/:id', { id }, SYSTEM_CORE_DAM, ENTITY) export const fetchAssetLicence = (id: number) => - apiFetchOne(damClient, END_POINT + '/:id', { id }, SYSTEM_CORE_DAM, ENTITY) + apiFetchOne(damClient, END_POINT + '/:id', { id }, SYSTEM_CORE_DAM, ENTITY) diff --git a/src/services/api/coreDam/audioApi.ts b/src/services/api/coreDam/audioApi.ts index 3bab1f04..42479013 100644 --- a/src/services/api/coreDam/audioApi.ts +++ b/src/services/api/coreDam/audioApi.ts @@ -290,4 +290,4 @@ export const makePrivate = (audioId: DocId) => { reject(err) }) }) -} +} \ No newline at end of file diff --git a/src/services/api/coreDam/documentApi.ts b/src/services/api/coreDam/documentApi.ts index b02ab5bf..aa9f3d71 100644 --- a/src/services/api/coreDam/documentApi.ts +++ b/src/services/api/coreDam/documentApi.ts @@ -290,4 +290,4 @@ export const makePrivate = (documentId: DocId) => { reject(err) }) }) -} +} \ No newline at end of file diff --git a/src/services/api/coreDam/imageApi.ts b/src/services/api/coreDam/imageApi.ts index 81c546eb..a5ea855d 100644 --- a/src/services/api/coreDam/imageApi.ts +++ b/src/services/api/coreDam/imageApi.ts @@ -306,4 +306,4 @@ export const makePrivate = (imageId: DocId) => { reject(err) }) }) -} +} \ No newline at end of file diff --git a/src/stores/coreDam/assetDetailStore.ts b/src/stores/coreDam/assetDetailStore.ts index 73d9a63b..fbb12888 100644 --- a/src/stores/coreDam/assetDetailStore.ts +++ b/src/stores/coreDam/assetDetailStore.ts @@ -17,6 +17,8 @@ export const useAssetDetailStore = defineStore('damAssetDetailStore', () => { const lastFetched = ref(Date.now()) const lastFetchedId = ref('') const mainFileSingleUse = ref(false) + const mainFileOverrideInternal = ref(false) + const mainFileInternal = ref(false) function updateLastFetched(id: DocId) { lastFetchedId.value = id @@ -42,6 +44,8 @@ export const useAssetDetailStore = defineStore('damAssetDetailStore', () => { function setAsset(newAsset: AssetDetailItemDto) { const { getAuthorConflicts } = useAssetSuggestions() mainFileSingleUse.value = newAsset?.mainFileSingleUse || false + mainFileOverrideInternal.value = newAsset?.mainFile?.flags.overrideInternal || false + mainFileInternal.value = newAsset?.mainFile?.flags.internal || false metadataAreTouched.value = false // todo check authorConflicts.value = getAuthorConflicts(newAsset.metadata.authorSuggestions) prefetchLazyData(newAsset) // todo check @@ -82,6 +86,8 @@ export const useAssetDetailStore = defineStore('damAssetDetailStore', () => { view.value = 'list' metadataAreTouched.value = false mainFileSingleUse.value = false + mainFileOverrideInternal.value = false + mainFileInternal.value = false } return { @@ -96,6 +102,8 @@ export const useAssetDetailStore = defineStore('damAssetDetailStore', () => { lastFetched, lastFetchedId, mainFileSingleUse, + mainFileOverrideInternal, + mainFileInternal, updateLastFetched, showDetail, hideDetail, diff --git a/src/stores/coreDam/assetLicenceStore.ts b/src/stores/coreDam/assetLicenceStore.ts index 53593fcc..85d3c4ba 100644 --- a/src/stores/coreDam/assetLicenceStore.ts +++ b/src/stores/coreDam/assetLicenceStore.ts @@ -1,14 +1,14 @@ import { acceptHMRUpdate, defineStore } from 'pinia' import { useAssetLicenceFactory } from '@/model/coreDam/factory/AssetLicenceFactory' -import type { DamAssetLicence } from '@anzusystems/common-admin' +import type { DamAssetLicenceExtended } from '@/model/coreDam/type/AssetLicence' import { ref } from 'vue' export const useAssetLicenceOneStore = defineStore('assetLicenceOneStore', () => { const { createDefault } = useAssetLicenceFactory() - const assetLicence = ref(createDefault()) + const assetLicence = ref(createDefault()) - function setAssetLicence(newAssetLicence: DamAssetLicence) { + function setAssetLicence(newAssetLicence: DamAssetLicenceExtended) { assetLicence.value = newAssetLicence } diff --git a/src/types/coreDam/Job.ts b/src/types/coreDam/Job.ts index f156fc40..b8dd9ddf 100644 --- a/src/types/coreDam/Job.ts +++ b/src/types/coreDam/Job.ts @@ -1,6 +1,13 @@ -import type { DocIdNullable, IntegerId, IntegerIdNullable, JobBase, JobUserDataDelete } from '@anzusystems/common-admin' +import type { DatetimeUTCNullable, DocIdNullable, IntegerId, IntegerIdNullable, JobBase, JobUserDataDelete } from '@anzusystems/common-admin' import type { JobResource } from '@/model/coreDam/valueObject/JobResource' +export interface JobAssetFileReprocessInternalFlag extends JobBase { + targetLicenceId: IntegerId + processFrom: DatetimeUTCNullable + processUntil: DatetimeUTCNullable + bulkSize: number +} + export interface JobPodcastSynchronizer extends JobBase { podcastId: DocIdNullable fullSync: boolean @@ -32,4 +39,16 @@ export interface JobAuthorCurrentOptimize extends JobBase { authorId: DocIdNullable } -export type Job = JobUserDataDelete | JobPodcastSynchronizer | JobImageCopy +export interface JobSynchronizeImageChanged extends JobBase { + targetLicenceId: IntegerId + processFrom: DatetimeUTCNullable + processUntil: DatetimeUTCNullable + bulkSize: number +} + +export type Job = + | JobUserDataDelete + | JobPodcastSynchronizer + | JobImageCopy + | JobAssetFileReprocessInternalFlag + | JobSynchronizeImageChanged diff --git a/src/views/coreDam/asset/components/AssetMetadata.vue b/src/views/coreDam/asset/components/AssetMetadata.vue index 7cc08125..58e67457 100644 --- a/src/views/coreDam/asset/components/AssetMetadata.vue +++ b/src/views/coreDam/asset/components/AssetMetadata.vue @@ -35,7 +35,14 @@ const { t } = useI18n() const panels = ref(['metadata', 'file']) -const { asset, authorConflicts, metadataTouch, mainFileSingleUse } = useAssetDetailActions() +const { + asset, + authorConflicts, + metadataTouch, + mainFileSingleUse, + mainFileOverrideInternal, + mainFileInternal, +} = useAssetDetailActions() const assetType = computed(() => { return asset.value?.attributes.assetType || DamAssetTypeDefault @@ -150,6 +157,31 @@ const onAnyMetadataChange = () => { /> + + + + + + + + + + diff --git a/src/views/coreDam/asset/detail/components/AssetDetailSidebarMetadata.vue b/src/views/coreDam/asset/detail/components/AssetDetailSidebarMetadata.vue index f6c27bcd..b694e958 100644 --- a/src/views/coreDam/asset/detail/components/AssetDetailSidebarMetadata.vue +++ b/src/views/coreDam/asset/detail/components/AssetDetailSidebarMetadata.vue @@ -8,7 +8,11 @@ import AssetDetailSidebarActionsWrapper from '@/views/coreDam/asset/detail/compo import AssetDownloadButton from '@/views/coreDam/asset/detail/components/AssetDownloadButton.vue' import { useAssetDetailActions } from '@/views/coreDam/asset/detail/composables/assetDetailActions' import type { DamAssetTypeType, DocId } from '@anzusystems/common-admin' -import { AActionDeleteButton, isNull, useAlerts } from '@anzusystems/common-admin' +import { + AActionDeleteButton, + isNull, + useAlerts, +} from '@anzusystems/common-admin' import useVuelidate from '@vuelidate/core' import { ref } from 'vue' import { useI18n } from 'vue-i18n' @@ -30,7 +34,7 @@ const emit = defineEmits<{ const { t } = useI18n() -const { asset, view, mainFileSingleUse } = useAssetDetailActions() +const { asset, view, mainFileSingleUse, mainFileOverrideInternal, mainFileInternal } = useAssetDetailActions() const uploadQueueStore = useUploadQueuesStore() const saveButtonLoading = ref(false) @@ -49,7 +53,9 @@ const onSave = async () => { return } try { - await updateAssetMetadata(asset.value, mainFileSingleUse.value) + await updateAssetMetadata( + asset.value, mainFileSingleUse.value, mainFileOverrideInternal.value, mainFileInternal.value + ) if (view.value === 'queue') { uploadQueueStore.updateAssetMetadata(asset.value) } diff --git a/src/views/coreDam/asset/detail/composables/assetDetailActions.ts b/src/views/coreDam/asset/detail/composables/assetDetailActions.ts index ee61e3d1..63cbc080 100644 --- a/src/views/coreDam/asset/detail/composables/assetDetailActions.ts +++ b/src/views/coreDam/asset/detail/composables/assetDetailActions.ts @@ -12,7 +12,16 @@ export function useAssetDetailActions() { const sidebar = ref(true) const assetDetailStore = useAssetDetailStore() - const { asset, authorConflicts, loader, metadataAreTouched, view, mainFileSingleUse } = storeToRefs(assetDetailStore) + const { + asset, + authorConflicts, + loader, + metadataAreTouched, + view, + mainFileSingleUse, + mainFileOverrideInternal, + mainFileInternal, + } = storeToRefs(assetDetailStore) const toggleSidebar = () => { sidebar.value = !sidebar.value @@ -113,5 +122,7 @@ export function useAssetDetailActions() { assetMainFile, toolbarTitle, mainFileSingleUse, + mainFileOverrideInternal, + mainFileInternal, } } diff --git a/src/views/coreDam/asset/list/components/AssetListSidebarMetadata.vue b/src/views/coreDam/asset/list/components/AssetListSidebarMetadata.vue index 341f3713..51f42e2d 100644 --- a/src/views/coreDam/asset/list/components/AssetListSidebarMetadata.vue +++ b/src/views/coreDam/asset/list/components/AssetListSidebarMetadata.vue @@ -31,7 +31,10 @@ const saveButtonLoading = ref(false) const { t } = useI18n() -const { asset, loader, metadataUnTouch, metadataAreTouched, mainFileSingleUse } = useAssetDetailActions() +const { + asset, loader, metadataUnTouch, metadataAreTouched, + mainFileSingleUse, mainFileOverrideInternal, mainFileInternal, +} = useAssetDetailActions() const { fetchCachedUsers, addToCachedUsers } = useDamCachedUsers() @@ -58,7 +61,9 @@ const onSave = async () => { } try { metadataUnTouch() - await updateAssetMetadata(asset.value, mainFileSingleUse.value) + await updateAssetMetadata( + asset.value, mainFileSingleUse.value, mainFileOverrideInternal.value, mainFileInternal.value + ) showRecordWas('updated') } catch (error) { showErrorsDefault(error) diff --git a/src/views/coreDam/assetLicence/components/AssetLicenceCreateButton.vue b/src/views/coreDam/assetLicence/components/AssetLicenceCreateButton.vue index 2d6daaaf..c9cd3ae4 100644 --- a/src/views/coreDam/assetLicence/components/AssetLicenceCreateButton.vue +++ b/src/views/coreDam/assetLicence/components/AssetLicenceCreateButton.vue @@ -1,7 +1,7 @@ + + diff --git a/src/views/coreDam/job/components/JobCreateFormSynchronizeImageChanged.vue b/src/views/coreDam/job/components/JobCreateFormSynchronizeImageChanged.vue new file mode 100644 index 00000000..40ace982 --- /dev/null +++ b/src/views/coreDam/job/components/JobCreateFormSynchronizeImageChanged.vue @@ -0,0 +1,114 @@ + + + diff --git a/src/views/coreDam/job/components/JobDetailAssetFileReprocessInternalFlag.vue b/src/views/coreDam/job/components/JobDetailAssetFileReprocessInternalFlag.vue new file mode 100644 index 00000000..c401f041 --- /dev/null +++ b/src/views/coreDam/job/components/JobDetailAssetFileReprocessInternalFlag.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/views/coreDam/job/components/JobDetailSynchronizeImageChanged.vue b/src/views/coreDam/job/components/JobDetailSynchronizeImageChanged.vue new file mode 100644 index 00000000..07ad98c5 --- /dev/null +++ b/src/views/coreDam/job/components/JobDetailSynchronizeImageChanged.vue @@ -0,0 +1,48 @@ + + + diff --git a/yarn.lock b/yarn.lock index 4067b17f..12dab2ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,9 +5,9 @@ __metadata: version: 8 cacheKey: 10 -"@anzusystems/common-admin@npm:1.47.0-beta.354": - version: 1.47.0-beta.354 - resolution: "@anzusystems/common-admin@npm:1.47.0-beta.354" +"@anzusystems/common-admin@npm:1.47.0-beta.357": + version: 1.47.0-beta.357 + resolution: "@anzusystems/common-admin@npm:1.47.0-beta.357" peerDependencies: "@sentry/vue": ^10.0.0 "@vuelidate/core": ^2.0.3 @@ -38,7 +38,7 @@ __metadata: optional: true vue-router: optional: true - checksum: 10/2caaa79386d810da4462312c5d7c56cea92f6de1e640672582acec95f2e8e6040c81fe30bee79b93db21d4f90baf9e6ddb7eb25ae3b5bee43a6ef90e8e53581a + checksum: 10/f2d4b5b2bb84cd6117bf53b27f97cb662a2df752c81dc97ec45f71f8de2c760969174f4fff9e295b3704e0db4f6c9f2635565f878946b5dd44230399afe26d73 languageName: node linkType: hard @@ -2945,7 +2945,7 @@ __metadata: version: 0.0.0-use.local resolution: "adam-admin@workspace:." dependencies: - "@anzusystems/common-admin": "npm:1.47.0-beta.354" + "@anzusystems/common-admin": "npm:1.47.0-beta.357" "@cypress/grep": "npm:^6.0.0" "@floating-ui/dom": "npm:^1.7.5" "@intlify/unplugin-vue-i18n": "npm:^11.0.3" From dac09f68ecc2b466ced52fb664a7032312fd8127 Mon Sep 17 00:00:00 2001 From: TomasHermanek Date: Tue, 10 Mar 2026 12:12:23 +0100 Subject: [PATCH 02/50] refs #84581 lints (#252) Co-authored-by: Tomas Hermanek --- .../job/components/JobCreateFormAuthorCurrentOptimize.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/views/coreDam/job/components/JobCreateFormAuthorCurrentOptimize.vue b/src/views/coreDam/job/components/JobCreateFormAuthorCurrentOptimize.vue index d910cf08..91e0e348 100644 --- a/src/views/coreDam/job/components/JobCreateFormAuthorCurrentOptimize.vue +++ b/src/views/coreDam/job/components/JobCreateFormAuthorCurrentOptimize.vue @@ -26,7 +26,7 @@ const onCancel = () => { const { createJob } = useJobApi(damClient, SYSTEM_CORE_DAM) const { showRecordWas, showErrorsDefault, showValidationError } = useAlerts() -const { required, minValue } = useValidate() +const { required } = useValidate() const rules = computed(() => { if (!job.value.processAll) { @@ -34,7 +34,6 @@ const rules = computed(() => { job: { authorId: { required, - minValue: minValue(1), }, }, } From d1bf18258c426c89ffc7fa6bd23f61e2c1a3d05d Mon Sep 17 00:00:00 2001 From: TomasHermanek Date: Wed, 11 Mar 2026 11:48:49 +0100 Subject: [PATCH 03/50] ref #73441 Add "mainFileInternal" filter and translations (#253) added `mainFileOverrideInternal` and `mainFileInternal` boolean fields in `AssetMetadataBulkItem` for asset metadata updates --- src/locales/en/coreDam/asset.json | 3 ++- src/locales/sk/coreDam/asset.json | 3 ++- src/model/coreDam/filter/AssetFilter.ts | 3 +++ .../asset/list/components/AssetListSidebarFilter.vue | 8 ++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/locales/en/coreDam/asset.json b/src/locales/en/coreDam/asset.json index 11c36833..a37f3d18 100644 --- a/src/locales/en/coreDam/asset.json +++ b/src/locales/en/coreDam/asset.json @@ -44,7 +44,8 @@ "slotsCountUntil": "Slots count until", "createdAtFrom": "CreatedAt from", "createdAtUntil": "CreatedAt until", - "mainFileSingleUse": "Single-use license" + "mainFileSingleUse": "Single-use license", + "mainFileInternal": "Internal" }, "meta": { "createEmpty": "Create empty asset" diff --git a/src/locales/sk/coreDam/asset.json b/src/locales/sk/coreDam/asset.json index 1364cba5..e3b9e633 100644 --- a/src/locales/sk/coreDam/asset.json +++ b/src/locales/sk/coreDam/asset.json @@ -44,7 +44,8 @@ "slotsCountUntil": "Počet slotov do", "createdAtFrom": "Vytvorené od", "createdAtUntil": "Vytvorené do", - "mainFileSingleUse": "Jednorazová licencia" + "mainFileSingleUse": "Jednorazová licencia", + "mainFileInternal": "Interný" }, "meta": { "createEmpty": "Vytvoriť prázdny asset" diff --git a/src/model/coreDam/filter/AssetFilter.ts b/src/model/coreDam/filter/AssetFilter.ts index 8a640c79..67253dbd 100644 --- a/src/model/coreDam/filter/AssetFilter.ts +++ b/src/model/coreDam/filter/AssetFilter.ts @@ -135,6 +135,9 @@ const filter = reactive({ mainFileSingleUse: { ...makeFilter({ name: 'mainFileSingleUse', default: null }), }, + mainFileInternal: { + ...makeFilter({ name: 'mainFileInternal', default: null }), + }, }) export function useAssetListFilter() { diff --git a/src/views/coreDam/asset/list/components/AssetListSidebarFilter.vue b/src/views/coreDam/asset/list/components/AssetListSidebarFilter.vue index 4f94a42f..3ca84e09 100644 --- a/src/views/coreDam/asset/list/components/AssetListSidebarFilter.vue +++ b/src/views/coreDam/asset/list/components/AssetListSidebarFilter.vue @@ -151,6 +151,14 @@ const { currentExtSystemId } = useCurrentExtSystem() /> + + + + + Date: Wed, 27 May 2026 08:02:36 +0200 Subject: [PATCH 04/50] vuetify 4 upgrade --- .browserslistrc | 4 + .github/workflows/ci.yml | 2 +- .oxfmtrc.json | 17 + .oxlintrc.json | 99 + .yarnrc.yml | 7 + bin/lint | 20 +- doc/changelog/unreleased/85491.md | 29 + doc/changelog/unreleased/template.md | 8 + docker/app/local/Dockerfile | 2 +- eslint.config.mjs | 6 +- index.html | 3 + package.json | 97 +- .../anzutap/components/AnzutapEditor.vue | 2 +- .../anzutap/components/AnzutapToolbar.vue | 8 +- .../anzutap/components/AnzutapToolbarItem.vue | 2 +- .../marks/link/components/LinkDialog.vue | 9 +- .../link/helpers/linkVariantValidation.ts | 5 +- src/components/anzutap/nodes/nodes.ts | 1 - src/components/coreDam/FileUpload.vue | 2 +- .../AssetCustomMetadataForm.vue | 2 +- .../AssetCustomMetadataFormMassOperations.vue | 2 +- src/composables/system/appInitialize.ts | 8 +- src/locales/en/coreDam/distribution.json | 1 - src/model/coreDam/factory/UserFactory.ts | 1 + src/stores/coreDam/assetSlotsStore.ts | 2 +- src/stores/coreDam/uploadQueuesStore.ts | 4 +- src/styles/anzutap/_anzutap.scss | 10 +- src/styles/anzutap/_prosemirror.scss | 6 +- src/styles/anzutap/_toolbar.scss | 6 +- src/styles/anzutap/index.scss | 6 +- src/styles/components/dam-image-grid.scss | 3 +- src/styles/components/upload-overlay.scss | 1 - src/styles/main.scss | 71 +- .../anzuUser/components/AnzuUserFilter.vue | 2 +- src/views/common/log/components/LogDetail.vue | 26 +- src/views/common/log/components/LogFilter.vue | 6 +- .../components/PermissionEditor.vue | 4 +- .../components/PermissionGroupFilter.vue | 2 +- .../coreDam/asset/components/AssetImage.vue | 10 +- .../coreDam/asset/components/AssetInfobox.vue | 28 +- .../asset/components/AssetMetadata.vue | 8 +- .../asset/components/AssetSlotsFilter.vue | 8 +- .../coreDam/asset/components/ImageFile.vue | 1 - .../coreDam/asset/components/ImagePreview.vue | 7 +- .../components/footer/AssetFooterSelected.vue | 2 +- .../footer/AssetFooterSelectedFull.vue | 2 +- .../footer/AssetFooterUploadOverlay.vue | 8 +- .../footer/AssetFooterUploadOverlayFull.vue | 6 +- .../footer/AssetFooterUploadSlotsOverlay.vue | 8 +- .../queue/AssetQueueItemEditable.vue | 16 +- .../components/queue/AssetQueueItemList.vue | 4 +- .../queue/AssetQueueSelectedItemSimple.vue | 2 +- .../queue/AssetQueueSelectedSidebar.vue | 6 +- .../AssetToolbarExtSystemLicenceDialog.vue | 4 +- .../toolbar/AssetToolbarIntegrations.vue | 4 +- .../coreDam/asset/detail/AssetDetailView.vue | 2 +- .../asset/detail/AssetFileDetailView.vue | 2 +- .../detail/components/AssetDetailDialog.vue | 2 +- .../components/AssetDetailDialogLoader.vue | 2 +- .../components/AssetDetailDialogSidebar.vue | 2 +- ...ebarImagePreviewFromDistributionDialog.vue | 4 +- .../components/AssetDetailSidebarROI.vue | 4 +- .../detail/components/AssetFileRotate.vue | 2 +- .../DistributionImagePreviewItem.vue | 2 +- .../AssetDetailSidebarDistribution.vue | 4 +- .../distribution/DistributionBlockedBy.vue | 2 +- .../DistributionCustomMetadataForm.vue | 2 +- .../DistributionListItemCustom.vue | 4 +- .../DistributionListItemEmpty.vue | 2 +- .../distribution/DistributionListItemJw.vue | 4 +- .../DistributionListItemYoutube.vue | 4 +- .../distribution/DistributionNewDialog.vue | 2 +- .../DistributionNewDialogYoutube.vue | 2 +- .../forms/DistributionItemView.vue | 5 +- .../podcast/AssetDetailSidebarPodcast.vue | 2 +- .../podcast/PodcastEpisodeListItem.vue | 2 +- .../slots/AssetDetailSidebarSlots.vue | 2 +- .../detail/components/slots/AssetSibling.vue | 4 +- .../components/slots/AssetSlotListItem.vue | 2 +- .../videoShow/AssetDetailSidebarVideoShow.vue | 2 +- .../videoShow/VideoShowEpisodeListItem.vue | 2 +- .../coreDam/asset/list/AssetListView.vue | 2 +- .../asset/list/components/AssetListItem.vue | 2 +- .../components/AssetLicenceFilter.vue | 2 +- .../components/AssetLicenceGroupFilter.vue | 2 +- .../author/components/AuthorFilter.vue | 2 +- .../AuthorRemoteAutocompleteWithCached.vue | 2 +- .../author/composables/authorConfig.ts | 4 +- .../components/AuthorCleanPhraseFilter.vue | 2 +- .../components/DistributionCategoryFilter.vue | 4 +- .../components/DistributionCategoryWidget.vue | 6 +- .../distributionCategoryActions.ts | 3 +- .../DistributionCategorySelectDetail.vue | 2 +- .../DistributionCategorySelectEditForm.vue | 6 +- .../DistributionCategorySelectFilter.vue | 4 +- ...DistributionCategorySelectOptionSelect.vue | 2 +- .../extSystem/components/ExtSystemFilter.vue | 2 +- .../ExternalProviderAssetListView.vue | 2 +- .../ExternalProviderAssetDetailDialog.vue | 2 +- .../ExternalProviderAssetFooterSelected.vue | 2 +- ...xternalProviderAssetFooterSelectedFull.vue | 2 +- .../ExternalProviderAssetListItem.vue | 2 +- .../ExternalProviderAssetMetadata.vue | 4 +- ...ExternalProviderAssetQueueItemReadonly.vue | 10 +- .../externalProviderAssetListActions.ts | 6 +- .../coreDam/job/components/JobFilter.vue | 2 +- .../keyword/components/KeywordFilter.vue | 2 +- .../keyword/composables/keywordConfig.ts | 4 +- .../PodcastExportDataManageDialog.vue | 10 +- .../podcast/components/PodcastFilter.vue | 2 +- .../components/PodcastEpisodeFilter.vue | 2 +- .../components/PublicExportFilter.vue | 2 +- .../coreDam/user/components/UserFilter.vue | 2 +- .../coreDam/user/composables/userActions.ts | 2 +- .../videoShow/components/VideoShowFilter.vue | 2 +- .../components/VideoShowEpisodeFilter.vue | 2 +- src/views/system/ClosePageView.vue | 4 +- src/views/system/LoginView.vue | 2 +- .../system/components/LoginFormSimple.vue | 4 +- src/views/system/settings/SettingsView.vue | 19 +- tsconfig.app.json | 1 - tsconfig.node.json | 4 +- unanswered.md | 51 + vite.config.ts | 10 + yarn.lock | 3762 +++++++++-------- 125 files changed, 2562 insertions(+), 2083 deletions(-) create mode 100644 .browserslistrc create mode 100644 .oxfmtrc.json create mode 100644 .oxlintrc.json create mode 100644 doc/changelog/unreleased/85491.md create mode 100644 doc/changelog/unreleased/template.md create mode 100644 unanswered.md diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 00000000..3d974746 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,4 @@ +> 0.5% +last 2 versions +not dead +not IE 11 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8cb438e7..b237d9bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: matrix: include: - node-version: 24 - docker-image: anzusystems/node:4.0.0-node24-nginx-browsers + docker-image: anzusystems/node:4.1.0-node24-nginx-browsers name: Node ${{ matrix.node-version }} runs-on: ubuntu-latest diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 00000000..309347bf --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,17 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 120, + "semi": false, + "singleQuote": true, + "trailingComma": "es5", + "ignorePatterns": [], + "overrides": [ + { + "files": ["**/*.scss"], + "options": { + "trailingComma": "none" + } + } + ], + "exclude": ["dist/", "cypress/", "coverage/"] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000..9bd588b4 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,99 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [ + "vue", + "typescript" + ], + "categories": { + "correctness": "off" + }, + "env": { + "builtin": true + }, + "ignorePatterns": [ + "**/dist/**", + "**/dist-ssr/**", + "**/coverage/**", + ".stylelintrc.js", + "cypress/**", + "eslint.config.mjs" + ], + "rules": { + "vue/no-arrow-functions-in-watch": "error", + "vue/no-deprecated-destroyed-lifecycle": "error", + "vue/no-export-in-script-setup": "error", + "vue/no-lifecycle-after-await": "error", + "vue/prefer-import-from-vue": "error", + "vue/valid-define-emits": "error", + "vue/valid-define-props": "error", + "vue/no-multiple-slot-args": "warn", + "vue/no-required-prop-with-default": "warn", + "vue/require-typed-ref": "error", + "no-array-constructor": "error", + "no-unused-vars": [ + "error", + { + "caughtErrors": "none" + } + ], + "no-restricted-imports": [ + "error", + { + "patterns": [ + { + "group": [ + "../*", + "./*" + ], + "message": "Use absolute imports with @ instead of relative imports" + } + ] + } + ], + "typescript/no-duplicate-enum-values": "error", + "typescript/no-extra-non-null-assertion": "error", + "typescript/no-misused-new": "error", + "typescript/no-namespace": "error", + "typescript/no-non-null-asserted-optional-chain": "error", + "typescript/no-require-imports": "error", + "typescript/no-this-alias": "error", + "typescript/no-unnecessary-type-constraint": "error", + "typescript/no-unsafe-declaration-merging": "error", + "typescript/no-unsafe-function-type": "error", + "typescript/no-wrapper-object-types": "error", + "typescript/prefer-as-const": "error", + "typescript/prefer-namespace-keyword": "error", + "typescript/triple-slash-reference": "error" + }, + "overrides": [ + { + "files": [ + "**/*.ts", + "**/*.tsx", + "**/*.mts", + "**/*.cts", + "**/*.vue" + ], + "rules": { + "constructor-super": "off", + "no-class-assign": "off", + "no-const-assign": "off", + "no-dupe-class-members": "off", + "no-dupe-keys": "off", + "no-func-assign": "off", + "no-import-assign": "off", + "no-new-native-nonconstructor": "off", + "no-obj-calls": "off", + "no-redeclare": "off", + "no-setter-return": "off", + "no-this-before-super": "off", + "no-unsafe-negation": "off", + "no-var": "error", + "no-with": "off", + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error" + } + } + ] +} diff --git a/.yarnrc.yml b/.yarnrc.yml index 91b1101f..a1ceb033 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,5 +1,12 @@ +approvedGitRepositories: + - "**" + compressionLevel: mixed enableGlobalCache: false +enableScripts: true + nodeLinker: node-modules + +npmMinimalAgeGate: 0 diff --git a/bin/lint b/bin/lint index 67be7c73..b9ae0fa9 100755 --- a/bin/lint +++ b/bin/lint @@ -2,6 +2,7 @@ # Script used to run the linter check in the application container PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)" +ARGS="$*" cd "${PROJECT_ROOT}" || exit 1 # Initialize default variables # shellcheck disable=SC1091 @@ -13,11 +14,20 @@ docker_env_vars if [ -f /.dockerenv ]; then env-config - echo "[INFO] Running Yarn lint" - yarn lint:tsc - yarn lint:eslint - yarn lint:stylelint + if [[ "$1" == -* ]] || [ $# -eq 0 ]; then + echo "[INFO] Running Yarn lint $@" + yarn run lint "$@" + else + SCRIPT=$1 + shift + # Automatically prefix with 'lint:' if the specific script doesn't exist but the prefixed one does + if ! grep -q "\"$SCRIPT\":" package.json && grep -q "\"lint:$SCRIPT\":" package.json; then + SCRIPT="lint:$SCRIPT" + fi + echo "[INFO] Running Yarn $SCRIPT $@" + yarn run "$SCRIPT" "$@" + fi exit fi -bin/docker-compose exec --user node "${DOCKER_COMPOSE_SERVICE_NAME}" bash -c \'bin/lint\' +bin/docker-compose exec --user node "${DOCKER_COMPOSE_SERVICE_NAME}" bash -c \'bin/lint "${ARGS}"\' diff --git a/doc/changelog/unreleased/85491.md b/doc/changelog/unreleased/85491.md new file mode 100644 index 00000000..271df007 --- /dev/null +++ b/doc/changelog/unreleased/85491.md @@ -0,0 +1,29 @@ +Vuetify 4 upgrade +=== + +### Added + +- ESLint plugin `eslint-plugin-vuetify` with `flat/recommended-v4` ruleset wired into `eslint.config.mjs` +- Oxlint toolchain: `oxlint`, `eslint-plugin-oxlint`, `oxfmt` (+ `.oxlintrc.json`, `.oxfmtrc.json` mirroring admin-cms; `e2e/**` → `cypress/**`) +- `lightningcss` + `browserslist` (+ `.browserslistrc`); vite `css.transformer: 'lightningcss'` with `targets: browserslistToTargets(browserslist('supports css-cascade-layers'))`, `build.cssMinify: 'lightningcss'`, `build.target: 'es2019'` +- `@layer vuetify-core.reset { ... }` block in `src/styles/main.scss` to restore the v3-style universal/form-element reset that v4 dropped +- `package.json` scripts: `lint:oxlint`, `lint:oxlint:fix`, `lint:fix`, `format`, `format:check`; `lint:oxlint` added to `ci`; `lint` dispatches to `lint:fix` on `--fix` +- `bin/lint` smart dispatch (forwards to `yarn lint`, supports `--fix`; `bin/lint ` auto-prefixes to `lint:` when the bare script doesn't exist) +- `unanswered.md` at repo root tracking open migration items (eslint config shape, browser verification, runtime-behavior changes from TS fixes) + +### Changed + +- Migrated 140 Vuetify v4 lint-detected issues automatically: MD2→MD3 typography classes, grid props (`align`/`justify`/`order`/`dense`) → utility classes, deprecated CSS classes and colors +- Manual v4 migrations: removed `dark` from `VAlert`/`VBtn`/`VIcon`; removed `contain` from `VImg` (v4 default is `object-fit: contain`) +- Theme color alpha syntax in anzutap SCSS: `rgb(var(--v-theme-X), 0.Y)` → `rgb(var(--v-theme-X) / Y%)` (broken comma form in v4); same fix for `--v-border-color` / `--v-border-opacity` +- TypeScript: fixed all 41 pre-existing errors. Root cause was unchecked access on optional fields of `DamExtSystemConfig` / `DamExtSystemConfigItem`; fixed with `?.` chaining + `?? false/0/{}` fallbacks. Added missing `locale: null` default to `UserFactory`. **Runtime behavior change**: code that would previously throw on missing config now silently uses the fallback — see `unanswered.md` §3 +- `tsconfig.app.json` — dropped redundant `baseUrl: "."` +- `tsconfig.node.json` — removed unused `nightwatch.conf.*` and `playwright.config.*` from `include` +- Downgraded `@vueuse/core` and `@vueuse/integrations` to exact `14.2.1` (14.3.0 ships malformed `/* #__PURE__ */` annotations that Rollup strips with warnings) +- Converted 3 relative imports in `AnzutapToolbar.vue` / `AnzutapToolbarItem.vue` to `@/...` absolute paths (oxlint `no-restricted-imports`) +- Many Vue/SCSS/TS files reformatted via `oxfmt` + +### Removed + +- `dark` prop usages (removed in v4) +- `contain` prop usages on `VImg` (v4 default behavior) diff --git a/doc/changelog/unreleased/template.md b/doc/changelog/unreleased/template.md new file mode 100644 index 00000000..548d3f31 --- /dev/null +++ b/doc/changelog/unreleased/template.md @@ -0,0 +1,8 @@ +planned +=== + +### Added + +### Changed + +### Removed diff --git a/docker/app/local/Dockerfile b/docker/app/local/Dockerfile index b2a44f6c..6b78402f 100644 --- a/docker/app/local/Dockerfile +++ b/docker/app/local/Dockerfile @@ -1,4 +1,4 @@ -FROM anzusystems/node:4.0.0-node24-nginx-browsers +FROM anzusystems/node:4.1.0-node24-nginx-browsers # ### Basic arguments and variables diff --git a/eslint.config.mjs b/eslint.config.mjs index 75fac62d..fcf28320 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,6 +2,8 @@ import stylistic from '@stylistic/eslint-plugin' // import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' import pluginVue from 'eslint-plugin-vue' import pluginPinia from 'eslint-plugin-pinia' +import pluginVuetify from 'eslint-plugin-vuetify' +import oxlint from 'eslint-plugin-oxlint' import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' const tsExtensionPlugin = { @@ -49,6 +51,7 @@ export default defineConfigWithVueTs( pluginVue.configs['flat/essential'], pluginVue.configs['flat/strongly-recommended'], pluginVue.configs['flat/recommended'], + ...pluginVuetify.configs['flat/recommended-v4'], vueTsConfigs.recommended, { plugins: { @@ -121,6 +124,7 @@ export default defineConfigWithVueTs( '@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-unused-expressions': 'off', }, - } + }, + ...oxlint.buildFromOxlintConfigFile('./.oxlintrc.json') // skipFormatting, ) diff --git a/index.html b/index.html index 4f17cf93..d73499af 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,9 @@ +
diff --git a/package.json b/package.json index 987d588f..5691a1d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "adam-admin", - "packageManager": "yarn@4.12.0", + "packageManager": "yarn@4.15.0", "license": "Apache-2.0", "version": "0.0.1", "type": "module", @@ -13,80 +13,91 @@ "production": "vite build --mode production", "development": "vite build --mode development --sourcemap", "preview": "vite preview", - "ci": "run-s --print-name lint:tsc lint:eslint lint:stylelint", - "lint": "yarn ci", + "ci": "run-s --print-name lint:tsc lint:oxlint lint:eslint lint:stylelint", + "lint": "[ \"$*\" = \"--fix\" ] && yarn lint:fix || yarn ci", + "lint:fix": "run-s --print-name lint:tsc format lint:oxlint:fix lint:eslint:fix lint:stylelint:fix", "lint:tsc": "vue-tsc --noEmit -p tsconfig.app.json --composite false", + "lint:oxlint": "oxlint .", + "lint:oxlint:fix": "oxlint . --fix", "lint:eslint": "eslint", "lint:eslint:fix": "eslint --fix", "lint:stylelint": "stylelint **/*.{scss,vue}", "lint:stylelint:fix": "stylelint **/*.{scss,vue} --fix", "lint:prettier": "prettier -c \"src/**/*.{ts,vue}\"", "lint:prettier:fix": "prettier -w \"src/**/*.{ts,vue}\"", + "format": "oxfmt src/", + "format:check": "oxfmt src/ --check", "cy:open": "CYPRESS_CACHE_FOLDER='node_modules/.cache/Cypress' yarn cypress open -C cypress/config/cypress.config.ts" }, "dependencies": { - "@anzusystems/common-admin": "1.47.0-beta.354", - "@floating-ui/dom": "^1.7.5", + "@anzusystems/common-admin": "1.47.0-beta.dev-1778753796", + "@floating-ui/dom": "^1.7.6", "@mdi/font": "7.4.47", - "@sentry/vue": "^10.38.0", - "@tiptap/core": "^3.19.0", - "@tiptap/extension-bold": "^3.19.0", - "@tiptap/extension-italic": "^3.19.0", - "@tiptap/extension-link": "^3.19.0", - "@tiptap/extension-underline": "^3.19.0", - "@tiptap/pm": "^3.19.0", - "@tiptap/starter-kit": "^3.19.0", - "@tiptap/vue-3": "^3.19.0", + "@sentry/vue": "^10.54.0", + "@tiptap/core": "^3.23.6", + "@tiptap/extension-bold": "^3.23.6", + "@tiptap/extension-italic": "^3.23.6", + "@tiptap/extension-link": "^3.23.6", + "@tiptap/extension-underline": "^3.23.6", + "@tiptap/pm": "^3.23.6", + "@tiptap/starter-kit": "^3.23.6", + "@tiptap/vue-3": "^3.23.6", "@vuelidate/core": "^2.0.3", "@vuelidate/validators": "^2.0.4", - "@vueuse/core": "14.2.0", - "@vueuse/integrations": "14.2.0", - "axios": "^1.13.5", + "@vueuse/core": "14.2.1", + "@vueuse/integrations": "14.2.1", + "axios": "^1.16.1", "jwt-decode": "^4.0.0", "pinia": "^3.0.4", "rusha": "^0.8.14", "socket.io-client": "^4.8.3", - "sortablejs": "^1.15.6", - "universal-cookie": "^8.0.1", - "uuid": "^13.0.0", - "vue": "3.5.28", - "vue-i18n": "^11.2.8", - "vue-router": "^5.0.2", - "vuetify": "^3.11.8" + "sortablejs": "^1.15.7", + "universal-cookie": "^8.1.2", + "uuid": "^14.0.0", + "vue": "3.5.34", + "vue-i18n": "^11.4.4", + "vue-router": "^5.0.7", + "vuetify": "^4.0.7" }, "devDependencies": { "@cypress/grep": "^6.0.0", - "@intlify/unplugin-vue-i18n": "^11.0.3", - "@sentry/vite-plugin": "^4.9.0", - "@stylistic/eslint-plugin": "^5.8.0", + "@intlify/unplugin-vue-i18n": "^11.2.3", + "@sentry/vite-plugin": "^5.3.0", + "@stylistic/eslint-plugin": "^5.10.0", "@tsconfig/node22": "^22.0.5", - "@types/node": "^24.10.12", + "@types/node": "^24.12.4", "@types/rusha": "^0.8.3", "@types/sortablejs": "^1.15.9", - "@vitejs/plugin-vue": "^6.0.4", + "@vitejs/plugin-vue": "^6.0.7", "@vue/eslint-config-prettier": "^10.2.0", - "@vue/eslint-config-typescript": "^14.6.0", - "@vue/language-server": "3.2.4", - "@vue/tsconfig": "^0.8.1", - "cypress": "^15.10.0", + "@vue/eslint-config-typescript": "^14.7.0", + "@vue/language-server": "3.3.2", + "@vue/tsconfig": "^0.9.1", + "browserslist": "^4.28.2", + "cypress": "15.15.0", "cypress-downloadfile": "1.2.4", "cypress-mochawesome-reporter": "^4.0.2", - "eslint": "9.39.2", + "eslint": "10.4.0", + "eslint-plugin-oxlint": "1.62.0", "eslint-plugin-pinia": "^0.4.2", - "eslint-plugin-vue": "^10.7.0", - "npm-run-all2": "^8.0.4", - "postcss": "^8.5.6", + "eslint-plugin-vue": "^10.9.1", + "eslint-plugin-vuetify": "^2.7.2", + "lightningcss": "^1.32.0", + "npm-run-all2": "^9.0.1", + "oxfmt": "^0.47.0", + "oxlint": "1.62.0", + "postcss": "^8.5.15", "postcss-html": "^1.8.1", - "prettier": "^3.8.1", - "sass": "^1.97.3", - "stylelint": "^17.1.1", + "prettier": "^3.8.3", + "sass": "^1.100.0", + "stylelint": "^17.12.0", "stylelint-config-recommended-vue": "^1.6.1", "stylelint-config-standard-scss": "^17.0.0", "typescript": "5.9.3", "unplugin": "^3.0.0", - "vite": "^7.3.1", + "vite": "^7.3.3", "vite-plugin-vuetify": "^2.1.3", - "vue-eslint-parser": "^10.2.0", - "vue-tsc": "3.2.4" + "vue-eslint-parser": "^10.4.0", + "vue-tsc": "3.3.2" } } diff --git a/src/components/anzutap/components/AnzutapEditor.vue b/src/components/anzutap/components/AnzutapEditor.vue index 10a00d6b..4aeed467 100644 --- a/src/components/anzutap/components/AnzutapEditor.vue +++ b/src/components/anzutap/components/AnzutapEditor.vue @@ -53,7 +53,7 @@ defineExpose({ >

diff --git a/src/components/anzutap/components/AnzutapToolbar.vue b/src/components/anzutap/components/AnzutapToolbar.vue index 5a88c892..c79a5cbc 100644 --- a/src/components/anzutap/components/AnzutapToolbar.vue +++ b/src/components/anzutap/components/AnzutapToolbar.vue @@ -1,7 +1,11 @@ diff --git a/src/components/coreDam/customMetadata/AssetCustomMetadataFormMassOperations.vue b/src/components/coreDam/customMetadata/AssetCustomMetadataFormMassOperations.vue index 7772601b..6bc74d19 100644 --- a/src/components/coreDam/customMetadata/AssetCustomMetadataFormMassOperations.vue +++ b/src/components/coreDam/customMetadata/AssetCustomMetadataFormMassOperations.vue @@ -58,7 +58,7 @@ const elements = computed(() => { diff --git a/src/composables/system/appInitialize.ts b/src/composables/system/appInitialize.ts index 03d144ae..e1fa78e7 100644 --- a/src/composables/system/appInitialize.ts +++ b/src/composables/system/appInitialize.ts @@ -58,10 +58,10 @@ export async function createAppInitialize( return } const enabledAssetTypes: DamAssetTypeType[] = [] - if (configExtSystem.audio.enabled) enabledAssetTypes.push(DamAssetType.Audio) - if (configExtSystem.video.enabled) enabledAssetTypes.push(DamAssetType.Video) - if (configExtSystem.image.enabled) enabledAssetTypes.push(DamAssetType.Image) - if (configExtSystem.document.enabled) enabledAssetTypes.push(DamAssetType.Document) + if (configExtSystem.audio?.enabled) enabledAssetTypes.push(DamAssetType.Audio) + if (configExtSystem.video?.enabled) enabledAssetTypes.push(DamAssetType.Video) + if (configExtSystem.image?.enabled) enabledAssetTypes.push(DamAssetType.Image) + if (configExtSystem.document?.enabled) enabledAssetTypes.push(DamAssetType.Document) await loadDamConfigAssetCustomFormElements(currentExtSystemId.value, enabledAssetTypes) initAppNotificationListeners() } catch (error) { diff --git a/src/locales/en/coreDam/distribution.json b/src/locales/en/coreDam/distribution.json index 42d3f204..100dd230 100644 --- a/src/locales/en/coreDam/distribution.json +++ b/src/locales/en/coreDam/distribution.json @@ -37,6 +37,5 @@ "extId": "Ext ID", "failReason": "Fail reason", "assetFileStatusCantDistribute": "Asset file must be processed before distribution" - } } diff --git a/src/model/coreDam/factory/UserFactory.ts b/src/model/coreDam/factory/UserFactory.ts index 5f552134..3c18ac9c 100644 --- a/src/model/coreDam/factory/UserFactory.ts +++ b/src/model/coreDam/factory/UserFactory.ts @@ -8,6 +8,7 @@ export function useUserFactory() { return { id: 0, email: '', + locale: null, person: { firstName: '', lastName: '', diff --git a/src/stores/coreDam/assetSlotsStore.ts b/src/stores/coreDam/assetSlotsStore.ts index 2ace7bd3..629d4999 100644 --- a/src/stores/coreDam/assetSlotsStore.ts +++ b/src/stores/coreDam/assetSlotsStore.ts @@ -34,7 +34,7 @@ export const useAssetSlotsStore = defineStore('damAssetSlotsStore', () => { if (isUndefined(configExtSystem)) { throw new Error('Ext system must be initialised.') } - assetSlotNames.value = cloneDeep(configExtSystem[assetType].slots) + assetSlotNames.value = cloneDeep(configExtSystem[assetType]?.slots ?? []) } function setList(items: Array) { diff --git a/src/stores/coreDam/uploadQueuesStore.ts b/src/stores/coreDam/uploadQueuesStore.ts index 5058fb35..a4d41d75 100644 --- a/src/stores/coreDam/uploadQueuesStore.ts +++ b/src/stores/coreDam/uploadQueuesStore.ts @@ -251,9 +251,7 @@ export const useUploadQueuesStore = defineStore('damUploadQueuesStore', () => { queues.value[queueId].items[foundIndex].authors = res[i].authors queues.value[queueId].items[foundIndex].customData = res[i].metadata.customData queues.value[queueId].items[foundIndex].status = UploadQueueItemStatus.Uploaded - queues.value[queueId].items[foundIndex].authorConflicts = getAuthorConflicts( - res[i].metadata.authorSuggestions - ) + queues.value[queueId].items[foundIndex].authorConflicts = getAuthorConflicts(res[i].metadata.authorSuggestions) queues.value[queueId].items[foundIndex].canEditMetadata = true addToCachedAuthors(queues.value[queueId].items[foundIndex].authors) } diff --git a/src/styles/anzutap/_anzutap.scss b/src/styles/anzutap/_anzutap.scss index 838675c6..2c505706 100644 --- a/src/styles/anzutap/_anzutap.scss +++ b/src/styles/anzutap/_anzutap.scss @@ -1,13 +1,13 @@ -@use "vars/name"; -@use "vars/colors"; -@use "vars/size"; +@use 'vars/name'; +@use 'vars/colors'; +@use 'vars/size'; .#{name.$editor} { position: relative; &__card { border-radius: 4px; - border: 1px solid rgb(var(--v-border-color), var(--v-border-opacity)); + border: 1px solid rgb(var(--v-border-color) / var(--v-border-opacity)); background-color: rgb(var(--v-theme-surface)); margin: 0; position: relative; @@ -22,7 +22,7 @@ &--readonly { .#{name.$editor}__content { - background-color: rgb(var(--v-theme-surface-variant), 0.3); + background-color: rgb(var(--v-theme-surface-variant) / 30%); } } diff --git a/src/styles/anzutap/_prosemirror.scss b/src/styles/anzutap/_prosemirror.scss index 7dd68971..b26fc00a 100644 --- a/src/styles/anzutap/_prosemirror.scss +++ b/src/styles/anzutap/_prosemirror.scss @@ -1,5 +1,5 @@ -@use "vars/name"; -@use "vars/colors"; +@use 'vars/name'; +@use 'vars/colors'; .#{name.$editor} { .#{name.$prosemirror} { @@ -63,7 +63,7 @@ p.is-editor-empty:first-child::before { content: attr(data-placeholder); float: left; - color: rgb(var(--v-theme-on-surface), 0.38); + color: rgb(var(--v-theme-on-surface) / 38%); pointer-events: none; height: 0; } diff --git a/src/styles/anzutap/_toolbar.scss b/src/styles/anzutap/_toolbar.scss index 31678f8e..37037347 100644 --- a/src/styles/anzutap/_toolbar.scss +++ b/src/styles/anzutap/_toolbar.scss @@ -1,5 +1,5 @@ -@use "vars/name"; -@use "vars/colors"; +@use 'vars/name'; +@use 'vars/colors'; $background-color-gray: #f8f8f8 !default; $item-color: #666 !default; @@ -42,7 +42,7 @@ $btn-width-from-padding: calc((var(--v-btn-height) + #{$btn-additional-size}) * } &.is-active { - background-color: rgb(var(--v-theme-primary), 0.1); + background-color: rgb(var(--v-theme-primary) / 10%); color: rgb(var(--v-theme-primary)); } diff --git a/src/styles/anzutap/index.scss b/src/styles/anzutap/index.scss index 7e517681..47ac5f03 100644 --- a/src/styles/anzutap/index.scss +++ b/src/styles/anzutap/index.scss @@ -1,3 +1,3 @@ -@use "anzutap"; -@use "prosemirror"; -@use "toolbar"; +@use 'anzutap'; +@use 'prosemirror'; +@use 'toolbar'; diff --git a/src/styles/components/dam-image-grid.scss b/src/styles/components/dam-image-grid.scss index 8d03a73a..a829cc74 100644 --- a/src/styles/components/dam-image-grid.scss +++ b/src/styles/components/dam-image-grid.scss @@ -52,7 +52,8 @@ $bg-color-actions-dark: #1a1a1a; } } - &-text, &-text .line-clamp-1 { + &-text, + &-text .line-clamp-1 { font-weight: 500; line-height: 30px; min-height: 38px; diff --git a/src/styles/components/upload-overlay.scss b/src/styles/components/upload-overlay.scss index f28c7355..9fe6a870 100644 --- a/src/styles/components/upload-overlay.scss +++ b/src/styles/components/upload-overlay.scss @@ -52,7 +52,6 @@ $bg-color-dark: #1a1a1a; } } - .v-toolbar.overflow-visible { & > .v-toolbar__content { overflow: visible; diff --git a/src/styles/main.scss b/src/styles/main.scss index 56850169..7339f741 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -1,4 +1,69 @@ @use 'vuetify/styles'; -@use "components/dam-image-grid"; -@use "components/upload-overlay"; -@use "anzutap"; +@use 'components/dam-image-grid'; +@use 'components/upload-overlay'; +@use 'anzutap'; + +@layer vuetify-core.reset { + * { + padding: 0; + margin: 0; + } + + a:active, + a:hover { + outline-width: 0; + } + + code, + kbd, + pre, + samp { + font-family: monospace; + } + + pre { + font-size: 1em; + } + + small { + font-size: 80%; + } + + sub, + sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + } + + sub { + bottom: -0.25em; + } + + sup { + top: -0.5em; + } + + textarea { + resize: vertical; + } + + button, + input, + select, + textarea { + background-color: transparent; + border-style: none; + } + + select { + appearance: none; + } + + legend { + display: table; + max-width: 100%; + white-space: normal; + } +} diff --git a/src/views/common/anzuUser/components/AnzuUserFilter.vue b/src/views/common/anzuUser/components/AnzuUserFilter.vue index 300cac4e..b4466ba9 100644 --- a/src/views/common/anzuUser/components/AnzuUserFilter.vue +++ b/src/views/common/anzuUser/components/AnzuUserFilter.vue @@ -37,7 +37,7 @@ const onAnyFilterUpdate = () => { enable-advanced @reset-filter="resetFilter" > - + { -

+

{{ t('common.log.model.id') }}

{{ log.id }}
-

+

{{ t('common.log.model.levelName') }}

-

+

{{ t('common.log.model.context.userId') }}

{{ log.context.userId }}
-

+

{{ t('common.log.model.context.appVersion') }}

{{ log.context.appVersion }}
-

+

{{ t('common.log.model.context.requestOriginAppVersion') }}

{{ log.context.requestOriginAppVersion }} @@ -54,19 +54,19 @@ const formattedJSON = (data: string) => {
-

+

{{ t('common.log.model.context.contextId') }}

{{ log.context.contextId }}
-

+

{{ t('common.log.model.context.ip') }}

{{ log.context.ip }}
-

+

{{ t('common.log.model.datetime') }}

{{ dateTimeFriendly(log.datetime) }} @@ -74,7 +74,7 @@ const formattedJSON = (data: string) => {
-

+

{{ t('common.log.model.message') }}

{{ log.message }}
@@ -82,13 +82,13 @@ const formattedJSON = (data: string) => {
-

+

{{ t('common.log.model.context.path') }}

{{ log.context.method }} {{ log.context.path }}
-

+

{{ t('common.log.model.context.httpStatus') }}

{{ log.context.httpStatus }} @@ -96,7 +96,7 @@ const formattedJSON = (data: string) => {
-

+

{{ t('common.log.model.context.content') }}

{{ formattedJSON(log.context.content ?? '') }}
@@ -104,7 +104,7 @@ const formattedJSON = (data: string) => {
-

+

{{ t('common.log.model.context.response') }}

{{ formattedJSON(log.context.response) }}
diff --git a/src/views/common/log/components/LogFilter.vue b/src/views/common/log/components/LogFilter.vue index 5282b29d..504a0528 100644 --- a/src/views/common/log/components/LogFilter.vue +++ b/src/views/common/log/components/LogFilter.vue @@ -50,7 +50,7 @@ const { logSystemOptions } = useLogSystem() enable-top @reset-filter="resetFilter" > - +