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": { "user_data": {
"first_name": "Denisa", "first_name": "gfgdfggf",
"last_name": "Cirstea", "last_name": "gfdgd",
"company_name": "Footprints AI", "company_name": "gfdgfd",
"email": "denisa@example.com", "email": "gfdgd",
"phone": "+40 712 345 678", "phone": "gfdgd",
"store_name": "Media", "store_name": "testtttttt",
"country": "Romania", "country": "gfdgd",
"starting_date": "01.01.2025", "starting_date": "2025-09-17",
"duration": 36, "duration": 36,
"store_types": ["convenience", "minimarket", "supermarket", "hypermarket"], "store_types": [
"open_days_per_month": 26, "Convenience"
],
"open_days_per_month": 30,
"convenience_store_type": { "convenience_store_type": {
"stores_number": 120, "stores_number": 231,
"monthly_transactions": 900000, "monthly_transactions": 321321321,
"has_digital_screens": true, "has_digital_screens": true,
"screen_count": 300, "screen_count": 2,
"screen_percentage": 70, "screen_percentage": 100,
"has_in_store_radio": true, "has_in_store_radio": true,
"radio_percentage": 60, "radio_percentage": 100,
"open_days_per_month": 26 "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": { "supermarket_store_type": {
"stores_number": 80, "stores_number": 0,
"monthly_transactions": 450000, "monthly_transactions": 0,
"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,
"has_digital_screens": false, "has_digital_screens": false,
"screen_count": 0, "screen_count": 0,
"screen_percentage": 0, "screen_percentage": 0,
"has_in_store_radio": true, "has_in_store_radio": false,
"radio_percentage": 100, "radio_percentage": 0,
"open_days_per_month": 26 "open_days_per_month": 30
}, },
"hypermarket_store_type": {
"on_site_channels": ["Homepage Banners", "Search Results", "Category Pages"], "stores_number": 0,
"website_visitors": 1200000, "monthly_transactions": 0,
"app_users": 350000, "has_digital_screens": false,
"loyalty_users": 500000, "screen_count": 0,
"screen_percentage": 0,
"off_site_channels": ["Social Display", "Programmatic Video", "Search Ads"], "has_in_store_radio": false,
"facebook_followers": 250000, "radio_percentage": 0,
"instagram_followers": 180000, "open_days_per_month": 30
"google_views": 4200000, },
"email_subscribers": 300000, "on_site_channels": [
"sms_users": 220000, "Mobile App"
"whatsapp_contacts": 150000 ],
"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('/')) day, month, year = map(int, starting_date.split('/'))
elif '.' in starting_date: elif '.' in starting_date:
day, month, year = map(int, starting_date.split('.')) 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: else:
# If format is not recognized, return default # If format is not recognized, return default
return default_years return default_years

View File

@@ -107,15 +107,15 @@
<div class="progress-bg"></div> <div class="progress-bg"></div>
<!-- Active progress bar --> <!-- 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 --> <!-- Step indicators with first at 0% and last at 100% -->
<div class="step-indicator active" style="left: 8.33%"></div> <div class="step-indicator active" style="left: 0%"></div>
<div class="step-indicator" style="left: 25%"></div> <div class="step-indicator" style="left: 20%"></div>
<div class="step-indicator" style="left: 41.66%"></div> <div class="step-indicator" style="left: 40%"></div>
<div class="step-indicator" style="left: 58.33%"></div> <div class="step-indicator" style="left: 60%"></div>
<div class="step-indicator" style="left: 75%"></div> <div class="step-indicator" style="left: 80%"></div>
<div class="step-indicator" style="left: 91.66%"></div> <div class="step-indicator" style="left: 100%"></div>
</div> </div>
<!-- Step labels --> <!-- 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"> <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 Continue
</button> </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 SUBMIT
</button> </button>
</div> </div>
@@ -631,7 +631,7 @@
function updateProgressBar() { function updateProgressBar() {
// Get the positions for each step // 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 // Set the progress bar width to the current step's position
const progressWidth = stepPositions[currentStep - 1]; 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]"> 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>
<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" <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]"> 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>
@@ -1546,7 +1546,7 @@
<!-- Radio Questions (Hidden by default) --> <!-- Radio Questions (Hidden by default) -->
<div id="${id}-radio-questions" class="space-y-4 hidden"> <div id="${id}-radio-questions" class="space-y-4 hidden">
<div> <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" <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]"> 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>

View File

@@ -11,6 +11,7 @@ const PORT = process.env.PORT || 3001;
// Middleware // Middleware
app.use(express.static(__dirname)); // Serve static files 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.json());
app.use(bodyParser.urlencoded({ extended: true })); 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')); 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 // API endpoint to handle form submissions
app.post('/calculate', async (req, res) => { app.post('/calculate', async (req, res) => {
try { try {
@@ -42,10 +81,20 @@ app.post('/calculate', async (req, res) => {
const stdout = execSync('python3 create_excel.py', { encoding: 'utf8' }); const stdout = execSync('python3 create_excel.py', { encoding: 'utf8' });
console.log(`Python script output: ${stdout}`); 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 // Send success response after Python script completes
res.json({ res.json({
success: true, 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'); console.log('Success response sent');
} catch (execError) { } catch (execError) {

View File

@@ -19,15 +19,32 @@
</svg> </svg>
</div> </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. Your submission has been received successfully. Our retail media specialists will reach out to you soon.
</p> </p>
<a href="/" <p class="text-base text-[#1f1a3e] mb-8">
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"> You can download your personalized business case Excel file using the button below.
Return Home </p>
</a>
<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>
</div> </div>
</body> </body>
</html>
</html> </html>