fix(app): resolve Bucket A/C/D lint items (trivial / style / Vuetify class)

WS-3 session 1b-ii Task 4 (audit Buckets A, C, D — 26 items resolved
this commit; 24 indent items in useTimeSlotDropdown.ts remain — see
deviations).

Bucket A — Trivial fixes (12 items resolved):
- A.1: second-pass eslint --fix on App.vue resolved 4 multi-attribute
  warnings. AppKpiCard / PortalLayout / PublicLayout
  lines-around-comment items were attempted via blank-line addition,
  but that introduced an equal number of vue/block-tag-newline
  errors (the rules conflict at the SFC <script>-tag boundary). The
  blank-line additions were reverted; net-zero, the 3 items remain
  for a 1b-iii .eslintrc.cjs override decision.
- A.3: 6 unused-imports / unused-vars manual deletes:
  * OrganisationSwitcher.vue: removed orphan toggleMenu() function
  * CreateShiftDialog.vue: removed unused 'scenario' from destructure
  * pages/events/[id]/time-slots/index.vue: removed unused 'event'
    slot scope binding (template <#default="{ event }"> → <#default>)
  * pages/organisation/companies.vue: removed unused authStore
    declaration + import
  * pages/platform/activity-log/index.vue: removed unused
    search/searchDebounced pair
  * PersonDetailPanel.vue:77: removed redundant single-statement
    if-braces (curly autofix that the original pass didn't reach)

Bucket C — Style preference (8 items resolved):
- DismissFailureDialog.vue:43: collapsed two consecutive `if cond return false`
  branches into `return !(cond)`
- FormFailureDetail.vue:44: replaced `void clipboard.writeText(...)` with
  `clipboard.writeText(...).catch(() => {})` — fire-and-forget with
  silent rejection (the no-void rule wants the void operator gone;
  .catch() handles it semantically).
- AssignShiftDialog.vue:40-46: hasOverlapWarning collapsed from
  always-false branching to `computed(() => false)` (the early-return
  was dead code; backend enforces the constraint).
- SectionsShiftsPanel.vue:333 + registration-fields.vue:335: rewrote
  `:delay-on-touch-only="true"` to attribute-shorthand `delay-on-touch-only`.
- AssignPersonDialog.vue:120-128: collapsed two `if outer { if inner ... }`
  pairs into single `if (outer && inner)` form (sonarjs/no-collapsible-if).
- useImpersonationStore.ts:99-104: collapsed the same nested-if pattern
  into `if (!data.data.active && state.value)`.

Bucket D — Vuetify utility class rename (5 items, 3 files):
- ml-1 → ms-1 (PersonDetailPanel:271, SectionsShiftsPanel:357,
  AssignPersonDialog:496)
- pl-4 → ps-4 (AssignPersonDialog:457)
- ml-auto → ms-auto (AssignPersonDialog:471)
LTR/RTL-aware Vuetify utilities, matching the Vuexy reference idiom.

Tests + typecheck verified green.

Lint baseline: 62 → 36.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 14:20:34 +02:00
parent d407cd17de
commit b4f5bbe7c2
14 changed files with 34 additions and 47 deletions

View File

@@ -31,8 +31,15 @@ authStore.initialize()
<VApp :style="`--v-global-theme-primary: ${hexToRgb(global.current.value.colors.primary)}`"> <VApp :style="`--v-global-theme-primary: ${hexToRgb(global.current.value.colors.primary)}`">
<!-- Show loading state while validating auth token --> <!-- Show loading state while validating auth token -->
<template v-if="!authStore.isInitialized"> <template v-if="!authStore.isInitialized">
<div class="d-flex align-center justify-center" style="min-height: 100vh;"> <div
<VProgressCircular indeterminate color="primary" size="48" /> class="d-flex align-center justify-center"
style="min-height: 100vh;"
>
<VProgressCircular
indeterminate
color="primary"
size="48"
/>
</div> </div>
</template> </template>
@@ -53,7 +60,10 @@ authStore.initialize()
{{ notificationStore.message }} {{ notificationStore.message }}
<template #actions> <template #actions>
<VBtn variant="text" @click="notificationStore.hide()"> <VBtn
variant="text"
@click="notificationStore.hide()"
>
Close Close
</VBtn> </VBtn>
</template> </template>

View File

@@ -40,10 +40,8 @@ const noteRequired = computed(() => reason.value === 'other')
const canSubmit = computed(() => { const canSubmit = computed(() => {
if (reason.value === null) if (reason.value === null)
return false return false
if (noteRequired.value && note.value.trim() === '')
return false
return true return !(noteRequired.value && note.value.trim() === '')
}) })
watch(isDialogOpen, open => { watch(isDialogOpen, open => {

View File

@@ -41,7 +41,7 @@ const dismissDialogOpen = ref(false)
function copyText(text: string): void { function copyText(text: string): void {
if (navigator?.clipboard) if (navigator?.clipboard)
void navigator.clipboard.writeText(text) navigator.clipboard.writeText(text).catch(() => {})
} }
const formattedContext = computed(() => { const formattedContext = computed(() => {

View File

@@ -27,11 +27,6 @@ const roleColor = (role: string) => ({
volunteer_coordinator: 'secondary', volunteer_coordinator: 'secondary',
}[role] ?? 'default') }[role] ?? 'default')
function toggleMenu() {
if (hasMultiple.value)
isMenuOpen.value = !isMenuOpen.value
}
function selectOrg(id: string) { function selectOrg(id: string) {
authStore.setActiveOrganisation(id) authStore.setActiveOrganisation(id)
isMenuOpen.value = false isMenuOpen.value = false

View File

@@ -74,9 +74,8 @@ function calculateAge(dateStr: string): number | null {
let age = today.getFullYear() - birth.getFullYear() let age = today.getFullYear() - birth.getFullYear()
const monthDiff = today.getMonth() - birth.getMonth() const monthDiff = today.getMonth() - birth.getMonth()
const dayDiff = today.getDate() - birth.getDate() const dayDiff = today.getDate() - birth.getDate()
if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) { if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0))
age-- age--
}
if (age < 0 || age > 130) if (age < 0 || age > 130)
return null return null
@@ -269,7 +268,7 @@ function handleManualLink(userId: string) {
Mogelijke match gevonden Mogelijke match gevonden
<VChip <VChip
size="x-small" size="x-small"
class="ml-1" class="ms-1"
variant="outlined" variant="outlined"
> >
{{ person.pending_identity_match.confidence_label }} {{ person.pending_identity_match.confidence_label }}

View File

@@ -37,13 +37,8 @@ const generalError = computed(() => {
}) })
// Check for overlap warning // Check for overlap warning
const hasOverlapWarning = computed(() => { // This is informational — the backend enforces the actual constraint
if (!selectedPersonId.value || !props.shift) const hasOverlapWarning = computed(() => false)
return false
// This is informational — the backend enforces the actual constraint
return false
})
const personItems = computed(() => const personItems = computed(() =>
persons.value.map((p: Person) => ({ persons.value.map((p: Person) => ({

View File

@@ -37,7 +37,7 @@ const { data: eventDetail } = useEventDetail(orgIdRef, eventIdRef)
const sectionRef = computed(() => props.section ?? null) const sectionRef = computed(() => props.section ?? null)
// Determine dropdown scenario // Determine dropdown scenario
const { scenario, showInfoTooltip, hasGroups, tooltipText, fetchParams, sortedItems } = useTimeSlotDropdown( const { showInfoTooltip, hasGroups, tooltipText, fetchParams, sortedItems } = useTimeSlotDropdown(
eventDetail, eventDetail,
sectionRef, sectionRef,
) )

View File

@@ -330,7 +330,7 @@ function onSectionCreated(payload: { name: string; redirectedToParent: boolean;
drag-class="section-drag" drag-class="section-drag"
:animation="200" :animation="200"
:delay="100" :delay="100"
:delay-on-touch-only="true" delay-on-touch-only
direction="vertical" direction="vertical"
class="v-list v-list--nav v-list--density-compact" class="v-list v-list--nav v-list--density-compact"
@end="onDragEnd" @end="onDragEnd"
@@ -354,7 +354,7 @@ function onSectionCreated(payload: { name: string; redirectedToParent: boolean;
<span>{{ element.name }}</span> <span>{{ element.name }}</span>
<span <span
v-if="element.type === 'cross_event' && parentEvent" v-if="element.type === 'cross_event' && parentEvent"
class="text-caption text-medium-emphasis ml-1" class="text-caption text-medium-emphasis ms-1"
> >
· {{ parentEvent.name }} · {{ parentEvent.name }}
</span> </span>

View File

@@ -117,15 +117,11 @@ const filteredPersons = computed(() => {
return false return false
} }
if (selectedCrowdType.value) { if (selectedCrowdType.value && person.crowd_type?.system_type !== selectedCrowdType.value)
if (person.crowd_type?.system_type !== selectedCrowdType.value) return false
return false
}
if (showOnlyAvailable.value) { if (showOnlyAvailable.value && (!person.is_available || person.already_assigned))
if (!person.is_available || person.already_assigned) return false
return false
}
if (showRecommendedOnly.value) { if (showRecommendedOnly.value) {
const hasPreference = person.section_preferences.some( const hasPreference = person.section_preferences.some(
@@ -458,7 +454,7 @@ async function executeAssign(person: AssignablePerson) {
</template> </template>
<div class="text-body-2 text-white"> <div class="text-body-2 text-white">
<strong>Aanbevolen omdat:</strong> <strong>Aanbevolen omdat:</strong>
<ul class="pl-4 mt-1 mb-0"> <ul class="ps-4 mt-1 mb-0">
<li <li
v-for="(reason, i) in getRecommendationReasons(person)" v-for="(reason, i) in getRecommendationReasons(person)"
:key="i" :key="i"
@@ -472,7 +468,7 @@ async function executeAssign(person: AssignablePerson) {
v-if="person.crowd_type" v-if="person.crowd_type"
size="x-small" size="x-small"
variant="tonal" variant="tonal"
class="ml-auto" class="ms-auto"
> >
{{ person.crowd_type.name }} {{ person.crowd_type.name }}
</VChip> </VChip>
@@ -497,7 +493,7 @@ async function executeAssign(person: AssignablePerson) {
{{ tag.name }} {{ tag.name }}
<span <span
v-if="tag.proficiency" v-if="tag.proficiency"
class="ml-1 font-italic" class="ms-1 font-italic"
> >
({{ { beginner: 'beg.', experienced: 'erv.', expert: 'exp.' }[tag.proficiency] }}) ({{ { beginner: 'beg.', experienced: 'erv.', expert: 'exp.' }[tag.proficiency] }})
</span> </span>

View File

@@ -332,7 +332,7 @@ const settingsTab = computed(() => {
handle=".drag-handle" handle=".drag-handle"
:animation="200" :animation="200"
:delay="100" :delay="100"
:delay-on-touch-only="true" delay-on-touch-only
direction="vertical" direction="vertical"
@end="onDragEnd" @end="onDragEnd"
> >

View File

@@ -239,7 +239,7 @@ function onDeleteExecute() {
<template> <template>
<EventTabsNav> <EventTabsNav>
<template #default="{ event }"> <template #default>
<!-- Header --> <!-- Header -->
<div class="d-flex align-center justify-space-between flex-wrap gap-2 mb-4"> <div class="d-flex align-center justify-space-between flex-wrap gap-2 mb-4">
<div class="d-flex align-center gap-x-2"> <div class="d-flex align-center gap-x-2">

View File

@@ -1,11 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { useCompanies, useDeleteCompany } from '@/composables/api/useCompanies' import { useCompanies, useDeleteCompany } from '@/composables/api/useCompanies'
import { useAuthStore } from '@/stores/useAuthStore'
import { useOrganisationStore } from '@/stores/useOrganisationStore' import { useOrganisationStore } from '@/stores/useOrganisationStore'
import CompanyDialog from '@/components/organisation/CompanyDialog.vue' import CompanyDialog from '@/components/organisation/CompanyDialog.vue'
import type { Company } from '@/types/organisation' import type { Company } from '@/types/organisation'
const authStore = useAuthStore()
const orgStore = useOrganisationStore() const orgStore = useOrganisationStore()
const orgId = computed(() => orgStore.activeOrganisationId ?? '') const orgId = computed(() => orgStore.activeOrganisationId ?? '')

View File

@@ -7,8 +7,6 @@ definePage({
}, },
}) })
const search = ref('')
const searchDebounced = refDebounced(search, 400)
const logNameFilter = ref('') const logNameFilter = ref('')
const page = ref(1) const page = ref(1)

View File

@@ -96,11 +96,9 @@ export const useImpersonationStore = defineStore('impersonation', () => {
'/admin/impersonate/status', '/admin/impersonate/status',
) )
if (!data.data.active) { if (!data.data.active && state.value) {
if (state.value) { clearState()
clearState() window.location.href = '/platform'
window.location.href = '/platform'
}
} }
else if (data.data.session) { else if (data.data.session) {
// Update expiry from server // Update expiry from server