Add BrandColorLight to menu API and setup wizard

- items.cfm: return BrandColorLight in menu response
- saveWizard.cfm: save BrandColorLight during business creation
- setup-wizard.html: second color picker for light brand color

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-03-05 19:24:45 -08:00
parent 771f70f2f3
commit 1568686ff1
3 changed files with 41 additions and 8 deletions

View file

@ -558,7 +558,7 @@
<cfset brandColor = ""> <cfset brandColor = "">
<cfset headerImageUrl = ""> <cfset headerImageUrl = "">
<cfset qBrand = queryTimed( <cfset qBrand = queryTimed(
"SELECT BrandColor AS BusinessBrandColor, TaxRate, PayfritFee, HeaderImageExtension, SessionEnabled FROM Businesses WHERE ID = ?", "SELECT BrandColor AS BusinessBrandColor, BrandColorLight AS BusinessBrandColorLight, TaxRate, PayfritFee, HeaderImageExtension, SessionEnabled FROM Businesses WHERE ID = ?",
[ { value = BusinessID, cfsqltype = "cf_sql_integer" } ], [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
{ datasource = "payfrit" } { datasource = "payfrit" }
)> )>
@ -577,6 +577,10 @@
<cfif len(trim(qBrand.BusinessBrandColor))> <cfif len(trim(qBrand.BusinessBrandColor))>
<cfset brandColor = left(qBrand.BusinessBrandColor, 1) EQ chr(35) ? qBrand.BusinessBrandColor : chr(35) & qBrand.BusinessBrandColor> <cfset brandColor = left(qBrand.BusinessBrandColor, 1) EQ chr(35) ? qBrand.BusinessBrandColor : chr(35) & qBrand.BusinessBrandColor>
</cfif> </cfif>
<cfset brandColorLight = "">
<cfif len(trim(qBrand.BusinessBrandColorLight))>
<cfset brandColorLight = left(qBrand.BusinessBrandColorLight, 1) EQ chr(35) ? qBrand.BusinessBrandColorLight : chr(35) & qBrand.BusinessBrandColorLight>
</cfif>
<cfif len(trim(qBrand.HeaderImageExtension))> <cfif len(trim(qBrand.HeaderImageExtension))>
<cfset headerImageUrl = "/uploads/headers/#BusinessID#.#qBrand.HeaderImageExtension#"> <cfset headerImageUrl = "/uploads/headers/#BusinessID#.#qBrand.HeaderImageExtension#">
</cfif> </cfif>
@ -588,6 +592,7 @@
"COUNT": arrayLen(rows), "COUNT": arrayLen(rows),
"SCHEMA": newSchemaActive ? "unified" : "legacy", "SCHEMA": newSchemaActive ? "unified" : "legacy",
"BRANDCOLOR": brandColor, "BRANDCOLOR": brandColor,
"BRANDCOLORLIGHT": brandColorLight,
"HEADERIMAGEURL": headerImageUrl, "HEADERIMAGEURL": headerImageUrl,
"TAXRATE": val(businessTaxRate), "TAXRATE": val(businessTaxRate),
"PAYFRITFEE": val(businessPayfritFee), "PAYFRITFEE": val(businessPayfritFee),

View file

@ -169,12 +169,16 @@ try {
// Extract tax rate (stored as decimal, e.g. 8.25% -> 0.0825) // Extract tax rate (stored as decimal, e.g. 8.25% -> 0.0825)
bizTaxRate = structKeyExists(biz, "taxRatePercent") && isSimpleValue(biz.taxRatePercent) ? val(biz.taxRatePercent) / 100 : 0; bizTaxRate = structKeyExists(biz, "taxRatePercent") && isSimpleValue(biz.taxRatePercent) ? val(biz.taxRatePercent) / 100 : 0;
// Extract brand color (6-digit hex without #) // Extract brand colors (6-digit hex without #)
bizBrandColor = structKeyExists(biz, "brandColor") && isSimpleValue(biz.brandColor) ? trim(biz.brandColor) : ""; bizBrandColor = structKeyExists(biz, "brandColor") && isSimpleValue(biz.brandColor) ? trim(biz.brandColor) : "";
// Ensure it's a valid 6-digit hex (remove # if present)
bizBrandColor = reReplace(bizBrandColor, "^##?", ""); bizBrandColor = reReplace(bizBrandColor, "^##?", "");
if (!reFind("^[0-9A-Fa-f]{6}$", bizBrandColor)) { if (!reFind("^[0-9A-Fa-f]{6}$", bizBrandColor)) {
bizBrandColor = ""; // Invalid format, skip it bizBrandColor = "";
}
bizBrandColorLight = structKeyExists(biz, "brandColorLight") && isSimpleValue(biz.brandColorLight) ? trim(biz.brandColorLight) : "";
bizBrandColorLight = reReplace(bizBrandColorLight, "^##?", "");
if (!reFind("^[0-9A-Fa-f]{6}$", bizBrandColorLight)) {
bizBrandColorLight = "";
} }
// Create address record first (use extracted address fields) - safely extract as simple values // Create address record first (use extracted address fields) - safely extract as simple values
@ -237,8 +241,8 @@ try {
// Create new business with address link and phone // Create new business with address link and phone
queryTimed(" queryTimed("
INSERT INTO Businesses (Name, Phone, UserID, AddressID, DeliveryZIPCodes, CommunityMealType, TaxRate, BrandColor, AddedOn) INSERT INTO Businesses (Name, Phone, UserID, AddressID, DeliveryZIPCodes, CommunityMealType, TaxRate, BrandColor, BrandColorLight, AddedOn)
VALUES (:name, :phone, :userId, :addressId, :deliveryZips, :communityMealType, :taxRate, :brandColor, NOW()) VALUES (:name, :phone, :userId, :addressId, :deliveryZips, :communityMealType, :taxRate, :brandColor, :brandColorLight, NOW())
", { ", {
name: bizName, name: bizName,
phone: bizPhone, phone: bizPhone,
@ -247,7 +251,8 @@ try {
deliveryZips: len(zip) ? zip : "", deliveryZips: len(zip) ? zip : "",
communityMealType: communityMealType, communityMealType: communityMealType,
taxRate: { value: bizTaxRate, cfsqltype: "cf_sql_decimal" }, taxRate: { value: bizTaxRate, cfsqltype: "cf_sql_decimal" },
brandColor: { value: len(bizBrandColor) ? bizBrandColor : javaCast("null", ""), cfsqltype: "cf_sql_varchar", null: !len(bizBrandColor) } brandColor: { value: len(bizBrandColor) ? bizBrandColor : javaCast("null", ""), cfsqltype: "cf_sql_varchar", null: !len(bizBrandColor) },
brandColorLight: { value: len(bizBrandColorLight) ? bizBrandColorLight : javaCast("null", ""), cfsqltype: "cf_sql_varchar", null: !len(bizBrandColorLight) }
}, { datasource: "payfrit" }); }, { datasource: "payfrit" });
qNewBiz = queryTimed("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" }); qNewBiz = queryTimed("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });

View file

@ -2024,6 +2024,18 @@
document.getElementById('bizBrandColorHex').value = colorVal.replace('#', '').toUpperCase(); document.getElementById('bizBrandColorHex').value = colorVal.replace('#', '').toUpperCase();
} }
function syncBrandColorLight(hexInput) {
let hex = hexInput.value.replace(/[^0-9A-Fa-f]/g, '').toUpperCase();
if (hex.length === 6) {
document.getElementById('bizBrandColorLight').value = '#' + hex;
}
}
function onBrandColorLightPick() {
const colorVal = document.getElementById('bizBrandColorLight').value;
document.getElementById('bizBrandColorLightHex').value = colorVal.replace('#', '').toUpperCase();
}
// Step 1: Business Info // Step 1: Business Info
async function showBusinessInfoStep() { async function showBusinessInfoStep() {
updateProgress(2); updateProgress(2);
@ -2138,6 +2150,14 @@
<span style="font-size:11px;color:var(--gray-400);">Used for menu accents</span> <span style="font-size:11px;color:var(--gray-400);">Used for menu accents</span>
</div> </div>
</div> </div>
<div class="extracted-value editable">
<label style="font-size:12px;color:var(--gray-500);display:block;margin-bottom:4px;">Brand Color Light</label>
<div style="display:flex;align-items:center;gap:12px;">
<input type="color" id="bizBrandColorLight" value="#${biz.brandColorLight || 'F5F5F5'}" style="width:50px;height:36px;padding:2px;border:1px solid var(--gray-300);border-radius:6px;cursor:pointer;" onchange="onBrandColorLightPick()">
<input type="text" id="bizBrandColorLightHex" value="${biz.brandColorLight || ''}" placeholder="F5F5F5" maxlength="6" style="width:80px;font-family:monospace;" oninput="syncBrandColorLight(this)">
<span style="font-size:11px;color:var(--gray-400);">Light tint for card backgrounds (optional)</span>
</div>
</div>
<div class="extracted-value editable"> <div class="extracted-value editable">
<label style="font-size:12px;color:var(--gray-500);display:block;margin-bottom:4px;">Business Hours</label> <label style="font-size:12px;color:var(--gray-500);display:block;margin-bottom:4px;">Business Hours</label>
<div style="font-size:11px;color:var(--gray-400);margin-bottom:4px;">12:00 PM = Noon &nbsp;&bull;&nbsp; 12:00 AM = Midnight</div> <div style="font-size:11px;color:var(--gray-400);margin-bottom:4px;">12:00 PM = Noon &nbsp;&bull;&nbsp; 12:00 AM = Midnight</div>
@ -2233,9 +2253,11 @@
} }
// Update stored data with any edits // Update stored data with any edits
// Get brand color from hex input (without #) // Get brand colors from hex inputs (without #)
let brandColor = document.getElementById('bizBrandColorHex').value.replace(/^#/, '').toUpperCase(); let brandColor = document.getElementById('bizBrandColorHex').value.replace(/^#/, '').toUpperCase();
if (!/^[0-9A-F]{6}$/.test(brandColor)) brandColor = 'E74C3C'; // Default if invalid if (!/^[0-9A-F]{6}$/.test(brandColor)) brandColor = 'E74C3C'; // Default if invalid
let brandColorLight = (document.getElementById('bizBrandColorLightHex').value || '').replace(/^#/, '').toUpperCase();
if (brandColorLight && !/^[0-9A-F]{6}$/.test(brandColorLight)) brandColorLight = '';
config.extractedData.business = { config.extractedData.business = {
name: document.getElementById('bizName').value, name: document.getElementById('bizName').value,
@ -2246,6 +2268,7 @@
phone: document.getElementById('bizPhone').value, phone: document.getElementById('bizPhone').value,
taxRatePercent: parseFloat(document.getElementById('bizTaxRate').value) || 0, taxRatePercent: parseFloat(document.getElementById('bizTaxRate').value) || 0,
brandColor: brandColor, brandColor: brandColor,
brandColorLight: brandColorLight,
hoursSchedule: hoursSchedule // Send the structured schedule instead of the raw hours string hoursSchedule: hoursSchedule // Send the structured schedule instead of the raw hours string
}; };