Fix form submission issues and add Excel download functionality

This commit is contained in:
denisacirstea
2025-09-17 12:38:32 +03:00
parent 3bb09839ae
commit de966ede2b
5 changed files with 146 additions and 76 deletions

View File

@@ -1,72 +1,68 @@
{
"user_data": {
"first_name": "Denisa",
"last_name": "Cirstea",
"company_name": "Footprints AI",
"email": "denisa@example.com",
"phone": "+40 712 345 678",
"store_name": "Media",
"country": "Romania",
"starting_date": "01.01.2025",
"first_name": "gfgdfggf",
"last_name": "gfdgd",
"company_name": "gfdgfd",
"email": "gfdgd",
"phone": "gfdgd",
"store_name": "testtttttt",
"country": "gfdgd",
"starting_date": "2025-09-17",
"duration": 36,
"store_types": ["convenience", "minimarket", "supermarket", "hypermarket"],
"open_days_per_month": 26,
"store_types": [
"Convenience"
],
"open_days_per_month": 30,
"convenience_store_type": {
"stores_number": 120,
"monthly_transactions": 900000,
"stores_number": 231,
"monthly_transactions": 321321321,
"has_digital_screens": true,
"screen_count": 300,
"screen_percentage": 70,
"screen_count": 2,
"screen_percentage": 100,
"has_in_store_radio": true,
"radio_percentage": 60,
"open_days_per_month": 26
"radio_percentage": 100,
"open_days_per_month": 30
},
"minimarket_store_type": {
"stores_number": 50,
"monthly_transactions": 600000,
"has_digital_screens": true,
"screen_count": 150,
"screen_percentage": 60,
"has_in_store_radio": true,
"radio_percentage": 70,
"open_days_per_month": 26
},
"supermarket_store_type": {
"stores_number": 80,
"monthly_transactions": 450000,
"has_digital_screens": true,
"screen_count": 200,
"screen_percentage": 50,
"has_in_store_radio": true,
"radio_percentage": 80,
"open_days_per_month": 26
},
"hypermarket_store_type": {
"stores_number": 5,
"monthly_transactions": 60000,
"stores_number": 0,
"monthly_transactions": 0,
"has_digital_screens": false,
"screen_count": 0,
"screen_percentage": 0,
"has_in_store_radio": true,
"radio_percentage": 100,
"open_days_per_month": 26
"has_in_store_radio": false,
"radio_percentage": 0,
"open_days_per_month": 30
},
"on_site_channels": ["Homepage Banners", "Search Results", "Category Pages"],
"website_visitors": 1200000,
"app_users": 350000,
"loyalty_users": 500000,
"off_site_channels": ["Social Display", "Programmatic Video", "Search Ads"],
"facebook_followers": 250000,
"instagram_followers": 180000,
"google_views": 4200000,
"email_subscribers": 300000,
"sms_users": 220000,
"whatsapp_contacts": 150000
"hypermarket_store_type": {
"stores_number": 0,
"monthly_transactions": 0,
"has_digital_screens": false,
"screen_count": 0,
"screen_percentage": 0,
"has_in_store_radio": false,
"radio_percentage": 0,
"open_days_per_month": 30
},
"on_site_channels": [
"Mobile App"
],
"website_visitors": 0,
"app_users": 323213,
"loyalty_users": 0,
"off_site_channels": [
"Facebook Business"
],
"facebook_followers": 321312,
"instagram_followers": 0,
"google_views": 0,
"email_subscribers": 0,
"sms_users": 0,
"whatsapp_contacts": 0,
"potential_reach_in_store": 0,
"unique_impressions_in_store": 0,
"potential_reach_on_site": 0,
"unique_impressions_on_site": 0,
"potential_reach_off_site": 0,
"unique_impressions_off_site": 0
}
}

View File

@@ -101,6 +101,14 @@ def calculate_years(starting_date, duration):
day, month, year = map(int, starting_date.split('/'))
elif '.' in starting_date:
day, month, year = map(int, starting_date.split('.'))
elif '-' in starting_date:
# Handle ISO format (yyyy-mm-dd)
date_parts = starting_date.split('-')
if len(date_parts) == 3:
year, month, day = map(int, date_parts)
else:
# Default to current date if format is not recognized
return default_years
else:
# If format is not recognized, return default
return default_years

View File

@@ -107,15 +107,15 @@
<div class="progress-bg"></div>
<!-- Active progress bar -->
<div id="progressBar" style="width: 8.33%"></div>
<div id="progressBar" style="width: 0%"></div>
<!-- Step indicators positioned exactly in the middle of the bar -->
<div class="step-indicator active" style="left: 8.33%"></div>
<div class="step-indicator" style="left: 25%"></div>
<div class="step-indicator" style="left: 41.66%"></div>
<div class="step-indicator" style="left: 58.33%"></div>
<div class="step-indicator" style="left: 75%"></div>
<div class="step-indicator" style="left: 91.66%"></div>
<!-- Step indicators with first at 0% and last at 100% -->
<div class="step-indicator active" style="left: 0%"></div>
<div class="step-indicator" style="left: 20%"></div>
<div class="step-indicator" style="left: 40%"></div>
<div class="step-indicator" style="left: 60%"></div>
<div class="step-indicator" style="left: 80%"></div>
<div class="step-indicator" style="left: 100%"></div>
</div>
<!-- Step labels -->
@@ -480,7 +480,7 @@
<button type="button" id="nextBtn" class="px-6 py-2 bg-gradient-to-r from-yellow-400 to-orange-500 text-white rounded-md hover:from-yellow-500 hover:to-orange-600 font-medium">
Continue
</button>
<button type="submit" id="submitBtn" class="px-10 py-3 bg-gradient-to-r from-yellow-400 to-orange-500 text-white rounded-[10px] hover:from-yellow-500 hover:to-orange-600 font-bold text-lg uppercase tracking-wide transition-all shadow-md hover:shadow-lg hidden" onclick="console.log('Submit button clicked');">
<button type="button" id="submitBtn" class="px-10 py-3 bg-gradient-to-r from-yellow-400 to-orange-500 text-white rounded-[10px] hover:from-yellow-500 hover:to-orange-600 font-bold text-lg uppercase tracking-wide transition-all shadow-md hover:shadow-lg hidden">
SUBMIT
</button>
</div>
@@ -631,7 +631,7 @@
function updateProgressBar() {
// Get the positions for each step
const stepPositions = [8.33, 25, 41.66, 58.33, 75, 91.66];
const stepPositions = [0, 20, 40, 60, 80, 100];
// Set the progress bar width to the current step's position
const progressWidth = stepPositions[currentStep - 1];
@@ -1520,7 +1520,7 @@
class="w-full p-3 border border-gray-300 bg-white rounded-md focus:outline-none focus:ring-1 focus:ring-[#eb742e] focus:border-[#eb742e]">
</div>
<div>
<label for="${id}-screen-percentage" class="block text-base font-medium text-gray-700 mb-1">Percentage of stores with digital screens</label>
<label for="${id}-screen-percentage" class="block text-base font-medium text-gray-700 mb-1">Number of stores with digital screens</label>
<input type="number" id="${id}-screen-percentage" name="${id}_screen_percentage" min="0" max="100"
class="w-full p-3 border border-gray-300 bg-white rounded-md focus:outline-none focus:ring-1 focus:ring-[#eb742e] focus:border-[#eb742e]">
</div>
@@ -1546,7 +1546,7 @@
<!-- Radio Questions (Hidden by default) -->
<div id="${id}-radio-questions" class="space-y-4 hidden">
<div>
<label for="${id}-radio-percentage" class="block text-base font-medium text-gray-700 mb-1">Percentage of stores with radio</label>
<label for="${id}-radio-percentage" class="block text-base font-medium text-gray-700 mb-1">Number of stores with radio</label>
<input type="number" id="${id}-radio-percentage" name="${id}_radio_percentage" min="0" max="100"
class="w-full p-3 border border-gray-300 bg-white rounded-md focus:outline-none focus:ring-1 focus:ring-[#eb742e] focus:border-[#eb742e]">
</div>

View File

@@ -11,6 +11,7 @@ const PORT = process.env.PORT || 3001;
// Middleware
app.use(express.static(__dirname)); // Serve static files
app.use('/output', express.static(path.join(__dirname, 'output'))); // Serve files from output directory
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
@@ -24,6 +25,44 @@ app.get('/thank-you.html', (req, res) => {
res.sendFile(path.join(__dirname, 'thank-you.html'));
});
// Route to download the generated Excel file
app.get('/download-excel', (req, res) => {
try {
// Read the latest config to get store name and other details
const configPath = path.join(__dirname, 'config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
const storeName = config.user_data?.store_name || 'Your Store';
// Find the most recent Excel file in the output directory
const outputDir = path.join(__dirname, 'output');
const files = fs.readdirSync(outputDir)
.filter(file => file.endsWith('.xlsx') && file.includes(storeName))
.map(file => ({
name: file,
time: fs.statSync(path.join(outputDir, file)).mtime.getTime()
}))
.sort((a, b) => b.time - a.time); // Sort by modified time, newest first
if (files.length > 0) {
const latestFile = files[0].name;
const filePath = path.join(outputDir, latestFile);
// Set headers for file download
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader('Content-Disposition', `attachment; filename="${latestFile}"`);
// Send the file
res.sendFile(filePath);
console.log(`Excel file sent for download: ${filePath}`);
} else {
res.status(404).send('No Excel file found');
}
} catch (error) {
console.error('Error downloading Excel file:', error);
res.status(500).send('Error downloading Excel file');
}
});
// API endpoint to handle form submissions
app.post('/calculate', async (req, res) => {
try {
@@ -42,10 +81,20 @@ app.post('/calculate', async (req, res) => {
const stdout = execSync('python3 create_excel.py', { encoding: 'utf8' });
console.log(`Python script output: ${stdout}`);
// Extract the filename from the Python script output
const filenameMatch = stdout.match(/Excel file created successfully: .*\/output\/(.*\.xlsx)/);
const excelFilename = filenameMatch ? filenameMatch[1] : null;
if (excelFilename) {
// Store the filename in a session variable or pass it to the thank-you page
console.log(`Excel filename extracted: ${excelFilename}`);
}
// Send success response after Python script completes
res.json({
success: true,
message: 'Form data saved and Excel file created successfully'
message: 'Form data saved and Excel file created successfully',
excelFilename: excelFilename
});
console.log('Success response sent');
} catch (execError) {

View File

@@ -19,15 +19,32 @@
</svg>
</div>
<p class="text-base text-[#1f1a3e] mb-8">
<p class="text-base text-[#1f1a3e] mb-6">
Your submission has been received successfully. Our retail media specialists will reach out to you soon.
</p>
<a href="/"
class="inline-block px-10 py-3 bg-gradient-to-r from-yellow-400 to-orange-500 text-white rounded-[10px] hover:from-yellow-500 hover:to-orange-600 font-bold text-lg uppercase tracking-wide transition-all shadow-md hover:shadow-lg">
Return Home
</a>
<p class="text-base text-[#1f1a3e] mb-8">
You can download your personalized business case Excel file using the button below.
</p>
<div class="flex flex-col sm:flex-row justify-center gap-4">
<a href="/download-excel"
class="inline-block px-10 py-3 bg-gradient-to-r from-green-500 to-teal-600 text-white rounded-[10px] hover:from-green-600 hover:to-teal-700 font-bold text-lg uppercase tracking-wide transition-all shadow-md hover:shadow-lg">
<div class="flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
Download Excel
</div>
</a>
<a href="/"
class="inline-block px-10 py-3 bg-gradient-to-r from-yellow-400 to-orange-500 text-white rounded-[10px] hover:from-yellow-500 hover:to-orange-600 font-bold text-lg uppercase tracking-wide transition-all shadow-md hover:shadow-lg">
Return Home
</a>
</div>
</div>
</div>
</body>
</html>
</html>