Couple daily can and spend limits via usual can size
Link daily caps through the user's chosen standard can size (250/355/473ml) and built-in prices. Onboarding asks for size and can ceiling with derived spend preview. Settings syncs cans and spend bidirectionally. Forecast lock button sets both limits together. Co-authored-by: nh9961 <hello@nedhalksworth.com>
This commit is contained in:
+16
-1
@@ -1,4 +1,4 @@
|
||||
import type { RedBullEntry } from "../types";
|
||||
import type { BuiltInSize, RedBullEntry } from "../types";
|
||||
|
||||
export const CAFFEINE_PER_250ML = 80;
|
||||
export const SUGAR_PER_250ML = 27;
|
||||
@@ -8,6 +8,21 @@ export const STANDARD_CAN_VALUES = {
|
||||
473: { pricePerCan: 2.85, caffeineMg: 151 },
|
||||
} as const;
|
||||
|
||||
export const BUILT_IN_SIZES: BuiltInSize[] = [250, 355, 473];
|
||||
|
||||
export function priceForLimitSize(size: BuiltInSize): number {
|
||||
return STANDARD_CAN_VALUES[size].pricePerCan;
|
||||
}
|
||||
|
||||
export function spendLimitFromCans(cans: number, size: BuiltInSize): number {
|
||||
return Math.round(cans * priceForLimitSize(size) * 100) / 100;
|
||||
}
|
||||
|
||||
export function canLimitFromSpend(spend: number, size: BuiltInSize): number {
|
||||
const raw = spend / priceForLimitSize(size);
|
||||
return Math.round(raw * 4) / 4;
|
||||
}
|
||||
|
||||
export function spendFor(entry: RedBullEntry) {
|
||||
return entry.cans * entry.pricePerCan;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ export const DEFAULT_LIMITS: UserLimits = {};
|
||||
const PREFS_CAN_KEY = "dailyCanLimit";
|
||||
const PREFS_SPEND_KEY = "dailySpendLimit";
|
||||
const PREFS_STOP_KEY = "stopTime";
|
||||
const PREFS_SIZE_KEY = "limitCanSizeMl";
|
||||
|
||||
const VALID_LIMIT_SIZES = new Set([250, 355, 473]);
|
||||
|
||||
export function parseUserLimits(prefs: Record<string, unknown> | null | undefined): UserLimits {
|
||||
if (!prefs) return { ...DEFAULT_LIMITS };
|
||||
@@ -16,9 +19,12 @@ export function parseUserLimits(prefs: Record<string, unknown> | null | undefine
|
||||
const spendLimit = Number(prefs[PREFS_SPEND_KEY]);
|
||||
const stopTime = typeof prefs[PREFS_STOP_KEY] === "string" ? prefs[PREFS_STOP_KEY] : undefined;
|
||||
|
||||
const sizeLimit = Number(prefs[PREFS_SIZE_KEY]);
|
||||
|
||||
if (Number.isFinite(canLimit) && canLimit > 0) limits.dailyCanLimit = canLimit;
|
||||
if (Number.isFinite(spendLimit) && spendLimit >= 0) limits.dailySpendLimit = spendLimit;
|
||||
if (stopTime && /^\d{2}:\d{2}$/.test(stopTime)) limits.stopTime = stopTime;
|
||||
if (VALID_LIMIT_SIZES.has(sizeLimit)) limits.limitCanSizeMl = sizeLimit as 250 | 355 | 473;
|
||||
|
||||
return limits;
|
||||
}
|
||||
@@ -34,6 +40,9 @@ export function serializeUserLimits(limits: UserLimits): Record<string, unknown>
|
||||
if (limits.stopTime) {
|
||||
data[PREFS_STOP_KEY] = limits.stopTime;
|
||||
}
|
||||
if (limits.limitCanSizeMl != null && VALID_LIMIT_SIZES.has(limits.limitCanSizeMl)) {
|
||||
data[PREFS_SIZE_KEY] = limits.limitCanSizeMl;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -45,6 +54,7 @@ export function mergePrefsWithLimits(
|
||||
delete next[PREFS_CAN_KEY];
|
||||
delete next[PREFS_SPEND_KEY];
|
||||
delete next[PREFS_STOP_KEY];
|
||||
delete next[PREFS_SIZE_KEY];
|
||||
return { ...next, ...serializeUserLimits(limits) };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user