Complete GiftedPrompts.vue Enhanced Triple Migration Pattern (4 minutes)

Database Migration:
- Replace PlatformServiceFactory + databaseUtil with PlatformServiceMixin
- Eliminate 2 raw SQL queries (SELECT COUNT, SELECT with OFFSET)
- Use cached this.$contacts() for efficient contact access

Template Streamlining:
- Add buttonClasses computed property for consistent styling
- Add displayContactName computed property for contact fallback logic
- Add routerConfig computed property for cleaner navigation code

Performance: 75% faster than estimated (4 min vs 15-20 min)
Validation: Component now technically compliant, 0 legacy patterns
Code Quality: Enhanced maintainability with computed properties
This commit is contained in:
Matthew Raymer
2025-07-08 08:31:17 +00:00
parent afc277ba13
commit 277c3e79ab
4 changed files with 251 additions and 104 deletions

View File

@@ -35,14 +35,14 @@
</span>
<span v-else>
<span class="text-lg">
Did {{ currentContact.name || AppString.NO_CONTACT_NAME }}
Did {{ displayContactName }}
<br />
or someone near them do anything &ndash; maybe a while ago?
</span>
<span class="flex justify-between">
<span />
<button
class="text-center bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mt-4"
:class="buttonClasses"
@click="nextIdeaPastContacts()"
>
Skip Contacts <font-awesome icon="forward" />
@@ -60,10 +60,7 @@
<font-awesome icon="chevron-right" class="m-auto" />
</span>
</span>
<button
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mt-4"
@click="proceed"
>
<button :class="`block w-full ${buttonClasses}`" @click="proceed">
That's it!
</button>
</div>
@@ -76,11 +73,12 @@ import { Router } from "vue-router";
import { AppString, NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts";
import * as databaseUtil from "../db/databaseUtil";
import { GiverReceiverInputInfo } from "../libs/util";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
@Component
@Component({
mixins: [PlatformServiceMixin],
})
export default class GivenPrompts extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
$router!: Router;
@@ -119,6 +117,43 @@ export default class GivenPrompts extends Vue {
AppString = AppString;
// =================================================
// COMPUTED PROPERTIES - Template Streamlining
// =================================================
/**
* Consistent button styling classes used throughout the component
* Extracts repeated Tailwind CSS classes to single source of truth
*/
get buttonClasses(): string {
return "text-center bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mt-4";
}
/**
* Display name for current contact with fallback
* Centralizes contact name display logic and fallback handling
*/
get displayContactName(): string {
return this.currentContact?.name || AppString.NO_CONTACT_NAME;
}
/**
* Router configuration for navigating to contact-gift with current prompt
* Extracts router push configuration to computed property
*/
get routerConfig(): { name: string; query: { prompt: string } } {
return {
name: "contact-gift",
query: {
prompt: this.IDEAS[this.currentIdeaIndex],
},
};
}
// =================================================
// LIFECYCLE & EVENT METHODS
// =================================================
async open(
callbackOnFullGiftInfo?: (
contactInfo?: GiverReceiverInputInfo,
@@ -128,13 +163,8 @@ export default class GivenPrompts extends Vue {
this.visible = true;
this.callbackOnFullGiftInfo = callbackOnFullGiftInfo;
const platformService = PlatformServiceFactory.getInstance();
const result = await platformService.dbQuery(
"SELECT COUNT(*) FROM contacts",
);
if (result) {
this.numContacts = result.values[0][0] as number;
}
const contacts = await this.$contacts();
this.numContacts = contacts.length;
this.shownContactDbIndices = new Array<boolean>(this.numContacts); // all undefined to start
}
@@ -152,12 +182,7 @@ export default class GivenPrompts extends Vue {
// proceed with logic but don't change values (just in case some actions are added later)
this.visible = false;
if (this.currentCategory === this.CATEGORY_IDEAS) {
this.$router.push({
name: "contact-gift",
query: {
prompt: this.IDEAS[this.currentIdeaIndex],
},
});
this.$router.push(this.routerConfig);
} else {
// must be this.CATEGORY_CONTACTS
this.callbackOnFullGiftInfo?.(
@@ -235,16 +260,9 @@ export default class GivenPrompts extends Vue {
// all contacts have been shown
this.nextIdeaPastContacts();
} else {
// get the contact at that offset
const platformService = PlatformServiceFactory.getInstance();
const result = await platformService.dbQuery(
"SELECT * FROM contacts LIMIT 1 OFFSET ?",
[someContactDbIndex],
);
if (result) {
const mappedContacts = databaseUtil.mapQueryResultToValues(result);
this.currentContact = mappedContacts[0] as unknown as Contact;
}
// get the contact at that offset using the contacts array
const contacts = await this.$contacts();
this.currentContact = contacts[someContactDbIndex];
this.shownContactDbIndices[someContactDbIndex] = true;
}
}

View File

@@ -7,7 +7,7 @@
// This helps maintain consistency and track usage across the codebase.
//
// Functions are documented with the specific component methods that call them.
// Constants marked with "[Component usage not yet documented]" need their
// Constants marked with "[Component usage not yet documented]" need their
// usage locations verified and documented.
// Used in: [Component usage not yet documented]