Migrate member fields to snake_case naming convention
Build And Push Image / docker (push) Successful in 3m11s
Details
Build And Push Image / docker (push) Successful in 3m11s
Details
Convert field names from space-separated format to snake_case across member API endpoints and validation logic. Add migration guide for reference.
This commit is contained in:
parent
f096a22824
commit
d36209818a
|
|
@ -0,0 +1,187 @@
|
|||
# MonacoUSA Portal - Snake Case Field Migration Guide
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
This document provides complete instructions for migrating from space-separated field names (e.g., "First Name") to snake_case field names (e.g., "first_name") to eliminate data corruption issues when editing records directly in NocoDB.
|
||||
|
||||
## 📊 Required NocoDB Field Name Changes
|
||||
|
||||
You need to rename the following fields in your NocoDB Members table:
|
||||
|
||||
| **Current Field Name** | **New Snake Case Name** | **Type** |
|
||||
|----------------------------|----------------------------|-------------|
|
||||
| `First Name` | `first_name` | Text |
|
||||
| `Last Name` | `last_name` | Text |
|
||||
| `Email` | `email` | Email |
|
||||
| `Phone` | `phone` | Text |
|
||||
| `Date of Birth` | `date_of_birth` | Date |
|
||||
| `Nationality` | `nationality` | Text |
|
||||
| `Address` | `address` | LongText |
|
||||
| `Membership Status` | `membership_status` | SingleSelect|
|
||||
| `Member Since` | `member_since` | Date |
|
||||
| `Current Year Dues Paid` | `current_year_dues_paid` | Text |
|
||||
| `Membership Date Paid` | `membership_date_paid` | Date |
|
||||
| `Payment Due Date` | `payment_due_date` | Date |
|
||||
|
||||
### 🔧 How to Rename Fields in NocoDB
|
||||
|
||||
1. **Open your NocoDB Members table**
|
||||
2. **For each field above:**
|
||||
- Click on the field header
|
||||
- Select "Edit" or click the gear icon
|
||||
- Change the field name from the old name to the new snake_case name
|
||||
- Click "Save"
|
||||
|
||||
⚠️ **Important**: Do NOT change the field types, only the names.
|
||||
|
||||
## 🏗️ Backend Files Updated
|
||||
|
||||
The following files have been completely updated to use snake_case field names:
|
||||
|
||||
### Type Definitions
|
||||
- ✅ `utils/types.ts` - Member interface updated
|
||||
|
||||
### NocoDB Utilities
|
||||
- ✅ `server/utils/nocodb.ts` - All CRUD operations updated
|
||||
|
||||
### API Endpoints
|
||||
- ✅ `server/api/members/index.get.ts` - List members API
|
||||
- ✅ `server/api/members/[id].get.ts` - Get single member API
|
||||
- ✅ `server/api/members/index.post.ts` - Create member API
|
||||
- ✅ `server/api/members/[id].put.ts` - Update member API
|
||||
|
||||
## 🎨 Frontend Files That Need Updates
|
||||
|
||||
The following frontend components still reference the old field names and need to be updated:
|
||||
|
||||
### Vue Components
|
||||
- `components/ViewMemberDialog.vue`
|
||||
- `components/MemberCard.vue`
|
||||
- `components/EditMemberDialog.vue`
|
||||
- `components/AddMemberDialog.vue`
|
||||
- `pages/dashboard/member-list.vue`
|
||||
|
||||
## 🔄 Complete Field Mapping Reference
|
||||
|
||||
### Data Access Patterns
|
||||
|
||||
**Before (❌ Old)**:
|
||||
```javascript
|
||||
member['First Name']
|
||||
member['Last Name']
|
||||
member['Membership Status']
|
||||
member['Current Year Dues Paid']
|
||||
```
|
||||
|
||||
**After (✅ New)**:
|
||||
```javascript
|
||||
member.first_name
|
||||
member.last_name
|
||||
member.membership_status
|
||||
member.current_year_dues_paid
|
||||
```
|
||||
|
||||
### API Request/Response Format
|
||||
|
||||
**Create/Update Member Payload**:
|
||||
```json
|
||||
{
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"email": "john.doe@example.com",
|
||||
"phone": "+1234567890",
|
||||
"nationality": "US",
|
||||
"membership_status": "Active",
|
||||
"current_year_dues_paid": "true",
|
||||
"date_of_birth": "1990-01-15",
|
||||
"member_since": "2023-01-01",
|
||||
"membership_date_paid": "2024-01-15",
|
||||
"payment_due_date": "2025-01-15",
|
||||
"address": "123 Main St, City, State"
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 Testing Checklist
|
||||
|
||||
After making the NocoDB field changes, test the following:
|
||||
|
||||
### Backend API Testing
|
||||
- [ ] `GET /api/members` - List all members
|
||||
- [ ] `GET /api/members/{id}` - Get single member
|
||||
- [ ] `POST /api/members` - Create new member
|
||||
- [ ] `PUT /api/members/{id}` - Update existing member
|
||||
- [ ] `DELETE /api/members/{id}` - Delete member
|
||||
|
||||
### Data Integrity Testing
|
||||
- [ ] Create member via portal → Verify in NocoDB
|
||||
- [ ] Edit member via portal → Verify in NocoDB
|
||||
- [ ] Edit member in NocoDB directly → Verify portal displays correctly
|
||||
- [ ] All fields display properly (names, flags, contact info)
|
||||
|
||||
### Frontend Display Testing
|
||||
- [ ] Member list page loads and displays all members
|
||||
- [ ] Member cards show correct information
|
||||
- [ ] Country flags display properly
|
||||
- [ ] Search and filtering work correctly
|
||||
- [ ] Add member dialog works
|
||||
- [ ] Edit member dialog works and pre-populates correctly
|
||||
- [ ] View member dialog shows all details
|
||||
|
||||
## 🚨 Critical Success Criteria
|
||||
|
||||
The migration is successful when:
|
||||
|
||||
1. ✅ **No "undefined undefined" names appear** in member cards
|
||||
2. ✅ **Country flags display properly** for all nationalities
|
||||
3. ✅ **Direct NocoDB edits sync properly** with portal display
|
||||
4. ✅ **All CRUD operations work** through the portal
|
||||
5. ✅ **No TypeScript errors** in the console
|
||||
6. ✅ **No API errors** in browser network tab
|
||||
|
||||
## 🔧 Rollback Plan
|
||||
|
||||
If issues occur, you can temporarily rollback by:
|
||||
|
||||
1. **Revert NocoDB field names** back to the original space-separated format
|
||||
2. **Revert the backend files** using git:
|
||||
```bash
|
||||
git checkout HEAD~1 -- utils/types.ts server/utils/nocodb.ts server/api/members/
|
||||
```
|
||||
|
||||
## 📈 Next Steps After Migration
|
||||
|
||||
1. **Update frontend components** to use snake_case field names
|
||||
2. **Test thoroughly** across all functionality
|
||||
3. **Update any documentation** that references old field names
|
||||
4. **Consider adding validation** to prevent future field name inconsistencies
|
||||
|
||||
## 💡 Benefits After Migration
|
||||
|
||||
- ✅ **Consistent data display** regardless of edit source (portal vs NocoDB)
|
||||
- ✅ **No more "undefined undefined" member names**
|
||||
- ✅ **Proper country flag rendering**
|
||||
- ✅ **Standard database naming conventions**
|
||||
- ✅ **Easier debugging and maintenance**
|
||||
- ✅ **Better API consistency**
|
||||
|
||||
## 🆘 Troubleshooting
|
||||
|
||||
### Issue: Members showing "undefined undefined"
|
||||
- **Cause**: NocoDB field names don't match backend expectations
|
||||
- **Solution**: Verify all field names in NocoDB match the snake_case format exactly
|
||||
|
||||
### Issue: Country flags not displaying
|
||||
- **Cause**: Nationality field not properly mapped
|
||||
- **Solution**: Ensure `Nationality` field is renamed to `nationality` in NocoDB
|
||||
|
||||
### Issue: API errors in console
|
||||
- **Cause**: Field name mismatch between frontend and backend
|
||||
- **Solution**: Update frontend components to use snake_case field names
|
||||
|
||||
### Issue: Direct NocoDB edits causing corruption
|
||||
- **Cause**: This was the original problem - should be fixed after migration
|
||||
- **Solution**: This migration specifically addresses this issue
|
||||
|
||||
---
|
||||
|
||||
**🎉 Once you complete the NocoDB field renaming, the backend will be fully compatible with snake_case field names and the data corruption issue should be completely resolved!**
|
||||
|
|
@ -20,8 +20,8 @@ export default defineEventHandler(async (event) => {
|
|||
// Add computed fields
|
||||
const processedMember = {
|
||||
...member,
|
||||
FullName: `${member['First Name'] || ''} ${member['Last Name'] || ''}`.trim(),
|
||||
FormattedPhone: formatPhoneNumber(member.Phone)
|
||||
FullName: `${member.first_name || ''} ${member.last_name || ''}`.trim(),
|
||||
FormattedPhone: formatPhoneNumber(member.phone)
|
||||
};
|
||||
|
||||
console.log('[api/members/[id].get] ✅ Successfully retrieved member:', id);
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ export default defineEventHandler(async (event) => {
|
|||
// Return processed member
|
||||
const processedMember = {
|
||||
...updatedMember,
|
||||
FullName: `${updatedMember['First Name'] || ''} ${updatedMember['Last Name'] || ''}`.trim(),
|
||||
FormattedPhone: formatPhoneNumber(updatedMember.Phone)
|
||||
FullName: `${updatedMember.first_name || ''} ${updatedMember.last_name || ''}`.trim(),
|
||||
FormattedPhone: formatPhoneNumber(updatedMember.phone)
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
@ -85,34 +85,34 @@ function validateMemberUpdateData(data: any): string[] {
|
|||
const errors: string[] = [];
|
||||
|
||||
// Only validate fields that are provided (partial updates allowed)
|
||||
if (data['First Name'] !== undefined) {
|
||||
if (!data['First Name'] || typeof data['First Name'] !== 'string' || data['First Name'].trim().length < 2) {
|
||||
if (data.first_name !== undefined) {
|
||||
if (!data.first_name || typeof data.first_name !== 'string' || data.first_name.trim().length < 2) {
|
||||
errors.push('First Name must be at least 2 characters');
|
||||
}
|
||||
}
|
||||
|
||||
if (data['Last Name'] !== undefined) {
|
||||
if (!data['Last Name'] || typeof data['Last Name'] !== 'string' || data['Last Name'].trim().length < 2) {
|
||||
if (data.last_name !== undefined) {
|
||||
if (!data.last_name || typeof data.last_name !== 'string' || data.last_name.trim().length < 2) {
|
||||
errors.push('Last Name must be at least 2 characters');
|
||||
}
|
||||
}
|
||||
|
||||
if (data.Email !== undefined) {
|
||||
if (!data.Email || typeof data.Email !== 'string' || !isValidEmail(data.Email)) {
|
||||
if (data.email !== undefined) {
|
||||
if (!data.email || typeof data.email !== 'string' || !isValidEmail(data.email)) {
|
||||
errors.push('Valid email address is required');
|
||||
}
|
||||
}
|
||||
|
||||
// Optional field validation
|
||||
if (data.Phone !== undefined && data.Phone && typeof data.Phone === 'string' && data.Phone.trim()) {
|
||||
if (data.phone !== undefined && data.phone && typeof data.phone === 'string' && data.phone.trim()) {
|
||||
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
|
||||
const cleanPhone = data.Phone.replace(/\D/g, '');
|
||||
const cleanPhone = data.phone.replace(/\D/g, '');
|
||||
if (!phoneRegex.test(cleanPhone)) {
|
||||
errors.push('Phone number format is invalid');
|
||||
}
|
||||
}
|
||||
|
||||
if (data['Membership Status'] !== undefined && !['Active', 'Inactive', 'Pending', 'Expired'].includes(data['Membership Status'])) {
|
||||
if (data.membership_status !== undefined && !['Active', 'Inactive', 'Pending', 'Expired'].includes(data.membership_status)) {
|
||||
errors.push('Invalid membership status');
|
||||
}
|
||||
|
||||
|
|
@ -123,25 +123,25 @@ function sanitizeMemberUpdateData(data: any): Partial<Member> {
|
|||
const sanitized: any = {};
|
||||
|
||||
// Only include fields that are provided (partial updates)
|
||||
if (data['First Name'] !== undefined) sanitized['First Name'] = data['First Name'].trim();
|
||||
if (data['Last Name'] !== undefined) sanitized['Last Name'] = data['Last Name'].trim();
|
||||
if (data.Email !== undefined) sanitized['Email'] = data.Email.trim().toLowerCase();
|
||||
if (data.Phone !== undefined) sanitized.Phone = data.Phone ? data.Phone.trim() : null;
|
||||
if (data.Nationality !== undefined) sanitized.Nationality = data.Nationality ? data.Nationality.trim() : null;
|
||||
if (data.Address !== undefined) sanitized.Address = data.Address ? data.Address.trim() : null;
|
||||
if (data['Date of Birth'] !== undefined) sanitized['Date of Birth'] = data['Date of Birth'];
|
||||
if (data['Member Since'] !== undefined) sanitized['Member Since'] = data['Member Since'];
|
||||
if (data['Membership Date Paid'] !== undefined) sanitized['Membership Date Paid'] = data['Membership Date Paid'];
|
||||
if (data['Payment Due Date'] !== undefined) sanitized['Payment Due Date'] = data['Payment Due Date'];
|
||||
if (data.first_name !== undefined) sanitized.first_name = data.first_name.trim();
|
||||
if (data.last_name !== undefined) sanitized.last_name = data.last_name.trim();
|
||||
if (data.email !== undefined) sanitized.email = data.email.trim().toLowerCase();
|
||||
if (data.phone !== undefined) sanitized.phone = data.phone ? data.phone.trim() : null;
|
||||
if (data.nationality !== undefined) sanitized.nationality = data.nationality ? data.nationality.trim() : null;
|
||||
if (data.address !== undefined) sanitized.address = data.address ? data.address.trim() : null;
|
||||
if (data.date_of_birth !== undefined) sanitized.date_of_birth = data.date_of_birth;
|
||||
if (data.member_since !== undefined) sanitized.member_since = data.member_since;
|
||||
if (data.membership_date_paid !== undefined) sanitized.membership_date_paid = data.membership_date_paid;
|
||||
if (data.payment_due_date !== undefined) sanitized.payment_due_date = data.payment_due_date;
|
||||
|
||||
// Boolean fields
|
||||
if (data['Current Year Dues Paid'] !== undefined) {
|
||||
sanitized['Current Year Dues Paid'] = Boolean(data['Current Year Dues Paid']);
|
||||
if (data.current_year_dues_paid !== undefined) {
|
||||
sanitized.current_year_dues_paid = Boolean(data.current_year_dues_paid) ? 'true' : 'false';
|
||||
}
|
||||
|
||||
// Enum fields
|
||||
if (data['Membership Status'] !== undefined) {
|
||||
sanitized['Membership Status'] = data['Membership Status'];
|
||||
if (data.membership_status !== undefined) {
|
||||
sanitized.membership_status = data.membership_status;
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
|
|
|
|||
|
|
@ -33,33 +33,33 @@ export default defineEventHandler(async (event) => {
|
|||
if (searchTerm) {
|
||||
const search = searchTerm.toLowerCase();
|
||||
members = members.filter(member =>
|
||||
member['First Name']?.toLowerCase().includes(search) ||
|
||||
member['Last Name']?.toLowerCase().includes(search) ||
|
||||
member.Email?.toLowerCase().includes(search)
|
||||
member.first_name?.toLowerCase().includes(search) ||
|
||||
member.last_name?.toLowerCase().includes(search) ||
|
||||
member.email?.toLowerCase().includes(search)
|
||||
);
|
||||
console.log('[api/members.get] After search filter:', members.length);
|
||||
}
|
||||
|
||||
if (nationality) {
|
||||
members = members.filter(member => member.Nationality === nationality);
|
||||
members = members.filter(member => member.nationality === nationality);
|
||||
console.log('[api/members.get] After nationality filter:', members.length);
|
||||
}
|
||||
|
||||
if (membershipStatus) {
|
||||
members = members.filter(member => member['Membership Status'] === membershipStatus);
|
||||
members = members.filter(member => member.membership_status === membershipStatus);
|
||||
console.log('[api/members.get] After status filter:', members.length);
|
||||
}
|
||||
|
||||
if (duesPaid === 'true' || duesPaid === 'false') {
|
||||
members = members.filter(member => member['Current Year Dues Paid'] === duesPaid);
|
||||
members = members.filter(member => member.current_year_dues_paid === duesPaid);
|
||||
console.log('[api/members.get] After dues filter:', members.length);
|
||||
}
|
||||
|
||||
// Add computed fields
|
||||
const processedMembers = members.map(member => ({
|
||||
...member,
|
||||
FullName: `${member['First Name'] || ''} ${member['Last Name'] || ''}`.trim(),
|
||||
FormattedPhone: formatPhoneNumber(member.Phone)
|
||||
FullName: `${member.first_name || ''} ${member.last_name || ''}`.trim(),
|
||||
FormattedPhone: formatPhoneNumber(member.phone)
|
||||
}));
|
||||
|
||||
console.log('[api/members.get] ✅ Successfully processed', processedMembers.length, 'members');
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ export default defineEventHandler(async (event) => {
|
|||
// Return processed member
|
||||
const processedMember = {
|
||||
...newMember,
|
||||
FullName: `${newMember['First Name'] || ''} ${newMember['Last Name'] || ''}`.trim(),
|
||||
FormattedPhone: formatPhoneNumber(newMember.Phone)
|
||||
FullName: `${newMember.first_name || ''} ${newMember.last_name || ''}`.trim(),
|
||||
FormattedPhone: formatPhoneNumber(newMember.phone)
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
@ -76,28 +76,28 @@ function validateMemberData(data: any): string[] {
|
|||
const errors: string[] = [];
|
||||
|
||||
// Required fields
|
||||
if (!data['First Name'] || typeof data['First Name'] !== 'string' || data['First Name'].trim().length < 2) {
|
||||
if (!data.first_name || typeof data.first_name !== 'string' || data.first_name.trim().length < 2) {
|
||||
errors.push('First Name is required and must be at least 2 characters');
|
||||
}
|
||||
|
||||
if (!data['Last Name'] || typeof data['Last Name'] !== 'string' || data['Last Name'].trim().length < 2) {
|
||||
if (!data.last_name || typeof data.last_name !== 'string' || data.last_name.trim().length < 2) {
|
||||
errors.push('Last Name is required and must be at least 2 characters');
|
||||
}
|
||||
|
||||
if (!data.Email || typeof data.Email !== 'string' || !isValidEmail(data.Email)) {
|
||||
if (!data.email || typeof data.email !== 'string' || !isValidEmail(data.email)) {
|
||||
errors.push('Valid email address is required');
|
||||
}
|
||||
|
||||
// Optional field validation
|
||||
if (data.Phone && typeof data.Phone === 'string' && data.Phone.trim()) {
|
||||
if (data.phone && typeof data.phone === 'string' && data.phone.trim()) {
|
||||
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
|
||||
const cleanPhone = data.Phone.replace(/\D/g, '');
|
||||
const cleanPhone = data.phone.replace(/\D/g, '');
|
||||
if (!phoneRegex.test(cleanPhone)) {
|
||||
errors.push('Phone number format is invalid');
|
||||
}
|
||||
}
|
||||
|
||||
if (data['Membership Status'] && !['Active', 'Inactive', 'Pending', 'Expired'].includes(data['Membership Status'])) {
|
||||
if (data.membership_status && !['Active', 'Inactive', 'Pending', 'Expired'].includes(data.membership_status)) {
|
||||
errors.push('Invalid membership status');
|
||||
}
|
||||
|
||||
|
|
@ -108,24 +108,24 @@ function sanitizeMemberData(data: any): Partial<Member> {
|
|||
const sanitized: any = {};
|
||||
|
||||
// Required fields
|
||||
sanitized['First Name'] = data['First Name'].trim();
|
||||
sanitized['Last Name'] = data['Last Name'].trim();
|
||||
sanitized['Email'] = data.Email.trim().toLowerCase();
|
||||
sanitized.first_name = data.first_name.trim();
|
||||
sanitized.last_name = data.last_name.trim();
|
||||
sanitized.email = data.email.trim().toLowerCase();
|
||||
|
||||
// Optional fields
|
||||
if (data.Phone) sanitized.Phone = data.Phone.trim();
|
||||
if (data.Nationality) sanitized.Nationality = data.Nationality.trim();
|
||||
if (data.Address) sanitized.Address = data.Address.trim();
|
||||
if (data['Date of Birth']) sanitized['Date of Birth'] = data['Date of Birth'];
|
||||
if (data['Member Since']) sanitized['Member Since'] = data['Member Since'];
|
||||
if (data['Membership Date Paid']) sanitized['Membership Date Paid'] = data['Membership Date Paid'];
|
||||
if (data['Payment Due Date']) sanitized['Payment Due Date'] = data['Payment Due Date'];
|
||||
if (data.phone) sanitized.phone = data.phone.trim();
|
||||
if (data.nationality) sanitized.nationality = data.nationality.trim();
|
||||
if (data.address) sanitized.address = data.address.trim();
|
||||
if (data.date_of_birth) sanitized.date_of_birth = data.date_of_birth;
|
||||
if (data.member_since) sanitized.member_since = data.member_since;
|
||||
if (data.membership_date_paid) sanitized.membership_date_paid = data.membership_date_paid;
|
||||
if (data.payment_due_date) sanitized.payment_due_date = data.payment_due_date;
|
||||
|
||||
// Boolean fields
|
||||
sanitized['Current Year Dues Paid'] = Boolean(data['Current Year Dues Paid']);
|
||||
sanitized.current_year_dues_paid = Boolean(data.current_year_dues_paid) ? 'true' : 'false';
|
||||
|
||||
// Enum fields
|
||||
sanitized['Membership Status'] = data['Membership Status'] || 'Pending';
|
||||
sanitized.membership_status = data.membership_status || 'Pending';
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,18 +189,18 @@ export const createMember = async (data: Partial<Member>): Promise<Member> => {
|
|||
|
||||
// Only include fields that are part of the member schema
|
||||
const allowedFields = [
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Email",
|
||||
"Phone",
|
||||
"Current Year Dues Paid",
|
||||
"Nationality",
|
||||
"Date of Birth",
|
||||
"Membership Date Paid",
|
||||
"Payment Due Date",
|
||||
"Membership Status",
|
||||
"Address",
|
||||
"Member Since"
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"phone",
|
||||
"current_year_dues_paid",
|
||||
"nationality",
|
||||
"date_of_birth",
|
||||
"membership_date_paid",
|
||||
"payment_due_date",
|
||||
"membership_status",
|
||||
"address",
|
||||
"member_since"
|
||||
];
|
||||
|
||||
// Filter the data to only include allowed fields
|
||||
|
|
@ -218,14 +218,14 @@ export const createMember = async (data: Partial<Member>): Promise<Member> => {
|
|||
delete cleanData.FormattedPhone;
|
||||
|
||||
// Fix date formatting for PostgreSQL
|
||||
if (cleanData['Date of Birth']) {
|
||||
cleanData['Date of Birth'] = convertDateFormat(cleanData['Date of Birth']);
|
||||
if (cleanData['date_of_birth']) {
|
||||
cleanData['date_of_birth'] = convertDateFormat(cleanData['date_of_birth']);
|
||||
}
|
||||
if (cleanData['Membership Date Paid']) {
|
||||
cleanData['Membership Date Paid'] = convertDateFormat(cleanData['Membership Date Paid']);
|
||||
if (cleanData['membership_date_paid']) {
|
||||
cleanData['membership_date_paid'] = convertDateFormat(cleanData['membership_date_paid']);
|
||||
}
|
||||
if (cleanData['Payment Due Date']) {
|
||||
cleanData['Payment Due Date'] = convertDateFormat(cleanData['Payment Due Date']);
|
||||
if (cleanData['payment_due_date']) {
|
||||
cleanData['payment_due_date'] = convertDateFormat(cleanData['payment_due_date']);
|
||||
}
|
||||
|
||||
console.log('[nocodb.createMember] Clean data fields:', Object.keys(cleanData));
|
||||
|
|
@ -271,18 +271,18 @@ export const updateMember = async (id: string, data: Partial<Member>, retryCount
|
|||
|
||||
// Only include fields that are part of the member schema
|
||||
const allowedFields = [
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Email",
|
||||
"Phone",
|
||||
"Current Year Dues Paid",
|
||||
"Nationality",
|
||||
"Date of Birth",
|
||||
"Membership Date Paid",
|
||||
"Payment Due Date",
|
||||
"Membership Status",
|
||||
"Address",
|
||||
"Member Since"
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"phone",
|
||||
"current_year_dues_paid",
|
||||
"nationality",
|
||||
"date_of_birth",
|
||||
"membership_date_paid",
|
||||
"payment_due_date",
|
||||
"membership_status",
|
||||
"address",
|
||||
"member_since"
|
||||
];
|
||||
|
||||
// Filter the data to only include allowed fields
|
||||
|
|
@ -301,14 +301,14 @@ export const updateMember = async (id: string, data: Partial<Member>, retryCount
|
|||
}
|
||||
|
||||
// Fix date formatting for PostgreSQL
|
||||
if (cleanData['Date of Birth']) {
|
||||
cleanData['Date of Birth'] = convertDateFormat(cleanData['Date of Birth']);
|
||||
if (cleanData['date_of_birth']) {
|
||||
cleanData['date_of_birth'] = convertDateFormat(cleanData['date_of_birth']);
|
||||
}
|
||||
if (cleanData['Membership Date Paid']) {
|
||||
cleanData['Membership Date Paid'] = convertDateFormat(cleanData['Membership Date Paid']);
|
||||
if (cleanData['membership_date_paid']) {
|
||||
cleanData['membership_date_paid'] = convertDateFormat(cleanData['membership_date_paid']);
|
||||
}
|
||||
if (cleanData['Payment Due Date']) {
|
||||
cleanData['Payment Due Date'] = convertDateFormat(cleanData['Payment Due Date']);
|
||||
if (cleanData['payment_due_date']) {
|
||||
cleanData['payment_due_date'] = convertDateFormat(cleanData['payment_due_date']);
|
||||
}
|
||||
|
||||
console.log('[nocodb.updateMember] Clean data fields:', Object.keys(cleanData));
|
||||
|
|
|
|||
|
|
@ -118,18 +118,18 @@ export enum MembershipStatus {
|
|||
|
||||
export interface Member {
|
||||
Id: string;
|
||||
"First Name": string;
|
||||
"Last Name": string;
|
||||
Email: string;
|
||||
Phone: string;
|
||||
"Current Year Dues Paid": string; // "true" or "false"
|
||||
Nationality: string; // "FR,MC,US" for multiple nationalities
|
||||
"Date of Birth": string;
|
||||
"Membership Date Paid": string;
|
||||
"Payment Due Date": string;
|
||||
"Membership Status": string;
|
||||
Address: string;
|
||||
"Member Since": string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
current_year_dues_paid: string; // "true" or "false"
|
||||
nationality: string; // "FR,MC,US" for multiple nationalities
|
||||
date_of_birth: string;
|
||||
membership_date_paid: string;
|
||||
payment_due_date: string;
|
||||
membership_status: string;
|
||||
address: string;
|
||||
member_since: string;
|
||||
|
||||
// Computed fields (added by processing)
|
||||
FullName?: string;
|
||||
|
|
|
|||
Loading…
Reference in New Issue