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": {
|
"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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
24
index.html
24
index.html
@@ -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>
|
||||||
|
|||||||
51
server.js
51
server.js
@@ -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) {
|
||||||
|
|||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user