app/components/advanced/StressTestPanel.vue

100 lines
2.9 KiB
Vue

<template>
<UCard>
<template #header>
<h4 class="font-medium">Stress Test</h4>
</template>
<div class="space-y-4">
<div>
<label class="text-sm font-medium text-neutral-700 mb-2 block">
Revenue Delay: {{ stress.revenueDelay }} months
</label>
<input
type="range"
:value="stress.revenueDelay"
min="0"
max="6"
step="1"
class="w-full h-2 bg-neutral-200 rounded-lg appearance-none cursor-pointer"
@input="
(e) => updateStress({ revenueDelay: Number(e.target.value) })
" />
</div>
<div>
<label class="text-sm font-medium text-neutral-700 mb-2 block">
Cost Shock: +{{ stress.costShockPct }}%
</label>
<input
type="range"
:value="stress.costShockPct"
min="0"
max="30"
step="5"
class="w-full h-2 bg-neutral-200 rounded-lg appearance-none cursor-pointer"
@input="
(e) => updateStress({ costShockPct: Number(e.target.value) })
" />
</div>
<div>
<UCheckbox
:model-value="stress.grantLost"
label="Major Grant Lost"
@update:model-value="(val) => updateStress({ grantLost: val })" />
</div>
<div
v-if="isStressActive"
class="p-3 bg-orange-50 border border-orange-200 rounded">
<div class="text-sm">
<div class="flex items-center gap-2 text-orange-800 mb-1">
<UIcon name="i-heroicons-exclamation-triangle" class="w-4 h-4" />
<span class="font-medium">Stress Test Active</span>
</div>
<div class="text-orange-700">
Projected runway:
<span class="font-semibold">{{ displayStressedRunway }}</span>
<span v-if="runwayChange !== 0" class="ml-2">
({{ runwayChange > 0 ? "+" : "" }}{{ runwayChange }} months)
</span>
</div>
</div>
</div>
</div>
</UCard>
</template>
<script setup lang="ts">
const { stress, updateStress, runwayMonths } = useCoopBuilder();
const isStressActive = computed(
() =>
stress.value.revenueDelay > 0 ||
stress.value.costShockPct > 0 ||
stress.value.grantLost
);
const stressedRunway = computed(() =>
runwayMonths(undefined, { useStress: true })
);
const normalRunway = computed(() => runwayMonths());
const displayStressedRunway = computed(() => {
const months = stressedRunway.value;
if (!isFinite(months)) return "";
if (months < 1) return "<1 month";
return `${Math.round(months)} months`;
});
const runwayChange = computed(() => {
const normal = normalRunway.value;
const stressed = stressedRunway.value;
if (!isFinite(normal) && !isFinite(stressed)) return 0;
if (!isFinite(normal)) return -99; // Very large negative change
if (!isFinite(stressed)) return 0;
return Math.round(stressed - normal);
});
</script>