fixes
Build And Push Image / docker (push) Has been cancelled
Details
Build And Push Image / docker (push) Has been cancelled
Details
This commit is contained in:
parent
9b183b48cc
commit
b49148cf95
|
|
@ -0,0 +1,313 @@
|
||||||
|
<template>
|
||||||
|
<v-dialog
|
||||||
|
v-model="isOpen"
|
||||||
|
max-width="500"
|
||||||
|
persistent
|
||||||
|
@keydown.esc="cancel"
|
||||||
|
>
|
||||||
|
<v-card class="rounded-lg">
|
||||||
|
<v-card-title class="d-flex align-center">
|
||||||
|
<v-icon color="primary" class="mr-3">mdi-account-plus</v-icon>
|
||||||
|
<span class="text-h6">Create Portal Account</span>
|
||||||
|
</v-card-title>
|
||||||
|
|
||||||
|
<v-card-text class="pb-2">
|
||||||
|
<div class="mb-4">
|
||||||
|
<p class="text-body-1 mb-2">
|
||||||
|
Create a portal account for <strong>{{ member?.FullName }}</strong>
|
||||||
|
</p>
|
||||||
|
<p class="text-body-2 text-medium-emphasis">
|
||||||
|
{{ member?.email }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<v-alert
|
||||||
|
type="info"
|
||||||
|
variant="tonal"
|
||||||
|
class="mb-4"
|
||||||
|
icon="mdi-information"
|
||||||
|
>
|
||||||
|
<template #text>
|
||||||
|
<div class="text-body-2">
|
||||||
|
The user will receive an email to set up their password and complete registration.
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-alert>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-form ref="formRef" v-model="formValid">
|
||||||
|
<v-select
|
||||||
|
v-model="selectedGroup"
|
||||||
|
:items="groupOptions"
|
||||||
|
label="Assign to Group"
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
:rules="groupRules"
|
||||||
|
prepend-inner-icon="mdi-account-group"
|
||||||
|
class="mb-3"
|
||||||
|
>
|
||||||
|
<template #item="{ props, item }">
|
||||||
|
<v-list-item v-bind="props">
|
||||||
|
<template #prepend>
|
||||||
|
<v-icon :color="item.raw.color">{{ item.raw.icon }}</v-icon>
|
||||||
|
</template>
|
||||||
|
<v-list-item-title>{{ item.raw.title }}</v-list-item-title>
|
||||||
|
<v-list-item-subtitle>{{ item.raw.description }}</v-list-item-subtitle>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
<template #selection="{ item }">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon :color="item.raw.color" class="mr-2">{{ item.raw.icon }}</v-icon>
|
||||||
|
{{ item.raw.title }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-select>
|
||||||
|
|
||||||
|
<!-- Group Description -->
|
||||||
|
<v-card
|
||||||
|
v-if="selectedGroup"
|
||||||
|
variant="tonal"
|
||||||
|
color="primary"
|
||||||
|
class="mb-3"
|
||||||
|
>
|
||||||
|
<v-card-text class="py-3">
|
||||||
|
<div class="d-flex align-center mb-2">
|
||||||
|
<v-icon :color="selectedGroupInfo?.color" class="mr-2">
|
||||||
|
{{ selectedGroupInfo?.icon }}
|
||||||
|
</v-icon>
|
||||||
|
<span class="font-weight-medium">{{ selectedGroupInfo?.title }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-body-2 mb-2">{{ selectedGroupInfo?.description }}</p>
|
||||||
|
<div class="text-caption">
|
||||||
|
<strong>Permissions:</strong>
|
||||||
|
<ul class="mt-1 ml-4">
|
||||||
|
<li v-for="permission in selectedGroupInfo?.permissions" :key="permission">
|
||||||
|
{{ permission }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
<!-- Error Alert -->
|
||||||
|
<v-alert
|
||||||
|
v-if="errorMessage"
|
||||||
|
type="error"
|
||||||
|
variant="tonal"
|
||||||
|
class="mb-3"
|
||||||
|
closable
|
||||||
|
@click:close="errorMessage = ''"
|
||||||
|
>
|
||||||
|
{{ errorMessage }}
|
||||||
|
</v-alert>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions class="px-6 pb-6">
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
variant="text"
|
||||||
|
@click="cancel"
|
||||||
|
:disabled="loading"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
variant="elevated"
|
||||||
|
@click="createAccount"
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="!formValid || loading"
|
||||||
|
>
|
||||||
|
<v-icon start>mdi-account-plus</v-icon>
|
||||||
|
Create Account
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Member } from '~/utils/types';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue: boolean;
|
||||||
|
member: Member | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', value: boolean): void;
|
||||||
|
(e: 'account-created', member: Member): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
// Reactive state
|
||||||
|
const loading = ref(false);
|
||||||
|
const errorMessage = ref('');
|
||||||
|
const formValid = ref(false);
|
||||||
|
const selectedGroup = ref('user');
|
||||||
|
const formRef = ref();
|
||||||
|
|
||||||
|
// Computed
|
||||||
|
const isOpen = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value) => emit('update:modelValue', value)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group options with detailed information
|
||||||
|
const groupOptions = [
|
||||||
|
{
|
||||||
|
title: 'User',
|
||||||
|
value: 'user',
|
||||||
|
description: 'Standard member access',
|
||||||
|
icon: 'mdi-account',
|
||||||
|
color: 'primary',
|
||||||
|
permissions: [
|
||||||
|
'View own profile and update personal information',
|
||||||
|
'View events and RSVP',
|
||||||
|
'Access member directory (if enabled)',
|
||||||
|
'View dues status and payment history'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Board Member',
|
||||||
|
value: 'board',
|
||||||
|
description: 'Board member privileges',
|
||||||
|
icon: 'mdi-account-tie',
|
||||||
|
color: 'warning',
|
||||||
|
permissions: [
|
||||||
|
'All user permissions',
|
||||||
|
'Create and manage members',
|
||||||
|
'Create and manage events',
|
||||||
|
'View member statistics',
|
||||||
|
'Access board tools and reports'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Administrator',
|
||||||
|
value: 'admin',
|
||||||
|
description: 'Full system access',
|
||||||
|
icon: 'mdi-shield-crown',
|
||||||
|
color: 'error',
|
||||||
|
permissions: [
|
||||||
|
'All board member permissions',
|
||||||
|
'System configuration and settings',
|
||||||
|
'User and group management',
|
||||||
|
'Delete members and sensitive operations',
|
||||||
|
'Access admin panel and logs'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectedGroupInfo = computed(() => {
|
||||||
|
return groupOptions.find(group => group.value === selectedGroup.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validation rules
|
||||||
|
const groupRules = [
|
||||||
|
(v: string) => !!v || 'Please select a group'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
const cancel = () => {
|
||||||
|
errorMessage.value = '';
|
||||||
|
selectedGroup.value = 'user';
|
||||||
|
isOpen.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAccount = async () => {
|
||||||
|
if (!formValid.value || !props.member) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
errorMessage.value = '';
|
||||||
|
|
||||||
|
console.log('[CreatePortalAccountDialog] Creating portal account for:', props.member.email, 'Group:', selectedGroup.value);
|
||||||
|
|
||||||
|
const response = await $fetch(`/api/members/${props.member.Id}/create-portal-account`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
membershipTier: selectedGroup.value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response?.success) {
|
||||||
|
console.log('[CreatePortalAccountDialog] Portal account created successfully');
|
||||||
|
|
||||||
|
// Update the member object with the keycloak_id
|
||||||
|
const updatedMember = {
|
||||||
|
...props.member,
|
||||||
|
keycloak_id: response.data?.keycloak_id
|
||||||
|
};
|
||||||
|
|
||||||
|
emit('account-created', updatedMember);
|
||||||
|
isOpen.value = false;
|
||||||
|
|
||||||
|
// Reset form
|
||||||
|
selectedGroup.value = 'user';
|
||||||
|
} else {
|
||||||
|
throw new Error(response?.message || 'Failed to create portal account');
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('[CreatePortalAccountDialog] Error creating portal account:', err);
|
||||||
|
|
||||||
|
// Better error handling
|
||||||
|
let message = 'Failed to create portal account. Please try again.';
|
||||||
|
if (err.statusCode === 409) {
|
||||||
|
message = 'This member already has a portal account or a user with this email already exists.';
|
||||||
|
} else if (err.statusCode === 400) {
|
||||||
|
message = 'Member must have email, first name, and last name to create a portal account.';
|
||||||
|
} else if (err.data?.message) {
|
||||||
|
message = err.data.message;
|
||||||
|
} else if (err.message) {
|
||||||
|
message = err.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessage.value = message;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset form when dialog opens
|
||||||
|
watch(() => props.modelValue, (newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
selectedGroup.value = 'user';
|
||||||
|
errorMessage.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.v-card {
|
||||||
|
border-radius: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-list-item-subtitle {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Improve list styling */
|
||||||
|
ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Better mobile spacing */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.v-card-actions {
|
||||||
|
padding: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-card-text {
|
||||||
|
padding: 16px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -29,6 +29,22 @@
|
||||||
|
|
||||||
<!-- Password Setup Form -->
|
<!-- Password Setup Form -->
|
||||||
<v-form ref="formRef" v-model="formValid" @submit.prevent="setupPassword">
|
<v-form ref="formRef" v-model="formValid" @submit.prevent="setupPassword">
|
||||||
|
<!-- Email Input (shown when email is not provided in URL) -->
|
||||||
|
<v-text-field
|
||||||
|
v-if="showEmailInput"
|
||||||
|
v-model="email"
|
||||||
|
label="Email Address"
|
||||||
|
variant="outlined"
|
||||||
|
density="comfortable"
|
||||||
|
:rules="emailRules"
|
||||||
|
:error="!!errorMessage"
|
||||||
|
prepend-inner-icon="mdi-email"
|
||||||
|
class="mb-3"
|
||||||
|
autocomplete="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="Enter your email address"
|
||||||
|
/>
|
||||||
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="password"
|
v-model="password"
|
||||||
:type="showPassword ? 'text' : 'password'"
|
:type="showPassword ? 'text' : 'password'"
|
||||||
|
|
@ -187,6 +203,7 @@ const confirmPassword = ref('');
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const email = ref((route.query.email as string) || '');
|
const email = ref((route.query.email as string) || '');
|
||||||
const token = ref((route.query.token as string) || '');
|
const token = ref((route.query.token as string) || '');
|
||||||
|
const showEmailInput = ref(!email.value); // Show email input if email is not provided
|
||||||
|
|
||||||
// Form ref
|
// Form ref
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
|
|
@ -235,6 +252,11 @@ const confirmPasswordRules = [
|
||||||
(v: string) => v === password.value || 'Passwords do not match',
|
(v: string) => v === password.value || 'Passwords do not match',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const emailRules = [
|
||||||
|
(v: string) => !!v || 'Email address is required',
|
||||||
|
(v: string) => /.+@.+\..+/.test(v) || 'Please enter a valid email address',
|
||||||
|
];
|
||||||
|
|
||||||
// Set page title with mobile viewport optimization
|
// Set page title with mobile viewport optimization
|
||||||
useHead({
|
useHead({
|
||||||
title: 'Set Your Password - MonacoUSA Portal',
|
title: 'Set Your Password - MonacoUSA Portal',
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,13 @@
|
||||||
@edit="editMember"
|
@edit="editMember"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Create Portal Account Dialog -->
|
||||||
|
<CreatePortalAccountDialog
|
||||||
|
v-model="showCreatePortalAccountDialog"
|
||||||
|
:member="selectedMemberForPortalAccount"
|
||||||
|
@account-created="handlePortalAccountCreated"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Delete Confirmation Dialog -->
|
<!-- Delete Confirmation Dialog -->
|
||||||
<v-dialog v-model="showDeleteDialog" max-width="400">
|
<v-dialog v-model="showDeleteDialog" max-width="400">
|
||||||
<v-card>
|
<v-card>
|
||||||
|
|
@ -285,7 +292,9 @@ const showAddDialog = ref(false);
|
||||||
const showEditDialog = ref(false);
|
const showEditDialog = ref(false);
|
||||||
const showViewDialog = ref(false);
|
const showViewDialog = ref(false);
|
||||||
const showDeleteDialog = ref(false);
|
const showDeleteDialog = ref(false);
|
||||||
|
const showCreatePortalAccountDialog = ref(false);
|
||||||
const selectedMember = ref<Member | null>(null);
|
const selectedMember = ref<Member | null>(null);
|
||||||
|
const selectedMemberForPortalAccount = ref<Member | null>(null);
|
||||||
const deleteLoading = ref(false);
|
const deleteLoading = ref(false);
|
||||||
|
|
||||||
// Success handling
|
// Success handling
|
||||||
|
|
@ -512,55 +521,20 @@ const handleMemberUpdated = (updatedMember: Member) => {
|
||||||
successMessage.value = `${updatedMember.FullName} has been updated successfully.`;
|
successMessage.value = `${updatedMember.FullName} has been updated successfully.`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createPortalAccount = async (member: Member) => {
|
const createPortalAccount = (member: Member) => {
|
||||||
if (!member.Id || creatingPortalAccountIds.value.includes(member.Id)) return;
|
selectedMemberForPortalAccount.value = member;
|
||||||
|
showCreatePortalAccountDialog.value = true;
|
||||||
// Add to creating array to show loading state
|
};
|
||||||
creatingPortalAccountIds.value.push(member.Id);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await $fetch<any>(`/api/members/${member.Id}/create-portal-account`, {
|
|
||||||
method: 'POST'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response?.success) {
|
const handlePortalAccountCreated = (updatedMember: Member) => {
|
||||||
// Update the member in the local array to reflect the new keycloak_id
|
// Update the member in the local array to reflect the new keycloak_id
|
||||||
const index = members.value.findIndex(m => m.Id === member.Id);
|
const index = members.value.findIndex(m => m.Id === updatedMember.Id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
// Get keycloak_id from response.data
|
members.value[index] = updatedMember;
|
||||||
members.value[index] = { ...members.value[index], keycloak_id: response.data?.keycloak_id };
|
|
||||||
}
|
|
||||||
|
|
||||||
showSuccess.value = true;
|
|
||||||
successMessage.value = response.message || `Portal account created successfully for ${member.FullName}.`;
|
|
||||||
} else {
|
|
||||||
throw new Error(response?.message || 'Failed to create portal account');
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error('Error creating portal account:', err);
|
|
||||||
|
|
||||||
// Better error handling
|
|
||||||
let errorMessage = 'Failed to create portal account. Please try again.';
|
|
||||||
if (err.statusCode === 409) {
|
|
||||||
errorMessage = 'This member already has a portal account or a user with this email already exists.';
|
|
||||||
} else if (err.statusCode === 400) {
|
|
||||||
errorMessage = 'Member must have email, first name, and last name to create a portal account.';
|
|
||||||
} else if (err.data?.message) {
|
|
||||||
errorMessage = err.data.message;
|
|
||||||
} else if (err.message) {
|
|
||||||
errorMessage = err.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error in snackbar
|
|
||||||
showSuccess.value = true; // Reuse success snackbar for errors
|
|
||||||
successMessage.value = errorMessage;
|
|
||||||
} finally {
|
|
||||||
// Remove from creating array
|
|
||||||
const index = creatingPortalAccountIds.value.indexOf(member.Id);
|
|
||||||
if (index > -1) {
|
|
||||||
creatingPortalAccountIds.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showSuccess.value = true;
|
||||||
|
successMessage.value = `Portal account created successfully for ${updatedMember.FullName}.`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Overdue dues handlers
|
// Overdue dues handlers
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,11 @@ export default defineEventHandler(async (event) => {
|
||||||
|
|
||||||
console.log('[api/members/[id]/create-portal-account.post] Processing member ID:', memberId);
|
console.log('[api/members/[id]/create-portal-account.post] Processing member ID:', memberId);
|
||||||
|
|
||||||
|
// Get request body for membershipTier
|
||||||
|
const body = await readBody(event).catch(() => ({}));
|
||||||
|
const requestedMembershipTier = body.membershipTier;
|
||||||
|
console.log('[api/members/[id]/create-portal-account.post] Requested membership tier:', requestedMembershipTier);
|
||||||
|
|
||||||
// 1. Get member data
|
// 1. Get member data
|
||||||
const { getMemberById } = await import('~/server/utils/nocodb');
|
const { getMemberById } = await import('~/server/utils/nocodb');
|
||||||
const member = await getMemberById(memberId);
|
const member = await getMemberById(memberId);
|
||||||
|
|
@ -81,8 +86,8 @@ export default defineEventHandler(async (event) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Determine membership tier based on member data
|
// 5. Determine membership tier based on request or member data
|
||||||
const membershipTier = determineMembershipTier(member);
|
const membershipTier = determineMembershipTier(member, requestedMembershipTier);
|
||||||
console.log('[api/members/[id]/create-portal-account.post] Determined membership tier:', membershipTier);
|
console.log('[api/members/[id]/create-portal-account.post] Determined membership tier:', membershipTier);
|
||||||
|
|
||||||
// 6. Prepare membership data for Keycloak sync
|
// 6. Prepare membership data for Keycloak sync
|
||||||
|
|
@ -191,7 +196,12 @@ export default defineEventHandler(async (event) => {
|
||||||
* Determine membership tier based on member data
|
* Determine membership tier based on member data
|
||||||
* This function analyzes member information to assign appropriate portal roles
|
* This function analyzes member information to assign appropriate portal roles
|
||||||
*/
|
*/
|
||||||
function determineMembershipTier(member: any): 'user' | 'board' | 'admin' {
|
function determineMembershipTier(member: any, requestedTier?: string): 'user' | 'board' | 'admin' {
|
||||||
|
// If a valid tier was specifically requested, use that
|
||||||
|
if (requestedTier && ['user', 'board', 'admin'].includes(requestedTier)) {
|
||||||
|
console.log(`[determineMembershipTier] Using requested tier: ${requestedTier}`);
|
||||||
|
return requestedTier as 'user' | 'board' | 'admin';
|
||||||
|
}
|
||||||
// Use stored portal_group value if available and valid
|
// Use stored portal_group value if available and valid
|
||||||
if (member.portal_group && ['user', 'board', 'admin'].includes(member.portal_group)) {
|
if (member.portal_group && ['user', 'board', 'admin'].includes(member.portal_group)) {
|
||||||
console.log(`[determineMembershipTier] Using stored portal_group: ${member.portal_group}`);
|
console.log(`[determineMembershipTier] Using stored portal_group: ${member.portal_group}`);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { createMember, handleNocoDbError } from '~/server/utils/nocodb';
|
import { createMember, handleNocoDbError, normalizeFieldsFromNocoDB } from '~/server/utils/nocodb';
|
||||||
import { createSessionManager } from '~/server/utils/session';
|
import { createSessionManager } from '~/server/utils/session';
|
||||||
import { generateMemberID } from '~/server/utils/member-id';
|
import { generateMemberID } from '~/server/utils/member-id';
|
||||||
import type { Member, MembershipStatus } from '~/utils/types';
|
import type { Member, MembershipStatus } from '~/utils/types';
|
||||||
|
|
@ -55,17 +55,32 @@ export default defineEventHandler(async (event) => {
|
||||||
console.log('[api/members.post] Sanitized data fields:', Object.keys(memberData));
|
console.log('[api/members.post] Sanitized data fields:', Object.keys(memberData));
|
||||||
|
|
||||||
// Create member in NocoDB
|
// Create member in NocoDB
|
||||||
const newMember = await createMember(memberData);
|
const rawNewMember = await createMember(memberData);
|
||||||
|
|
||||||
console.log('[api/members.post] ✅ Member created successfully with ID:', newMember.Id);
|
console.log('[api/members.post] ✅ Member created successfully with ID:', rawNewMember.Id);
|
||||||
|
|
||||||
|
// DIAGNOSTIC: Log raw member data structure from creation
|
||||||
|
console.log('[api/members.post] DIAGNOSTIC - Raw created member:', JSON.stringify(rawNewMember, null, 2));
|
||||||
|
console.log('[api/members.post] DIAGNOSTIC - Raw member fields:', Object.keys(rawNewMember));
|
||||||
|
|
||||||
// Return processed member
|
// Apply field normalization (same as in get members API)
|
||||||
|
const normalizedMember = normalizeFieldsFromNocoDB(rawNewMember);
|
||||||
|
console.log('[api/members.post] Applied field normalization');
|
||||||
|
console.log('[api/members.post] DIAGNOSTIC - Normalized first_name:', normalizedMember.first_name);
|
||||||
|
console.log('[api/members.post] DIAGNOSTIC - Normalized last_name:', normalizedMember.last_name);
|
||||||
|
|
||||||
|
// Add computed fields (same as in get members API)
|
||||||
|
const fullName = `${normalizedMember.first_name || ''} ${normalizedMember.last_name || ''}`.trim();
|
||||||
const processedMember = {
|
const processedMember = {
|
||||||
...newMember,
|
...normalizedMember,
|
||||||
FullName: `${newMember.first_name || ''} ${newMember.last_name || ''}`.trim(),
|
FullName: fullName,
|
||||||
FormattedPhone: formatPhoneNumber(newMember.phone)
|
FormattedPhone: formatPhoneNumber(normalizedMember.phone)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('[api/members.post] DIAGNOSTIC - Final FullName:', `"${processedMember.FullName}"`);
|
||||||
|
console.log('[api/members.post] DIAGNOSTIC - FullName calculation:',
|
||||||
|
`"${normalizedMember.first_name || ''}" + " " + "${normalizedMember.last_name || ''}" = "${fullName}"`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: processedMember,
|
data: processedMember,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue