feat: add initial application structure with configuration, UI components, and state management
This commit is contained in:
parent
fadf94002c
commit
0af6b17792
56 changed files with 6137 additions and 129 deletions
131
stores/budget.ts
Normal file
131
stores/budget.ts
Normal 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"],
|
||||
},
|
||||
}
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue