fix(app): resolve Bucket B (type safety) lint items

WS-3 session 1b-ii Task 3 (audit Bucket B — 34 items: 21 absorbed
via ignorePatterns + 14 real fixes; the count of 21 is the actual
non-Tier-3 lint-count drop from the .eslintrc edit, slightly above
the audit's predicted 20 because additional vendored-Vuexy items
beyond the 23 no-explicit-any landed in those paths too).

Config:
- .eslintrc.cjs: add src/@core/** and src/@layouts/** to ignorePatterns.
  Vendored Vuexy code, precedent: src/plugins/iconify/*.js. The
  CLAUDE.md no-any rule remains in force for our own code under src/.

Real type-safety fixes:
- B.1 ref<any> in our code (3 occurrences):
  * blank.vue / default.vue: AppLoadingIndicator template ref now
    typed as InstanceType<typeof AppLoadingIndicator> | null. Picks
    up the defineExpose'd fallbackHandle / resolveHandle methods.
  * NavSearchBar.vue:109: useApi<any>(...) → useApi<SearchResults[]>(...)
    matching the existing searchResult ref type.
- B.2 ShiftDetailPanel.vue: moved the Cancel-dialog ref declarations
  (isCancelDialogOpen, cancellingAssignment) from line 305-307 to
  line 248 — directly above the onCancel handler that uses them.
  Resolves all 7 no-use-before-define items in one move. Same-file,
  no logic change.
- B.3 useImpersonationStore.ts:119: renamed inner 'stored' to
  'storedSnapshot' to resolve shadowing of the outer 'stored' on
  line 18.
- B.4 useFormSchemas.ts:97-99: renamed local mutationFn parameter
  'confirmed_name' to camelCase 'confirmedName'. Wire-format key
  stays snake_case via destructure-alias:
    params: confirmedName ? { confirmed_name: confirmedName } : undefined
  No callers found in apps/app/src — safe rename.

Tests + typecheck verified green.

Lint baseline: 97 → 62.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 14:11:05 +02:00
parent dcb8d53b61
commit d407cd17de
7 changed files with 17 additions and 12 deletions

View File

@@ -40,6 +40,8 @@ module.exports = {
'*.d.ts', '*.d.ts',
'vendor', 'vendor',
'*.json', '*.json',
'src/@core/**',
'src/@layouts/**',
], ],
rules: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',

View File

@@ -245,6 +245,10 @@ function executeApprove(assignment: ShiftAssignment) {
}) })
} }
// Cancel dialog
const isCancelDialogOpen = ref(false)
const cancellingAssignment = ref<ShiftAssignment | null>(null)
function onCancel(assignment: ShiftAssignment) { function onCancel(assignment: ShiftAssignment) {
cancellingAssignment.value = assignment cancellingAssignment.value = assignment
isCancelDialogOpen.value = true isCancelDialogOpen.value = true
@@ -302,10 +306,6 @@ function onRejectExecute() {
) )
} }
// Cancel dialog
const isCancelDialogOpen = ref(false)
const cancellingAssignment = ref<ShiftAssignment | null>(null)
// Bulk approve dialog // Bulk approve dialog
const isBulkApproveDialogOpen = ref(false) const isBulkApproveDialogOpen = ref(false)

View File

@@ -94,9 +94,9 @@ export function useDeleteFormSchema(orgId: Ref<string>) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
return useMutation({ return useMutation({
mutationFn: async ({ id, confirmed_name }: { id: string; confirmed_name?: string }) => { mutationFn: async ({ id, confirmedName }: { id: string; confirmedName?: string }) => {
await apiClient.delete(`/organisations/${orgId.value}/forms/schemas/${id}`, { await apiClient.delete(`/organisations/${orgId.value}/forms/schemas/${id}`, {
params: confirmed_name ? { confirmed_name } : undefined, params: confirmedName ? { confirmed_name: confirmedName } : undefined,
}) })
}, },
onSuccess: () => { onSuccess: () => {

View File

@@ -1,4 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type AppLoadingIndicator from '@/components/AppLoadingIndicator.vue'
const { injectSkinClasses } = useSkins() const { injectSkinClasses } = useSkins()
// This will inject classes in body tag for accurate styling // This will inject classes in body tag for accurate styling
@@ -6,7 +8,7 @@ injectSkinClasses()
// SECTION: Loading Indicator // SECTION: Loading Indicator
const isFallbackStateActive = ref(false) const isFallbackStateActive = ref(false)
const refLoadingIndicator = ref<any>(null) const refLoadingIndicator = ref<InstanceType<typeof AppLoadingIndicator> | null>(null)
// watching if the fallback state is active and the refLoadingIndicator component is available // watching if the fallback state is active and the refLoadingIndicator component is available
watch([isFallbackStateActive, refLoadingIndicator], () => { watch([isFallbackStateActive, refLoadingIndicator], () => {

View File

@@ -106,7 +106,7 @@ const searchResult = ref<SearchResults[]>([])
const fetchResults = async () => { const fetchResults = async () => {
isLoading.value = true isLoading.value = true
const { data } = await useApi<any>(withQuery('/app-bar/search', { q: searchQuery.value })) const { data } = await useApi<SearchResults[]>(withQuery('/app-bar/search', { q: searchQuery.value }))
searchResult.value = data.value searchResult.value = data.value

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type AppLoadingIndicator from '@/components/AppLoadingIndicator.vue'
import { useConfigStore } from '@core/stores/config' import { useConfigStore } from '@core/stores/config'
import { AppContentLayoutNav } from '@layouts/enums' import { AppContentLayoutNav } from '@layouts/enums'
import { switchToVerticalNavOnLtOverlayNavBreakpoint } from '@layouts/utils' import { switchToVerticalNavOnLtOverlayNavBreakpoint } from '@layouts/utils'
@@ -18,7 +19,7 @@ injectSkinClasses()
// SECTION: Loading Indicator // SECTION: Loading Indicator
const isFallbackStateActive = ref(false) const isFallbackStateActive = ref(false)
const refLoadingIndicator = ref<any>(null) const refLoadingIndicator = ref<InstanceType<typeof AppLoadingIndicator> | null>(null)
// watching if the fallback state is active and the refLoadingIndicator component is available // watching if the fallback state is active and the refLoadingIndicator component is available
watch([isFallbackStateActive, refLoadingIndicator], () => { watch([isFallbackStateActive, refLoadingIndicator], () => {

View File

@@ -116,10 +116,10 @@ export const useImpersonationStore = defineStore('impersonation', () => {
} }
function restoreFromStorage(): void { function restoreFromStorage(): void {
const stored = sessionStorage.getItem(SESSION_STORAGE_KEY) const storedSnapshot = sessionStorage.getItem(SESSION_STORAGE_KEY)
if (stored) { if (storedSnapshot) {
try { try {
state.value = JSON.parse(stored) as ImpersonationState state.value = JSON.parse(storedSnapshot) as ImpersonationState
} }
catch { catch {
sessionStorage.removeItem(SESSION_STORAGE_KEY) sessionStorage.removeItem(SESSION_STORAGE_KEY)