forked from jsnbuchanan/crowd-funder-for-time-pwa
Add comprehensive guidance to prevent common migration oversights: - Remove unused notification imports - Replace hardcoded timeout values with constants - Remove legacy wrapper functions - Extract long class attributes to computed properties - Replace literal strings with constants Based on lessons learned from ContactQRScanShowView.vue migration. Includes validation commands and specific examples for each pattern.
761 lines
23 KiB
Markdown
761 lines
23 KiB
Markdown
# 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
// ❌ 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.ts` for 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**:
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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**:
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// ❌ 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 `title` above)
|
|
- 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
|
|
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
```typescript
|
|
// ❌ 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**:
|
|
```typescript
|
|
// 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**:
|
|
|
|
```bash
|
|
# 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**:
|
|
|
|
```bash
|
|
# 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
|
|
```typescript
|
|
// ❌ 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
|
|
```typescript
|
|
// ❌ 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
|
|
```typescript
|
|
// ❌ 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
|
|
```typescript
|
|
// ❌ 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
|
|
|
|
1. **Improved Readability**: Template becomes cleaner and easier to understand
|
|
2. **Better Performance**: Vue caches computed properties, avoiding recalculation
|
|
3. **Easier Testing**: Logic can be unit tested independently
|
|
4. **Reduced Duplication**: Common expressions defined once
|
|
5. **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**: `userDisplayName` instead of `getName()`
|
|
|
|
## After Migration Checklist
|
|
|
|
⚠️ **CRITICAL**: Use `docs/migration-templates/COMPLETE_MIGRATION_CHECKLIST.md` for comprehensive validation
|
|
|
|
### Phase 1: Database Migration
|
|
- [ ] All `databaseUtil` imports removed
|
|
- [ ] All `logConsoleAndDb` imports removed
|
|
- [ ] All direct `PlatformServiceFactory.getInstance()` calls removed
|
|
- [ ] Component includes `PlatformServiceMixin` in 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)
|
|
- [ ] `createNotifyHelpers` imported and initialized
|
|
- [ ] `notify!` property declared and created in `created()`
|
|
- [ ] **All `this.$notify()` calls replaced with helper methods**
|
|
- [ ] **Hardcoded timeouts replaced with `TIMEOUTS` constants**
|
|
- [ ] **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.)**
|
|
|
|
### Final Validation
|
|
- [ ] Error handling includes component name context
|
|
- [ ] Component compiles without TypeScript errors
|
|
- [ ] Component functionality works as expected
|
|
- [ ] `scripts/validate-migration.sh` shows "Technically Compliant"
|
|
- [ ] `scripts/validate-notification-completeness.sh` shows as complete
|
|
|
|
### Validation Commands
|
|
```bash
|
|
# 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
|
|
|
|
1. **Compile Check**: `npm run build` should complete without errors
|
|
2. **Runtime Check**: Component should load and function normally
|
|
3. **Logging Check**: Verify logs appear in console and database
|
|
4. **Error Handling Check**: Verify errors are properly logged and handled
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Missing Mixin Methods**: Ensure component properly extends PlatformServiceMixin
|
|
2. **TypeScript Errors**: Check that all types are properly imported
|
|
3. **Runtime Errors**: Verify all async operations are properly awaited
|
|
4. **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:**
|
|
|
|
```markdown
|
|
## 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]
|
|
``` |