From cdacb4a114cb15cdffdda29b429650e082fc1570 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 15 Aug 2025 16:02:14 +0200 Subject: [PATCH] fixes --- components/DuesPaymentBanner.vue | 176 ++++++++++++++++++++++++++++--- 1 file changed, 159 insertions(+), 17 deletions(-) diff --git a/components/DuesPaymentBanner.vue b/components/DuesPaymentBanner.vue index 4d5ce13..c629227 100644 --- a/components/DuesPaymentBanner.vue +++ b/components/DuesPaymentBanner.vue @@ -98,34 +98,73 @@ - - mdi-check-circle + + mdi-calendar-check Mark Dues as Paid - -

Are you sure you want to mark the dues as paid for this member?

-

- This will remove the payment banner and update the member's status. -

+ +
+

+ {{ memberData?.FullName || `${memberData?.first_name || ''} ${memberData?.last_name || ''}`.trim() }} +

+

+ Select the date when the dues payment was received: +

+
+ +
+ + +
+ Select the date when the payment was received +
+
+ + + mdi-information + Future dates are not allowed. Please select today or an earlier date. +
- + Cancel - Mark as Paid + mdi-check-circle + Confirm Payment
@@ -172,6 +211,10 @@ const config = ref({ accountHolder: '' }); +// Reactive state for payment date dialog +const selectedPaymentDate = ref(''); +const selectedPaymentModel = ref(null); + const snackbar = ref({ show: false, message: '', @@ -311,6 +354,21 @@ const paymentMessage = computed(() => { } }); +const todayDate = computed(() => { + return new Date().toISOString().split('T')[0]; // YYYY-MM-DD format +}); + +const isDateInFuture = computed(() => { + if (!selectedPaymentDate.value) return false; + + const selectedDate = new Date(selectedPaymentDate.value); + const today = new Date(); + today.setHours(0, 0, 0, 0); // Reset time to start of day + selectedDate.setHours(0, 0, 0, 0); // Reset time to start of day + + return selectedDate > today; +}); + // Methods function dismissBanner() { dismissed.value = true; @@ -325,30 +383,31 @@ function dismissBanner() { } async function markDuesAsPaid() { - if (!memberData.value?.Id) return; + if (!memberData.value?.Id || !selectedPaymentDate.value || isDateInFuture.value) return; updating.value = true; try { - // Update member's dues status + // Update member's dues status with the selected payment date await $fetch(`/api/members/${memberData.value.Id}`, { method: 'PUT', body: { current_year_dues_paid: 'true', - membership_date_paid: new Date().toISOString(), - payment_due_date: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString() // Next year + membership_date_paid: selectedPaymentDate.value, + payment_due_date: new Date(new Date(selectedPaymentDate.value).getTime() + 365 * 24 * 60 * 60 * 1000).toISOString() // Next year from payment date } }); // Update local member state if (memberData.value) { memberData.value.current_year_dues_paid = 'true'; - memberData.value.membership_date_paid = new Date().toISOString(); + memberData.value.membership_date_paid = selectedPaymentDate.value; } - // Hide banner + // Hide banner and reset showBanner.value = false; markAsPaidDialog.value = false; + selectedPaymentDate.value = ''; // Show success message snackbar.value = { @@ -369,6 +428,19 @@ async function markDuesAsPaid() { } } +// Date picker handler +const handleDateUpdate = (date: Date | null) => { + if (date) { + selectedPaymentDate.value = date.toISOString().split('T')[0]; + } +}; + +const cancelPaymentDialog = () => { + markAsPaidDialog.value = false; + selectedPaymentDate.value = ''; + selectedPaymentModel.value = null; +}; + // Load member data for the current user from session async function loadMemberData() { if (!user.value) return; @@ -460,6 +532,76 @@ onMounted(() => { border: 1px solid rgba(255, 255, 255, 0.2) !important; } +/* Date picker styling to match Vuetify */ +.date-picker-wrapper { + width: 100%; +} + +.date-picker-label { + font-size: 16px; + color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity)); + font-weight: 400; + line-height: 1.5; + letter-spacing: 0.009375em; + margin-bottom: 8px; + display: block; +} + +/* Style the Vue DatePicker to match Vuetify inputs */ +:deep(.dp__input) { + border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); + border-radius: 4px; + padding: 16px 12px; + padding-right: 48px; /* Make room for calendar icon */ + font-size: 16px; + line-height: 1.5; + background: rgb(var(--v-theme-surface)); + color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity)); + transition: border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1); + width: 100%; + min-height: 56px; +} + +:deep(.dp__input:hover) { + border-color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity)); +} + +:deep(.dp__input:focus) { + border-color: rgb(var(--v-theme-primary)); + border-width: 2px; + outline: none; +} + +:deep(.dp__input_readonly) { + cursor: pointer; +} + +/* Style the date picker dropdown */ +:deep(.dp__menu) { + border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + background: rgb(var(--v-theme-surface)); +} + +/* Primary color theming for the date picker */ +:deep(.dp__primary_color) { + background-color: rgb(var(--v-theme-primary)); +} + +:deep(.dp__primary_text) { + color: rgb(var(--v-theme-primary)); +} + +:deep(.dp__active_date) { + background-color: rgb(var(--v-theme-primary)); + color: rgb(var(--v-theme-on-primary)); +} + +:deep(.dp__today) { + border: 1px solid rgb(var(--v-theme-primary)); +} + /* Mobile responsiveness */ @media (max-width: 600px) { .banner-content .text-h6 {