feat: implemented AgileCalculator component

master
Katja Lutz 2 years ago
parent 0a599cf9d4
commit 9c5a8fd2c4

@ -0,0 +1,150 @@
import Big from "big.js";
import { Component, createMemo } from "solid-js";
import { createStore } from "solid-js/store";
import { TextInput } from "./Form";
import { calculateAgileQuantity } from "./Positions";
import { formatAmount } from "./SwissInvoice";
const AgileCalculator: Component = () => {
const [agileCalculator, setAgileCalculator] = createStore({
minPoints: 0,
maxPoints: 10,
risk: 70,
singlePrice: 10.0,
hoursPerPoint: 1,
});
const calculatorQuantity = createMemo(() =>
calculateAgileQuantity(
agileCalculator.hoursPerPoint,
new Big(agileCalculator.risk).div(100).toNumber(),
agileCalculator.minPoints,
agileCalculator.maxPoints
)
);
return (
<div class="border border-slate-500/40 flex flex-col gap-2 p-2 rounded mb-3">
<h3 class="mt-0">Rechnungsbeispiel</h3>
<TextInput
labelMinWidth="300px"
label="Risiko Faktor"
suffix="%"
step="10"
min="0"
max="100"
type="number"
value={agileCalculator.risk}
onInput={(e) =>
!Number.isNaN(e.currentTarget.valueAsNumber) &&
setAgileCalculator("risk", e.currentTarget.valueAsNumber)
}
/>
<TextInput
labelMinWidth="300px"
label="Stunden pro Story Point"
min="0"
type="number"
value={agileCalculator.hoursPerPoint}
onInput={(e) =>
!Number.isNaN(e.currentTarget.valueAsNumber) &&
setAgileCalculator("hoursPerPoint", e.currentTarget.valueAsNumber)
}
/>
<TextInput
labelMinWidth="300px"
label="Story Points Minimum"
min="0"
type="number"
value={agileCalculator.minPoints}
onInput={(e) =>
!Number.isNaN(e.currentTarget.valueAsNumber) &&
setAgileCalculator("minPoints", e.currentTarget.valueAsNumber)
}
/>
<TextInput
labelMinWidth="300px"
label="Story Points Maximum"
min="0"
type="number"
value={agileCalculator.maxPoints}
onInput={(e) =>
!Number.isNaN(e.currentTarget.valueAsNumber) &&
setAgileCalculator("maxPoints", e.currentTarget.valueAsNumber)
}
/>
<TextInput
labelMinWidth="300px"
label="Einzelpreis"
step="0.01"
suffix="CHF"
type="number"
value={agileCalculator.singlePrice}
onInput={(e) =>
!Number.isNaN(e.currentTarget.valueAsNumber) &&
setAgileCalculator("singlePrice", e.currentTarget.valueAsNumber)
}
/>
<div class="items-center grid grid-cols-[300px_1fr] gap-2">
<div class="p-1 font-mono text-sm text-right">
{"("}
<span title="Nichtrisiko Anteil" class="text-title-border">
{new Big(-100).plus(agileCalculator.risk).abs().toNumber()}%
</span>{" "}
*{" "}
<span title="Story Points Minimum" class="text-title-border">
{agileCalculator.minPoints} SP
</span>
{")"} + {"("}
<span title="Risiko Anteil" class="text-title-border">
{agileCalculator.risk}%
</span>{" "}
*{" "}
<span title="Story Points Maximum" class="text-title-border">
{agileCalculator.maxPoints > agileCalculator.minPoints
? agileCalculator.maxPoints
: agileCalculator.minPoints}{" "}
SP
</span>
{")"} =
</div>
<div class="p-1">
<div class="flex justify-between">
<span>Gewichtete Story Points:</span>
{new Big(calculatorQuantity())
.div(agileCalculator.hoursPerPoint)
.toNumber()}{" "}
SP
</div>
</div>
<div class="p-1 font-mono text-sm text-right">
<span title="Gewichtete Story Points" class="text-title-border">
{new Big(calculatorQuantity())
.div(agileCalculator.hoursPerPoint)
.toNumber()}{" "}
SP
</span>{" "}
*{" "}
<span title="Stunden pro Story Point" class="text-title-border">
{agileCalculator.hoursPerPoint} h
</span>{" "}
*{" "}
<span title="Einzelpreis" class="text-title-border">
{agileCalculator.singlePrice} CHF
</span>{" "}
=
</div>
<div class="p-1 font-bold">
<div class="flex justify-between">
<span>Gesamtpreis:</span>
{formatAmount(
calculatorQuantity() * agileCalculator.singlePrice
)}{" "}
CHF
</div>
</div>
</div>
</div>
);
};
export default AgileCalculator;
Loading…
Cancel
Save