import { Controller } from "@hotwired/stimulus" export default class extends Controller { static values = { prices: Object } connect() { console.log(this.pricesValue) this.computed = { team: { 'team-size': 1, }, web: { 'web-cpu': 0.5, 'web-memory': 0.5, 'web-instances': 1, }, worker: { 'worker-cpu': 0.5, 'worker-memory': 0.5, 'worker-instances': 0, }, } // Initialize sliders and pricing on page load this.calculateAll(); } instanceSize(tiers, cpuNeeded, memoryNeeded) { for (let i = 0; i < tiers.length; i++) { if (cpuNeeded <= tiers[i].cpu && memoryNeeded <= tiers[i].memory) { return {instanceNeeded: tiers[i], i} } } return {instanceNeeded: null, i: -1} } calculateBreakdown(service) { const prices = this.pricesValue[service] const costs = [] costs.push({ text: "Team", cost: prices.seat * this.computed.team['team-size'] }) const webCpuNeeded = this.computed.web['web-cpu'] const webMemoryNeeded = this.computed.web['web-memory'] const {instanceNeeded: webInstanceNeeded, i: webInstanceIndex} = this.instanceSize(prices.tiers, webCpuNeeded, webMemoryNeeded) const webReplicas = this.computed.web['web-instances'] if (webInstanceNeeded) { costs.push({ text: `Web - ${webInstanceNeeded.name} (${webReplicas}x)`, cost: prices.tiers[webInstanceIndex].price * webReplicas, }) } else { return { error: 'Contact Sales', } } const workerCpuNeeded = this.computed.worker['worker-cpu'] const workerMemoryNeeded = this.computed.worker['worker-memory'] const {instanceNeeded: workerInstanceNeeded, i: workerInstanceIndex} = this.instanceSize(prices.tiers, workerCpuNeeded, workerMemoryNeeded) const workerReplicas = this.computed.worker['worker-instances'] if (workerInstanceNeeded) { costs.push({ text: `Worker - ${workerInstanceNeeded.name} (${workerReplicas}x)`, cost: prices.tiers[workerInstanceIndex].price * workerReplicas, }) } else { return { error: 'Contact Sales', } } return costs } calculateAll() { const services = ["render", "heroku", "digitalocean", "hetzner"]; services.forEach(service => { this.place(this.render(service, this.calculateBreakdown(service)), `${service.toLowerCase()}-breakdown`); }); } place(html, id) { const el = document.createElement('div') el.classList.add('border-t', 'border-slate-700', 'py-4') el.innerHTML = html const container = document.getElementById(id); container.innerHTML = ''; container.appendChild(el); } render(service, breakdown) { console.log(breakdown); const serviceName = this.pricesValue[service].name if (breakdown.error) { return `
${serviceName}
${breakdown.error}
` } const total = breakdown.reduce((sum, b) => sum + (typeof b.cost === 'number' ? b.cost : 0), 0); const header = `
${serviceName}
${total == 0 ? 'FREE' : `$${total}.00`}
` return header + breakdown.map(row => { return `
${row.text}
${row.cost == 0 ? '—' : `$${row.cost}`}
` }).join(''); } sliderChanged(event) { const type = event.target.dataset.type; const service = event.target.dataset.service; this._set(`${service}.${type}`, event.target.value, this.computed) document.getElementById(`${type}-value`).innerHTML = event.target.value this.calculateAll(); } _set(key, value, object) { // Split the key by dots to handle nested properties const keys = key.split('.'); const lastKey = keys.pop(); const lastObj = keys.reduce((obj, k) => { // Create nested object if it doesn't exist if (!obj[k]) obj[k] = {}; return obj[k]; }, object); // Set the final value lastObj[lastKey] = value; return object; } }