Browse Source

fix: resolve TypeScript and ESLint errors, fix Android build

TypeScript Import Fixes:
- Use type-only imports for interfaces in all lib files
- Fix import statements in schema-validation.ts, error-handling.ts, typed-plugin.ts, diagnostics-export.ts, StatusView.vue

ESLint Error Fixes:
- Replace all 'any' types with proper type annotations
- Use 'unknown' for unvalidated inputs with proper type guards
- Use Record<string, unknown> for object properties
- Add proper type casting for Performance API and Navigator properties
- Fix deprecated Vue filter by replacing type assertion with function

StatusCard Component Fixes:
- Fix prop type mismatch by changing template structure
- Add getStatusType() function for type-safe status conversion
- Add getStatusDescription() function for descriptive text
- Update HomeView.vue to use multiple StatusCard components in grid

Android Build Fix:
- Fix capacitor.settings.gradle plugin path from 'android' to 'android/plugin'
- Resolve Gradle dependency resolution issue
- Enable successful Android APK generation

Key improvements:
- Full type safety with proper TypeScript interfaces
- ESLint compliance with no remaining errors
- Successful web and Android builds
- Better error handling with typed error objects
- Improved developer experience with IntelliSense support
master
Matthew Raymer 1 day ago
parent
commit
be632b2f0e
  1. 3
      BUILDING.md
  2. 26
      test-apps/daily-notification-test/src/lib/diagnostics-export.ts
  3. 22
      test-apps/daily-notification-test/src/lib/error-handling.ts
  4. 73
      test-apps/daily-notification-test/src/lib/schema-validation.ts
  5. 44
      test-apps/daily-notification-test/src/lib/typed-plugin.ts
  6. 49
      test-apps/daily-notification-test/src/views/HomeView.vue
  7. 2
      test-apps/daily-notification-test/src/views/StatusView.vue

3
BUILDING.md

@ -227,12 +227,14 @@ android/
### Important Distinctions ### Important Distinctions
#### Plugin Module (`android/plugin/`) #### Plugin Module (`android/plugin/`)
- **Purpose**: Contains the actual plugin code - **Purpose**: Contains the actual plugin code
- **No MainActivity** - This is a library, not an app - **No MainActivity** - This is a library, not an app
- **No UI Components** - Plugins provide functionality to host apps - **No UI Components** - Plugins provide functionality to host apps
- **Output**: AAR library files - **Output**: AAR library files
#### Test App Module (`android/app/`) #### Test App Module (`android/app/`)
- **Purpose**: Test application for the plugin - **Purpose**: Test application for the plugin
- **Has MainActivity** - Full Capacitor app with BridgeActivity - **Has MainActivity** - Full Capacitor app with BridgeActivity
- **Has UI Components** - HTML/JS interface for testing - **Has UI Components** - HTML/JS interface for testing
@ -392,6 +394,7 @@ npx cap run ios
``` ```
**Test App Features:** **Test App Features:**
- Interactive plugin testing interface - Interactive plugin testing interface
- Plugin diagnostics and status checking - Plugin diagnostics and status checking
- Notification scheduling and management - Notification scheduling and management

26
test-apps/daily-notification-test/src/lib/diagnostics-export.ts

@ -9,9 +9,9 @@
*/ */
import { import {
PermissionStatus, type PermissionStatus,
NotificationStatus, type NotificationStatus,
ExactAlarmStatus type ExactAlarmStatus
} from './bridge' } from './bridge'
export interface ComprehensiveDiagnostics { export interface ComprehensiveDiagnostics {
@ -179,13 +179,15 @@ export class DiagnosticsExporter {
* Collect network information * Collect network information
*/ */
private collectNetworkInfo() { private collectNetworkInfo() {
const connection = (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection const connection = (navigator as Navigator & { connection?: unknown }).connection ||
(navigator as Navigator & { mozConnection?: unknown }).mozConnection ||
(navigator as Navigator & { webkitConnection?: unknown }).webkitConnection
return { return {
connectionType: connection?.type || 'unknown', connectionType: (connection as { type?: string })?.type || 'unknown',
effectiveType: connection?.effectiveType, effectiveType: (connection as { effectiveType?: string })?.effectiveType,
downlink: connection?.downlink, downlink: (connection as { downlink?: number })?.downlink,
rtt: connection?.rtt rtt: (connection as { rtt?: number })?.rtt
} }
} }
@ -206,7 +208,7 @@ export class DiagnosticsExporter {
*/ */
private isStorageAvailable(type: string): boolean { private isStorageAvailable(type: string): boolean {
try { try {
const storage = (window as any)[type] const storage = (window as unknown as Record<string, unknown>)[type]
if (!storage) return false if (!storage) return false
if (type === 'indexedDB') { if (type === 'indexedDB') {
@ -219,8 +221,8 @@ export class DiagnosticsExporter {
// Test localStorage/sessionStorage // Test localStorage/sessionStorage
const test = '__storage_test__' const test = '__storage_test__'
storage.setItem(test, test) ;(storage as { setItem: (key: string, value: string) => void }).setItem(test, test)
storage.removeItem(test) ;(storage as { removeItem: (key: string) => void }).removeItem(test)
return true return true
} catch { } catch {
return false return false
@ -231,7 +233,7 @@ export class DiagnosticsExporter {
* Get memory usage (if available) * Get memory usage (if available)
*/ */
private getMemoryUsage(): number | undefined { private getMemoryUsage(): number | undefined {
const memory = (performance as any).memory const memory = (performance as Performance & { memory?: { usedJSHeapSize: number } }).memory
return memory ? memory.usedJSHeapSize : undefined return memory ? memory.usedJSHeapSize : undefined
} }

22
test-apps/daily-notification-test/src/lib/error-handling.ts

@ -8,15 +8,17 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import { ErrorCode, ErrorInfo } from './bridge' import { ErrorCode, type ErrorInfo } from './bridge'
export class ErrorHandler { export class ErrorHandler {
/** /**
* Map native error to canonical error * Map native error to canonical error
*/ */
mapNativeError(error: any): ErrorInfo { mapNativeError(error: unknown): ErrorInfo {
const errorMessage = error?.message || error?.toString() || 'Unknown error' const errorMessage = (error as { message?: string })?.message ||
(error as { toString?: () => string })?.toString?.() ||
'Unknown error'
// Map common error patterns // Map common error patterns
if (errorMessage.includes('Permission denied')) { if (errorMessage.includes('Permission denied')) {
@ -123,18 +125,18 @@ export class ErrorHandler {
/** /**
* Log error with context * Log error with context
*/ */
logError(error: any, context: string = 'DailyNotification') { logError(error: unknown, context: string = 'DailyNotification') {
console.error(`[${context}] Error:`, error) console.error(`[${context}] Error:`, error)
if (error?.stack) { if ((error as { stack?: string })?.stack) {
console.error(`[${context}] Stack:`, error.stack) console.error(`[${context}] Stack:`, (error as { stack: string }).stack)
} }
} }
/** /**
* Handle plugin method error * Handle plugin method error
*/ */
handlePluginError(error: any, method: string): ErrorInfo { handlePluginError(error: unknown, method: string): ErrorInfo {
this.logError(error, `Plugin.${method}`) this.logError(error, `Plugin.${method}`)
return this.mapNativeError(error) return this.mapNativeError(error)
} }
@ -144,7 +146,7 @@ export class ErrorHandler {
export const errorHandler = new ErrorHandler() export const errorHandler = new ErrorHandler()
// Utility functions // Utility functions
export function mapNativeError(error: any): ErrorInfo { export function mapNativeError(error: unknown): ErrorInfo {
return errorHandler.mapNativeError(error) return errorHandler.mapNativeError(error)
} }
@ -152,10 +154,10 @@ export function createUserMessage(error: ErrorInfo): string {
return errorHandler.createUserMessage(error) return errorHandler.createUserMessage(error)
} }
export function logError(error: any, context?: string): void { export function logError(error: unknown, context?: string): void {
errorHandler.logError(error, context) errorHandler.logError(error, context)
} }
export function handlePluginError(error: any, method: string): ErrorInfo { export function handlePluginError(error: unknown, method: string): ErrorInfo {
return errorHandler.handlePluginError(error, method) return errorHandler.handlePluginError(error, method)
} }

73
test-apps/daily-notification-test/src/lib/schema-validation.ts

@ -9,11 +9,10 @@
*/ */
import { import {
ScheduleRequest, type ValidationResult,
ValidationResult,
ErrorCode, ErrorCode,
PriorityType, type PriorityType,
PermissionType type PermissionType
} from './bridge' } from './bridge'
export class SchemaValidator { export class SchemaValidator {
@ -21,37 +20,48 @@ export class SchemaValidator {
/** /**
* Validate schedule request input * Validate schedule request input
*/ */
validateScheduleRequest(request: any): ValidationResult { validateScheduleRequest(request: unknown): ValidationResult {
const errors: string[] = [] const errors: string[] = []
// Type guard: ensure request is an object
if (!request || typeof request !== 'object') {
return {
isValid: false,
errors: ['Request must be an object'],
message: 'Request must be an object'
}
}
const req = request as Record<string, unknown>
// Validate time format (HH:mm) // Validate time format (HH:mm)
if (!this.isValidTimeFormat(request.time)) { if (!this.isValidTimeFormat(req.time as string)) {
errors.push('Time must be in HH:mm format (24-hour)') errors.push('Time must be in HH:mm format (24-hour)')
} }
// Validate title length (enforce exactly: title ≤ 100 chars) // Validate title length (enforce exactly: title ≤ 100 chars)
if (request.title && request.title.length > 100) { if (req.title && typeof req.title === 'string' && req.title.length > 100) {
errors.push('Title must be 100 characters or less') errors.push('Title must be 100 characters or less')
} }
// Validate body length (enforce exactly: body ≤ 500 chars) // Validate body length (enforce exactly: body ≤ 500 chars)
if (request.body && request.body.length > 500) { if (req.body && typeof req.body === 'string' && req.body.length > 500) {
errors.push('Body must be 500 characters or less') errors.push('Body must be 500 characters or less')
} }
// Validate boolean fields // Validate boolean fields
if (typeof request.sound !== 'boolean') { if (typeof req.sound !== 'boolean') {
errors.push('Sound must be a boolean') errors.push('Sound must be a boolean')
} }
// Validate priority // Validate priority
if (!this.isValidPriority(request.priority)) { if (!this.isValidPriority(req.priority)) {
errors.push('Priority must be low, default, or high') errors.push('Priority must be low, default, or high')
} }
// Reject unknown fields // Reject unknown fields
const allowedFields = ['time', 'title', 'body', 'sound', 'priority'] const allowedFields = ['time', 'title', 'body', 'sound', 'priority']
const unknownFields = Object.keys(request).filter(key => !allowedFields.includes(key)) const unknownFields = Object.keys(req).filter(key => !allowedFields.includes(key))
if (unknownFields.length > 0) { if (unknownFields.length > 0) {
errors.push(`Unknown fields: ${unknownFields.join(', ')}`) errors.push(`Unknown fields: ${unknownFields.join(', ')}`)
} }
@ -66,14 +76,15 @@ export class SchemaValidator {
/** /**
* Validate permission status response * Validate permission status response
*/ */
validatePermissionStatus(status: any): ValidationResult { validatePermissionStatus(status: unknown): ValidationResult {
const errors: string[] = [] const errors: string[] = []
const statusObj = status as Record<string, unknown>
if (!this.isValidPermissionType(status.notifications)) { if (!this.isValidPermissionType(statusObj.notifications)) {
errors.push('Notifications permission must be granted or denied') errors.push('Notifications permission must be granted or denied')
} }
if (typeof status.notificationsEnabled !== 'boolean') { if (typeof statusObj.notificationsEnabled !== 'boolean') {
errors.push('NotificationsEnabled must be a boolean') errors.push('NotificationsEnabled must be a boolean')
} }
@ -87,18 +98,19 @@ export class SchemaValidator {
/** /**
* Validate notification status response * Validate notification status response
*/ */
validateNotificationStatus(status: any): ValidationResult { validateNotificationStatus(status: unknown): ValidationResult {
const errors: string[] = [] const errors: string[] = []
const statusObj = status as Record<string, unknown>
if (typeof status.isEnabled !== 'boolean') { if (typeof statusObj.isEnabled !== 'boolean') {
errors.push('IsEnabled must be a boolean') errors.push('IsEnabled must be a boolean')
} }
if (typeof status.isScheduled !== 'boolean') { if (typeof statusObj.isScheduled !== 'boolean') {
errors.push('IsScheduled must be a boolean') errors.push('IsScheduled must be a boolean')
} }
if (typeof status.pending !== 'boolean') { if (typeof statusObj.pending !== 'boolean') {
errors.push('Pending must be a boolean') errors.push('Pending must be a boolean')
} }
@ -112,14 +124,15 @@ export class SchemaValidator {
/** /**
* Validate exact alarm status response * Validate exact alarm status response
*/ */
validateExactAlarmStatus(status: any): ValidationResult { validateExactAlarmStatus(status: unknown): ValidationResult {
const errors: string[] = [] const errors: string[] = []
const statusObj = status as Record<string, unknown>
if (typeof status.enabled !== 'boolean') { if (typeof statusObj.enabled !== 'boolean') {
errors.push('Enabled must be a boolean') errors.push('Enabled must be a boolean')
} }
if (typeof status.supported !== 'boolean') { if (typeof statusObj.supported !== 'boolean') {
errors.push('Supported must be a boolean') errors.push('Supported must be a boolean')
} }
@ -143,15 +156,15 @@ export class SchemaValidator {
/** /**
* Check if priority is valid * Check if priority is valid
*/ */
private isValidPriority(priority: any): priority is PriorityType { private isValidPriority(priority: unknown): priority is PriorityType {
return ['low', 'default', 'high'].includes(priority) return typeof priority === 'string' && ['low', 'default', 'high'].includes(priority)
} }
/** /**
* Check if permission type is valid * Check if permission type is valid
*/ */
private isValidPermissionType(permission: any): permission is PermissionType { private isValidPermissionType(permission: unknown): permission is PermissionType {
return ['granted', 'denied'].includes(permission) return typeof permission === 'string' && ['granted', 'denied'].includes(permission)
} }
/** /**
@ -171,7 +184,7 @@ export class SchemaValidator {
/** /**
* Create success response * Create success response
*/ */
createSuccessResponse(data?: any) { createSuccessResponse(data?: Record<string, unknown>) {
return { return {
success: true, success: true,
...data ...data
@ -183,18 +196,18 @@ export class SchemaValidator {
export const schemaValidator = new SchemaValidator() export const schemaValidator = new SchemaValidator()
// Utility functions // Utility functions
export function validateScheduleRequest(request: any): ValidationResult { export function validateScheduleRequest(request: unknown): ValidationResult {
return schemaValidator.validateScheduleRequest(request) return schemaValidator.validateScheduleRequest(request)
} }
export function validatePermissionStatus(status: any): ValidationResult { export function validatePermissionStatus(status: unknown): ValidationResult {
return schemaValidator.validatePermissionStatus(status) return schemaValidator.validatePermissionStatus(status)
} }
export function validateNotificationStatus(status: any): ValidationResult { export function validateNotificationStatus(status: unknown): ValidationResult {
return schemaValidator.validateNotificationStatus(status) return schemaValidator.validateNotificationStatus(status)
} }
export function validateExactAlarmStatus(status: any): ValidationResult { export function validateExactAlarmStatus(status: unknown): ValidationResult {
return schemaValidator.validateExactAlarmStatus(status) return schemaValidator.validateExactAlarmStatus(status)
} }

44
test-apps/daily-notification-test/src/lib/typed-plugin.ts

@ -9,23 +9,23 @@
*/ */
import { import {
DailyNotificationBridge, type DailyNotificationBridge,
ScheduleRequest, type ScheduleRequest,
ScheduleResponse, type ScheduleResponse,
PermissionStatus, type PermissionStatus,
NotificationStatus, type NotificationStatus,
ExactAlarmStatus, type ExactAlarmStatus,
PermissionResult, type PermissionResult,
NotificationContent type NotificationContent
} from './bridge' } from './bridge'
import { validateScheduleRequest } from './schema-validation' import { validateScheduleRequest } from './schema-validation'
import { handlePluginError, logError } from './error-handling' import { handlePluginError, logError } from './error-handling'
export class TypedDailyNotificationPlugin implements DailyNotificationBridge { export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
private plugin: any private plugin: unknown
constructor(plugin: any) { constructor(plugin: unknown) {
this.plugin = plugin this.plugin = plugin
} }
@ -48,7 +48,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
} }
// Call native plugin // Call native plugin
const result = await this.plugin.scheduleDailyNotification(request) const result = await (this.plugin as { scheduleDailyNotification: (req: ScheduleRequest) => Promise<ScheduleResponse> }).scheduleDailyNotification(request)
// Validate response // Validate response
if (result && typeof result.success === 'boolean') { if (result && typeof result.success === 'boolean') {
@ -75,7 +75,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async checkPermissions(): Promise<PermissionStatus> { async checkPermissions(): Promise<PermissionStatus> {
try { try {
const result = await this.plugin.checkPermissions() const result = await (this.plugin as { checkPermissions: () => Promise<PermissionStatus> }).checkPermissions()
// Ensure response has required fields // Ensure response has required fields
return { return {
@ -97,7 +97,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async getNotificationStatus(): Promise<NotificationStatus> { async getNotificationStatus(): Promise<NotificationStatus> {
try { try {
const result = await this.plugin.getNotificationStatus() const result = await (this.plugin as { getNotificationStatus: () => Promise<NotificationStatus> }).getNotificationStatus()
// Ensure response has required fields // Ensure response has required fields
return { return {
@ -125,7 +125,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async getExactAlarmStatus(): Promise<ExactAlarmStatus> { async getExactAlarmStatus(): Promise<ExactAlarmStatus> {
try { try {
const result = await this.plugin.getExactAlarmStatus() const result = await (this.plugin as { getExactAlarmStatus: () => Promise<ExactAlarmStatus> }).getExactAlarmStatus()
// Ensure response has required fields // Ensure response has required fields
return { return {
@ -147,11 +147,11 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async requestPermissions(): Promise<PermissionResult> { async requestPermissions(): Promise<PermissionResult> {
try { try {
const result = await this.plugin.requestPermissions() const result = await (this.plugin as { requestPermissions: () => Promise<PermissionStatus> }).requestPermissions()
return { return {
granted: Boolean(result.granted), granted: result.notifications === 'granted',
permissions: await this.checkPermissions() permissions: result
} }
} catch (error) { } catch (error) {
@ -171,7 +171,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async openExactAlarmSettings(): Promise<void> { async openExactAlarmSettings(): Promise<void> {
try { try {
await this.plugin.openExactAlarmSettings() await (this.plugin as { openExactAlarmSettings: () => Promise<void> }).openExactAlarmSettings()
} catch (error) { } catch (error) {
logError(error, 'openExactAlarmSettings') logError(error, 'openExactAlarmSettings')
throw error throw error
@ -183,7 +183,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async openChannelSettings(): Promise<void> { async openChannelSettings(): Promise<void> {
try { try {
await this.plugin.openChannelSettings() await (this.plugin as { openChannelSettings: () => Promise<void> }).openChannelSettings()
} catch (error) { } catch (error) {
logError(error, 'openChannelSettings') logError(error, 'openChannelSettings')
throw error throw error
@ -195,7 +195,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async requestBatteryOptimizationExemption(): Promise<void> { async requestBatteryOptimizationExemption(): Promise<void> {
try { try {
await this.plugin.requestBatteryOptimizationExemption() await (this.plugin as { requestBatteryOptimizationExemption: () => Promise<void> }).requestBatteryOptimizationExemption()
} catch (error) { } catch (error) {
logError(error, 'requestBatteryOptimizationExemption') logError(error, 'requestBatteryOptimizationExemption')
throw error throw error
@ -207,7 +207,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async cancelAllNotifications(): Promise<void> { async cancelAllNotifications(): Promise<void> {
try { try {
await this.plugin.cancelAllNotifications() await (this.plugin as { cancelAllNotifications: () => Promise<void> }).cancelAllNotifications()
} catch (error) { } catch (error) {
logError(error, 'cancelAllNotifications') logError(error, 'cancelAllNotifications')
throw error throw error
@ -219,7 +219,7 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
*/ */
async getLastNotification(): Promise<NotificationContent | null> { async getLastNotification(): Promise<NotificationContent | null> {
try { try {
const result = await this.plugin.getLastNotification() const result = await (this.plugin as { getLastNotification: () => Promise<NotificationContent | null> }).getLastNotification()
return result || null return result || null
} catch (error) { } catch (error) {
logError(error, 'getLastNotification') logError(error, 'getLastNotification')

49
test-apps/daily-notification-test/src/views/HomeView.vue

@ -77,7 +77,17 @@
<!-- System Status --> <!-- System Status -->
<div class="system-status"> <div class="system-status">
<h2 class="section-title">System Status</h2> <h2 class="section-title">System Status</h2>
<StatusCard :status="systemStatus" @refresh="refreshSystemStatus" /> <div class="status-grid">
<StatusCard
v-for="item in systemStatus"
:key="item.label"
:title="item.label"
:status="getStatusType(item.status)"
:value="item.value"
:description="getStatusDescription(item.label)"
@refresh="refreshSystemStatus"
/>
</div>
<!-- Diagnostic Actions --> <!-- Diagnostic Actions -->
<div class="section"> <div class="section">
@ -218,10 +228,10 @@ const checkSystemStatus = async (): Promise<void> => {
console.log('📊 Plugin permissions:', permissions) console.log('📊 Plugin permissions:', permissions)
console.log('📊 Permissions details:') console.log('📊 Permissions details:')
console.log(' - notifications:', permissions.notifications) console.log(' - notifications:', permissions.notifications)
console.log(' - notificationsEnabled:', (permissions as any).notificationsEnabled) console.log(' - notificationsEnabled:', (permissions as unknown as Record<string, unknown>).notificationsEnabled)
console.log(' - exactAlarmEnabled:', (permissions as any).exactAlarmEnabled) console.log(' - exactAlarmEnabled:', (permissions as unknown as Record<string, unknown>).exactAlarmEnabled)
console.log(' - wakeLockEnabled:', (permissions as any).wakeLockEnabled) console.log(' - wakeLockEnabled:', (permissions as unknown as Record<string, unknown>).wakeLockEnabled)
console.log(' - allPermissionsGranted:', (permissions as any).allPermissionsGranted) console.log(' - allPermissionsGranted:', (permissions as unknown as Record<string, unknown>).allPermissionsGranted)
console.log('📊 Exact alarm status:', exactAlarmStatus) console.log('📊 Exact alarm status:', exactAlarmStatus)
// Map plugin response to app store format // Map plugin response to app store format
@ -302,6 +312,35 @@ const checkSystemStatus = async (): Promise<void> => {
} }
} }
const getStatusType = (status: string): 'success' | 'warning' | 'error' | 'info' => {
switch (status) {
case 'success':
case 'warning':
case 'error':
case 'info':
return status
default:
return 'info'
}
}
const getStatusDescription = (label: string): string => {
switch (label) {
case 'Platform':
return 'Current platform information'
case 'Plugin':
return 'DailyNotification plugin availability'
case 'Permissions':
return 'Notification permission status'
case 'Can Schedule':
return 'Ready to schedule notifications'
case 'Next Scheduled':
return 'Next scheduled notification time'
default:
return 'System status information'
}
}
const refreshSystemStatus = async (): Promise<void> => { const refreshSystemStatus = async (): Promise<void> => {
console.log('🔄 CLICK: Refresh System Status') console.log('🔄 CLICK: Refresh System Status')
await checkSystemStatus() await checkSystemStatus()

2
test-apps/daily-notification-test/src/views/StatusView.vue

@ -88,7 +88,7 @@
<script lang="ts"> <script lang="ts">
import { Vue, Component, toNative } from 'vue-facing-decorator' import { Vue, Component, toNative } from 'vue-facing-decorator'
import StatusCard from '../components/cards/StatusCard.vue' import StatusCard from '../components/cards/StatusCard.vue'
import { createTypedPlugin, type PermissionStatus, type NotificationStatus, type ExactAlarmStatus } from '../lib/typed-plugin' import { createTypedPlugin } from '../lib/typed-plugin'
import { collectDiagnostics, copyDiagnosticsToClipboard, type ComprehensiveDiagnostics } from '../lib/diagnostics-export' import { collectDiagnostics, copyDiagnosticsToClipboard, type ComprehensiveDiagnostics } from '../lib/diagnostics-export'
interface StatusItem { interface StatusItem {

Loading…
Cancel
Save