1
0
Fork 0

feat: cap Amount input max to stored amount and remaining capacity

- Withdraw/Transfer tabs: max = currently stored amount of selected resource
- Deposit tab: max = remaining capacity divided by resource massPerUnit
- Amount auto-clamps when resource selection changes
- Show 'Max: X' hint below each amount input
This commit is contained in:
Jason Fraley 2026-07-27 19:34:47 -04:00
parent b719f25ff2
commit 866a470750

View file

@ -114,6 +114,8 @@ export function ManageStorageDialog({
return total + (resource.massPerUnit ?? 0) * entry.amount; return total + (resource.massPerUnit ?? 0) * entry.amount;
}, 0); }, 0);
const remainingMass = maxMass ? maxMass - currentMass : null;
const findResource = (id: string): Resource | undefined => const findResource = (id: string): Resource | undefined =>
allResources.find((r) => r.id === Number(id)); allResources.find((r) => r.id === Number(id));
@ -130,6 +132,32 @@ export function ManageStorageDialog({
return { resource, amount: entry.amount }; return { resource, amount: entry.amount };
}; };
const getDepositMax = (): number | undefined => {
const resource = findResource(depositResourceId);
if (!resource) return undefined;
if (remainingMass === null) return undefined;
const massPerUnit = resource.massPerUnit ?? 0;
if (massPerUnit <= 0) return undefined;
return Math.floor(remainingMass / massPerUnit);
};
const getWithdrawMax = (): number | undefined => {
const stored = findStoredResource(withdrawResourceId);
return stored?.amount;
};
const getTransferMax = (): number | undefined => {
const stored = findStoredResource(transferResourceId);
return stored?.amount;
};
const clampAmount = (value: string, max: number | undefined): string => {
const num = Number(value);
if (isNaN(num) || num < 1) return "1";
if (max !== undefined && num > max) return String(max);
return value;
};
const getPreviewMass = (tab: "deposit" | "withdraw" | "transfer"): number | null => { const getPreviewMass = (tab: "deposit" | "withdraw" | "transfer"): number | null => {
if (!maxMass) return null; if (!maxMass) return null;
@ -331,7 +359,20 @@ export function ManageStorageDialog({
<TabsContent value="deposit" className="flex flex-col gap-4 pt-4"> <TabsContent value="deposit" className="flex flex-col gap-4 pt-4">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>Resource</Label> <Label>Resource</Label>
<Select value={depositResourceId} onValueChange={setDepositResourceId}> <Select
value={depositResourceId}
onValueChange={(val) => {
setDepositResourceId(val);
const res = findResource(val);
if (res) {
const massPerUnit = res.massPerUnit ?? 0;
const depositMax = remainingMass !== null && massPerUnit > 0
? Math.floor(remainingMass / massPerUnit)
: undefined;
setDepositAmount(clampAmount(depositAmount, depositMax));
}
}}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select a resource..." /> <SelectValue placeholder="Select a resource..." />
</SelectTrigger> </SelectTrigger>
@ -351,9 +392,15 @@ export function ManageStorageDialog({
<Input <Input
type="number" type="number"
min="1" min="1"
max={getDepositMax()}
value={depositAmount} value={depositAmount}
onChange={(e) => setDepositAmount(e.target.value)} onChange={(e) => setDepositAmount(clampAmount(e.target.value, getDepositMax()))}
/> />
{getDepositMax() !== undefined && (
<span className="text-xs text-muted-foreground">
Max: {getDepositMax()!.toLocaleString()} (limited by remaining capacity)
</span>
)}
</div> </div>
{renderCapacityPreview("deposit")} {renderCapacityPreview("deposit")}
<Button <Button
@ -373,7 +420,14 @@ export function ManageStorageDialog({
<TabsContent value="withdraw" className="flex flex-col gap-4 pt-4"> <TabsContent value="withdraw" className="flex flex-col gap-4 pt-4">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>Resource</Label> <Label>Resource</Label>
<Select value={withdrawResourceId} onValueChange={setWithdrawResourceId}> <Select
value={withdrawResourceId}
onValueChange={(val) => {
setWithdrawResourceId(val);
const stored = findStoredResource(val);
setWithdrawAmount(clampAmount(withdrawAmount, stored?.amount));
}}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select a resource..." /> <SelectValue placeholder="Select a resource..." />
</SelectTrigger> </SelectTrigger>
@ -395,9 +449,15 @@ export function ManageStorageDialog({
<Input <Input
type="number" type="number"
min="1" min="1"
max={getWithdrawMax()}
value={withdrawAmount} value={withdrawAmount}
onChange={(e) => setWithdrawAmount(e.target.value)} onChange={(e) => setWithdrawAmount(clampAmount(e.target.value, getWithdrawMax()))}
/> />
{getWithdrawMax() !== undefined && (
<span className="text-xs text-muted-foreground">
Max: {getWithdrawMax()!.toLocaleString()} stored
</span>
)}
</div> </div>
{renderCapacityPreview("withdraw")} {renderCapacityPreview("withdraw")}
<Button <Button
@ -433,7 +493,14 @@ export function ManageStorageDialog({
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>Resource</Label> <Label>Resource</Label>
<Select value={transferResourceId} onValueChange={setTransferResourceId}> <Select
value={transferResourceId}
onValueChange={(val) => {
setTransferResourceId(val);
const stored = findStoredResource(val);
setTransferAmount(clampAmount(transferAmount, stored?.amount));
}}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select a resource..." /> <SelectValue placeholder="Select a resource..." />
</SelectTrigger> </SelectTrigger>
@ -455,9 +522,15 @@ export function ManageStorageDialog({
<Input <Input
type="number" type="number"
min="1" min="1"
max={getTransferMax()}
value={transferAmount} value={transferAmount}
onChange={(e) => setTransferAmount(e.target.value)} onChange={(e) => setTransferAmount(clampAmount(e.target.value, getTransferMax()))}
/> />
{getTransferMax() !== undefined && (
<span className="text-xs text-muted-foreground">
Max: {getTransferMax()!.toLocaleString()} stored
</span>
)}
</div> </div>
{renderCapacityPreview("transfer")} {renderCapacityPreview("transfer")}
<Button <Button