feat: migrate QuickActionBvcBeginView to Enhanced Triple Migration Pattern

- Database: Replace databaseUtil with PlatformServiceMixin
- Notifications: Add BVC constants and helper system
- Template: Extract computed properties and goBack method
- Enhanced logging and comprehensive documentation
- All BVC meeting functionality preserved
This commit is contained in:
Matthew Raymer
2025-07-09 03:18:50 +00:00
parent b4c7a01463
commit 4f92656b7f
5 changed files with 356 additions and 256 deletions

View File

@@ -8,7 +8,7 @@
<div class="text-lg text-center font-light relative px-7">
<h1
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
@click="$router.back()"
@click="goBack()"
>
<font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
</h1>
@@ -44,11 +44,11 @@
</div>
<div
v-if="attended || (gaveTime && hoursStr && hoursStr != '0')"
v-if="canSubmit"
class="flex justify-center mt-4"
>
<button
class="block text-center text-md font-bold 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-2 py-3 rounded-md w-56"
:class="activeButtonClass"
@click="record()"
>
Sign & Send
@@ -56,7 +56,7 @@
</div>
<div v-else class="flex justify-center mt-4">
<button
class="block text-center text-md font-bold 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-2 py-3 rounded-md w-56"
:class="disabledButtonClass"
>
Select Your Actions
</button>
@@ -73,7 +73,14 @@ import { Component, Vue } from "vue-facing-decorator";
import QuickNav from "../components/QuickNav.vue";
import TopMessage from "../components/TopMessage.vue";
import { NotificationIface } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import {
NOTIFY_BVC_PROCESSING,
NOTIFY_BVC_TIME_ERROR,
NOTIFY_BVC_ATTENDANCE_ERROR,
NOTIFY_BVC_SUBMISSION_ERROR,
createBvcSuccessMessage,
} from "../constants/notifications";
import { createNotifyHelpers, TIMEOUTS } from "../utils/notify";
import {
BVC_MEETUPS_PROJECT_CLAIM_ID,
bvcMeetingJoinClaim,
@@ -82,21 +89,42 @@ import {
} from "../libs/endorserServer";
import * as libsUtil from "../libs/util";
import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
/**
* @file QuickActionBvcBeginView.vue
* @description BVC (Bountiful Volunteerism Community) meeting attendance tracker
* for Saturday meetings. Allows users to record attendance and time contributions
* for weekly BVC meetings in Bountiful, using America/Denver timezone.
* @author Matthew Raymer
*/
@Component({
components: {
QuickNav,
TopMessage,
},
mixins: [PlatformServiceMixin],
})
export default class QuickActionBvcBeginView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
$router!: Router;
// Notification helper system
private notify = createNotifyHelpers(this.$notify);
attended = true;
gaveTime = true;
hoursStr = "1";
todayOrPreviousStartDate = "";
/**
* Lifecycle hook to calculate the current or previous Saturday meeting date
* Uses America/Denver timezone for Bountiful location
*/
async mounted() {
logger.debug("[QuickActionBvcBeginView] Mounted - calculating meeting date");
// use the time zone for Bountiful
let currentOrPreviousSat = DateTime.now().setZone("America/Denver");
if (currentOrPreviousSat.weekday < 6) {
@@ -114,21 +142,52 @@ export default class QuickActionBvcBeginView extends Vue {
eventStartDateObj.toISO({
suppressMilliseconds: true,
}) || "";
logger.debug(
"[QuickActionBvcBeginView] Meeting date calculated:",
this.todayOrPreviousStartDate
);
}
/**
* Records attendance and/or time contribution to BVC meeting
* Creates claims for both attendance and time if applicable
*/
async record() {
const settings = await databaseUtil.retrieveSettingsForActiveAccount();
logger.debug("[QuickActionBvcBeginView] Recording BVC meeting participation");
// Get account settings using PlatformServiceMixin
const settings = await this.$accountSettings();
const activeDid = settings.activeDid || "";
const apiServer = settings.apiServer || "";
if (!activeDid || !apiServer) {
logger.error(
"[QuickActionBvcBeginView] Missing required settings:",
{ activeDid: !!activeDid, apiServer: !!apiServer }
);
return;
}
try {
const hoursNum = libsUtil.numberOrZero(this.hoursStr);
logger.debug(
"[QuickActionBvcBeginView] Processing submission:",
{ attended: this.attended, gaveTime: this.gaveTime, hours: hoursNum }
);
this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000);
// Use notification helper with proper timeout
this.notify.toast(NOTIFY_BVC_PROCESSING.title, NOTIFY_BVC_PROCESSING.message, TIMEOUTS.BRIEF);
// first send the claim for time given
let timeSuccess = false;
if (this.gaveTime && hoursNum > 0) {
logger.debug(
"[QuickActionBvcBeginView] Submitting time gift:",
{ hours: hoursNum }
);
const timeResult = await createAndSubmitGive(
axios,
apiServer,
@@ -140,18 +199,15 @@ export default class QuickActionBvcBeginView extends Vue {
"HUR",
BVC_MEETUPS_PROJECT_CLAIM_ID,
);
if (timeResult.success) {
timeSuccess = true;
logger.debug("[QuickActionBvcBeginView] Time gift submission successful");
} else {
logger.error("Error sending time:", timeResult);
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: timeResult?.error || "There was an error sending the time.",
},
5000,
logger.error("[QuickActionBvcBeginView] Error sending time:", timeResult);
this.notify.error(
timeResult?.error || NOTIFY_BVC_TIME_ERROR.message,
TIMEOUTS.LONG
);
}
}
@@ -159,62 +215,76 @@ export default class QuickActionBvcBeginView extends Vue {
// now send the claim for attendance
let attendedSuccess = false;
if (this.attended) {
logger.debug("[QuickActionBvcBeginView] Submitting attendance claim");
const attendResult = await createAndSubmitClaim(
bvcMeetingJoinClaim(activeDid, this.todayOrPreviousStartDate),
activeDid,
apiServer,
axios,
);
if (attendResult.success) {
attendedSuccess = true;
logger.debug("[QuickActionBvcBeginView] Attendance claim submission successful");
} else {
logger.error("Error sending attendance:", attendResult);
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text:
attendResult?.error ||
"There was an error sending the attendance.",
},
5000,
logger.error("[QuickActionBvcBeginView] Error sending attendance:", attendResult);
this.notify.error(
attendResult?.error || NOTIFY_BVC_ATTENDANCE_ERROR.message,
TIMEOUTS.LONG
);
}
}
if (timeSuccess || attendedSuccess) {
const actions =
timeSuccess && attendedSuccess
? "Your attendance and time have been recorded."
: timeSuccess
? "Your time has been recorded."
: "Your attendance has been recorded.";
this.$notify(
{
group: "alert",
type: "success",
title: "Success",
text: actions,
},
3000,
const successMessage = createBvcSuccessMessage(timeSuccess, attendedSuccess);
logger.debug(
"[QuickActionBvcBeginView] Submission completed successfully:",
{ timeSuccess, attendedSuccess }
);
this.notify.success(successMessage, TIMEOUTS.STANDARD);
this.$router.push({ path: "/quick-action-bvc" });
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
logger.error("Error sending claims.", error);
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: error.userMessage || "There was an error sending the claims.",
},
5000,
logger.error("[QuickActionBvcBeginView] Error sending claims:", error);
this.notify.error(
error.userMessage || NOTIFY_BVC_SUBMISSION_ERROR.message,
TIMEOUTS.LONG
);
}
}
/**
* Navigates back to the previous page
*/
goBack() {
this.$router.back();
}
/**
* Computed property for active button styling
*/
get activeButtonClass() {
return "block text-center text-md font-bold 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-2 py-3 rounded-md w-56";
}
/**
* Computed property for disabled button styling
*/
get disabledButtonClass() {
return "block text-center text-md font-bold 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-2 py-3 rounded-md w-56";
}
/**
* Computed property to determine if the submit button should be enabled
* Returns true if user has attended or provided valid time contribution
*/
get canSubmit() {
return this.attended || (this.gaveTime && this.hoursStr && this.hoursStr !== '0');
}
}
</script>