From e48744e26ac3db28df47ea730752cc1beeb87668 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Wed, 29 Apr 2026 20:41:08 +0200 Subject: [PATCH 01/28] [hardware-items] Migrate page to Composition API Convert HardwareItems.vue to From 81fddb9eccba78241ab45c626fe77978a20ddd95 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Wed, 29 Apr 2026 20:41:39 +0200 Subject: [PATCH 02/28] [software-licenses] Migrate page to Composition API Convert SoftwareLicenses.vue to From 53af85ed86ee59c6465cee1b7f37524ba35f01e6 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Thu, 30 Apr 2026 14:26:53 +0200 Subject: [PATCH 03/28] [budget] Migrate SalaryScale page to Composition API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert SalaryScale.vue to From 2a7b89284a7eec481a104f1c3028f0dffa3ca392 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Thu, 30 Apr 2026 19:16:01 +0200 Subject: [PATCH 08/28] [departments] Convert list to Composition API Convert DepartmentList.vue to From 6f715d5af05def8fc076d5460408d1d13c4c056f Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 1 May 2026 21:01:46 +0200 Subject: [PATCH 09/28] [layouts] Convert PageLayout and PageLeftSideLayout to Composition API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both layouts are slot-only wrappers — convert them to diff --git a/src/components/layouts/PageLeftSideLayout.vue b/src/components/layouts/PageLeftSideLayout.vue index d651864cd4..371e70ab80 100644 --- a/src/components/layouts/PageLeftSideLayout.vue +++ b/src/components/layouts/PageLeftSideLayout.vue @@ -1,5 +1,5 @@ - + From 23680ad76d7f80a5156d9d70e12650e8f00fe2d1 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 1 May 2026 21:11:59 +0200 Subject: [PATCH 10/28] [cells] Convert cells to Composition API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert 10 cells to diff --git a/src/components/modals/EditLabelModal.vue b/src/components/modals/EditLabelModal.vue index 2ce5c3d13a..2f2a887bf0 100644 --- a/src/components/modals/EditLabelModal.vue +++ b/src/components/modals/EditLabelModal.vue @@ -1,114 +1,78 @@ - +const { t } = useI18n() + +// Props / Emits + +const props = defineProps({ + active: { type: Boolean, default: false }, + isError: { type: Boolean, default: false }, + isLoading: { type: Boolean, default: false }, + label: { type: String, default: 'animate' } +}) + +const emit = defineEmits(['cancel', 'confirm']) + +// State - + +// Watchers + +watch( + () => props.label, + label => { + form.value.label = label + } +) + +// Lifecycle + +onMounted(() => { + form.value.label = props.label +}) + diff --git a/src/components/modals/EditMilestoneModal.vue b/src/components/modals/EditMilestoneModal.vue index 1bdfc231d0..b7c68169ec 100644 --- a/src/components/modals/EditMilestoneModal.vue +++ b/src/components/modals/EditMilestoneModal.vue @@ -1,139 +1,108 @@ - From 1dd1a198adb9ff726205398894949df0fc22e702 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 1 May 2026 21:50:06 +0200 Subject: [PATCH 14/28] [modals] Drop dead CSS in BaseModal-using modals Audit the scoped styles on the five modals migrated to BaseModal in the previous batch. Three of them carried selectors that no longer match anything: scoped styles can't reach into 's .modal-content / .box, and the .is-danger / .task-types / p.text markup hooks weren't actually used in the original templates either. - EditHardwareItemModal: drop the entire diff --git a/src/components/modals/EditSoftwareLicenseModal.vue b/src/components/modals/EditSoftwareLicenseModal.vue index c2750d6ab8..98098203b7 100644 --- a/src/components/modals/EditSoftwareLicenseModal.vue +++ b/src/components/modals/EditSoftwareLicenseModal.vue @@ -150,18 +150,3 @@ onMounted(() => { store.dispatch('loadSoftwareLicenses') }) - - From c9714db1970cd873f6c5e57f3f9d39fdf577fe46 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 1 May 2026 22:34:44 +0200 Subject: [PATCH 15/28] [modals] Convert asset-type / background / episode / sequence modals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert four edit modals from Options API: EditAssetTypeModal, EditBackgroundModal, EditEpisodeModal, EditSequenceModal. Each switches to wrapping its content in with a single :title computed (collapsing the duplicated v-if/v-else

) and drops modalMixin. Cleanups picked up along the way: - EditAssetTypeModal: drop the unused loadTaskTypes mapped action, the unused assetTypes / assetTypeStatusOptions getters, and the ref="shortNameField" that wasn't accessed; pull the inline @update:model-value handler into a named addTaskType helper. - EditBackgroundModal: bind ref="fileUpload" properly so resetForm can call .reset() on it; eslint-disable no-unused-vars on the FileUpload import to work around the vue-eslint-parser false positive (same as the earlier DepartmentList case). - EditEpisodeModal / EditSequenceModal: drop the duplicated form initialization that was happening in both data() and resetForm — keep only the schema and let resetForm populate it. - EditEpisodeModal: drop unused episodeSuccessText state and three unused template refs (descriptionField, resolutionField, nameField — v-focus already auto-focuses on mount). - EditSequenceModal: same — drop sequenceSuccessText and three unused refs; flatten the runConfirmation → confirmClicked indirection. - All four: drop dead CSS rules (.modal-content .box p.text, .is-danger, .info-message — none matched anything in the templates and scoped styles can't reach into ). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/modals/EditAssetTypeModal.vue | 377 ++++++++---------- src/components/modals/EditBackgroundModal.vue | 291 ++++++-------- src/components/modals/EditEpisodeModal.vue | 337 +++++++--------- src/components/modals/EditSequenceModal.vue | 308 ++++++-------- 4 files changed, 534 insertions(+), 779 deletions(-) diff --git a/src/components/modals/EditAssetTypeModal.vue b/src/components/modals/EditAssetTypeModal.vue index 326a632661..c39c791df9 100644 --- a/src/components/modals/EditAssetTypeModal.vue +++ b/src/components/modals/EditAssetTypeModal.vue @@ -1,244 +1,191 @@ - + +// Watchers + +watch(() => props.backgroundToEdit, resetForm) + +watch( + () => props.active, + active => { + if (active) resetForm() + } +) + diff --git a/src/components/modals/EditEpisodeModal.vue b/src/components/modals/EditEpisodeModal.vue index 2b4dd3d1a5..59f3b7debd 100644 --- a/src/components/modals/EditEpisodeModal.vue +++ b/src/components/modals/EditEpisodeModal.vue @@ -1,223 +1,154 @@ - - +watch( + () => props.active, + active => { + if (active) resetForm() + } +) + +watch(() => props.episodeToEdit, resetForm) + +// Lifecycle + +onMounted(resetForm) + diff --git a/src/components/modals/EditSequenceModal.vue b/src/components/modals/EditSequenceModal.vue index 2e37e7ddde..ad128bfb15 100644 --- a/src/components/modals/EditSequenceModal.vue +++ b/src/components/modals/EditSequenceModal.vue @@ -1,210 +1,130 @@ - - +// Watchers + +watch(() => props.active, resetForm) + +watch(() => props.sequenceToEdit, resetForm) + +// Lifecycle + +onMounted(() => { + if (props.active) resetForm() +}) + From bc6932b7471825e88bde9dfa025dabfb32e0761f Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 1 May 2026 22:50:12 +0200 Subject: [PATCH 16/28] [modals] Convert task-status / task-type / day-off / change-password modals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert four more modals from Options API: EditTaskStatusModal, EditTaskTypeModal, DayOffModal, ChangePasswordModal. Each switches to wrapping its content in with a computed title and drops modalMixin. Notable details: - EditTaskStatusModal: the 25-color array becomes a top-level constant (not reactive). nameField / shortNameField refs preserved — confirmClicked uses them to focus the empty field. - EditTaskTypeModal: bug fix — dedicatedToOptions was using this.$t inside data(), so the labels were frozen at first render and didn't react to locale switches; now a computed. Dropped two unused mapped getters (taskTypeStatusOptions, departments) and two unused template refs. - DayOffModal: original made the
itself the .box — now the form sits inside BaseModal's slot and inherits BaseModal's box. Kept the .ml2 override (live, used on the start-date column). - ChangePasswordModal: internal isLoading / isError / isValid state preserved as refs. The kebab-case ref="first-password" is renamed to firstPassword (Vue 3 expects a valid identifier when binding template refs in - + +// Watchers + +watch(() => props.person, resetForm) + +watch( + () => props.active, + active => { + if (active) { + resetForm() + setTimeout(() => { + firstPassword.value?.focus() + }, 100) + } + } +) + diff --git a/src/components/modals/DayOffModal.vue b/src/components/modals/DayOffModal.vue index 1f01455d4b..5f46d7eacf 100644 --- a/src/components/modals/DayOffModal.vue +++ b/src/components/modals/DayOffModal.vue @@ -1,177 +1,136 @@ - diff --git a/src/components/modals/EditTaskTypeModal.vue b/src/components/modals/EditTaskTypeModal.vue index 53420fff6d..adb17cac79 100644 --- a/src/components/modals/EditTaskTypeModal.vue +++ b/src/components/modals/EditTaskTypeModal.vue @@ -1,223 +1,171 @@ - - - From 538f16111640b5ad8b1d057a9460ec96d043d33d Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 1 May 2026 23:09:48 +0200 Subject: [PATCH 17/28] [modals] Convert edit / history / search-filter modals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert four more modals from Options API: EditEditModal, EditHistoryModal, EditSearchFilterModal, EditSearchFilterGroupModal. Each switches to wrapping its content in with a computed or static title and drops modalMixin. Notable details: - EditEditModal: drop unused editSuccessText state and the editCreated watcher that wrote to it (its only consumer); drop two unused template refs (descriptionField, resolutionField). Lose the .title { border-bottom: 2px } rule — scoped styles can't reach BaseModal's title, and the underline isn't used by the other admin modals anyway. - EditHistoryModal: drop the unused headerWrapper template ref. Convert the loadEditHistory promise chain to async/await. Use the imported formatDate directly (drop the methods passthrough). - EditSearchFilterModal: preserve the keyCode-guarded confirm (only emit on Enter or programmatic call). - EditSearchFilterGroupModal: computed title interpolates the group name with embedded quotes. CSS: dropped dead .modal-content .box p.text / .is-danger / .info-message rules across all four. EditHistoryModal keeps its table styling (still applies to elements rendered in this component's scope, not in BaseModal). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/modals/EditEditModal.vue | 393 +++++++----------- src/components/modals/EditHistoryModal.vue | 235 +++++------ .../modals/EditSearchFilterGroupModal.vue | 268 ++++++------ .../modals/EditSearchFilterModal.vue | 323 +++++++------- 4 files changed, 517 insertions(+), 702 deletions(-) diff --git a/src/components/modals/EditEditModal.vue b/src/components/modals/EditEditModal.vue index 720b8a7c0d..d797ff96a0 100644 --- a/src/components/modals/EditEditModal.vue +++ b/src/components/modals/EditEditModal.vue @@ -1,261 +1,176 @@ - - - diff --git a/src/components/modals/EditHistoryModal.vue b/src/components/modals/EditHistoryModal.vue index 48aa911355..7eeb9a58b1 100644 --- a/src/components/modals/EditHistoryModal.vue +++ b/src/components/modals/EditHistoryModal.vue @@ -1,154 +1,121 @@ - + +// Watchers + +watch(currentProduction, updateNamingConvention) + +watch( + () => props.active, + () => { + formData.value = null + inputFile.value?.reset() + } +) + +// Lifecycle + +onMounted(() => { + formData.value = null + if (isTVShow.value) updateNamingConvention() +}) + diff --git a/src/components/modals/ImportModal.vue b/src/components/modals/ImportModal.vue index a00b6c11e2..2ed4c4a385 100644 --- a/src/components/modals/ImportModal.vue +++ b/src/components/modals/ImportModal.vue @@ -1,186 +1,143 @@ - diff --git a/src/components/modals/EditAvatarModal.vue b/src/components/modals/EditAvatarModal.vue index 33da27880f..d75c4f3e39 100644 --- a/src/components/modals/EditAvatarModal.vue +++ b/src/components/modals/EditAvatarModal.vue @@ -1,149 +1,114 @@ - From 4bf2c91036faf39607c2ccfb026ea52673408808 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 1 May 2026 23:49:54 +0200 Subject: [PATCH 20/28] [modals] Convert set-frames / token / create-tasks / playlist modals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert four more modals from Options API: SetFramesFromTaskTypePreviewsModal, NewTokenModal, CreateTasksModal, EditPlaylistModal. Drop modalMixin from each. Two switch to (SetFrames…, EditPlaylistModal); two stay on a custom shell with useModal directly: - NewTokenModal — the original's intentionally has no @click handler so a stray click outside can't dismiss the modal mid-copy and lose an unrecoverable token. BaseModal would silently re-enable background-click cancel; keep the custom shell and add a code comment explaining why. - CreateTasksModal — uses as its title instead of

, so BaseModal's own

would render alongside. Bug fixes: - SetFramesFromTaskTypePreviewsModal: reset() set this.taskType = null but the data field is taskTypeId — the original was creating a new reactive property on every active toggle instead of resetting the combobox value. Now reset() correctly clears taskTypeId. - EditPlaylistModal: the active prop had value: false (typo) instead of default: false — corrected. NewTokenModal also swaps timeMixin (only used `today`) for the existing useTime() composable from @/composables/time. Other cleanups: drop dead .is-danger, dropping unused refs, simplify forEntityOptions, factor the duplicated emit payload in CreateTasksModal into a buildPayload helper. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/modals/CreateTasksModal.vue | 206 ++++------ src/components/modals/EditPlaylistModal.vue | 385 ++++++++---------- src/components/modals/NewTokenModal.vue | 135 +++--- .../SetFramesFromTaskTypePreviewsModal.vue | 155 +++---- 4 files changed, 351 insertions(+), 530 deletions(-) diff --git a/src/components/modals/CreateTasksModal.vue b/src/components/modals/CreateTasksModal.vue index 0a1d5195fd..fd50129c36 100644 --- a/src/components/modals/CreateTasksModal.vue +++ b/src/components/modals/CreateTasksModal.vue @@ -75,142 +75,90 @@ - + +watch(() => props.playlistToEdit, resetForm) + +watch( + () => props.active, + active => { + if (active) { + forClient.value = props.playlistToEdit.for_client ? 'true' : 'false' + resetForm() + setTimeout(() => { + nameField.value?.focus() + }, 100) + } + } +) + diff --git a/src/components/modals/NewTokenModal.vue b/src/components/modals/NewTokenModal.vue index 604608d41f..b98210e076 100644 --- a/src/components/modals/NewTokenModal.vue +++ b/src/components/modals/NewTokenModal.vue @@ -1,5 +1,10 @@ - +watch( + () => props.active, + active => { + resetForm() + if (sequences.value.length === 0) { + store.dispatch('loadSequences') + } + if (active) { + setTimeout(() => { + nameField.value?.focus() + }, 100) + } + } +) + +watch(() => form.value.frameIn, updateNbFramesFromRange) +watch(() => form.value.frameOut, updateNbFramesFromRange) +watch(() => props.shotToEdit, resetForm) + +onMounted(resetForm) + From 5c0a24cc967e9933c084361d9d5d556169034e6a Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Sat, 2 May 2026 08:57:58 +0200 Subject: [PATCH 22/28] [modals] Convert build-people-filter / view-playlist / add-preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert three more modals from Options API: BuildPeopleFilterModal, ViewPlaylistModal, AddPreviewModal. BuildPeopleFilterModal switches to ; the other two stay on a custom shell with useModal: - ViewPlaylistModal has no title

(its content is the directly inside the box), and the root
carries an id="temp-playlist-modal" plus the dark theme class. - AddPreviewModal puts the

ABOVE the

(same pattern as AddAttachmentModal — switching to BaseModal would force the subtitle into the slot, after the h1). Notable cleanups: - BuildPeopleFilterModal: drop a duplicate 'peopleSearchText' entry in mapGetters; drop the unused unionOptions array (its combobox was already commented out in the template); drop two helper methods (addDepartmentFilter, removeDepartmentFilter) that weren't called from the template or anywhere else. - ViewPlaylistModal: maps stay as plain `let` variables — they're used as lookups in event handlers, not rendered, so reactivity is unnecessary; eslint-disable on the PlaylistPlayer import (the same vue-eslint-parser false positive we hit before). - AddPreviewModal: refactor the dynamic `:ref="`video-${index}`"` pattern into a direct @loadedmetadata handler — script setup doesn't have a clean equivalent of iterating $refs, and the event handler is simpler anyway. Drop three unused template refs (modal, background, dropMask) and the empty onDrag method. Expose setFiles via defineExpose for parent-component callers. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/modals/AddPreviewModal.vue | 275 ++++----- .../modals/BuildPeopleFilterModal.vue | 354 +++++------- src/components/modals/ViewPlaylistModal.vue | 521 ++++++++---------- 3 files changed, 445 insertions(+), 705 deletions(-) diff --git a/src/components/modals/AddPreviewModal.vue b/src/components/modals/AddPreviewModal.vue index 97353c231a..fb545d6dc9 100644 --- a/src/components/modals/AddPreviewModal.vue +++ b/src/components/modals/AddPreviewModal.vue @@ -5,13 +5,8 @@ modal: true, 'is-active': active }" - ref="modal" > - +