telephone updates
Build And Push Image / docker (push) Successful in 2m52s
Details
Build And Push Image / docker (push) Successful in 2m52s
Details
This commit is contained in:
parent
13fa95a9a2
commit
024eca02ac
|
|
@ -10,7 +10,7 @@
|
||||||
<v-card-title class="d-flex align-center pa-6 bg-primary">
|
<v-card-title class="d-flex align-center pa-6 bg-primary">
|
||||||
<v-icon class="mr-3 text-white">mdi-account-edit</v-icon>
|
<v-icon class="mr-3 text-white">mdi-account-edit</v-icon>
|
||||||
<h2 class="text-h5 text-white font-weight-bold flex-grow-1">
|
<h2 class="text-h5 text-white font-weight-bold flex-grow-1">
|
||||||
Edit Member: {{ member?.FullName || `${member?.['First Name']} ${member?.['Last Name']}` }}
|
Edit Member: {{ member?.FullName || `${member?.first_name} ${member?.last_name}` }}
|
||||||
</h2>
|
</h2>
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
|
|
@ -32,81 +32,81 @@
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form['First Name']"
|
v-model="form.first_name"
|
||||||
label="First Name"
|
label="First Name"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:rules="[rules.required]"
|
:rules="[rules.required]"
|
||||||
required
|
required
|
||||||
:error="hasFieldError('First Name')"
|
:error="hasFieldError('first_name')"
|
||||||
:error-messages="getFieldError('First Name')"
|
:error-messages="getFieldError('first_name')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form['Last Name']"
|
v-model="form.last_name"
|
||||||
label="Last Name"
|
label="Last Name"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:rules="[rules.required]"
|
:rules="[rules.required]"
|
||||||
required
|
required
|
||||||
:error="hasFieldError('Last Name')"
|
:error="hasFieldError('last_name')"
|
||||||
:error-messages="getFieldError('Last Name')"
|
:error-messages="getFieldError('last_name')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form.Email"
|
v-model="form.email"
|
||||||
label="Email Address"
|
label="Email Address"
|
||||||
type="email"
|
type="email"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:rules="[rules.required, rules.email]"
|
:rules="[rules.required, rules.email]"
|
||||||
required
|
required
|
||||||
:error="hasFieldError('Email')"
|
:error="hasFieldError('email')"
|
||||||
:error-messages="getFieldError('Email')"
|
:error-messages="getFieldError('email')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<PhoneInputWrapper
|
<EnhancedPhoneInput
|
||||||
v-model="form.Phone"
|
v-model="form.phone"
|
||||||
label="Phone Number"
|
label="Phone Number"
|
||||||
placeholder="Enter phone number"
|
placeholder="Enter phone number"
|
||||||
:error="hasFieldError('Phone')"
|
:error="hasFieldError('phone')"
|
||||||
:error-message="getFieldError('Phone')"
|
:error-message="getFieldError('phone')"
|
||||||
@phone-data="handlePhoneData"
|
@phone-data="handlePhoneData"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form['Date of Birth']"
|
v-model="form.date_of_birth"
|
||||||
label="Date of Birth"
|
label="Date of Birth"
|
||||||
type="date"
|
type="date"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:error="hasFieldError('Date of Birth')"
|
:error="hasFieldError('date_of_birth')"
|
||||||
:error-messages="getFieldError('Date of Birth')"
|
:error-messages="getFieldError('date_of_birth')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<MultipleNationalityInput
|
<MultipleNationalityInput
|
||||||
v-model="form.Nationality"
|
v-model="form.nationality"
|
||||||
label="Nationality"
|
label="Nationality"
|
||||||
:error="hasFieldError('Nationality')"
|
:error="hasFieldError('nationality')"
|
||||||
:error-message="getFieldError('Nationality')"
|
:error-message="getFieldError('nationality')"
|
||||||
:max-nationalities="3"
|
:max-nationalities="3"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-textarea
|
<v-textarea
|
||||||
v-model="form.Address"
|
v-model="form.address"
|
||||||
label="Address"
|
label="Address"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
rows="2"
|
rows="2"
|
||||||
:error="hasFieldError('Address')"
|
:error="hasFieldError('address')"
|
||||||
:error-messages="getFieldError('Address')"
|
:error-messages="getFieldError('address')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
|
@ -118,25 +118,25 @@
|
||||||
|
|
||||||
<v-col cols="12" md="4">
|
<v-col cols="12" md="4">
|
||||||
<v-select
|
<v-select
|
||||||
v-model="form['Membership Status']"
|
v-model="form.membership_status"
|
||||||
:items="membershipStatusOptions"
|
:items="membershipStatusOptions"
|
||||||
label="Membership Status"
|
label="Membership Status"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:rules="[rules.required]"
|
:rules="[rules.required]"
|
||||||
required
|
required
|
||||||
:error="hasFieldError('Membership Status')"
|
:error="hasFieldError('membership_status')"
|
||||||
:error-messages="getFieldError('Membership Status')"
|
:error-messages="getFieldError('membership_status')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="4">
|
<v-col cols="12" md="4">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form['Member Since']"
|
v-model="form.member_since"
|
||||||
label="Member Since"
|
label="Member Since"
|
||||||
type="date"
|
type="date"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:error="hasFieldError('Member Since')"
|
:error="hasFieldError('member_since')"
|
||||||
:error-messages="getFieldError('Member Since')"
|
:error-messages="getFieldError('member_since')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
|
@ -146,30 +146,30 @@
|
||||||
label="Current Year Dues Paid"
|
label="Current Year Dues Paid"
|
||||||
color="success"
|
color="success"
|
||||||
inset
|
inset
|
||||||
:error="hasFieldError('Current Year Dues Paid')"
|
:error="hasFieldError('current_year_dues_paid')"
|
||||||
:error-messages="getFieldError('Current Year Dues Paid')"
|
:error-messages="getFieldError('current_year_dues_paid')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6" v-if="duesPaid">
|
<v-col cols="12" md="6" v-if="duesPaid">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form['Membership Date Paid']"
|
v-model="form.membership_date_paid"
|
||||||
label="Payment Date"
|
label="Payment Date"
|
||||||
type="date"
|
type="date"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:error="hasFieldError('Membership Date Paid')"
|
:error="hasFieldError('membership_date_paid')"
|
||||||
:error-messages="getFieldError('Membership Date Paid')"
|
:error-messages="getFieldError('membership_date_paid')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12" md="6" v-if="!duesPaid">
|
<v-col cols="12" md="6" v-if="!duesPaid">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form['Payment Due Date']"
|
v-model="form.payment_due_date"
|
||||||
label="Payment Due Date"
|
label="Payment Due Date"
|
||||||
type="date"
|
type="date"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:error="hasFieldError('Payment Due Date')"
|
:error="hasFieldError('payment_due_date')"
|
||||||
:error-messages="getFieldError('Payment Due Date')"
|
:error-messages="getFieldError('payment_due_date')"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
@ -201,7 +201,6 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Member } from '~/utils/types';
|
import type { Member } from '~/utils/types';
|
||||||
import { formatBooleanAsString } from '~/server/utils/nocodb';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue: boolean;
|
modelValue: boolean;
|
||||||
|
|
@ -221,20 +220,20 @@ const formRef = ref();
|
||||||
const formValid = ref(false);
|
const formValid = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
// Form data
|
// Form data - using snake_case field names
|
||||||
const form = ref({
|
const form = ref({
|
||||||
'First Name': '',
|
first_name: '',
|
||||||
'Last Name': '',
|
last_name: '',
|
||||||
Email: '',
|
email: '',
|
||||||
Phone: '',
|
phone: '',
|
||||||
'Date of Birth': '',
|
date_of_birth: '',
|
||||||
Nationality: '',
|
nationality: '',
|
||||||
Address: '',
|
address: '',
|
||||||
'Membership Status': 'Active',
|
membership_status: 'Active',
|
||||||
'Member Since': '',
|
member_since: '',
|
||||||
'Current Year Dues Paid': 'false',
|
current_year_dues_paid: 'false',
|
||||||
'Membership Date Paid': '',
|
membership_date_paid: '',
|
||||||
'Payment Due Date': ''
|
payment_due_date: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
// Additional form state
|
// Additional form state
|
||||||
|
|
@ -246,20 +245,20 @@ const fieldErrors = ref<Record<string, string>>({});
|
||||||
|
|
||||||
// Watch dues paid switch
|
// Watch dues paid switch
|
||||||
watch(duesPaid, (newValue) => {
|
watch(duesPaid, (newValue) => {
|
||||||
form.value['Current Year Dues Paid'] = formatBooleanAsString(newValue);
|
form.value.current_year_dues_paid = newValue ? 'true' : 'false';
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
form.value['Payment Due Date'] = '';
|
form.value.payment_due_date = '';
|
||||||
if (!form.value['Membership Date Paid']) {
|
if (!form.value.membership_date_paid) {
|
||||||
form.value['Membership Date Paid'] = new Date().toISOString().split('T')[0];
|
form.value.membership_date_paid = new Date().toISOString().split('T')[0];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
form.value['Membership Date Paid'] = '';
|
form.value.membership_date_paid = '';
|
||||||
if (!form.value['Payment Due Date']) {
|
if (!form.value.payment_due_date) {
|
||||||
// Set due date to one year from member since date or today
|
// Set due date to one year from member since date or today
|
||||||
const memberSince = form.value['Member Since'] || new Date().toISOString().split('T')[0];
|
const memberSince = form.value.member_since || new Date().toISOString().split('T')[0];
|
||||||
const dueDate = new Date(memberSince);
|
const dueDate = new Date(memberSince);
|
||||||
dueDate.setFullYear(dueDate.getFullYear() + 1);
|
dueDate.setFullYear(dueDate.getFullYear() + 1);
|
||||||
form.value['Payment Due Date'] = dueDate.toISOString().split('T')[0];
|
form.value.payment_due_date = dueDate.toISOString().split('T')[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -304,28 +303,44 @@ const handlePhoneData = (data: any) => {
|
||||||
phoneData.value = data;
|
phoneData.value = data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Form pre-population
|
// Form pre-population - Updated to use snake_case field names
|
||||||
const populateForm = () => {
|
const populateForm = () => {
|
||||||
if (!props.member) return;
|
if (!props.member) return;
|
||||||
|
|
||||||
|
console.log('[EditMemberDialog] Populating form with member data:', props.member);
|
||||||
|
|
||||||
const member = props.member;
|
const member = props.member;
|
||||||
|
|
||||||
|
// Convert date fields to proper format for input[type="date"]
|
||||||
|
const formatDateForInput = (dateString: string) => {
|
||||||
|
if (!dateString) return '';
|
||||||
|
try {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return date.toISOString().split('T')[0];
|
||||||
|
} catch {
|
||||||
|
return dateString;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
form.value = {
|
form.value = {
|
||||||
'First Name': member['First Name'] || '',
|
first_name: member.first_name || '',
|
||||||
'Last Name': member['Last Name'] || '',
|
last_name: member.last_name || '',
|
||||||
Email: member.Email || '',
|
email: member.email || '',
|
||||||
Phone: member.Phone || '',
|
phone: member.phone || '',
|
||||||
'Date of Birth': member['Date of Birth'] || '',
|
date_of_birth: formatDateForInput(member.date_of_birth || ''),
|
||||||
Nationality: member.Nationality || '',
|
nationality: member.nationality || '',
|
||||||
Address: member.Address || '',
|
address: member.address || '',
|
||||||
'Membership Status': member['Membership Status'] || 'Active',
|
membership_status: member.membership_status || 'Active',
|
||||||
'Member Since': member['Member Since'] || '',
|
member_since: formatDateForInput(member.member_since || ''),
|
||||||
'Current Year Dues Paid': member['Current Year Dues Paid'] || 'false',
|
current_year_dues_paid: member.current_year_dues_paid || 'false',
|
||||||
'Membership Date Paid': member['Membership Date Paid'] || '',
|
membership_date_paid: formatDateForInput(member.membership_date_paid || ''),
|
||||||
'Payment Due Date': member['Payment Due Date'] || ''
|
payment_due_date: formatDateForInput(member.payment_due_date || '')
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set dues paid switch based on the string value
|
// Set dues paid switch based on the string value
|
||||||
duesPaid.value = member['Current Year Dues Paid'] === 'true';
|
duesPaid.value = member.current_year_dues_paid === 'true';
|
||||||
|
|
||||||
|
console.log('[EditMemberDialog] Form populated:', form.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Form submission
|
// Form submission
|
||||||
|
|
@ -345,13 +360,13 @@ const handleSubmit = async () => {
|
||||||
const memberData = { ...form.value };
|
const memberData = { ...form.value };
|
||||||
|
|
||||||
// Ensure required fields are not empty
|
// Ensure required fields are not empty
|
||||||
if (!memberData['First Name']?.trim()) {
|
if (!memberData.first_name?.trim()) {
|
||||||
throw new Error('First Name is required');
|
throw new Error('First Name is required');
|
||||||
}
|
}
|
||||||
if (!memberData['Last Name']?.trim()) {
|
if (!memberData.last_name?.trim()) {
|
||||||
throw new Error('Last Name is required');
|
throw new Error('Last Name is required');
|
||||||
}
|
}
|
||||||
if (!memberData.Email?.trim()) {
|
if (!memberData.email?.trim()) {
|
||||||
throw new Error('Email is required');
|
throw new Error('Email is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
<template>
|
||||||
|
<div class="enhanced-phone-input">
|
||||||
|
<PhoneInput
|
||||||
|
v-model="phoneValue"
|
||||||
|
:country-code="selectedCountryCode"
|
||||||
|
:placeholder="placeholder || 'Enter phone number'"
|
||||||
|
:preferred-countries="['US', 'FR', 'GB', 'DE', 'CA', 'AU', 'ES', 'IT', 'NL', 'BE']"
|
||||||
|
:auto-format="true"
|
||||||
|
:no-formatting-as-you-type="false"
|
||||||
|
country-locale="en-US"
|
||||||
|
@update="handlePhoneUpdate"
|
||||||
|
@input="handlePhoneInput"
|
||||||
|
class="phone-input-wrapper"
|
||||||
|
>
|
||||||
|
<template #input="{ inputValue, updateInputValue, placeholder: inputPlaceholder }">
|
||||||
|
<v-text-field
|
||||||
|
:model-value="inputValue"
|
||||||
|
@update:model-value="updateInputValue"
|
||||||
|
:placeholder="inputPlaceholder"
|
||||||
|
:label="label"
|
||||||
|
variant="outlined"
|
||||||
|
:error="hasError"
|
||||||
|
:error-messages="errorMessage"
|
||||||
|
:rules="rules"
|
||||||
|
class="phone-number-input"
|
||||||
|
hide-details="auto"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</PhoneInput>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PhoneInput from 'base-vue-phone-input';
|
||||||
|
import type { Results as PhoneResults } from 'base-vue-phone-input';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue?: string;
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
error?: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
|
rules?: Array<(value: any) => boolean | string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:model-value', value: string): void;
|
||||||
|
(e: 'phone-data', data: PhoneResults): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
modelValue: '',
|
||||||
|
label: 'Phone Number',
|
||||||
|
placeholder: 'Enter phone number',
|
||||||
|
error: false,
|
||||||
|
errorMessage: '',
|
||||||
|
rules: () => []
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
// Local state
|
||||||
|
const phoneValue = ref(props.modelValue);
|
||||||
|
const selectedCountryCode = ref('US');
|
||||||
|
const phoneData = ref<PhoneResults | null>(null);
|
||||||
|
|
||||||
|
// Computed properties
|
||||||
|
const hasError = computed(() => props.error);
|
||||||
|
|
||||||
|
// Watch for external changes to modelValue
|
||||||
|
watch(() => props.modelValue, (newValue) => {
|
||||||
|
if (newValue !== phoneValue.value) {
|
||||||
|
phoneValue.value = newValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle phone input events
|
||||||
|
const handlePhoneInput = (value: string) => {
|
||||||
|
phoneValue.value = value;
|
||||||
|
emit('update:model-value', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePhoneUpdate = (data: PhoneResults) => {
|
||||||
|
phoneData.value = data;
|
||||||
|
|
||||||
|
// Update country code if available
|
||||||
|
if (data.countryCode) {
|
||||||
|
selectedCountryCode.value = data.countryCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the formatted phone number
|
||||||
|
const formattedNumber = data.formatInternational || data.e164 || phoneValue.value;
|
||||||
|
if (formattedNumber !== phoneValue.value) {
|
||||||
|
phoneValue.value = formattedNumber;
|
||||||
|
emit('update:model-value', formattedNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit phone data for parent component
|
||||||
|
emit('phone-data', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize with modelValue
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.modelValue) {
|
||||||
|
phoneValue.value = props.modelValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.enhanced-phone-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.country-select {
|
||||||
|
flex: 0 0 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-number-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag-emoji {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.country-code {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: rgba(var(--v-theme-on-surface), 0.87);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure proper spacing in Vuetify forms */
|
||||||
|
:deep(.v-input) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.v-input__details) {
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue