[composition-api] Migrate modals, cells, layouts and admin pages#2004
Open
frankrousseau wants to merge 28 commits intomainfrom
Open
[composition-api] Migrate modals, cells, layouts and admin pages#2004frankrousseau wants to merge 28 commits intomainfrom
frankrousseau wants to merge 28 commits intomainfrom
Conversation
Convert HardwareItems.vue to <script setup> following Studios.vue pattern: store getters via useStore, async/await for confirm handlers, useHead for the title, and computed tabs so labels react to locale changes. Drop the unused choices state and fix the dead .software-license-list selector to .hardware-item-list. Align responsive with other admin pages by adding the 768px page padding rule, and on HardwareItemList give the name column a min-width with collapsing paddings on mobile. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert SoftwareLicenses.vue to <script setup> mirroring the HardwareItems migration: useStore for getters and dispatches, async/await for confirm handlers, useHead for the title, computed tabs for locale reactivity, and drop the unused choices state. Align responsive with other admin pages by adding the 768px page padding rule, and on SoftwareLicenseList give the name column a min-width with extension/version/cells collapsing paddings on mobile. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert SalaryScale.vue to <script setup>: store getter/dispatches through useStore, refs for isLoading and salaryScale state. Dedupe the table body — the nine hand-rolled <tr> blocks (3 positions × 3 seniorities) collapse into a single <tr v-for> driven by static positions and seniorities arrays, with v-if guards on the department and position cells to preserve their rowspan layout. The salary lookup uses chained optional access so it stays safe while the scale is loading. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert HardwareItemList.vue to <script setup> with defineProps /
defineEmits. Fix the remainingHardwareItems prop default — it was
() => {} (a function with an empty block, returning undefined)
instead of the intended () => ({}). Replace the deprecated $tc with
$t for the entry-count pluralization.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert SoftwareLicenseList.vue to <script setup> with defineProps / defineEmits. Replace the deprecated $tc with $t for the entry-count pluralization. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bring Studios.vue and StudioList.vue (already on <script setup>) in
line with the other admin pages: imports alphabetical by source path
with @unhead/vue first; section comments; refs and reactive object
keys alphabetized; Vuex getters alphabetized; on*Clicked handlers
grouped together; confirmEditStudio no longer mutates the form arg
and uses { ...form, id } spread; [headers].concat(rows) replaced by
[headers, ...rows]; try/catch param renamed to err.
On StudioList, alphabetize defineProps and switch to single-line
form, and add class="color" to the color column header so it gets
the same width:60px / text-align:center as the body cells.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert Departments.vue to <script setup>: store getters/dispatches
through useStore, ref/reactive state, computed tabs (so labels react
to locale switches), watchers and onMounted at the bottom, useHead
for the title.
Apply the project cleanup conventions: imports alphabetical by source
path with @unhead/vue first and components grouped lists / modals /
pages / widgets, section comments, refs and reactive keys
alphabetized, Vuex getters alphabetized, [headers, ...rows] spread,
confirmEditDepartment async/await with { ...form, id } spread (no
form mutation), confirmDeleteDepartment flattened so loading.del is
reset once after the try/catch, onLinkItem/onUnlinkItem destructure
{ itemId, departmentId } at the param list, and onDeleteClicked
regrouped with the other on*Clicked handlers.
DepartmentList is also imported with an eslint-disable comment for
no-unused-vars: vue-eslint-parser can't disambiguate <department-list>
from <department-links> (same kebab prefix).
Add the 768px page-padding rule to align with the other admin pages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert DepartmentList.vue to <script setup> with defineProps / defineEmits. Switch each prop from required: true to typed defaults matching the other lists. Replace deprecated $tc with $t and rename the nb-asset-types class (copy-paste leftover) to nb-departments. Align CSS with the other admin lists: move the first-row border-top reset to the top, switch .name from width:300px to min-width:300px so the column can grow on wide screens, drop the misleading height:20px on the .color td (overridden by sibling cells anyway), widen .color to 60px so the header text fits, and add the 768px breakpoint that collapses cell padding and resets .name / .items min-widths. Add class="color" to the color column header so it shares the body cell's width and centering. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both layouts are slot-only wrappers — convert them to <script setup>. PageLayout keeps its single side prop via defineProps with a typed default; PageLeftSideLayout has no script content so the block stays empty (the original only declared the component name, which is now auto-derived from the filename). Drop the ref="page" template ref on both — no consumer reads $refs.page anywhere in the codebase, so it was vestigial. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert 10 cells to <script setup>: BooleanCell, DepartmentNamesCell,
DescriptionCell, LastCommentCell, PeopleNameCell, StatsCell,
TaskStatusCell, TaskTypeCell, TimeSliderCell, ValidationHeader.
Standard transforms: defineProps with typed defaults (single-line
form, alphabetized), defineEmits, computed for Vuex getters via
useStore, top-level arrow functions for methods, refs for state and
template refs (DescriptionCell), watch / nextTick imports from vue.
A few cleanups picked up along the way:
- TaskStatusCell entry default was () => {} (returns undefined) →
() => ({}); same fix as the recent HardwareItemList one.
- LastCommentCell: drop unused timeout state and the
methods: { renderMarkdown } pass-through (just import it).
- StatsCell: simplify the percent helper (early return on zero
total) and the reduce accumulator.
- TimeSliderCell: extract the repeated label-style object and inline
the static stepStyle / marks tables (they're not reactive).
Skip ValidationCell — it depends on formatListMixin which is shared
with 27+ other files; converting that mixin to a composable is a
separate workstream.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extract the formatting helpers from formatListMixin into a Composition
API composable at src/composables/format.js. useFormat() returns
organisation, isDurationInHours, formatBoolean, formatDuration and
formatPriority (which need vue-i18n + the store) plus the pure
helpers, while formatDate / formatFullDate / formatSimpleDate /
formatPrioritySymbol / sanitizeInteger / sanitizeIntegerLight are also
exposed as plain named exports for callers that don't need to
instantiate the composable.
The legacy mixin in src/components/mixins/format.js stays in place —
its 27+ Options API consumers can migrate one at a time.
Use the new composable to migrate ValidationCell.vue to <script setup>
(it was the last cell still on Options API). Replace the dynamic
emit('select' or 'unselect') with explicit branches so vue's
no-unused-emit-declarations rule can see both events being used.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert the five modals that already wrap their content in
<base-modal> but still mixed in modalMixin: EditHardwareItemModal,
EditSoftwareLicenseModal, EditBudgetModal, EditBudgetEntryModal,
NotifyClientModal.
Drop modalMixin from all five — BaseModal already drives Escape via
useModal, so the mixin was actually adding a second window-level
keydown listener that emitted 'cancel' a second time on each Escape.
Convert each component to <script setup> with section comments,
defineProps with typed defaults, store getters via useStore, and the
existing watchers / lifecycle / template refs preserved.
Two small cleanups picked up along the way:
- EditBudgetEntryModal had a refreshKeys: { endMonth: 0 } reactivity
hack (read once inside the endMonth computed but never written
anywhere) — pure dead code, dropped.
- NotifyClientModal had ref="studioField" / ref="departmentField"
declared on combobox fields but never accessed in the script —
vestigial, dropped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert four small modals from Options API: ConfirmModal, EditDepartmentsModal, EditLabelModal, EditMilestoneModal. Drop modalMixin from each. ConfirmModal keeps its custom modal markup (no title bar, just text + buttons) and uses useModal(toRef(props, 'active'), emit) directly. The other three switch to wrapping their content in <base-modal>, collapsing the duplicated v-if/v-else <h1> blocks into a single :title computed. CSS cleanup: drop the dead .error rule on EditLabelModal (the class isn't used anywhere in the template), and the entire scoped style block on EditDepartmentsModal (.modal-content .box p.text and .is-danger never matched anything in the original markup either, and scoped styles can't reach into <base-modal> now anyway). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 <base-modal>'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 <style> block
(.modal-content .box p.text and .is-danger were both dead).
- EditSoftwareLicenseModal: drop the entire <style> block (same
two rules plus an unused .task-types).
- EditBudgetModal: drop only .modal-content .box p.text; keep the
live .revision-number rule that styles the v{n} number.
EditBudgetEntryModal and NotifyClientModal had no dead CSS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert four edit modals from Options API: EditAssetTypeModal, EditBackgroundModal, EditEpisodeModal, EditSequenceModal. Each switches to wrapping its content in <base-modal> with a single :title computed (collapsing the duplicated v-if/v-else <h1>) 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 <base-modal>). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…modals Convert four more modals from Options API: EditTaskStatusModal, EditTaskTypeModal, DayOffModal, ChangePasswordModal. Each switches to wrapping its content in <base-modal> 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 <form> 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 <script setup>). Promise chains converted to async/await. CSS: dropped dead .modal-content .box p.text / .is-danger rules across all four. The new .ml2 in DayOffModal is the only override that survived. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert four more modals from Options API: EditEditModal,
EditHistoryModal, EditSearchFilterModal, EditSearchFilterGroupModal.
Each switches to wrapping its content in <base-modal> 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) <noreply@anthropic.com>
Convert four more modals from Options API: ImportEdlModal, ImportModal, ShotHistoryModal, ShortcutModal. Each switches to wrapping its content in <base-modal> with a static title and drops modalMixin. Notable details: - ImportEdlModal: pull the duplicated TVShow-naming-convention logic in mounted() and the currentProduction watcher into a single updateNamingConvention helper. - ImportModal: bug fix — tabs array was using $t inside data() so the tab labels were frozen at first render and didn't react to locale switches; now a computed. - ShotHistoryModal: same pattern as EditHistoryModal (just migrated). Promise chain → async/await; drop the unused headerWrapper template ref; use the imported formatDate directly. - ShortcutModal: bug fix — the large shortcutGroups array was using $t inside data() (locale-frozen); now a computed. Object literals compressed to single-line. ImportEdlModal and ImportModal both need eslint-disable no-unused-vars on the FileUpload import — vue-eslint-parser's same false positive hit before on DepartmentList / EditBackgroundModal. CSS: dropped dead .modal-content .box p.text on ImportEdlModal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert four more modals from Options API: ChangeAvatarModal, EditAvatarModal, PreviewModal, SelectTaskTypeModal. Drop modalMixin from each. Three switch to <base-modal>; PreviewModal stays on its custom shell (full-screen image overlay with absolute-positioned action buttons — no title, no .box, BaseModal doesn't fit) and just swaps the mixin for useModal(toRef(props, 'active'), emit). Notable details: - ChangeAvatarModal: BaseModal with the title passed as a prop. - EditAvatarModal: BaseModal + custom button row (no ModalFooter). Rename ref="input-file" (kebab) to ref="inputFile" so script setup can bind it. Wire <base-modal @cancel="close"> so Escape and background-click both go through the close-while-busy guard. - SelectTaskTypeModal: bug fix — taskTypeList had default: () => {} (returns undefined, would crash on taskTypeList[0] if no value passed); now () => []. Added ?.id || '' guard in the active watcher so it won't throw on an empty list either. CSS: dropped dead rules (.modal-content .box p.text, .description, .is-danger) across the four. Kept .error and .right where they're live. ChangeAvatarModal needs eslint-disable no-unused-vars on the FileUpload import — same vue-eslint-parser false positive as before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert four more modals from Options API: SetFramesFromTaskTypePreviewsModal, NewTokenModal, CreateTasksModal, EditPlaylistModal. Drop modalMixin from each. Two switch to <base-modal> (SetFrames…, EditPlaylistModal); two stay on a custom shell with useModal directly: - NewTokenModal — the original's <div class="modal-background"></div> 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 <page-title> as its title instead of <h1 class="title">, so BaseModal's own <h1> 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) <noreply@anthropic.com>
Convert four medium modals from Options API: AddAttachmentModal,
AddThumbnailsModal, EditShotModal, EditAssetModal.
Three switch to <base-modal>; AddAttachmentModal stays on a custom
shell with useModal — its <h2 class="subtitle"> sits ABOVE the
<h1 class="title">, and BaseModal would force the subtitle into the
slot (after the h1), changing the visual order.
EditShotModal pulls in sanitizeInteger from @/composables/format
instead of formatListMixin (it was only using that one helper).
Notable cleanups:
- AddAttachmentModal: kebab ref="file-field" → "fileField" for Vue 3
binding; expose showAnnotationLoading / hideAnnotationLoading via
defineExpose (parent components call them on the modal ref); drop
the empty onDrag() method.
- AddThumbnailsModal: bug fix — entityMap was only declared
implicitly via this.entityMap = {} inside a method, dynamically
added properties on `this` aren't reactive and are a code smell;
now a proper ref({}). Expose markLoading / markUploaded.
- EditShotModal: drop seven unused template refs (only nameField is
read in script); drop unused shotSuccessText state and the
shotCreated watcher that wrote to it; inline the five single-use
frame*/fps/etc. computeds into resetForm; pull the duplicated
frameIn/frameOut watcher logic into one updateNbFramesFromRange.
- EditAssetModal: extract buildPayload to dedupe the two confirm
emits; collapse the source_id reset into a [...].includes(...)
check.
CSS: drop dead .modal-content .box p.text / .is-danger / .info-message
across all four where they no longer matched anything; the
.title { border-bottom } rule on EditShotModal is dropped (scoped
styles can't reach BaseModal's title).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert three more modals from Options API: BuildPeopleFilterModal,
ViewPlaylistModal, AddPreviewModal.
BuildPeopleFilterModal switches to <base-modal>; the other two stay
on a custom shell with useModal:
- ViewPlaylistModal has no title <h1> (its content is the
<playlist-player> directly inside the box), and the root <div>
carries an id="temp-playlist-modal" plus the dark theme class.
- AddPreviewModal puts the <h2 class="subtitle"> ABOVE the
<h1 class="title"> (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) <noreply@anthropic.com>
Convert ManageShotsModal and ImportRenderModal from Options API. - ManageShotsModal: useModal directly. Uses <page-title> instead of <h1 class="title">, same pattern as CreateTasksModal — BaseModal would render its own h1 alongside. Drop the unused ref="shot-padding". The reactive blocks for `names` and `loading` use `reactive()`; the rest is plain refs. The three input refs (addEpisodeInput / addSequenceInput / addShotInput) are accessed in the active watcher and methods, so they stay. focusAddSequence and focusAddShot exposed via defineExpose for parents. - ImportRenderModal: switch to <base-modal> with a static title. Inline the small onReupload / onConfirmClicked handlers directly in the template (one-liner emits). The columnSelect computed intentionally returns parsedCsv[0] so v-model writes back into the parsed CSV — added a code comment to make the deliberate mutation pattern explicit. Drop the mounted hook that just reset formData to null (it starts as null via ref(null) already). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert EditCommentModal and EditPersonModal from Options API. EditCommentModal stays on a custom shell with useModal — its title is conditional (`v-if="commentToEdit?.id"` with no `else` branch), so only the editing case shows an h1; BaseModal would render an empty <h1> for new comments and push the form down. Also: complex template with at-mentions textarea, attachments, file-upload. EditPersonModal switches to <base-modal> with a computed title that covers both create/edit and bot/person variants. timeMixin (only `today` was used) → useTime() composable. Custom button row with multiple actions stays as-is. Notable details: - EditCommentModal: bug fix — attachmentFilesToDelete was added implicitly via this.attachmentFilesToDelete = [] inside reset(); now declared as a proper ref([]). Drop ref="file-field" (kebab, never accessed); rename ref="input-link" → "inputLink"; eslint- disable on the TextField import (vue-eslint-parser false positive). - EditPersonModal: rename ref="name-field" → "nameField". emitForm dispatches one of 'confirm' / 'confirm-invite' / 'invite' dynamically, so vue/no-unused-emit-declarations can't trace it statically — disabled rule for the whole defineEmits block with a comment explaining why. CSS: drop dead .modal-content .box p.text and .is-danger rules from both files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert BuildFilterModal.vue (1035 lines) — the last and largest
modal still on Options API — to <script setup>.
The modal mixed in descriptorMixin only to call one helper —
getDescriptorChecklistValues. Rather than pull the whole mixin (used
by 27+ files) through a composable extraction, expose the helper as
a named export from src/components/mixins/descriptors.js and call it
directly. The mixin's method becomes a one-liner pointing to the
same export, so the existing 27+ Options API consumers still work
unchanged.
Standard transforms: mapGetters → store.getters via computed, methods
→ top-level arrow functions, watch.active → watch with toRef, mounted
→ onMounted, mixins: [modalMixin] → useModal(toRef(props, 'active'),
emit). Reactive blocks (assignation, hasThumbnail, priority…) stay as
reactive(); union becomes a ref. Replace dynamic computed lookups
(this[`${entityType}MetadataDescriptors`]) with explicit lookup
tables (validationColumnsByEntity, metadataDescriptorsByEntity,
searchTextByEntity).
Cleanups per CLAUDE.md:
- Pull static option arrays out of the `general` reactive object
(operatorOptions, taskTypeOperatorOptions, booleanOptions,
checklistOptions, unionOptions) up to module scope as plain
constants — they never change. Update the template to reference
them directly. taskTypeOperatorOptions aliases operatorOptions
(they were identical).
- Replace the long if/else chain in setFiltersFromCurrentQuery with
a filterDispatchByType lookup map.
- Add // Static options / // State / // Computed / // Functions /
// Watchers / // Lifecycle section comments.
CSS:
- Drop dead .field { margin-top: 0; margin-bottom: 0 } rule (no
class="field" element at this component's scope).
- Drop dead .descriptor-text-value nested rule (class never used in
the template).
- Add a 768px responsive block — the modal keeps its custom shell
rather than wrapping in BaseModal, so it doesn't inherit
BaseModal's mobile defaults. Mirror them locally (margin/max-height,
box padding, title font) and let the multi-combobox filter rows
flex-wrap on narrow screens.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add the WebKit/Firefox appearance overrides on .input-editor[type='number'] so the small up/down spinner buttons don't render in the metadata cell. Same pattern as the SalaryScale page. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regression from the script-setup migration: parents call methods on the modal via `$refs[...].method()`, but defineExpose has to opt in explicitly (the Options API auto-exposed everything). - AddPreviewModal: expose reset (called from mixins/task.js resetModals which would throw "reset is not a function") - AddAttachmentModal: expose addFiles (called from widgets/AddComment.vue's getAttachmentModal().addFiles(files)) Other Add* modals already had the right exposes — verified by grepping for `$refs['add-*-modal']` and `addThumbnailsModal()` callers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Body metadata cells with data_type === 'taglist' set their inline z-index to up to 1000 so an open combo dropdown can spill over the next row. The header z-indexes were sized for a much lower body range and ended up below 1000 when scrolling, so the metadata cells visibly overlapped the headers — and sticky-left body columns (name, episode, sticky metadata cells) sat at z-index 1 which let non-sticky body cells slide over them on horizontal scroll. Move every layer above 1000 with a documented hierarchy: body taglist ≤ 1000 (inline) body sticky-left 1001 .datatable-row-header header non-sticky 1002 .datatable-head th header sticky-left 1003 .datatable-head .datatable-row-header The +1 between body sticky-left and header non-sticky breaks the document-order tie at the vertical-scroll seam (otherwise the body, which comes after the header in the DOM, wins and re-covers it). Drop the now-redundant inline `z-index: 1001` on `<metadata-header is-stick>` in AssetList and ShotList — that was originally there to beat the body taglist range, but with the new non-sticky header at 1002 the inline value would actually slide the sticky metadata header *under* non-sticky cells during horizontal scroll. The CSS rule (1003) gives it the right stacking now. Inline comment on the .datatable-head th rule documents the whole hierarchy so future contributors don't lower a layer back below 1000. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problems
formatListMixin(27+ consumers) anddescriptorMixinhad no Composition API equivalent, so new<script setup>components had to either keep a legacy<script>block alongside or duplicate the helpers.() => {}returning undefined,EditPlaylistModal'sactiveprop typo (value:instead ofdefault:),SetFrames…Modal.reset()clearing the wrong field, locale-dependent arrays initialised indata()that froze at first render.Solutions