fix: replace any types in frontend components

- use Leaflet types (L.Map, L.Marker, L.LeafletEvent) for map components
- use Post and Project types from Prisma for form components
- use JSONContent type for editor instances
- use Snippet type for Svelte 5 render functions
- use EditorView type for TipTap/ProseMirror views
- use proper type guards for error handling
- add editor interface types for save/clear methods
This commit is contained in:
Justin Edmund 2025-11-23 05:32:09 -08:00
parent 8ec4c582c1
commit 9c746d51c0
12 changed files with 50 additions and 42 deletions

View file

@ -19,9 +19,9 @@
}: Props = $props()
let mapContainer: HTMLDivElement
let map: any
let marker: any
let leaflet: any
let map: L.Map | null = null
let marker: L.Marker | null = null
let leaflet: typeof L | null = null
// Load Leaflet dynamically
async function loadLeaflet() {

View file

@ -1,7 +1,9 @@
<script lang="ts">
import type { Snippet } from 'svelte'
interface Props {
left?: any
right?: any
left?: Snippet
right?: Snippet
}
let { left, right }: Props = $props()

View file

@ -37,8 +37,8 @@
let isSaving = $state(false)
let validationErrors = $state<Record<string, string>>({})
let showBulkAlbumModal = $state(false)
let albumMedia = $state<any[]>([])
let editorInstance = $state<any>()
let albumMedia = $state<Array<{ media: Media; displayOrder: number }>>([])
let editorInstance = $state<{ save: () => Promise<JSONContent>; clear: () => void } | undefined>()
let activeTab = $state('metadata')
let pendingMediaIds = $state<number[]>([]) // Photos to add after album creation

View file

@ -10,7 +10,8 @@
import { makeDraftKey, saveDraft, loadDraft, clearDraft, timeAgo } from '$lib/admin/draftStore'
import { createAutoSaveStore } from '$lib/admin/autoSave.svelte'
import AutoSaveStatus from './AutoSaveStatus.svelte'
import type { JSONContent } from '@tiptap/core'
import type { JSONContent, Editor as TipTapEditor } from '@tiptap/core'
import type { Post } from '@prisma/client'
interface Props {
postId?: number
@ -43,7 +44,7 @@
let tagInput = $state('')
// Ref to the editor component
let editorRef: any
let editorRef: { save: () => Promise<JSONContent> } | undefined
// Draft backup
const draftKey = $derived(makeDraftKey('post', postId ?? 'new'))
@ -80,8 +81,8 @@ let autoSave = mode === 'edit' && postId
if (!response.ok) throw new Error('Failed to save')
return await response.json()
},
onSaved: (saved: any, { prime }) => {
updatedAt = saved.updatedAt
onSaved: (saved: Post, { prime }) => {
updatedAt = saved.updatedAt.toISOString()
prime(buildPayload())
if (draftKey) clearDraft(draftKey)
}
@ -144,7 +145,7 @@ $effect(() => {
// Show restore prompt if a draft exists
$effect(() => {
const draft = loadDraft<any>(draftKey)
const draft = loadDraft<ReturnType<typeof buildPayload>>(draftKey)
if (draft) {
showDraftPrompt = true
draftTimestamp = draft.ts
@ -152,7 +153,7 @@ $effect(() => {
})
function restoreDraft() {
const draft = loadDraft<any>(draftKey)
const draft = loadDraft<ReturnType<typeof buildPayload>>(draftKey)
if (!draft) return
const p = draft.payload
title = p.title ?? title

View file

@ -1,16 +1,18 @@
<script lang="ts">
import type { Snippet } from 'svelte'
interface Props {
label: string
name?: string
type?: string
value?: any
value?: string | number
placeholder?: string
required?: boolean
error?: string
helpText?: string
disabled?: boolean
onchange?: (e: Event) => void
children?: any
children?: Snippet
}
let {

View file

@ -42,7 +42,7 @@
content: [{ type: 'paragraph' }]
}
let characterCount = 0
let editorInstance: any
let editorInstance: { save: () => Promise<JSONContent>; clear: () => void } | undefined
// Essay metadata
let essayTitle = ''

View file

@ -1,8 +1,9 @@
<script lang="ts">
import GenericMetadataPopover, { type MetadataConfig } from './GenericMetadataPopover.svelte'
import type { Post } from '@prisma/client'
type Props = {
post: any
post: Post
postType: 'post' | 'essay'
slug: string
tags: string[]
@ -12,7 +13,7 @@
onRemoveTag: (tag: string) => void
onDelete: () => void
onClose?: () => void
onFieldUpdate?: (key: string, value: any) => void
onFieldUpdate?: (key: string, value: unknown) => void
}
let {
@ -29,11 +30,11 @@
onFieldUpdate
}: Props = $props()
function handleFieldUpdate(key: string, value: any) {
if (key === 'slug') {
function handleFieldUpdate(key: string, value: unknown) {
if (key === 'slug' && typeof value === 'string') {
slug = value
onFieldUpdate?.(key, value)
} else if (key === 'tagInput') {
} else if (key === 'tagInput' && typeof value === 'string') {
tagInput = value
}
}

View file

@ -17,6 +17,7 @@
import { useFormGuards } from '$lib/admin/useFormGuards.svelte'
import { makeDraftKey, saveDraft, clearDraft } from '$lib/admin/draftStore'
import type { ProjectFormData } from '$lib/types/project'
import type { JSONContent } from '@tiptap/core'
interface Props {
project?: Project | null
@ -37,7 +38,7 @@
let successMessage = $state<string | null>(null)
// Ref to the editor component
let editorRef: any
let editorRef: { save: () => Promise<JSONContent> } | undefined
// Draft key for autosave fallback
const draftKey = $derived(mode === 'edit' && project ? makeDraftKey('project', project.id) : null)
@ -50,7 +51,7 @@
save: async (payload, { signal }) => {
return await api.put(`/api/projects/${project?.id}`, payload, { signal })
},
onSaved: (savedProject: any, { prime }) => {
onSaved: (savedProject: Project, { prime }) => {
project = savedProject
formStore.populateFromProject(savedProject)
prime(formStore.buildPayload())
@ -112,7 +113,7 @@
}
})
function handleEditorChange(content: any) {
function handleEditorChange(content: JSONContent) {
formStore.setField('caseStudyContent', content)
}
@ -158,7 +159,7 @@
}
} catch (err) {
toast.dismiss(loadingToastId)
if ((err as any)?.status === 409) {
if (err && typeof err === 'object' && 'status' in err && (err as { status: number }).status === 409) {
toast.error('This project has changed in another tab. Please reload.')
} else {
toast.error(`Failed to ${mode === 'edit' ? 'save' : 'create'} project`)

View file

@ -1,4 +1,4 @@
import type { Editor } from '@tiptap/core'
import type { Editor, EditorView } from '@tiptap/core'
import type { ComposerMediaHandler } from './ComposerMediaHandler.svelte'
import { focusEditor } from '$lib/components/edra/utils'
@ -12,7 +12,7 @@ export interface UseComposerEventsOptions {
export function useComposerEvents(options: UseComposerEventsOptions) {
// Handle paste events
function handlePaste(view: any, event: ClipboardEvent): boolean {
function handlePaste(view: EditorView, event: ClipboardEvent): boolean {
const clipboardData = event.clipboardData
if (!clipboardData) return false
@ -30,7 +30,7 @@ export function useComposerEvents(options: UseComposerEventsOptions) {
event.preventDefault()
// Use editor commands to insert HTML content
const editorInstance = (view as any).editor
const editorInstance = options.editor
if (editorInstance) {
editorInstance
.chain()
@ -66,7 +66,7 @@ export function useComposerEvents(options: UseComposerEventsOptions) {
}
// Handle drag and drop for images
function handleDrop(view: any, event: DragEvent): boolean {
function handleDrop(view: EditorView, event: DragEvent): boolean {
if (!options.features.imageUpload || !options.mediaHandler) return false
const files = event.dataTransfer?.files

View file

@ -189,7 +189,7 @@
}
// Block manipulation functions
function convertBlockType(type: string, attrs?: any) {
function convertBlockType(type: string, attrs?: Record<string, unknown>) {
console.log('convertBlockType called:', type, attrs)
// Use menuNode which was captured when menu was opened
const nodeToConvert = menuNode || currentNode
@ -486,10 +486,11 @@
// Find the existing drag handle created by the plugin and add click listener
const checkForDragHandle = setInterval(() => {
const existingDragHandle = document.querySelector('.drag-handle')
if (existingDragHandle && !(existingDragHandle as any).__menuListener) {
const element = existingDragHandle as HTMLElement & { __menuListener?: boolean }
if (existingDragHandle && !element.__menuListener) {
console.log('Found drag handle, adding click listener')
existingDragHandle.addEventListener('click', handleMenuClick)
;(existingDragHandle as any).__menuListener = true
element.__menuListener = true
// Update our reference to use the existing drag handle
dragHandleContainer = existingDragHandle as HTMLElement

View file

@ -7,9 +7,9 @@
let { node, updateAttributes }: Props = $props()
let mapContainer: HTMLDivElement
let map: any
let marker: any
let leaflet: any
let map: L.Map | null = null
let marker: L.Marker | null = null
let leaflet: typeof L | null = null
let isEditing = $state(false)
// Extract attributes

View file

@ -19,9 +19,9 @@
// Map picker state
let showMapPicker = $state(false)
let mapContainer: HTMLDivElement
let pickerMap: any
let pickerMarker: any
let leaflet: any
let pickerMap: L.Map | null = null
let pickerMarker: L.Marker | null = null
let leaflet: typeof L | null = null
// Load Leaflet for map picker
async function loadLeaflet() {
@ -77,15 +77,15 @@
.addTo(pickerMap)
// Update coordinates on marker drag
pickerMarker.on('dragend', (e: any) => {
const position = e.target.getLatLng()
pickerMarker.on('dragend', (e: L.LeafletEvent) => {
const position = (e.target as L.Marker).getLatLng()
latitude = position.lat.toFixed(6)
longitude = position.lng.toFixed(6)
})
// Update marker on map click
pickerMap.on('click', (e: any) => {
pickerMarker.setLatLng(e.latlng)
pickerMap.on('click', (e: L.LeafletMouseEvent) => {
pickerMarker!.setLatLng(e.latlng)
latitude = e.latlng.lat.toFixed(6)
longitude = e.latlng.lng.toFixed(6)
})