28 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	Component Migration Template
Overview
This template provides step-by-step instructions for migrating Vue components from legacy patterns to PlatformServiceMixin.
Before Migration Checklist
- Component uses import * as databaseUtil
- Component uses import { logConsoleAndDb }
- Component has direct PlatformServiceFactory.getInstance()calls
- Component has manual error handling for database operations
- Component has verbose SQL result processing
Step-by-Step Migration
Step 1: Update Imports
// ❌ BEFORE - Legacy imports
import * as databaseUtil from "../db/databaseUtil";
import { logConsoleAndDb } from "../db/databaseUtil";
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
// ✅ AFTER - Clean imports
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { Contact } from "@/db/tables/contacts";
import { Settings } from "@/db/tables/settings";
Step 2: Add Mixin to Component
// ❌ BEFORE - No mixin
@Component({
  components: { /* ... */ }
})
export default class MyComponent extends Vue {
  // ...
}
// ✅ AFTER - With mixin
@Component({
  components: { /* ... */ }
})
export default class MyComponent extends Vue {
  mixins: [PlatformServiceMixin],
  // ...
}
Step 3: Replace Database Operations
// ❌ BEFORE - Legacy database access
async loadContacts() {
  try {
    const platformService = PlatformServiceFactory.getInstance();
    const result = await platformService.dbQuery("SELECT * FROM contacts");
    const contacts = databaseUtil.mapQueryResultToValues(result);
    await logConsoleAndDb("Contacts loaded successfully");
    return contacts;
  } catch (error) {
    await logConsoleAndDb("Error loading contacts: " + error, true);
    throw error;
  }
}
// ✅ AFTER - Mixin methods
async loadContacts() {
  try {
    const contacts = await this.$getAllContacts();
    await this.$log("Contacts loaded successfully");
    return contacts;
  } catch (error) {
    await this.$logError(`[${this.$options.name}] Error loading contacts: ${error}`);
    throw error;
  }
}
Step 4: Replace Settings Operations
// ❌ BEFORE - Legacy settings access
async loadSettings() {
  const settingsRow = await databaseUtil.retrieveSettingsForActiveAccount();
  const settings = settingsRow || {};
  return settings;
}
async saveSettings(changes: Partial<Settings>) {
  await databaseUtil.updateDefaultSettings(changes);
  await logConsoleAndDb("Settings saved");
}
// ✅ AFTER - Mixin methods
async loadSettings() {
  return await this.$settings();
}
async saveSettings(changes: Partial<Settings>) {
  await this.$saveSettings(changes);
  await this.$log("Settings saved");
}
Step 5: Replace Logging Operations
// ❌ BEFORE - Legacy logging
try {
  // operation
} catch (error) {
  console.error("Error occurred:", error);
  await logConsoleAndDb("Error: " + error, true);
}
// ✅ AFTER - Mixin logging
try {
  // operation
} catch (error) {
  await this.$logError(`[${this.$options.name}] Error: ${error}`);
}
Common Migration Patterns
Pattern 1: Database Query + Result Processing
// ❌ BEFORE
const platformService = PlatformServiceFactory.getInstance();
const result = await platformService.dbQuery(sql, params);
const processed = databaseUtil.mapQueryResultToValues(result);
// ✅ AFTER
const processed = await this.$query(sql, params);
Pattern 2: Settings Retrieval
// ❌ BEFORE
const settingsRow = await databaseUtil.retrieveSettingsForActiveAccount();
const value = settingsRow?.[field] || defaultValue;
// ✅ AFTER
const settings = await this.$settings();
const value = settings[field] || defaultValue;
Pattern 3: Contact Operations
// ❌ BEFORE
const platformService = PlatformServiceFactory.getInstance();
const contacts = await platformService.dbQuery("SELECT * FROM contacts");
const mappedContacts = databaseUtil.mapQueryResultToValues(contacts);
// ✅ AFTER
const contacts = await this.$getAllContacts();
Pattern 4: Error Handling
// ❌ BEFORE
try {
  // operation
} catch (error) {
  console.error("[MyComponent] Error:", error);
  await databaseUtil.logToDb("Error: " + error, "error");
}
// ✅ AFTER
try {
  // operation
} catch (error) {
  await this.$logError(`[${this.$options.name}] Error: ${error}`);
}
Notification Migration (Additional Step)
If component uses this.$notify() calls, also migrate to notification helpers:
Import and Setup
// Add imports
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import {
  NOTIFY_CONTACT_LOADING_ISSUE,
  NOTIFY_FEED_LOADING_ISSUE,
  // Add other constants as needed
} from "@/constants/notifications";
// Add property
notify!: ReturnType<typeof createNotifyHelpers>;
// Initialize in created()
created() {
  this.notify = createNotifyHelpers(this.$notify);
}
Replace Notification Calls
// ❌ BEFORE
this.$notify({
  group: "alert",
  type: "warning",
  title: "Warning", 
  text: "Something went wrong"
}, 5000);
// ✅ AFTER - Use constants for reusable messages
this.notify.warning(NOTIFY_CONTACT_LOADING_ISSUE.message, TIMEOUTS.LONG);
// ✅ AFTER - Literal strings for dynamic content
this.notify.error(userMessage || "Fallback error message", TIMEOUTS.LONG);
Common Notification Patterns
- Warning: this.notify.warning(NOTIFY_CONSTANT.message, TIMEOUTS.LONG)
- Error: this.notify.error(NOTIFY_CONSTANT.message, TIMEOUTS.LONG)
- Success: this.notify.success(NOTIFY_CONSTANT.message, TIMEOUTS.STANDARD)
- Toast: this.notify.toast(title, message, TIMEOUTS.SHORT)
- Confirm: this.notify.confirm(message, onYes)
- Standard patterns: this.notify.confirmationSubmitted(),this.notify.sent(), etc.
Notification Constants Guidelines
- Use constants for static, reusable messages (defined in src/constants/notifications.ts)
- Use literal strings for dynamic messages with variables
- Add new constants to notifications.tsfor new reusable messages
Extract Literals from Complex Modals
IMPORTANT: Even when complex modals must remain as raw $notify calls due to advanced features (custom buttons, nested callbacks, promptToStopAsking, etc.), always extract literal strings to constants:
// ❌ BAD - Literals in complex modal
this.$notify({
  group: "modal",
  type: "confirm", 
  title: "Are you nearby with cameras?",
  text: "If so, we'll use those with QR codes to share.",
  yesText: "we are nearby with cameras",
  noText: "we will share another way",
  onNo: () => { /* complex callback */ }
});
// ✅ GOOD - Constants used even in complex modal
export const NOTIFY_CAMERA_SHARE_METHOD = {
  title: "Are you nearby with cameras?",
  text: "If so, we'll use those with QR codes to share.", 
  yesText: "we are nearby with cameras",
  noText: "we will share another way",
};
this.$notify({
  group: "modal",
  type: "confirm",
  title: NOTIFY_CAMERA_SHARE_METHOD.title,
  text: NOTIFY_CAMERA_SHARE_METHOD.text,
  yesText: NOTIFY_CAMERA_SHARE_METHOD.yesText,
  noText: NOTIFY_CAMERA_SHARE_METHOD.noText,
  onNo: () => { /* complex callback preserved */ }
});
This approach provides:
- Consistency: All user-facing text centralized
- Maintainability: Easy to update messages
- Localization: Ready for future i18n support
- Testability: Constants can be imported in tests
Critical Migration Omissions to Avoid
1. Remove Unused Notification Imports
❌ COMMON MISTAKE: Importing notification constants that aren't actually used
// ❌ BAD - Unused imports
import {
  NOTIFY_CONTACT_ADDED,           // Not used
  NOTIFY_CONTACT_ADDED_SUCCESS,   // Not used  
  NOTIFY_CONTACT_ERROR,           // Actually used
  NOTIFY_CONTACT_EXISTS,          // Actually used
} from "@/constants/notifications";
// ✅ GOOD - Only import what's used
import {
  NOTIFY_CONTACT_ERROR,
  NOTIFY_CONTACT_EXISTS,
} from "@/constants/notifications";
How to check: Use IDE "Find Usages" or grep to verify each imported constant is actually used in the file.
2. Replace ALL Hardcoded Timeout Values
❌ COMMON MISTAKE: Converting $notify() calls but leaving hardcoded timeout values
// ❌ BAD - Hardcoded timeout values
this.notify.error(NOTIFY_CONTACT_ERROR.message, 5000);
this.notify.success(NOTIFY_CONTACT_ADDED.message, 3000);
this.notify.warning(NOTIFY_CONTACT_EXISTS.message, 5000);
this.notify.toast(NOTIFY_URL_COPIED.message, 2000);
// ✅ GOOD - Use timeout constants
this.notify.error(NOTIFY_CONTACT_ERROR.message, QR_TIMEOUT_LONG);
this.notify.success(NOTIFY_CONTACT_ADDED.message, QR_TIMEOUT_STANDARD);
this.notify.warning(NOTIFY_CONTACT_EXISTS.message, QR_TIMEOUT_LONG);
this.notify.toast(NOTIFY_URL_COPIED.message, QR_TIMEOUT_MEDIUM);
Add timeout constants to your constants file:
// Add to src/constants/notifications.ts
export const QR_TIMEOUT_SHORT = 1000;      // Short operations
export const QR_TIMEOUT_MEDIUM = 2000;     // Medium operations  
export const QR_TIMEOUT_STANDARD = 3000;   // Standard success messages
export const QR_TIMEOUT_LONG = 5000;       // Error messages and warnings
3. Remove Legacy Wrapper Functions
❌ COMMON MISTAKE: Keeping legacy notification wrapper functions that are inconsistent with the new system
// ❌ BAD - Legacy wrapper function
danger(message: string, title: string = "Error", timeout = 5000) {
  this.notify.error(message, timeout);
}
// Usage (inconsistent with rest of system)
this.danger(result.error as string, "Error Setting Visibility");
// ✅ GOOD - Direct usage of notification system
this.notify.error(result.error as string, QR_TIMEOUT_LONG);
Why remove legacy wrappers:
- Creates inconsistency in the codebase
- Adds unnecessary abstraction layer
- Often have unused parameters (like titleabove)
- Bypasses the centralized notification system benefits
4. Extract Long Class Attributes to Computed Properties
❌ COMMON MISTAKE: Leaving long class strings in template instead of extracting to computed properties
// ❌ BAD - Long class strings in template
<template>
  <div class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 my-4">
    <button class="inline-block text-md uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md">
      Set Name
    </button>
  </div>
</template>
// ✅ GOOD - Extract to computed properties
<template>
  <div :class="nameWarningClasses">
    <button :class="setNameButtonClasses">
      Set Name
    </button>
  </div>
</template>
// Class methods
get nameWarningClasses(): string {
  return "bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 my-4";
}
get setNameButtonClasses(): string {
  return "inline-block text-md uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md";
}
Benefits of extracting long classes:
- Improves template readability
- Enables reusability of styles
- Makes testing easier
- Allows for dynamic class computation
5. Ensure ALL Literal Strings Use Constants
❌ COMMON MISTAKE: Converting $notify() calls to helpers but not replacing literal strings with constants
// ❌ BAD - Literal strings in notification calls
this.notify.error("This QR code does not contain valid contact information.");
this.notify.warning("The contact DID is missing.");
this.notify.success("Registration submitted...");
// ✅ GOOD - Use constants for all static messages
this.notify.error(NOTIFY_QR_INVALID_QR_CODE.message);
this.notify.warning(NOTIFY_QR_MISSING_DID.message);
this.notify.success(NOTIFY_QR_REGISTRATION_SUBMITTED.message);
Add constants for ALL static messages:
// Add to src/constants/notifications.ts
export const NOTIFY_QR_INVALID_QR_CODE = {
  message: "This QR code does not contain valid contact information.",
};
export const NOTIFY_QR_MISSING_DID = {
  message: "The contact DID is missing.",
};
export const NOTIFY_QR_REGISTRATION_SUBMITTED = {
  message: "Registration submitted...",
};
6. Validation Checklist for Omissions
Before marking migration complete, verify these items:
# Check for unused imports
grep -n "import.*NOTIFY_" src/views/YourComponent.vue
# Then verify each imported constant is actually used in the file
# Check for hardcoded timeouts
grep -n "notify\.[a-z]*(" src/views/YourComponent.vue | grep -E "[0-9]{3,4}"
# Check for legacy wrapper functions
grep -n "danger\|success\|warning\|info.*(" src/views/YourComponent.vue | grep -v "notify\."
# Check for long class attributes (>50 chars)
grep -n "class=\"[^\"]\{50,\}" src/views/YourComponent.vue
# Check for literal strings in notifications
grep -n "notify\.[a-z]*(" src/views/YourComponent.vue | grep -v "NOTIFY_\|message"
7. Post-Migration Cleanup Commands
Run these commands after migration to catch omissions:
# Check TypeScript compilation
npm run lint-fix
# Run validation scripts
scripts/validate-migration.sh
scripts/validate-notification-completeness.sh
# Check for any remaining databaseUtil references
grep -r "databaseUtil" src/views/YourComponent.vue
# Check for any remaining $notify calls
grep -r "\$notify(" src/views/YourComponent.vue
Template Logic Streamlining
Move Complex Template Logic to Class
When migrating components, look for opportunities to simplify template expressions by moving logic into computed properties or methods:
Pattern 1: Repeated Function Calls
// ❌ BEFORE - Template with repeated function calls
<template>
  <div>{{ formatName(user.firstName, user.lastName, user.title) }}</div>
  <div>{{ formatName(contact.firstName, contact.lastName, contact.title) }}</div>
</template>
// ✅ AFTER - Computed properties for repeated logic
<template>
  <div>{{ userDisplayName }}</div>
  <div>{{ contactDisplayName }}</div>
</template>
// Class methods
get userDisplayName() {
  return this.formatName(this.user?.firstName, this.user?.lastName, this.user?.title);
}
get contactDisplayName() {
  return this.formatName(this.contact?.firstName, this.contact?.lastName, this.contact?.title);
}
Pattern 2: Complex Conditional Logic
// ❌ BEFORE - Complex template conditions
<template>
  <div v-if="profile?.locLat && profile?.locLon && profile?.showLocation">
    <l-map :center="[profile.locLat, profile.locLon]" :zoom="12">
      <!-- map content -->
    </l-map>
  </div>
</template>
// ✅ AFTER - Computed properties for clarity
<template>
  <div v-if="shouldShowMap">
    <l-map :center="mapCenter" :zoom="mapZoom">
      <!-- map content -->
    </l-map>
  </div>
</template>
// Class methods
get shouldShowMap() {
  return this.profile?.locLat && this.profile?.locLon && this.profile?.showLocation;
}
get mapCenter() {
  return [this.profile?.locLat, this.profile?.locLon];
}
get mapZoom() {
  return 12;
}
Pattern 3: Repeated Configuration Objects
// ❌ BEFORE - Repeated inline objects
<template>
  <l-tile-layer
    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    layer-type="base"
    name="OpenStreetMap"
  />
  <l-tile-layer
    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    layer-type="base"
    name="OpenStreetMap"
  />
</template>
// ✅ AFTER - Computed property for configuration
<template>
  <l-tile-layer
    :url="tileLayerUrl"
    layer-type="base"
    name="OpenStreetMap"
  />
  <l-tile-layer
    :url="tileLayerUrl"
    layer-type="base"
    name="OpenStreetMap"
  />
</template>
// Class methods
get tileLayerUrl() {
  return "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
}
Pattern 4: Array/Object Construction in Template
// ❌ BEFORE - Complex array construction in template
<template>
  <component :coords="[item.lat || 0, item.lng || 0]" />
</template>
// ✅ AFTER - Computed property for complex data
<template>
  <component :coords="itemCoordinates" />
</template>
// Class methods
get itemCoordinates() {
  return [this.item?.lat || 0, this.item?.lng || 0];
}
Benefits of Logic Streamlining
- Improved Readability: Template becomes cleaner and easier to understand
- Better Performance: Vue caches computed properties, avoiding recalculation
- Easier Testing: Logic can be unit tested independently
- Reduced Duplication: Common expressions defined once
- Type Safety: TypeScript can better validate computed property return types
Guidelines for Logic Streamlining
- Move to computed properties: Expressions used multiple times or complex calculations
- Keep in template: Simple property access (user.name) or single-use expressions
- Document computed properties: Add JSDoc comments explaining purpose and return types
- Use descriptive names: userDisplayNameinstead ofgetName()
Component Extraction Patterns
When to Extract Components
Extract components when you identify:
- Repeated UI patterns used in multiple places
- Complex template sections that could be simplified
- Form elements with similar structure and behavior
- Layout patterns that appear consistently
- Validation patterns with repeated logic
Component Extraction Examples
Form Input Extraction
// Before: Repeated form input pattern
<template>
  <div class="form-group">
    <label class="form-label">Name</label>
    <input class="form-input" v-model="name" />
    <div class="error-message" v-if="nameError">{{ nameError }}</div>
  </div>
  <div class="form-group">
    <label class="form-label">Email</label>
    <input class="form-input" v-model="email" />
    <div class="error-message" v-if="emailError">{{ emailError }}</div>
  </div>
</template>
// After: Extracted FormInput component
<template>
  <FormInput 
    label="Name" 
    v-model="name" 
    :error="nameError" 
  />
  <FormInput 
    label="Email" 
    v-model="email" 
    :error="emailError" 
  />
</template>
// New FormInput.vue component
<template>
  <div class="form-group">
    <label class="form-label">{{ label }}</label>
    <input 
      class="form-input" 
      :value="value" 
      @input="$emit('input', $event.target.value)" 
    />
    <div class="error-message" v-if="error">{{ error }}</div>
  </div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-facing-decorator";
/**
 * Reusable form input component with label and error handling
 */
@Component
export default class FormInput extends Vue {
  @Prop({ required: true }) label!: string;
  @Prop({ required: true }) value!: string;
  @Prop() error?: string;
}
</script>
Button Group Extraction
// Before: Repeated button patterns
<template>
  <div class="button-group">
    <button class="btn btn-primary" @click="save">Save</button>
    <button class="btn btn-secondary" @click="cancel">Cancel</button>
  </div>
</template>
// After: Extracted ButtonGroup component
<template>
  <ButtonGroup 
    :primary-action="{ text: 'Save', handler: save }"
    :secondary-action="{ text: 'Cancel', handler: cancel }"
  />
</template>
// New ButtonGroup.vue component
<template>
  <div class="button-group">
    <button 
      class="btn btn-primary" 
      @click="primaryAction.handler"
    >
      {{ primaryAction.text }}
    </button>
    <button 
      class="btn btn-secondary" 
      @click="secondaryAction.handler"
    >
      {{ secondaryAction.text }}
    </button>
  </div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-facing-decorator";
interface ButtonAction {
  text: string;
  handler: () => void;
}
/**
 * Reusable button group component for common action patterns
 */
@Component
export default class ButtonGroup extends Vue {
  @Prop({ required: true }) primaryAction!: ButtonAction;
  @Prop({ required: true }) secondaryAction!: ButtonAction;
}
</script>
Component Quality Standards
Single Responsibility
- Each extracted component should have one clear purpose
- Component name should clearly indicate its function
- Props should be focused and relevant to the component's purpose
Reusability
- Component should work in multiple contexts
- Props should be flexible enough for different use cases
- Events should provide appropriate communication with parent
Type Safety
- All props should have proper TypeScript interfaces
- Event emissions should be properly typed
- Component should compile without type errors
Documentation
- JSDoc comments explaining component purpose
- Usage examples in comments
- Clear prop descriptions and types
Validation Checklist
After component extraction:
- No template duplication: Extracted patterns don't appear elsewhere
- Proper component registration: All components properly imported and registered
- Event handling works: Parent components receive and handle events correctly
- Props validation: All required props are provided with correct types
- Styling consistency: Extracted components maintain visual consistency
- Functionality preserved: All original functionality works with extracted components
After Migration Checklist
⚠️ CRITICAL: Use docs/migration-templates/COMPLETE_MIGRATION_CHECKLIST.md for comprehensive validation
Phase 1: Database Migration
- All databaseUtilimports removed
- All logConsoleAndDbimports removed
- All direct PlatformServiceFactory.getInstance()calls removed
- Component includes PlatformServiceMixinin mixins array
- Database operations use mixin methods ($db,$query,$getAllContacts, etc.)
- Settings operations use mixin methods ($settings,$saveSettings)
- Logging uses mixin methods ($log,$logError,$logAndConsole)
Phase 2: SQL Abstraction (if applicable)
- All raw SQL queries replaced with service methods
- Contact operations use $getContact(),$deleteContact(),$updateContact()
- Settings operations use $accountSettings(),$saveSettings()
- NO raw SQL queries remain (SELECT,INSERT,UPDATE,DELETE)
Phase 3: Notification Migration (if applicable)
- createNotifyHelpersimported and initialized
- notify!property declared and created in- created()
- All this.$notify()calls replaced with helper methods
- Hardcoded timeouts replaced with TIMEOUTSconstants
- Static messages use notification constants from @/constants/notifications
- Dynamic messages use literal strings appropriately
- Unused notification constants removed from imports but these can mean that notifications have been overlooked
- Legacy wrapper functions removed (e.g., danger(),success(), etc.)
Phase 4: Template Streamlining (if applicable)
- All long class attributes (50+ characters) extracted to computed properties
- Complex conditional logic moved to computed properties
- Repeated expressions extracted to computed properties
- Configuration objects moved to computed properties
- All computed properties have JSDoc documentation
Phase 5: Component Extraction (if applicable)
- Reusable UI patterns identified and extracted to separate components
- Form elements extracted to reusable components
- Layout patterns extracted to reusable components
- Validation patterns extracted to reusable components
- All extracted components have clear props interfaces
- All extracted components have proper event handling
- All extracted components have JSDoc documentation
- Parent components properly import and use extracted components
Final Validation
- Error handling includes component name context
- Component compiles without TypeScript errors
- Component functionality works as expected
- scripts/validate-migration.shshows "Technically Compliant"
- scripts/validate-notification-completeness.shshows as complete
Validation Commands
# Check overall migration status
scripts/validate-migration.sh
# Check notification migration completeness  
scripts/validate-notification-completeness.sh
# Check for compilation errors
npm run lint-fix
Testing Migration
- Compile Check: npm run buildshould complete without errors
- Runtime Check: Component should load and function normally
- Logging Check: Verify logs appear in console and database
- Error Handling Check: Verify errors are properly logged and handled
Troubleshooting
Common Issues
- Missing Mixin Methods: Ensure component properly extends PlatformServiceMixin
- TypeScript Errors: Check that all types are properly imported
- Runtime Errors: Verify all async operations are properly awaited
- Missing Context: Add component name to error messages for better debugging
Performance Considerations
- Mixin methods include caching for frequently accessed data
- Database operations are queued and optimized
- Error logging includes proper context and formatting
Phase 4: Testing and Validation
4.1 Multi-Platform Testing Requirements
ALL MIGRATIONS MUST BE TESTED ON ALL SUPPORTED PLATFORMS:
Web Platform Testing (Required)
- Test in Chrome/Chromium (primary browser)
- Test in Firefox (secondary browser)
- Test in Safari (if applicable)
- Verify PWA functionality works correctly
- Test responsive design on different screen sizes
Desktop Platform Testing (Required)
- Test Electron app functionality
- Verify desktop-specific features work
- Test file system access (if applicable)
- Verify native desktop integrations
Mobile Platform Testing (Required)
- Test iOS app via Capacitor
- Test Android app via Capacitor
- Verify mobile-specific features (camera, contacts, etc.)
- Test deep linking functionality
- Verify push notifications work
4.2 Functional Testing Per Platform
For each platform, test these core scenarios:
Database Operations
- Create/Read/Update/Delete operations work
- Data persistence across app restarts
- Database migration handling (if applicable)
Logging and Error Handling
- Errors are logged correctly to console
- Errors are stored in database logs
- Error messages display appropriately to users
- Network errors are handled gracefully
User Interface
- All buttons and interactions work
- Loading states display correctly
- Error states display appropriately
- Responsive design works on platform
4.3 Platform-Specific Testing Notes
Web Platform
- Test offline/online scenarios
- Verify IndexedDB storage works
- Test service worker functionality
- Check browser developer tools for errors
Desktop Platform
- Test native menu integrations
- Verify file system permissions
- Test auto-updater functionality
- Check Electron developer tools
Mobile Platform
- Test device permissions (camera, storage, etc.)
- Verify app store compliance
- Test background/foreground transitions
- Check native debugging tools
4.4 Sign-Off Requirements
MIGRATION IS NOT COMPLETE UNTIL ALL PLATFORMS ARE TESTED AND SIGNED OFF:
## Testing Sign-Off Checklist
### Web Platform ✅/❌
- [ ] Chrome: Tested by [Name] on [Date]
- [ ] Firefox: Tested by [Name] on [Date]  
- [ ] Safari: Tested by [Name] on [Date]
- [ ] Notes: [Any platform-specific issues or observations]
### Desktop Platform ✅/❌
- [ ] Windows: Tested by [Name] on [Date]
- [ ] macOS: Tested by [Name] on [Date]
- [ ] Linux: Tested by [Name] on [Date]
- [ ] Notes: [Any platform-specific issues or observations]
### Mobile Platform ✅/❌
- [ ] iOS: Tested by [Name] on [Date]
- [ ] Android: Tested by [Name] on [Date]
- [ ] Notes: [Any platform-specific issues or observations]
### Final Sign-Off
- [ ] All platforms tested and working
- [ ] No regressions identified
- [ ] Performance is acceptable
- [ ] Migration completed by: [Name] on [Date]