Main functions
This commit is contained in:
289
.history/public/js/price-island_20251026100040.js
Normal file
289
.history/public/js/price-island_20251026100040.js
Normal file
@@ -0,0 +1,289 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user