Fix form submission issues and add Excel download functionality
This commit is contained in:
112
config.json
112
config.json
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
24
index.html
24
index.html
@@ -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>
|
||||
|
||||
51
server.js
51
server.js
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user