forked from jsnbuchanan/crowd-funder-for-time-pwa
Mark UserProfileView.vue as human tested, update migration tracker
- Human testing confirmed UserProfileView.vue works correctly - Updated testing tracker with 21 complete migrations (88% success) - Added ImportAccountView.vue to ready-for-testing list - Migration progress: 4 components human tested, 17 ready for testing
This commit is contained in:
@@ -233,6 +233,139 @@ this.notify.error(userMessage || "Fallback error message", TIMEOUTS.LONG);
|
||||
- **Use literal strings** for dynamic messages with variables
|
||||
- **Add new constants** to `notifications.ts` for new reusable messages
|
||||
|
||||
## 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
|
||||
|
||||
Reference in New Issue
Block a user