feat: add initial application structure with configuration, UI components, and state management

This commit is contained in:
Jennie Robinson Faber 2025-08-09 18:13:16 +01:00
parent fadf94002c
commit 0af6b17792
56 changed files with 6137 additions and 129 deletions

131
stores/budget.ts Normal file
View file

@ -0,0 +1,131 @@
import { defineStore } from "pinia";
export const useBudgetStore = defineStore(
"budget",
() => {
// Schema version for persistence
const schemaVersion = "1.0";
// Monthly budget lines by period (YYYY-MM)
const budgetLines = ref({});
// Overhead costs (recurring monthly)
const overheadCosts = ref([]);
// Production costs (variable monthly)
const productionCosts = ref([]);
// Current selected period
const currentPeriod = ref("2024-01");
// Computed current budget
const currentBudget = computed(() => {
return (
budgetLines.value[currentPeriod.value] || {
period: currentPeriod.value,
revenueByStream: {},
payrollCosts: { memberHours: [], oncostApplied: 0 },
overheadCosts: [],
productionCosts: [],
savingsChange: 0,
net: 0,
}
);
});
// Actions
function setBudgetLine(period, budgetData) {
budgetLines.value[period] = {
period,
...budgetData,
};
}
function updateRevenue(period, streamId, type, amount) {
if (!budgetLines.value[period]) {
budgetLines.value[period] = { period, revenueByStream: {} };
}
if (!budgetLines.value[period].revenueByStream[streamId]) {
budgetLines.value[period].revenueByStream[streamId] = {};
}
budgetLines.value[period].revenueByStream[streamId][type] = amount;
}
// Wizard-required actions
function addOverheadLine(cost) {
// Allow creating a blank line so the user can fill it out in the UI
const safeName = cost?.name ?? "";
const safeAmountMonthly =
typeof cost?.amountMonthly === "number" &&
!Number.isNaN(cost.amountMonthly)
? cost.amountMonthly
: 0;
overheadCosts.value.push({
id: Date.now().toString(),
name: safeName,
amount: safeAmountMonthly,
category: cost?.category || "Operations",
recurring: cost?.recurring ?? true,
...cost,
});
}
function removeOverheadLine(id) {
const index = overheadCosts.value.findIndex((c) => c.id === id);
if (index > -1) {
overheadCosts.value.splice(index, 1);
}
}
function addOverheadCost(cost) {
addOverheadLine({ name: cost.name, amountMonthly: cost.amount, ...cost });
}
function addProductionCost(cost) {
productionCosts.value.push({
id: Date.now().toString(),
name: cost.name,
amount: cost.amount,
category: cost.category || "Production",
period: cost.period,
...cost,
});
}
function setCurrentPeriod(period) {
currentPeriod.value = period;
}
// Reset function
function resetBudgetOverhead() {
overheadCosts.value = [];
productionCosts.value = [];
}
return {
budgetLines,
overheadCosts,
productionCosts,
currentPeriod: readonly(currentPeriod),
currentBudget,
schemaVersion,
setBudgetLine,
updateRevenue,
// Wizard actions
addOverheadLine,
removeOverheadLine,
resetBudgetOverhead,
// Legacy actions
addOverheadCost,
addProductionCost,
setCurrentPeriod,
};
},
{
persist: {
key: "urgent-tools-budget",
paths: ["overheadCosts", "productionCosts", "currentPeriod"],
},
}
);