Skip to content

Commit 3e5bb70

Browse files
committed
lowercase ids, remove daily price interval, show item ids
1 parent 94f68f4 commit 3e5bb70

File tree

4 files changed

+67
-59
lines changed

4 files changed

+67
-59
lines changed

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/item-dialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ export function ItemDialog({
9191
id="item-id"
9292
value={itemId}
9393
onChange={(e) => {
94-
setItemId(e.target.value);
94+
const nextValue = e.target.value.toLowerCase();
95+
setItemId(nextValue);
9596
if (errors.itemId) {
9697
setErrors(prev => {
9798
const newErrors = { ...prev };

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx

Lines changed: 61 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ type Product = CompleteConfig['payments']['products'][keyof CompleteConfig['paym
4646
type Price = (Product['prices'] & object)[string];
4747
type PricesObject = Exclude<Product['prices'], 'include-by-default'>;
4848

49+
const DEFAULT_INTERVAL_UNITS: DayInterval[1][] = ['day', 'week', 'month', 'year'];
50+
const PRICE_INTERVAL_UNITS: DayInterval[1][] = ['week', 'month', 'year'];
51+
4952

5053
function intervalLabel(tuple: DayInterval | undefined): string | null {
5154
if (!tuple) return null;
@@ -92,6 +95,7 @@ function IntervalPopover({
9295
setCount,
9396
onChange,
9497
noneLabel = 'one time',
98+
allowedUnits,
9599
}: {
96100
readOnly?: boolean,
97101
intervalText: string | null,
@@ -103,8 +107,25 @@ function IntervalPopover({
103107
setCount: (n: number) => void,
104108
onChange: (interval: DayInterval | null) => void,
105109
noneLabel?: string,
110+
allowedUnits?: DayInterval[1][],
106111
}) {
107112
const [open, setOpen] = useState(false);
113+
const buttonLabels: Record<DayInterval[1], string> = {
114+
day: 'daily',
115+
week: 'weekly',
116+
month: 'monthly',
117+
year: 'yearly',
118+
};
119+
120+
const units = allowedUnits ?? DEFAULT_INTERVAL_UNITS;
121+
const normalizedUnits = units.length > 0 ? units : DEFAULT_INTERVAL_UNITS;
122+
const defaultUnit = (normalizedUnits[0] ?? 'month') as DayInterval[1];
123+
const effectiveUnit = unit && normalizedUnits.includes(unit) ? unit : defaultUnit;
124+
const isIntervalUnit = intervalSelection !== 'custom' && intervalSelection !== 'one-time';
125+
const effectiveSelection: 'one-time' | 'custom' | DayInterval[1] =
126+
isIntervalUnit && !normalizedUnits.includes(intervalSelection)
127+
? 'custom'
128+
: intervalSelection;
108129

109130
const selectOneTime = () => {
110131
setIntervalSelection('one-time');
@@ -114,19 +135,21 @@ function IntervalPopover({
114135
setOpen(false);
115136
};
116137

117-
const selectFixed = (unit: DayInterval[1]) => {
118-
setIntervalSelection(unit);
119-
setUnit(unit);
138+
const selectFixed = (unitOption: DayInterval[1]) => {
139+
if (!normalizedUnits.includes(unitOption)) return;
140+
setIntervalSelection(unitOption);
141+
setUnit(unitOption);
120142
setCount(1);
121-
if (!readOnly) onChange([1, unit]);
143+
if (!readOnly) onChange([1, unitOption]);
122144
setOpen(false);
123145
};
124146

125-
const applyCustom = (count: number, unit: DayInterval[1]) => {
147+
const applyCustom = (countValue: number, maybeUnit?: DayInterval[1]) => {
148+
const safeUnit = maybeUnit && normalizedUnits.includes(maybeUnit) ? maybeUnit : defaultUnit;
126149
setIntervalSelection('custom');
127-
setUnit(unit);
128-
setCount(count);
129-
if (!readOnly) onChange([count, unit]);
150+
setUnit(safeUnit);
151+
setCount(countValue);
152+
if (!readOnly) onChange([countValue, safeUnit]);
130153
};
131154

132155
const triggerLabel = intervalText || noneLabel;
@@ -142,60 +165,38 @@ function IntervalPopover({
142165
<PopoverContent align="start" className="w-60 p-2">
143166
<div className="flex flex-col gap-1">
144167
<Button
145-
variant={intervalSelection === 'one-time' ? 'secondary' : 'ghost'}
168+
variant={effectiveSelection === 'one-time' ? 'secondary' : 'ghost'}
146169
size="sm"
147170
className="justify-start"
148171
onClick={selectOneTime}
149172
>
150173
{noneLabel}
151174
</Button>
152-
<Button
153-
variant={intervalSelection === 'day' ? 'secondary' : 'ghost'}
154-
size="sm"
155-
className="justify-start"
156-
onClick={() => selectFixed('day')}
157-
>
158-
daily
159-
</Button>
160-
<Button
161-
variant={intervalSelection === 'week' ? 'secondary' : 'ghost'}
162-
size="sm"
163-
className="justify-start"
164-
onClick={() => selectFixed('week')}
165-
>
166-
weekly
167-
</Button>
168-
<Button
169-
variant={intervalSelection === 'month' ? 'secondary' : 'ghost'}
170-
size="sm"
171-
className="justify-start"
172-
onClick={() => selectFixed('month')}
173-
>
174-
monthly
175-
</Button>
176-
<Button
177-
variant={intervalSelection === 'year' ? 'secondary' : 'ghost'}
178-
size="sm"
179-
className="justify-start"
180-
onClick={() => selectFixed('year')}
181-
>
182-
yearly
183-
</Button>
175+
{normalizedUnits.map((unitOption) => (
176+
<Button
177+
key={unitOption}
178+
variant={effectiveSelection === unitOption ? 'secondary' : 'ghost'}
179+
size="sm"
180+
className="justify-start"
181+
onClick={() => selectFixed(unitOption)}
182+
>
183+
{buttonLabels[unitOption]}
184+
</Button>
185+
))}
184186

185187
<Button
186-
variant={intervalSelection === 'custom' ? 'secondary' : 'ghost'}
188+
variant={effectiveSelection === 'custom' ? 'secondary' : 'ghost'}
187189
size="sm"
188190
className="justify-start"
189191
onClick={() => {
190192
setIntervalSelection('custom');
191-
const nextUnit = (unit || 'month') as DayInterval[1];
192-
setUnit(nextUnit);
193+
setUnit(effectiveUnit);
193194
}}
194195
>
195196
custom
196197
</Button>
197198

198-
{intervalSelection === 'custom' && (
199+
{effectiveSelection === 'custom' && (
199200
<div className="mt-2 px-1">
200201
<div className="text-xs text-muted-foreground mb-1">Custom</div>
201202
<div className="flex items-center gap-2">
@@ -209,13 +210,13 @@ function IntervalPopover({
209210
const v = e.target.value;
210211
if (!/^\d*$/.test(v)) return;
211212
const n = v === '' ? 0 : parseInt(v, 10);
212-
applyCustom(n, (unit || 'month') as DayInterval[1]);
213+
applyCustom(n, effectiveUnit);
213214
}}
214215
/>
215216
</div>
216217
<div className="w-24">
217218
<Select
218-
value={(unit || 'month') as DayInterval[1]}
219+
value={effectiveUnit}
219220
onValueChange={(u) => {
220221
const newUnit = u as DayInterval[1];
221222
applyCustom(count, newUnit);
@@ -225,10 +226,11 @@ function IntervalPopover({
225226
<SelectValue />
226227
</SelectTrigger>
227228
<SelectContent>
228-
<SelectItem value="day">day</SelectItem>
229-
<SelectItem value="week">week</SelectItem>
230-
<SelectItem value="month">month</SelectItem>
231-
<SelectItem value="year">year</SelectItem>
229+
{normalizedUnits.map((unitOption) => (
230+
<SelectItem key={unitOption} value={unitOption}>
231+
{unitOption}
232+
</SelectItem>
233+
))}
232234
</SelectContent>
233235
</Select>
234236
</div>
@@ -248,6 +250,7 @@ type ProductEditableInputProps = {
248250
readOnly?: boolean,
249251
placeholder?: string,
250252
inputClassName?: string,
253+
transform?: (value: string) => string,
251254
};
252255

253256
function ProductEditableInput({
@@ -256,6 +259,7 @@ function ProductEditableInput({
256259
readOnly,
257260
placeholder,
258261
inputClassName,
262+
transform,
259263
}: ProductEditableInputProps) {
260264
const [isActive, setIsActive] = useState(false);
261265

@@ -278,7 +282,8 @@ function ProductEditableInput({
278282
<Input
279283
value={value}
280284
onChange={(event) => {
281-
const nextValue = event.target.value;
285+
const rawValue = event.target.value;
286+
const nextValue = transform ? transform(rawValue) : rawValue;
282287
void onUpdate?.(nextValue);
283288
}}
284289
placeholder={placeholder}
@@ -383,6 +388,7 @@ function ProductPriceRow({
383388
setIntervalSelection={setIntervalSelection}
384389
setUnit={setPriceInterval}
385390
setCount={setIntervalCount}
391+
allowedUnits={PRICE_INTERVAL_UNITS}
386392
onChange={(interval) => {
387393
if (readOnly) return;
388394
const normalized = amount === '' ? '0.00' : (Number.isNaN(parseFloat(amount)) ? '0.00' : parseFloat(amount).toFixed(2));
@@ -496,7 +502,7 @@ function ProductItemRow({
496502
<Popover open={itemSelectOpen} onOpenChange={setItemSelectOpen}>
497503
<PopoverTrigger>
498504
<div className="text-sm px-2 py-0.5 rounded bg-muted hover:bg-muted/70 cursor-pointer select-none flex items-center gap-1">
499-
{itemDisplayName}
505+
{itemId}
500506
<ChevronsUpDown className="h-4 w-4" />
501507
</div>
502508
</PopoverTrigger>
@@ -627,7 +633,7 @@ function ProductItemRow({
627633
<ChevronDown className={cn("h-4 w-4 transition-transform", isOpen ? "rotate-0" : "-rotate-90")} />
628634
</button>
629635
</CollapsibleTrigger >
630-
<div className="text-sm">{itemDisplayName}</div>
636+
<div className="text-sm">{itemId}</div>
631637
<div className="ml-auto w-16 text-right text-sm text-muted-foreground tabular-nums">{prettyPrintWithMagnitudes(item.quantity)}</div>
632638
<div className="ml-2">
633639
<div className="text-xs px-2 py-0.5 rounded bg-muted text-muted-foreground">{shortRepeatText}</div>
@@ -863,6 +869,7 @@ function ProductCard({ id, activeType, product, allProducts, existingItems, onSa
863869
readOnly={!isDraft || !isEditing}
864870
placeholder={"Product ID"}
865871
inputClassName="text-xs font-mono text-center text-muted-foreground"
872+
transform={(value) => value.toLowerCase()}
866873
/>
867874
<ProductEditableInput
868875
value={draft.displayName || ""}

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/price-dialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ export function PriceDialog({
163163
id="price-id"
164164
value={priceId}
165165
onChange={(e) => {
166-
setPriceId(e.target.value);
166+
const nextValue = e.target.value.toLowerCase();
167+
setPriceId(nextValue);
167168
if (errors.priceId) {
168169
setErrors(prev => {
169170
const newErrors = { ...prev };
@@ -371,4 +372,3 @@ export function PriceDialog({
371372
</Dialog>
372373
);
373374
}
374-

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,8 @@ export function ProductDialog({
382382
id="product-id"
383383
value={productId}
384384
onChange={(e) => {
385-
setProductId(e.target.value);
385+
const nextValue = e.target.value.toLowerCase();
386+
setProductId(nextValue);
386387
if (errors.productId) {
387388
setErrors(prev => {
388389
const newErrors = { ...prev };
@@ -818,4 +819,3 @@ export function ProductDialog({
818819
</>
819820
);
820821
}
821-

0 commit comments

Comments
 (0)