export function expensePreApproval(form: FormTs) {
// Expense Pre-Approval Request Form
// Demonstrates: Money, Datepicker, Dropdown, PriceDisplay, computedValue, Slider, conditional visibility based on amount
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Expense Pre-Approval Request',
computedValue: () => 'Request approval before making purchases. Complete all required fields for faster processing.',
customStyles: {
background: 'linear-gradient(135deg, #059669 0%, #10b981 100%)',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Requestor Information
// ============================================
const requestorSection = form.addSubform('requestorSection', {
title: 'Requestor Information',
customStyles: { backgroundColor: '#f0fdf4', padding: '16px', borderRadius: '8px' }
});
requestorSection.addRow(row => {
row.addTextbox('requestorName', {
label: 'Your name',
placeholder: 'First and last name',
isRequired: true
}, '1fr');
row.addEmail('requestorEmail', {
label: 'Email',
placeholder: 'your@company.com',
isRequired: true
}, '1fr');
});
requestorSection.addRow(row => {
row.addDropdown('department', {
label: 'Department',
options: [
{ id: 'engineering', name: 'Engineering' },
{ id: 'marketing', name: 'Marketing' },
{ id: 'sales', name: 'Sales' },
{ id: 'hr', name: 'Human Resources' },
{ id: 'finance', name: 'Finance' },
{ id: 'operations', name: 'Operations' },
{ id: 'legal', name: 'Legal' },
{ id: 'it', name: 'IT' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select department',
isRequired: true
}, '1fr');
row.addTextbox('costCenter', {
label: 'Cost center / Project code',
placeholder: 'e.g., CC-1234 or PROJ-567',
isRequired: true
}, '1fr');
});
// ============================================
// SECTION 2: Expense Details
// ============================================
const expenseSection = form.addSubform('expenseSection', {
title: 'Expense Details'
});
expenseSection.addRow(row => {
row.addDropdown('expenseCategory', {
label: 'Expense category',
options: [
{ id: 'equipment', name: 'Equipment & Hardware' },
{ id: 'software', name: 'Software & Subscriptions' },
{ id: 'travel', name: 'Travel & Accommodation' },
{ id: 'training', name: 'Training & Conferences' },
{ id: 'supplies', name: 'Office Supplies' },
{ id: 'services', name: 'Professional Services' },
{ id: 'marketing', name: 'Marketing & Advertising' },
{ id: 'client', name: 'Client Entertainment' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select category',
isRequired: true
}, '1fr');
row.addRadioButton('urgency', {
label: 'Urgency',
options: [
{ id: 'low', name: 'Low - Can wait' },
{ id: 'medium', name: 'Medium - Within 2 weeks' },
{ id: 'high', name: 'High - This week' },
{ id: 'critical', name: 'Critical - ASAP' }
],
orientation: 'vertical'
}, '1fr');
});
expenseSection.addRow(row => {
row.addTextbox('expenseDescription', {
label: 'What are you purchasing?',
placeholder: 'Brief description of items or services',
isRequired: true
});
});
expenseSection.addSpacer({ height: '12px' });
expenseSection.addRow(row => {
row.addMoney('estimatedAmount', {
label: 'Estimated amount (before tax)',
currency: '$',
placeholder: '0.00',
min: 0,
isRequired: true
}, '1fr');
row.addSlider('taxRate', {
label: 'Tax rate (%)',
min: 0,
max: 25,
step: 0.5,
defaultValue: 8,
showValue: true,
unit: '%'
}, '1fr');
});
expenseSection.addRow(row => {
row.addPriceDisplay('taxAmount', {
label: 'Estimated tax',
computedValue: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
const rate = expenseSection.slider('taxRate')?.value() || 0;
return amount * (rate / 100);
},
currency: '$',
decimals: 2,
alignment: 'left',
variant: 'default'
}, '1fr');
row.addPriceDisplay('totalAmount', {
label: 'Total estimated cost',
computedValue: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
const rate = expenseSection.slider('taxRate')?.value() || 0;
return amount * (1 + rate / 100);
},
currency: '$',
decimals: 2,
variant: 'highlight',
alignment: 'left'
}, '1fr');
});
expenseSection.addRow(row => {
row.addDatepicker('neededByDate', {
label: 'When do you need this?',
minDate: () => new Date().toISOString().split('T')[0],
isRequired: true
}, '1fr');
row.addRadioButton('recurring', {
label: 'Is this a recurring expense?',
options: [
{ id: 'no', name: 'One-time purchase' },
{ id: 'monthly', name: 'Monthly' },
{ id: 'quarterly', name: 'Quarterly' },
{ id: 'annual', name: 'Annual' }
],
orientation: 'horizontal'
}, '1fr');
});
// ============================================
// SECTION 3: Business Justification
// ============================================
const justificationSection = form.addSubform('justificationSection', {
title: 'Business Justification'
});
justificationSection.addRow(row => {
row.addTextarea('businessNeed', {
label: 'Why is this expense necessary?',
placeholder: 'Explain the business need and expected benefit...',
rows: 3,
autoExpand: true,
isRequired: true
});
});
justificationSection.addRow(row => {
row.addRadioButton('budgetStatus', {
label: 'Is this expense in your approved budget?',
options: [
{ id: 'yes', name: 'Yes - Budgeted' },
{ id: 'partial', name: 'Partially budgeted' },
{ id: 'no', name: 'No - Unbudgeted' }
],
orientation: 'horizontal',
isRequired: true
});
});
justificationSection.addRow(row => {
row.addTextarea('unbudgetedReason', {
label: 'Please explain why this unbudgeted expense is needed',
placeholder: 'Describe the circumstances requiring this unplanned expense...',
rows: 3,
autoExpand: true,
isRequired: () => {
const status = justificationSection.radioButton('budgetStatus')?.value();
return status === 'no' || status === 'partial';
},
isVisible: () => {
const status = justificationSection.radioButton('budgetStatus')?.value();
return status === 'no' || status === 'partial';
}
});
});
// ============================================
// SECTION 4: Vendor Information
// ============================================
const vendorSection = form.addSubform('vendorSection', {
title: 'Vendor Information'
});
vendorSection.addRow(row => {
row.addTextbox('vendorName', {
label: 'Preferred vendor/supplier',
placeholder: 'Company or store name',
isRequired: true
}, '1fr');
row.addRadioButton('existingVendor', {
label: 'Is this an approved vendor?',
options: [
{ id: 'yes', name: 'Yes - Existing vendor' },
{ id: 'no', name: 'No - New vendor' },
{ id: 'unsure', name: 'Not sure' }
],
orientation: 'horizontal'
}, '1fr');
});
// Additional quotes required for larger expenses
const quotesSection = form.addSubform('quotesSection', {
title: 'Competitive Quotes Required',
isVisible: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
return amount >= 1000;
},
customStyles: { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' }
});
quotesSection.addRow(row => {
row.addTextPanel('quotesNotice', {
computedValue: () => 'Expenses over $1,000 require comparison of at least 2 vendors. Please provide alternative quote information.',
customStyles: {
backgroundColor: '#fffbeb',
padding: '12px',
borderRadius: '6px',
fontSize: '14px',
borderLeft: '4px solid #f59e0b'
}
});
});
quotesSection.addRow(row => {
row.addTextbox('altVendor1', {
label: 'Alternative vendor 1',
placeholder: 'Vendor name'
}, '1fr');
row.addMoney('altQuote1', {
label: 'Quote amount',
currency: '$',
placeholder: '0.00'
}, '1fr');
});
quotesSection.addRow(row => {
row.addTextbox('altVendor2', {
label: 'Alternative vendor 2 (optional)',
placeholder: 'Vendor name'
}, '1fr');
row.addMoney('altQuote2', {
label: 'Quote amount',
currency: '$',
placeholder: '0.00'
}, '1fr');
});
quotesSection.addRow(row => {
row.addTextarea('vendorJustification', {
label: 'Why is the preferred vendor the best choice?',
placeholder: 'Explain factors beyond price: quality, reliability, support, etc.',
rows: 2,
autoExpand: true
});
});
// ============================================
// SECTION 5: High-Value Approval (>$5000)
// ============================================
const highValueSection = form.addSubform('highValueSection', {
title: 'Executive Approval Required',
isVisible: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
return amount >= 5000;
},
customStyles: { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' }
});
highValueSection.addRow(row => {
row.addTextPanel('highValueNotice', {
computedValue: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
return `This expense request of $${amount.toLocaleString()} exceeds the $5,000 threshold and requires executive approval.`;
},
customStyles: {
backgroundColor: '#fef2f2',
padding: '12px',
borderRadius: '6px',
fontSize: '14px',
borderLeft: '4px solid #ef4444'
}
});
});
highValueSection.addRow(row => {
row.addTextbox('executiveApprover', {
label: 'Executive approver name',
placeholder: 'Director or VP who will approve',
isRequired: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
return amount >= 5000;
}
}, '1fr');
row.addCheckbox('preApprovalDiscussed', {
label: 'I have discussed this expense with the approver'
}, '1fr');
});
// ============================================
// SECTION 6: Attachments & Additional Info
// ============================================
const additionalSection = form.addSubform('additionalSection', {
title: 'Additional Information'
});
additionalSection.addRow(row => {
row.addCheckboxList('attachments', {
label: 'Documents to attach (will be requested after submission)',
options: [
{ id: 'quote', name: 'Vendor quote/estimate' },
{ id: 'proposal', name: 'Vendor proposal' },
{ id: 'comparison', name: 'Price comparison' },
{ id: 'contract', name: 'Contract/Agreement' },
{ id: 'specs', name: 'Technical specifications' },
{ id: 'other', name: 'Other supporting documents' }
],
orientation: 'vertical'
}, '1fr');
row.addTextarea('additionalNotes', {
label: 'Additional notes for approver',
placeholder: 'Any other information that would help with the approval decision...',
rows: 4,
autoExpand: true
}, '1fr');
});
// ============================================
// SECTION 7: Request Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Request Summary',
isVisible: () => {
const amount = expenseSection.money('estimatedAmount')?.value();
return amount !== null && amount !== undefined && amount > 0;
}
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
const taxRate = expenseSection.slider('taxRate')?.value() || 0;
const total = amount * (1 + taxRate / 100);
const category = expenseSection.dropdown('expenseCategory')?.value();
const urgency = expenseSection.radioButton('urgency')?.value();
const recurring = expenseSection.radioButton('recurring')?.value();
const budgetStatus = justificationSection.radioButton('budgetStatus')?.value();
const neededBy = expenseSection.datepicker('neededByDate')?.value();
if (amount === 0) return '';
const categoryLabels: Record<string, string> = {
'equipment': 'Equipment & Hardware',
'software': 'Software & Subscriptions',
'travel': 'Travel & Accommodation',
'training': 'Training & Conferences',
'supplies': 'Office Supplies',
'services': 'Professional Services',
'marketing': 'Marketing & Advertising',
'client': 'Client Entertainment',
'other': 'Other'
};
const urgencyEmojis: Record<string, string> = {
'low': '🟢',
'medium': '🟡',
'high': '🟠',
'critical': '🔴'
};
let approvalLevel = 'Manager';
if (amount >= 5000) approvalLevel = 'Executive';
else if (amount >= 1000) approvalLevel = 'Director';
let summary = `💰 Expense Request Summary\n`;
summary += `${'═'.repeat(28)}\n\n`;
summary += `📦 Category: ${categoryLabels[category || ''] || category || 'Not selected'}\n`;
summary += `💵 Amount: $${amount.toLocaleString('en-US', { minimumFractionDigits: 2 })}\n`;
summary += `📊 Tax (${taxRate}%): $${(amount * taxRate / 100).toLocaleString('en-US', { minimumFractionDigits: 2 })}\n`;
summary += `💳 Total: $${total.toLocaleString('en-US', { minimumFractionDigits: 2 })}\n`;
if (urgency) {
summary += `\n${urgencyEmojis[urgency] || ''} Urgency: ${urgency.charAt(0).toUpperCase() + urgency.slice(1)}`;
}
if (neededBy) {
summary += `\n📅 Needed by: ${neededBy}`;
}
if (recurring && recurring !== 'no') {
summary += `\n🔄 Recurring: ${recurring}`;
}
if (budgetStatus) {
const budgetLabels: Record<string, string> = {
'yes': '✅ Budgeted',
'partial': '⚠️ Partially budgeted',
'no': '❌ Unbudgeted'
};
summary += `\n${budgetLabels[budgetStatus] || budgetStatus}`;
}
summary += `\n\n👤 Approval level: ${approvalLevel}`;
return summary;
},
customStyles: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (amount >= 5000) {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
} else if (amount >= 1000) {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
}
});
});
// ============================================
// SECTION 8: Acknowledgment
// ============================================
const acknowledgmentSection = form.addSubform('acknowledgmentSection', {
title: 'Acknowledgment'
});
acknowledgmentSection.addRow(row => {
row.addCheckbox('policyAcknowledgment', {
label: 'I confirm this expense complies with company expense policy',
isRequired: true
});
});
acknowledgmentSection.addRow(row => {
row.addCheckbox('accuracyConfirmation', {
label: 'The information provided is accurate and complete',
isRequired: true
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: () => {
const amount = expenseSection.money('estimatedAmount')?.value() || 0;
if (amount >= 5000) return 'Submit for Executive Approval';
if (amount >= 1000) return 'Submit for Director Approval';
return 'Submit for Approval';
},
isVisible: () => {
const amount = expenseSection.money('estimatedAmount')?.value();
const policy = acknowledgmentSection.checkbox('policyAcknowledgment')?.value();
const accuracy = acknowledgmentSection.checkbox('accuracyConfirmation')?.value();
return amount !== null && amount !== undefined && amount > 0 && policy === true && accuracy === true;
}
});
form.configureCompletionScreen({
type: 'text',
title: 'Request Submitted Successfully!',
message: 'Your expense pre-approval request has been submitted. You will receive an email notification once a decision is made. Please retain all receipts and documentation.'
});
}