Initial commit: Add price calculator

This commit is contained in:
2025-11-13 09:57:14 -07:00
commit 41c89a4ebf

331
price_calculator.html Normal file
View File

@@ -0,0 +1,331 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Handmade Soap Price Calculator</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #0d0d1a;
background-image:
radial-gradient(circle at 50% 0, rgba(157, 78, 221, 0.1) 0%, transparent 50%),
radial-gradient(circle at 100% 100%, rgba(0, 191, 165, 0.1) 0%, transparent 50%),
radial-gradient(circle at 0 100%, rgba(0, 123, 255, 0.1) 0%, transparent 50%);
background-size: 50px 50px;
color: #e0e0e0;
margin: 0;
padding: 20px;
}
.container {
max-width: 800px;
margin: auto;
background: #2a2a3e;
padding: 25px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
}
h1, h2 {
text-align: center;
color: #a0a0d0;
}
h2 {
border-bottom: 2px solid #4a4a6e;
padding-bottom: 10px;
margin-top: 30px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 15px;
}
th, td {
border: 1px solid #4a4a6e;
padding: 12px;
text-align: left;
}
th {
background-color: #3a3a5e;
font-weight: 600;
}
input[type="text"], input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #5a5a7e;
border-radius: 4px;
background-color: #1a1a2e;
color: #e0e0e0;
}
.remove-btn, .add-btn {
background-color: #9d4edd;
color: #e0e0e0;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 0.9em;
}
.add-btn {
background-color: #00bfa5;
margin-top: 10px;
display: inline-block;
}
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 600;
color: #a0a0d0;
}
#calculate-btn {
width: 100%;
padding: 15px;
background-color: #007bff;
color: #e0e0e0;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1.2em;
margin-top: 20px;
}
#results {
margin-top: 30px;
background: #1a1a2e;
padding: 20px;
border-radius: 8px;
}
#results h2 {
margin-top: 0;
}
.result-item {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #4a4a6e;
}
.result-item:last-child {
border-bottom: none;
}
.result-item strong {
color: #50fa7b;
}
</style>
</head>
<body>
<div class="container">
<h1>Handmade Soap Price Calculator</h1>
<!-- Section for Oils, Butters, Lye -->
<h2>Oils, Butters & Lye</h2>
<table id="oils-table">
<thead>
<tr>
<th>Ingredient</th>
<th>Cost per Unit (e.g., oz, g)</th>
<th>Amount Used</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" class="ingredient-name" placeholder="e.g., Olive Oil"></td>
<td><input type="number" class="cost-per-unit" placeholder="0.25"></td>
<td><input type="number" class="amount-used" placeholder="16"></td>
<td><button class="remove-btn" onclick="removeItem(this)">Remove</button></td>
</tr>
</tbody>
</table>
<button class="add-btn" onclick="addItem('oils-table')">Add Oil/Butter/Lye</button>
<!-- Section for Additives -->
<h2>Additives (Essential Oils, Colorants, etc.)</h2>
<table id="additives-table">
<thead>
<tr>
<th>Item</th>
<th>Cost</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" class="additive-name" placeholder="e.g., Lavender EO"></td>
<td><input type="number" class="additive-cost" placeholder="5.00"></td>
<td><button class="remove-btn" onclick="removeItem(this)">Remove</button></td>
</tr>
</tbody>
</table>
<button class="add-btn" onclick="addItem('additives-table')">Add Additive</button>
<!-- Section for Packaging -->
<h2>Packaging</h2>
<table id="packaging-table">
<thead>
<tr>
<th>Item</th>
<th>Cost per Bar</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" class="packaging-name" placeholder="e.g., Label"></td>
<td><input type="number" class="packaging-cost" placeholder="0.15"></td>
<td><button class="remove-btn" onclick="removeItem(this)">Remove</button></td>
</tr>
</tbody>
</table>
<button class="add-btn" onclick="addItem('packaging-table')">Add Packaging Item</button>
<!-- Section for Labor & Overhead -->
<h2>Labor & Overhead</h2>
<div class="grid-container">
<div class="form-group">
<label for="hourly-rate">Your Hourly Rate ($)</label>
<input type="number" id="hourly-rate" value="25">
</div>
<div class="form-group">
<label for="time-spent">Time Spent per Batch (hours)</label>
<input type="number" id="time-spent" value="2">
</div>
<div class="form-group">
<label for="overhead-percent">Overhead (%)</label>
<input type="number" id="overhead-percent" value="15">
</div>
</div>
<!-- Section for Batch Details -->
<h2>Batch Details</h2>
<div class="grid-container">
<div class="form-group">
<label for="bars-per-batch">Number of Bars in Batch</label>
<input type="number" id="bars-per-batch" value="12">
</div>
<div class="form-group">
<label for="markup-factor">Markup Factor</label>
<input type="number" id="markup-factor" value="3">
</div>
</div>
<button id="calculate-btn" onclick="calculatePrice()">Calculate Price</button>
<div id="results"></div>
</div>
<script>
function addItem(tableId) {
const tableBody = document.getElementById(tableId).getElementsByTagName('tbody')[0];
const newRow = tableBody.insertRow();
let cells = '';
if (tableId === 'oils-table') {
cells = `
<td><input type="text" class="ingredient-name" placeholder="e.g., Coconut Oil"></td>
<td><input type="number" class="cost-per-unit" placeholder="0.15"></td>
<td><input type="number" class="amount-used" placeholder="12"></td>
<td><button class="remove-btn" onclick="removeItem(this)">Remove</button></td>
`;
} else if (tableId === 'additives-table') {
cells = `
<td><input type="text" class="additive-name" placeholder="e.g., Clay"></td>
<td><input type="number" class="additive-cost" placeholder="1.50"></td>
<td><button class="remove-btn" onclick="removeItem(this)">Remove</button></td>
`;
} else if (tableId === 'packaging-table') {
cells = `
<td><input type="text" class="packaging-name" placeholder="e.g., Box"></td>
<td><input type="number" class="packaging-cost" placeholder="0.50"></td>
<td><button class="remove-btn" onclick="removeItem(this)">Remove</button></td>
`;
}
newRow.innerHTML = cells;
}
function removeItem(button) {
const row = button.parentNode.parentNode;
// Prevent removing the last row in each table
if (row.parentNode.rows.length > 1) {
row.parentNode.removeChild(row);
} else {
alert("You must have at least one item in this section.");
}
}
function getTableTotal(tableId, col1Class, col2Class) {
let total = 0;
const rows = document.getElementById(tableId).getElementsByTagName('tbody')[0].rows;
for (const row of rows) {
const val1 = parseFloat(row.querySelector(col1Class).value) || 0;
const val2 = col2Class ? (parseFloat(row.querySelector(col2Class).value) || 0) : 1;
total += val1 * val2;
}
return total;
}
function calculatePrice() {
// 1. Calculate Material Costs
const oilsCost = getTableTotal('oils-table', '.cost-per-unit', '.amount-used');
const additivesCost = getTableTotal('additives-table', '.additive-cost');
const totalMaterialCost = oilsCost + additivesCost;
// 2. Get Batch Details
const barsPerBatch = parseFloat(document.getElementById('bars-per-batch').value) || 1;
// 3. Calculate Packaging Cost
const packagingCostPerBar = getTableTotal('packaging-table', '.packaging-cost');
const totalPackagingCost = packagingCostPerBar * barsPerBatch;
// 4. Calculate Labor Cost
const hourlyRate = parseFloat(document.getElementById('hourly-rate').value) || 0;
const timeSpent = parseFloat(document.getElementById('time-spent').value) || 0;
const totalLaborCost = hourlyRate * timeSpent;
// 5. Calculate Subtotal (Materials + Packaging + Labor)
const subtotal = totalMaterialCost + totalPackagingCost + totalLaborCost;
// 6. Calculate Overhead
const overheadPercent = parseFloat(document.getElementById('overhead-percent').value) || 0;
const totalOverheadCost = subtotal * (overheadPercent / 100);
// 7. Calculate Total Batch Cost
const totalBatchCost = subtotal + totalOverheadCost;
// 8. Calculate Cost Per Bar
const costPerBar = totalBatchCost / barsPerBatch;
// 9. Calculate Suggested Pricing
const markupFactor = parseFloat(document.getElementById('markup-factor').value) || 3;
const retailPrice = costPerBar * markupFactor;
const wholesalePrice = retailPrice / 2;
// 10. Display Results
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = `
<h2>Cost & Pricing Breakdown</h2>
<div class="result-item"><span>Total Material Cost:</span> <strong>$${totalMaterialCost.toFixed(2)}</strong></div>
<div class="result-item"><span>Total Packaging Cost:</span> <strong>$${totalPackagingCost.toFixed(2)}</strong></div>
<div class="result-item"><span>Total Labor Cost:</span> <strong>$${totalLaborCost.toFixed(2)}</strong></div>
<div class="result-item"><span>Overhead (${overheadPercent}%):</span> <strong>$${totalOverheadCost.toFixed(2)}</strong></div>
<div class="result-item"><span><strong>Total Cost for Batch:</strong></span> <strong>$${totalBatchCost.toFixed(2)}</strong></div>
<hr>
<div class="result-item"><span>Bars in Batch:</span> <strong>${barsPerBatch}</strong></div>
<div class="result-item"><span><strong>Cost per Bar:</strong></span> <strong>$${costPerBar.toFixed(2)}</strong></div>
<hr>
<h2>Suggested Pricing</h2>
<div class="result-item"><span>Markup Factor:</span> <strong>${markupFactor}x</strong></div>
<div class="result-item"><span><strong>Suggested Retail Price per Bar:</strong></span> <strong>$${retailPrice.toFixed(2)}</strong></div>
<div class="result-item"><span>Suggested Wholesale Price per Bar:</span> <strong>$${wholesalePrice.toFixed(2)}</strong></div>
`;
}
</script>
</body>
</html>