chore: update application configuration and UI components for improved styling and functionality

This commit is contained in:
Jennie Robinson Faber 2025-08-16 08:13:35 +01:00
parent 0af6b17792
commit 37ab8d7bab
54 changed files with 23293 additions and 1666 deletions

View file

@ -15,16 +15,20 @@
</template>
<div class="space-y-4">
<div class="text-center">
<div class="text-4xl font-bold text-red-600 mb-2">65%</div>
<div class="text-sm text-gray-600 mb-3">Top source percentage</div>
<div class="text-4xl font-bold mb-2" :class="concentrationColor">
{{ topSourcePct }}%
</div>
<div class="text-sm text-neutral-600 mb-3">
Top source percentage
</div>
<ConcentrationChip
status="red"
:top-source-pct="65"
:status="concentrationStatus"
:top-source-pct="topSourcePct"
:show-percentage="false"
variant="solid"
size="md" />
</div>
<p class="text-sm text-gray-600 text-center">
<p class="text-sm text-neutral-600 text-center">
Most of your money comes from one place. Add another stream to
reduce risk.
</p>
@ -38,10 +42,12 @@
<div class="space-y-4">
<div class="text-center">
<div class="text-4xl font-bold text-yellow-600 mb-2">35 days</div>
<div class="text-sm text-gray-600 mb-3">Weighted average delay</div>
<div class="text-sm text-neutral-600 mb-3">
Weighted average delay
</div>
<UBadge color="yellow" variant="subtle">Moderate Risk</UBadge>
</div>
<p class="text-sm text-gray-600 text-center">
<p class="text-sm text-neutral-600 text-center">
Money is earned now but arrives later. Delays can create mid-month
dips.
</p>
@ -64,7 +70,7 @@
<template #name-data="{ row }">
<div>
<div class="font-medium">{{ row.name }}</div>
<div class="text-xs text-gray-500">{{ row.category }}</div>
<div class="text-xs text-neutral-500">{{ row.category }}</div>
</div>
</template>
@ -76,13 +82,13 @@
size="xs"
class="w-16"
@update:model-value="updateStream(row.id, 'targetPct', $event)" />
<span class="text-xs text-gray-500">%</span>
<span class="text-xs text-neutral-500">%</span>
</div>
</template>
<template #targetAmount-data="{ row }">
<div class="flex items-center gap-2">
<span class="text-xs text-gray-500"></span>
<span class="text-xs text-neutral-500"></span>
<UInput
v-model="row.targetMonthlyAmount"
type="number"
@ -104,7 +110,7 @@
</div>
<div
v-if="row.platformFeePct === 0 && row.revenueSharePct === 0"
class="text-gray-400">
class="text-neutral-400">
None
</div>
</div>
@ -120,7 +126,7 @@
@update:model-value="
updateStream(row.id, 'payoutDelayDays', $event)
" />
<span class="text-xs text-gray-500">days</span>
<span class="text-xs text-neutral-500">days</span>
</div>
</template>
@ -147,7 +153,7 @@
</template>
</UTable>
<div class="mt-4 p-4 bg-gray-50 rounded-lg">
<div class="mt-4 p-4 bg-neutral-50 rounded-lg">
<div class="flex justify-between text-sm">
<span class="font-medium">Totals</span>
<div class="flex gap-6">
@ -162,24 +168,10 @@
<script setup lang="ts">
const { $format } = useNuxtApp();
const { loadStreams } = useFixtures();
// Load fixture data
const fixtureData = await loadStreams();
const streams = ref(
fixtureData.revenueStreams.map((stream) => ({
id: stream.id,
name: stream.name,
category: stream.category,
targetPct: stream.targetPct,
targetMonthlyAmount: stream.targetMonthlyAmount,
certainty: stream.certainty,
payoutDelayDays: stream.payoutDelayDays,
platformFeePct: stream.platformFeePct || 0,
revenueSharePct: stream.revenueSharePct || 0,
restrictions: stream.restrictions,
}))
);
// Use real store data instead of fixtures
const streamsStore = useStreamsStore();
const { streams } = storeToRefs(streamsStore);
const columns = [
{ id: "name", key: "name", label: "Stream" },
@ -192,16 +184,29 @@ const columns = [
{ id: "actions", key: "actions", label: "" },
];
const totalTargetPct = computed(() =>
streams.value.reduce((sum, stream) => sum + (stream.targetPct || 0), 0)
);
const totalTargetPct = computed(() => streamsStore.totalTargetPct);
const totalMonthlyAmount = computed(() => streamsStore.totalMonthlyAmount);
const totalMonthlyAmount = computed(() =>
streams.value.reduce(
(sum, stream) => sum + (stream.targetMonthlyAmount || 0),
0
)
);
// Calculate concentration metrics
const topSourcePct = computed(() => {
if (streams.value.length === 0) return 0;
const amounts = streams.value.map((s) => s.targetMonthlyAmount || 0);
return (
Math.round((Math.max(...amounts) / totalMonthlyAmount.value) * 100) || 0
);
});
const concentrationStatus = computed(() => {
if (topSourcePct.value > 50) return "red";
if (topSourcePct.value > 35) return "yellow";
return "green";
});
const concentrationColor = computed(() => {
if (topSourcePct.value > 50) return "text-red-600";
if (topSourcePct.value > 35) return "text-yellow-600";
return "text-green-600";
});
function getCertaintyColor(certainty: string) {
switch (certainty) {
@ -242,12 +247,28 @@ function updateStream(id: string, field: string, value: any) {
const stream = streams.value.find((s) => s.id === id);
if (stream) {
stream[field] = Number(value) || value;
streamsStore.upsertStream(stream);
}
}
function addStream() {
// Add stream logic
console.log("Add new stream");
const newStream = {
id: Date.now().toString(),
name: "",
category: "games",
subcategory: "",
targetPct: 0,
targetMonthlyAmount: 0,
certainty: "Aspirational",
payoutDelayDays: 30,
terms: "Net 30",
revenueSharePct: 0,
platformFeePct: 0,
restrictions: "General",
seasonalityWeights: new Array(12).fill(1),
effortHoursPerMonth: 0,
};
streamsStore.upsertStream(newStream);
}
function editStream(row: any) {
@ -261,10 +282,7 @@ function duplicateStream(row: any) {
}
function removeStream(row: any) {
const index = streams.value.findIndex((s) => s.id === row.id);
if (index > -1) {
streams.value.splice(index, 1);
}
streamsStore.removeStream(row.id);
}
function sendToBudget() {