1. Populate Sheet from Showroom

Enter your showroom UUID and share link UUID. n8n will fetch all assets and create a Google Sheet with everything pre-filled.

Paste the UUID or the full embed/platform URL — the UUID will be extracted automatically.
Leave blank if you only need styling updates.

Sheet ready

Your sheet is pre-filled with assets from the showroom. Fill in quantities and styling, then click the Submit button inside the sheet.

Open Google Sheet

2. Fill Out and Submit

Open the Google Sheet. On the Assets tab, fill in quantities for items you want to order. Edit the Group and Order columns to update showroom grouping.
On the Styling tab, edit font, colors, and layout. For colors, fill the Color Swatch cell with your chosen color, then use VNTANA > Read Color Swatches to pull the hex value in.
Click VNTANA > Submit to VNTANA in the menu bar. n8n processes orders, group changes, and styling in one pass and updates VNTANA via the API.
function onOpen() { SpreadsheetApp.getUi() .createMenu('VNTANA') .addItem('Submit to VNTANA', 'submitToVNTANA') .addItem('Read Color Swatches', 'readColorSwatches') .addToUi(); const ss = SpreadsheetApp.getActiveSpreadsheet(); ss.getSheets().forEach(sheet => { sheet.autoResizeColumns(1, sheet.getLastColumn()); }); setupStylingDropdowns(ss); } function setupStylingDropdowns(ss) { const sheet = ss.getSheetByName('Styling'); if (!sheet) return; const data = sheet.getDataRange().getValues(); const rules = SpreadsheetApp.newDataValidation; data.forEach((row, i) => { const field = String(row[0]).trim(); const valueCell = sheet.getRange(i + 1, 2); if (field === 'Font Family') { valueCell.clearDataValidations(); valueCell.setNote('Apply the font you want to this cell. The font family will be read automatically on submit.'); } else if (field === 'Image Style') { valueCell.setDataValidation( SpreadsheetApp.newDataValidation() .requireValueInList(['Cover', 'Contain', 'Tile'], true) .setAllowInvalid(false).build() ); } else if (field === 'Products Per Row') { valueCell.setDataValidation( SpreadsheetApp.newDataValidation() .requireValueInList(['1', '2', '3', '4', '5'], true) .setAllowInvalid(false).build() ); } }); } function readColorSwatches() { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName('Styling'); if (!sheet) return; const data = sheet.getDataRange().getValues(); const colorFields = ['Background Color', 'Text Color', 'Divider Color']; data.forEach((row, i) => { if (!colorFields.includes(String(row[0]).trim())) return; const swatchCell = sheet.getRange(i + 1, 4); // column D const bg = swatchCell.getBackground(); if (bg && bg !== '#ffffff' && bg !== '#000000') { // only overwrite Value if user actually picked a non-default color } if (bg) sheet.getRange(i + 1, 2).setValue(bg.toUpperCase()); }); SpreadsheetApp.getUi().alert('Done!', 'Hex values copied from swatches into the Value column.', SpreadsheetApp.getUi().ButtonSet.OK); } function submitToVNTANA() { const WEBHOOK = 'https://vntana.app.n8n.cloud/webhook/vntana-showroom-submit'; const ss = SpreadsheetApp.getActiveSpreadsheet(); const ui = SpreadsheetApp.getUi(); // Read Styling tab (key-value rows; skip section headers starting with —) const stylingSheet = ss.getSheetByName('Styling'); const stylingData = stylingSheet.getDataRange().getValues(); const stylingRows = stylingData.slice(1); const stylingMap = {}; stylingRows.forEach(r => { if (r[0] && !String(r[0]).startsWith('—')) stylingMap[String(r[0]).trim()] = r[1]; }); // Font Family: read the actual font applied to the value cell, not its text let fontFamily = ''; stylingData.forEach((r, i) => { if (String(r[0]).trim() === 'Font Family') { fontFamily = stylingSheet.getRange(i + 1, 2).getFontFamily() || ''; } }); const shareLinkUuid = stylingMap['Share Link UUID'] || ''; // Read Assets tab — columns: Asset UUID, Asset Name, Style #, Group, Order, Quantity, Notes const assetsSheet = ss.getSheetByName('Assets'); const assetRows = assetsSheet.getDataRange().getValues().slice(1); const orders = assetRows .filter(r => r[0] && Number(r[5]) > 0) .map(r => ({ shareLinkUuid, assetUuid: String(r[0]).trim(), quantity: Number(r[5]), notes: String(r[6] || '') })); // Build asset → group/order map for groups reconstruction const assetGroupMap = {}; assetRows.filter(r => r[0]).forEach(r => { assetGroupMap[String(r[0]).trim()] = { group: String(r[3] || '').trim(), order: Number(r[4]) || 0 }; }); // Read Groups tab — columns: Group Title, Divider Top, Divider Bottom, Visible const groupsSheet = ss.getSheetByName('Groups'); const groups = []; if (groupsSheet) { const groupRows = groupsSheet.getDataRange().getValues().slice(1); groupRows.filter(r => r[0]).forEach(r => { const header = String(r[0]).trim(); const dividers = []; if (String(r[1]).toLowerCase() === 'yes') dividers.push('TOP'); if (String(r[2]).toLowerCase() === 'yes') dividers.push('BOTTOM'); const visible = String(r[3]).toLowerCase() !== 'no'; const productsInfo = Object.entries(assetGroupMap) .filter(([, info]) => info.group === header) .sort((a, b) => a[1].order - b[1].order) .map(([uuid, info], idx) => ({ productUuid: uuid, visible: true, order: info.order || idx + 1 })); groups.push({ title: header, dividers, visible, productsInfo }); }); } const payload = { showroomUuid: stylingMap['Showroom UUID'] || '', orders, groups, styling: { fontFamily: fontFamily, backgroundColor: stylingMap['Background Color'] || '', textColor: stylingMap['Text Color'] || '', dividerColor: stylingMap['Divider Color'] || '', productsPerRow: stylingMap['Products Per Row'] || '', imageStyle: stylingMap['Image Style'] || '' } }; try { const response = UrlFetchApp.fetch(WEBHOOK, { method: 'post', contentType: 'application/json', payload: JSON.stringify(payload), muteHttpExceptions: true }); const result = JSON.parse(response.getContentText()); ui.alert( 'Done!', 'We have successfully created your new showroom.' + (result.ordersProcessed ? ' ' + result.ordersProcessed + ' order rows submitted.' : ''), ui.ButtonSet.OK ); } catch(e) { ui.alert('Error', e.message, ui.ButtonSet.OK); } }

In the sheet: Extensions > Apps Script → paste the code → Save → reload the sheet. The VNTANA menu appears in the menu bar.