289 lines
10 KiB
JavaScript
289 lines
10 KiB
JavaScript
// Dynamic Price Island Controller
|
||
class PriceIsland {
|
||
constructor() {
|
||
this.island = document.getElementById('priceIsland');
|
||
this.container = document.getElementById('islandContainer');
|
||
this.content = document.getElementById('islandContent');
|
||
this.totalPrice = document.getElementById('totalPrice');
|
||
|
||
this.selectedServices = [];
|
||
this.projectDetails = {};
|
||
this.appliedPromo = null;
|
||
this.isExpanded = false;
|
||
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
// Listen for calculator changes
|
||
document.addEventListener('calculatorStateChange', (e) => {
|
||
this.updateIsland(e.detail);
|
||
});
|
||
|
||
// Initialize promo code functionality
|
||
this.initPromoCode();
|
||
|
||
// Show island initially as compact
|
||
this.showCompact();
|
||
}
|
||
|
||
updateIsland(calculatorData) {
|
||
const { selectedServices, complexity, timeline, totalPrice } = calculatorData;
|
||
|
||
this.selectedServices = selectedServices || [];
|
||
this.projectDetails = { complexity, timeline };
|
||
|
||
// Update total price
|
||
this.updateTotalPrice(totalPrice || 0);
|
||
|
||
// Update content
|
||
this.updateContent();
|
||
|
||
// Expand if there's data to show
|
||
if (this.selectedServices.length > 0 || complexity || timeline) {
|
||
this.expand();
|
||
} else {
|
||
this.collapse();
|
||
}
|
||
}
|
||
|
||
updateTotalPrice(price) {
|
||
if (this.totalPrice) {
|
||
this.totalPrice.textContent = this.formatPrice(price);
|
||
this.totalPrice.classList.add('price-update');
|
||
setTimeout(() => {
|
||
this.totalPrice.classList.remove('price-update');
|
||
}, 300);
|
||
}
|
||
}
|
||
|
||
updateContent() {
|
||
this.updateSelectedServices();
|
||
this.updateProjectDetails();
|
||
this.updatePriceBreakdown();
|
||
}
|
||
|
||
updateSelectedServices() {
|
||
const container = document.getElementById('selectedServices');
|
||
const list = document.getElementById('servicesList');
|
||
|
||
if (this.selectedServices.length > 0) {
|
||
container.classList.remove('hidden');
|
||
list.innerHTML = this.selectedServices.map(service => `
|
||
<div class="flex justify-between items-center py-1">
|
||
<span class="text-sm text-gray-600 dark:text-gray-400">${service.name}</span>
|
||
<span class="text-sm font-medium text-gray-900 dark:text-white">${this.formatPrice(service.price)}</span>
|
||
</div>
|
||
`).join('');
|
||
} else {
|
||
container.classList.add('hidden');
|
||
}
|
||
}
|
||
|
||
updateProjectDetails() {
|
||
const container = document.getElementById('projectDetails');
|
||
const list = document.getElementById('detailsList');
|
||
|
||
const details = [];
|
||
if (this.projectDetails.complexity) {
|
||
details.push(`Сложность: ${this.projectDetails.complexity.name} (×${this.projectDetails.complexity.multiplier})`);
|
||
}
|
||
if (this.projectDetails.timeline) {
|
||
details.push(`Сроки: ${this.projectDetails.timeline.name} (×${this.projectDetails.timeline.multiplier})`);
|
||
}
|
||
|
||
if (details.length > 0) {
|
||
container.classList.remove('hidden');
|
||
list.innerHTML = details.map(detail => `
|
||
<div class="text-sm text-gray-600 dark:text-gray-400">${detail}</div>
|
||
`).join('');
|
||
} else {
|
||
container.classList.add('hidden');
|
||
}
|
||
}
|
||
|
||
updatePriceBreakdown() {
|
||
const container = document.getElementById('priceBreakdown');
|
||
const list = document.getElementById('breakdownList');
|
||
|
||
if (this.selectedServices.length > 0) {
|
||
container.classList.remove('hidden');
|
||
|
||
const basePrice = this.selectedServices.reduce((sum, service) => sum + service.price, 0);
|
||
const complexityMultiplier = this.projectDetails.complexity?.multiplier || 1;
|
||
const timelineMultiplier = this.projectDetails.timeline?.multiplier || 1;
|
||
|
||
const afterComplexity = basePrice * complexityMultiplier;
|
||
const final = afterComplexity * timelineMultiplier;
|
||
|
||
let breakdown = [
|
||
{ label: 'Базовая стоимость', value: basePrice }
|
||
];
|
||
|
||
if (complexityMultiplier !== 1) {
|
||
breakdown.push({
|
||
label: `Сложность (×${complexityMultiplier})`,
|
||
value: afterComplexity
|
||
});
|
||
}
|
||
|
||
if (timelineMultiplier !== 1) {
|
||
breakdown.push({
|
||
label: `Сроки (×${timelineMultiplier})`,
|
||
value: final
|
||
});
|
||
}
|
||
|
||
if (this.appliedPromo) {
|
||
const discount = final * (this.appliedPromo.discount / 100);
|
||
breakdown.push({
|
||
label: `Скидка ${this.appliedPromo.code} (-${this.appliedPromo.discount}%)`,
|
||
value: final - discount,
|
||
isDiscount: true
|
||
});
|
||
}
|
||
|
||
list.innerHTML = breakdown.map(item => `
|
||
<div class="flex justify-between items-center py-1 ${item.isDiscount ? 'text-green-600 dark:text-green-400' : ''}">
|
||
<span class="text-sm">${item.label}</span>
|
||
<span class="text-sm font-medium">${this.formatPrice(item.value)}</span>
|
||
</div>
|
||
`).join('');
|
||
} else {
|
||
container.classList.add('hidden');
|
||
}
|
||
}
|
||
|
||
initPromoCode() {
|
||
const promoBtn = document.getElementById('applyPromoBtn');
|
||
const promoInput = document.getElementById('promoInput');
|
||
const promoStatus = document.getElementById('promoStatus');
|
||
|
||
if (promoBtn && promoInput) {
|
||
promoBtn.addEventListener('click', () => {
|
||
const code = promoInput.value.trim().toUpperCase();
|
||
this.applyPromoCode(code);
|
||
});
|
||
|
||
promoInput.addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') {
|
||
const code = promoInput.value.trim().toUpperCase();
|
||
this.applyPromoCode(code);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
applyPromoCode(code) {
|
||
const promoStatus = document.getElementById('promoStatus');
|
||
|
||
// Simulate promo code validation
|
||
const validCodes = {
|
||
'WELCOME10': { discount: 10, name: 'Скидка новичка' },
|
||
'SAVE20': { discount: 20, name: 'Экономия 20%' },
|
||
'VIP15': { discount: 15, name: 'VIP скидка' }
|
||
};
|
||
|
||
if (validCodes[code]) {
|
||
this.appliedPromo = { code, ...validCodes[code] };
|
||
promoStatus.textContent = `✓ Применен: ${this.appliedPromo.name}`;
|
||
promoStatus.className = 'text-xs mt-1 text-green-600 dark:text-green-400';
|
||
|
||
// Trigger recalculation
|
||
this.updateContent();
|
||
this.recalculateTotal();
|
||
} else if (code) {
|
||
promoStatus.textContent = '✗ Неверный промокод';
|
||
promoStatus.className = 'text-xs mt-1 text-red-600 dark:text-red-400';
|
||
} else {
|
||
promoStatus.textContent = '';
|
||
}
|
||
}
|
||
|
||
recalculateTotal() {
|
||
if (this.selectedServices.length > 0) {
|
||
const basePrice = this.selectedServices.reduce((sum, service) => sum + service.price, 0);
|
||
const complexityMultiplier = this.projectDetails.complexity?.multiplier || 1;
|
||
const timelineMultiplier = this.projectDetails.timeline?.multiplier || 1;
|
||
|
||
let total = basePrice * complexityMultiplier * timelineMultiplier;
|
||
|
||
if (this.appliedPromo) {
|
||
total = total * (1 - this.appliedPromo.discount / 100);
|
||
}
|
||
|
||
this.updateTotalPrice(total);
|
||
|
||
// Also update mobile price if exists
|
||
const mobilePrice = document.getElementById('mobilePriceValue');
|
||
if (mobilePrice) {
|
||
mobilePrice.textContent = this.formatPrice(total);
|
||
}
|
||
}
|
||
}
|
||
|
||
expand() {
|
||
if (!this.isExpanded) {
|
||
this.isExpanded = true;
|
||
this.content.style.maxHeight = this.content.scrollHeight + 'px';
|
||
|
||
// Show promo section when expanded
|
||
const promoSection = document.getElementById('promoSection');
|
||
if (promoSection) {
|
||
promoSection.classList.remove('hidden');
|
||
}
|
||
}
|
||
}
|
||
|
||
collapse() {
|
||
if (this.isExpanded) {
|
||
this.isExpanded = false;
|
||
this.content.style.maxHeight = '0';
|
||
|
||
// Hide promo section when collapsed
|
||
const promoSection = document.getElementById('promoSection');
|
||
if (promoSection) {
|
||
promoSection.classList.add('hidden');
|
||
}
|
||
}
|
||
}
|
||
|
||
showCompact() {
|
||
this.island.classList.remove('hidden');
|
||
}
|
||
|
||
hide() {
|
||
this.island.classList.add('hidden');
|
||
}
|
||
|
||
formatPrice(price) {
|
||
return new Intl.NumberFormat('ko-KR', {
|
||
style: 'currency',
|
||
currency: 'KRW',
|
||
maximumFractionDigits: 0
|
||
}).format(price);
|
||
}
|
||
}
|
||
|
||
// CSS for price update animation
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
.price-update {
|
||
animation: priceUpdate 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes priceUpdate {
|
||
0% { transform: scale(1); }
|
||
50% { transform: scale(1.1); }
|
||
100% { transform: scale(1); }
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
|
||
// Initialize when DOM is loaded
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
window.priceIsland = new PriceIsland();
|
||
});
|
||
|
||
// Export for use in calculator
|
||
window.PriceIsland = PriceIsland; |