fixes
Build And Push Image / docker (push) Successful in 1m39s Details

This commit is contained in:
Matt 2025-08-15 16:02:14 +02:00
parent 136eb5229e
commit cdacb4a114
1 changed files with 159 additions and 17 deletions

View File

@ -98,34 +98,73 @@
<!-- Mark as Paid Dialog --> <!-- Mark as Paid Dialog -->
<v-dialog v-model="markAsPaidDialog" max-width="400"> <v-dialog v-model="markAsPaidDialog" max-width="400">
<v-card> <v-card>
<v-card-title class="text-h6"> <v-card-title class="text-h6 pa-4">
<v-icon left color="success">mdi-check-circle</v-icon> <v-icon left color="success">mdi-calendar-check</v-icon>
Mark Dues as Paid Mark Dues as Paid
</v-card-title> </v-card-title>
<v-card-text> <v-card-text class="pa-4">
<p>Are you sure you want to mark the dues as paid for this member?</p> <div class="mb-4">
<p class="text-body-2 text-medium-emphasis"> <h4 class="text-subtitle-1 mb-2">
This will remove the payment banner and update the member's status. {{ memberData?.FullName || `${memberData?.first_name || ''} ${memberData?.last_name || ''}`.trim() }}
</p> </h4>
<p class="text-body-2 text-medium-emphasis">
Select the date when the dues payment was received:
</p>
</div>
<div class="date-picker-wrapper">
<label class="date-picker-label">Payment Date</label>
<VueDatePicker
v-model="selectedPaymentModel"
:timezone="{
timezone: 'Europe/Monaco',
emitTimezone: 'UTC'
}"
:format="'dd/MM/yyyy (Monaco)'"
:max-date="new Date()"
placeholder="Select payment date"
:enable-time-picker="false"
auto-apply
:clearable="false"
:required="true"
@update:model-value="handleDateUpdate"
/>
<div class="text-caption text-medium-emphasis mt-1">
Select the date when the payment was received
</div>
</div>
<v-alert
v-if="selectedPaymentDate && isDateInFuture"
type="warning"
variant="tonal"
class="mt-2"
density="compact"
>
<v-icon start>mdi-information</v-icon>
Future dates are not allowed. Please select today or an earlier date.
</v-alert>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions class="pa-4 pt-0">
<v-spacer /> <v-spacer />
<v-btn <v-btn
color="grey" color="grey"
variant="text" variant="text"
@click="markAsPaidDialog = false" @click="cancelPaymentDialog"
> >
Cancel Cancel
</v-btn> </v-btn>
<v-btn <v-btn
color="success" color="success"
variant="flat" variant="elevated"
:disabled="!selectedPaymentDate || isDateInFuture"
:loading="updating" :loading="updating"
@click="markDuesAsPaid" @click="markDuesAsPaid"
> >
Mark as Paid <v-icon start>mdi-check-circle</v-icon>
Confirm Payment
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
@ -172,6 +211,10 @@ const config = ref<RegistrationConfig>({
accountHolder: '' accountHolder: ''
}); });
// Reactive state for payment date dialog
const selectedPaymentDate = ref('');
const selectedPaymentModel = ref<Date | null>(null);
const snackbar = ref({ const snackbar = ref({
show: false, show: false,
message: '', 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 // Methods
function dismissBanner() { function dismissBanner() {
dismissed.value = true; dismissed.value = true;
@ -325,30 +383,31 @@ function dismissBanner() {
} }
async function markDuesAsPaid() { async function markDuesAsPaid() {
if (!memberData.value?.Id) return; if (!memberData.value?.Id || !selectedPaymentDate.value || isDateInFuture.value) return;
updating.value = true; updating.value = true;
try { try {
// Update member's dues status // Update member's dues status with the selected payment date
await $fetch(`/api/members/${memberData.value.Id}`, { await $fetch(`/api/members/${memberData.value.Id}`, {
method: 'PUT', method: 'PUT',
body: { body: {
current_year_dues_paid: 'true', current_year_dues_paid: 'true',
membership_date_paid: new Date().toISOString(), membership_date_paid: selectedPaymentDate.value,
payment_due_date: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString() // Next year 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 // Update local member state
if (memberData.value) { if (memberData.value) {
memberData.value.current_year_dues_paid = 'true'; 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; showBanner.value = false;
markAsPaidDialog.value = false; markAsPaidDialog.value = false;
selectedPaymentDate.value = '';
// Show success message // Show success message
snackbar.value = { 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 // Load member data for the current user from session
async function loadMemberData() { async function loadMemberData() {
if (!user.value) return; if (!user.value) return;
@ -460,6 +532,76 @@ onMounted(() => {
border: 1px solid rgba(255, 255, 255, 0.2) !important; 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 */ /* Mobile responsiveness */
@media (max-width: 600px) { @media (max-width: 600px) {
.banner-content .text-h6 { .banner-content .text-h6 {