How can I create a Event with Category using wixEventsV2 API?

I’m having trouble with
When I tried to create wix event with category, I can create event only but it doesn’t add selected category with that event.

Working in
Wix Editor

What I’m trying to do
I am trying to create a event with categories created in Wix Events category dashboard. In the UI I shows existing list of category using getCategory API.

What I’ve tried so far

import { eventCategories, createEvent, updateEventCategory } from "backend/events.web.js";

import wixLocation from 'wix-location';

import wixUsers from 'wix-users';

import { authentication, currentMember } from "wix-members-frontend";




function isSupportedFile(fileName, mimeType, fileSize) {

const supportedExtensions = ['jpg', 'jpeg', 'png'];

const maxSize = 15 * 1024 * 1024;

const extension = fileName.split('.').pop().toLowerCase();

return supportedExtensions.includes(extension) && fileSize <= maxSize;

}




$w.onReady(async function () {

const isLoggedIn = authentication.loggedIn();




if (!isLoggedIn) {

wixLocation.to('/customer-portal');

return;

    }




$w('#toastText').hide();

$w('#toastBox').hide();




$w('#logout').onClick(async () => {

$w('#toastText').hide();

$w('#toastBox').hide();

await wixUsers.logout()

wixLocation.to('/customer-portal')

    });




const requestCategory = await eventCategories();

const categoryItems = requestCategory._items;




const dropdownOptions = categoryItems.map(item => ({

label: item.name,

value: item._id

    }));

categoryItems.forEach(element => {

console.log("Category: ", element.name);

console.log("ID", element._id);

    });




$w('#eventCategory').options = dropdownOptions;




if (dropdownOptions.length > 0) {

$w('#eventCategory').value = dropdownOptions[0].value;

    }




const countryMap = {

"India": "IN",

"United States": "US",

"United Kingdom": "GB",

"Australia": "AU",

"IN": "IN",

"US": "US",

"GB": "GB",

"AU": "AU"

    };




let uploadedImageUrl = null;

$w('#toastText').hide();

$w('#toastBox').hide();

// Function to toggle field visibility based on radio button selection

function toggleFields(selectedValue) {

console.log('Selected radio button value:', selectedValue);




// Hide all fields initially

$w('#physicalLocationInput').hide();

$w('#onlineLocationInput').hide();

$w('#locationTBD').hide();




// Show relevant fields based on selected value

if (selectedValue === 'Radio button1') {

$w('#physicalLocationInput').show();

console.log('Showing physicalLocationInput');

        } else if (selectedValue === 'Radio button2') {

$w('#onlineLocationInput').show();

console.log('Showing onlineLocationInput');

        } else if (selectedValue === 'Radio button3') {

$w('#locationTBD').show();

console.log('Showing input6');

        } else {

console.warn('Unknown radio button value:', selectedValue);

        }

    }




// Set default radio button and trigger field visibility

$w('#radioGroup2').value = 'Radio button1'; // Default to Radio button1

toggleFields('Radio button1'); // Initialize field visibility




// Handle radio button change

$w('#radioGroup2').onChange(() => {

const selectedValue = $w('#radioGroup2').value;

toggleFields(selectedValue);

    });




$w('#imageUpload').onChange(async () => {




if ($w('#imageUpload').value.length === 0) return;




const file = $w('#imageUpload').value[0];

const fileName = file.name;

const fileSize = file.size;




console.log('Is Supported::', isSupportedFile(fileName, fileSize));

if (!isSupportedFile(fileName, fileSize)) {

showToast('Only JPG and PNG images are allowed.','error');




await $w('#imageUpload').reset();

uploadedImageUrl = null;

return;

        }




try {

const uploadResponse = await $w('#imageUpload').startUpload();

uploadedImageUrl = uploadResponse.url;

console.log('Uploaded image URL:', uploadedImageUrl);

        } catch (error) {

console.error('Image upload error:', error);

showToast('Failed to upload image','error');

        }

    });




function getMimeType(fileName) {

const extension = fileName.split('.').pop().toLowerCase();

const mimeTypes = {

jpg: 'image/jpeg',

jpeg: 'image/jpeg',

png: 'image/png',

gif: 'image/gif',

bmp: 'image/bmp',

        };

return mimeTypes[extension] || 'image/jpeg';

    }




function isSupportedFile(fileName, fileSize) {

const supportedExtensions = ['jpg', 'jpeg', 'png'];

const maxSize = 25 * 1024 * 1024;

const extension = fileName.split('.').pop().toLowerCase();

return supportedExtensions.includes(extension) && fileSize <= maxSize;

    }




// Helper function to safely get and trim string values

function safeTrim(value, defaultValue = '') {

return typeof value === 'string' ? value.trim() : defaultValue;

    }




// Helper function to reset the form

function resetForm() {

// Reset text inputs

$w('#eventNameInput').value = '';

$w('#onlineLocationInput').value = '';

$w('#input6').value = '';

$w('#conferenceLinkInput').value = '';

$w('#shortTeaserInput').value = '';




// Reset dropdowns to defaults

$w('#dropdownCurrency').value = 'GBP';

$w('#inputTicketLimit').value = '30';

$w('#eventCategory').value = $w('#eventCategory').options[0]?.value || '';

$w('#timezoneDropdown').value = 'America/Chicago';





// Reset date/time pickers

$w('#startDatePicker').value = null;

$w('#endDatePicker').value = null;

$w('#startTimePicker').value = null;

$w('#endTimePicker').value = null;




// Reset image upload

// $w('#imageUpload').value = [];

uploadedImageUrl = null;

console.log('Image upload field reset:', $w('#imageUpload').value);

$w('#imageUpload').reset();




// Reset physical location input

$w('#physicalLocationInput').value = null; // Explicitly reset address input

console.log('Physical location input reset:', $w('#physicalLocationInput').value);




// Maintain visibility based on current radio button selection

toggleFields($w('#radioGroup2').value);

    }




// === Toast Notification System ===




function showToast(message, type = 'success') {

const isError = type === 'error';

const color = isError ? '#FF3B30' : '#28A745';

const icon = isError ? '❌' : '✅';

const backgroundColor = isError ? '#ffe6e6' : '#e6ffed';




if ($w('#toastBox').timeoutId) clearTimeout($w('#toastBox').timeoutId);




// Update text

$w('#toastText').text = `${icon} ${message}`;

$w('#toastText').style.color = color;

$w('#toastText').style.fontSize = '16px';

$w('#toastText').style.fontWeight = '600';

$w('#toastText').show();




// Style box

$w('#toastBox').style.backgroundColor = backgroundColor;

$w('#toastBox').style.border = `1px solid ${color}`;

$w('#toastBox').style.borderRadius = '12px';

$w('#toastBox').style.boxShadow = '0px 4px 12px rgba(0,0,0,0.2)';

$w('#toastBox').style.padding = '10px 18px';




// Scroll to toast

$w('#toastBox').scrollTo();




// Show and hide logic

$w('#toastBox').show('slide', { direction: 'bottom', duration: 300 });

$w('#toastBox').timeoutId = setTimeout(() => {

$w('#toastBox').hide('slide', { direction: 'bottom', duration: 300 });

    }, 4000);

}




$w('#createEventBtn').onClick(async () => {

// Validate required fields before showing loader

const eventName = safeTrim($w('#eventNameInput').value);

const selectedLocationRaw = $w('#radioGroup2').value;

const addressInput = $w('#physicalLocationInput').value;

const onlineLink = safeTrim($w('#onlineLocationInput').value);

const otherLocation = safeTrim($w('#locationTBD').value);

const startDate = $w('#startDatePicker').value;

const endDate = $w('#endDatePicker').value;

const startTime = $w('#startTimePicker').value;

const endTime = $w('#endTimePicker').value;





const result2 = addressInput.formatted.split(",");

const streetname = result2[0];

// console.log("===== Result2", streetname);




console.log('Raw inputs:', {

eventName,

physicalLocation: addressInput,

onlineLocation: onlineLink,

otherLocation,

selectedLocationType: selectedLocationRaw,

imageUpload: $w('#imageUpload').value,

uploadedImageUrl

        });




// Validate event name

if (!eventName) {

showToast('Event name is required','error');

return;

        }




const valueMap = {

'Radio button1': 'Physical location',

'Radio button2': 'Online',

'Radio button3': 'To be decided (TBD)'

        };




const selectedLocationType = valueMap[selectedLocationRaw];

//Location object

let locationData = {};






if (selectedLocationType === 'Physical location') {

if (!addressInput || !addressInput.formatted || addressInput.formatted.trim() === '') {

showToast('Physical Location is required', 'error');

return;

    }




const { city, country, streetAddress, postalCode, subdivision } = addressInput;




const countryCode = countryMap[country] || countryMap[country?.toUpperCase()] || 'IN';




// Street Name




const result2 = addressInput.formatted.split(",");

const streetname = result2[0];

console.log("===== Result2", streetname);




const streetName = streetname;

const streetNumber = safeTrim(streetAddress?.number) || '';

const validPostalCode = safeTrim(postalCode) && postalCode !== 'undefined' ? postalCode : '000000';




locationData = {

type: 'VENUE',

address: {

country: countryCode,

city: safeTrim(city),

streetAddress: {

name: streetName,

number: streetNumber

                    },

postalCode: validPostalCode,

subdivision: safeTrim(subdivision) || ''

                }

            };

        } 

else if (selectedLocationType === 'Online') {

if (onlineLink || onlineLink.trim() !== '') {

showToast('Physical location type is required', 'error');

return;

    }

locationData = {

type: 'ONLINE',

conferenceLink: safeTrim($w('#conferenceLinkInput').value)

            };

        } 




else if (selectedLocationType === 'To be decided (TBD)') {

if (otherLocation || otherLocation.trim() !== '') {

showToast('Physical location type is required', 'error');

return;

    }

locationData = {

type: 'UNKNOWN_LOCATION',

locationTbd: true,

name: otherLocation || 'To be decided'

            };

        } else {

showToast('Invalid location type selected','error');

return;

        }




// Validate date and time fields

if (!startDate || !endDate && !startTime || !endTime) {

showToast('Date and time fields are required','error');

return;

        }




// Check if end date is before start date

if (endDate < startDate) {

showToast('End date cannot be before start date', 'error');

return;

}




if (!startDate || !endDate) {

showToast('Date fields are required', 'error');

return;

        }




if (!startTime || !endTime) {

showToast('Time fields are required', 'error');

return;

        }




// If all validations pass, show the loader

const createBtn = $w('#createEventBtn');

const originalText = createBtn.label;

createBtn.label = '⏳ Creating event...';




createBtn.disable();




try {

const startTimeParsed = parseTimeValue(startTime);

const endTimeParsed = parseTimeValue(endTime);




function parseTimeValue(timeVal) {

if (!timeVal) return null;

if (timeVal instanceof Date) {

return { hours: timeVal.getHours(), minutes: timeVal.getMinutes() };

                }

if (typeof timeVal === 'string') {

const match = timeVal.match(/(\d{1,2}):(\d{2})\s*(AM|PM)?/i);

if (!match) return null;

let hours = parseInt(match[1], 10);

const minutes = parseInt(match[2], 10);

const ampm = match[3]?.toUpperCase();

if (ampm === 'PM' && hours < 12) hours += 12;

if (ampm === 'AM' && hours === 12) hours = 0;

return { hours, minutes };

                }

return null;

            }




const startDateTime = new Date(startDate);

startDateTime.setHours(startTimeParsed.hours, startTimeParsed.minutes, 0, 0);




const endDateTime = new Date(endDate);

endDateTime.setHours(endTimeParsed.hours, endTimeParsed.minutes, 0, 0);




const descriptionText = safeTrim($w('#conferenceLinkInput').value) ?

`${safeTrim($w('#conferenceLinkInput').value).replace(/<[^>]+>/g, '')}` :

'';

const category = $w('#eventCategory').value




const eventData = {

event: {

title: eventName,

location: locationData,

dateAndTimeSettings: {

startDate: startDateTime.toISOString(),

endDate: endDateTime.toISOString(),

// timeZoneId: $w('#timezoneDropdown').value || 'Asia/Kolkata'

timeZoneId: 'America/Chicago'




                    },

description: descriptionText,

languageCode: 'en',

registration: {

initialType: 'TICKETING',

tickets: {

items: [{

name: 'General Admission',

price: {

amount: '0',

currency: safeTrim($w('#dropdownCurrency').value, 'GBP')

                                },

taxSettings: { type: 'INCLUDED_IN_PRICE' },

limitPerOrder: Number(safeTrim($w('#inputTicketLimit').value, '30')) || 30

                            }]

                        }

                    },

mainImage: uploadedImageUrl || undefined

                },

draft: true,

categories: [category]

            };




console.log('Sending eventData to backend:', JSON.stringify(eventData, null, 2));




const result = await createEvent(eventData);

console.log('Event Created Result: ', result);

console.log('EventID:::', [result.data._id]);

console.log('Category Selected', category);

const categoryUpdated = await updateEventCategory(category, [result.data._id]);

console.log('Category Updated Successfully::', categoryUpdated);

if (result.success) {

showToast('Event created successfully!', 'success');

resetForm();

            } else {

console.log('Result:::', result);

showToast(result.error || 'Failed to create event','error');

            }

        } catch (error) {

console.log('Result:::', error);

showToast(error.message || 'An unexpected error occurred','error');

        } finally {

// Restore button state

createBtn.label = originalText;

createBtn.enable();

        }

    });

});
import { wixEventsV2, categories } from "wix-events.v2";
import { webMethod, Permissions } from "wix-web-module";
import { elevate } from "wix-auth";

const elevatedQueryCategories = elevate(categories.queryCategories);
const elevatedCreateEvent = elevate(wixEventsV2.createEvent);
const elevatedCategoryUpdate = elevate(categories.assignEvents);

export const eventCategories = webMethod(
    Permissions.SiteMember,
    async () => {
        try {
            const items = await elevatedQueryCategories().find();
            return items;
        } catch (error) {
            return error;
        }
    },
);

export const updateEventCategory = webMethod(
    Permissions.SiteMember,
    async (categoryId, eventId) => {
        try {
            const result = await elevatedCategoryUpdate(categoryId, eventId)
            return { status: true, data: result }
        } catch (error) {
            return error;
        }
    }
);

const countryMap = {
    'India': 'IN',
    'United States': 'US',
    'United Kingdom': 'GB',
    'Australia': 'AU',
    'IN': 'IN',
    'US': 'US',
    'GB': 'GB',
    'AU': 'AU'
};

export const createEvent = webMethod(
    Permissions.Anyone,
    async (event, options) => {
        try {
            const e = event.event || event;

            // Normalize initialType
            const allowedInitialTypes = ['PUBLIC', 'TICKETING', 'PRIVATE'];
            const initialType = allowedInitialTypes.includes(e.registration?.initialType?.toUpperCase()) ?
                e.registration.initialType.toUpperCase() :
                'TICKETING';

            // Validate required fields
            if (!e.title) {
                throw new Error('Event title is required');
            }
            if (!e.dateAndTimeSettings?.startDate || !e.dateAndTimeSettings?.endDate) {
                throw new Error('Start and end dates are required');
            }
            if (!e.location?.type) {
                throw new Error('Location type is required');
            }

            let locationData = {};

            // Handle different location types
            if (e.location.type === 'VENUE') {
                if (!e.location?.address?.city) {
                    throw new Error('City is required for VENUE');
                }
                if (!e.location?.address?.country) {
                    throw new Error('Country is required for VENUE');
                }
                if (!e.location?.address?.streetAddress?.name) {
                    throw new Error('Street name is required for VENUE');
                }
                if (!e.location?.address?.postalCode) {
                    throw new Error('Postal code is required for VENUE');
                }

                const countryCode = countryMap[e.location?.address?.country] ||
                    e.location?.address?.country || 'IN';

                locationData = {
                    type: 'VENUE',
                    address: {
                        country: countryCode.toUpperCase(),
                        city: e.location?.address?.city || '',
                        streetAddress: {
                            name: e.location?.address?.streetAddress?.name || '',
                            number: e.location?.address?.streetAddress?.number || ''
                        },
                        postalCode: e.location?.address?.postalCode || '000000',
                        subdivision: e.location?.address?.subdivision || '',
                        latitude: e.location?.address?.latitude || undefined,
                        longitude: e.location?.address?.longitude || undefined
                    }
                };
            } else if (e.location.type === 'ONLINE') {
                if (!e.location?.onlineDetails?.conferencingLink) {
                    throw new Error('Conferencing link is required for ONLINE');
                }
                locationData = {
                    type: 'ONLINE',
                    onlineDetails: {
                        conferencingLink: e.location.onlineDetails.conferencingLink
                    }
                };
            } else if (e.location.type === 'TBD') {
                locationData = {
                    type: 'VENUE',
                    address: {
                        country: 'IN',
                        city: 'To be decided',
                        streetAddress: {
                            name: e.location.name || 'To be decided',
                            number: ''
                        },
                        postalCode: '000000',
                        subdivision: ''
                    }
                };
            } else {
                throw new Error('Invalid location type');
            }
            const eventInfo = {
                title: e.title,
                language: e.languageCode || 'en',
                description: e.description ? { nodes: [{ type: 'PARAGRAPH', nodes: [{ type: 'TEXT', textData: { text: e.description } }] }] } : undefined,
                mainImage: e.mainImage || undefined, // Handle empty or undefined mainImage
                location: locationData,
                dateAndTimeSettings: {
                    dateAndTimeTbd: e.location.type === 'TBD',
                    startDate: e.dateAndTimeSettings.startDate,
                    endDate: e.dateAndTimeSettings.endDate,
                    showTimeZone: true,
                    timeZoneId: e.dateAndTimeSettings?.timeZoneId || 'Asia/Kolkata',
                },
                registration: {
                    initialType,
                    tickets: {
                        items: e.registration?.tickets?.items?.map(item => ({
                            name: item.name || 'General Admission',
                            price: {
                                amount: item.price?.amount || '0',
                                currency: item.price?.currency || 'GBP'
                            },
                            taxSettings: {
                                type: item.taxSettings?.type || 'INCLUDED_IN_PRICE'
                            },
                            limitPerOrder: Number(item.limitPerOrder) || 30
                        })) || [{
                            name: 'General Admission',
                            price: {
                                amount: '0',
                                currency: 'GBP'
                            },
                            taxSettings: {
                                type: 'INCLUDED_IN_PRICE'
                            },
                            limitPerOrder: 30
                        }]
                    }
                },
                draft: e.draft !== undefined ? e.draft : true

            };
            const result = await elevatedCreateEvent(eventInfo, { draft: true });
            return { success: true, message: "Event Created Successfully, Wait for Admin Approval", data: result };
        } catch (error) {
            return { success: false, message: `Event failed to create, Try again Later: ${error};` };
        }
    },
);