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:
parent
b719f25ff2
commit
866a470750
1 changed files with 79 additions and 6 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue