mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 14:36:31 +00:00
revert(collections): a saved place carries no price, website or phone again
Takes back the editable price, currency, website and phone on collection places from0df82185band its review follow-up ina910c2a83. Costs are kept inside a trip only; a collection does not carry them, and #2471 is a feature request rather than a bug. The collection files are back to how they were before, including the dimmed save button fromf16698bdb, which only mattered for the price field.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
collectionDeleteManyRequestSchema,
|
||||
collectionPlaceCurrencySchema,
|
||||
collectionPlaceUpdateRequestSchema,
|
||||
collectionReorderRequestSchema,
|
||||
collectionSavePlaceRequestSchema,
|
||||
@@ -48,65 +47,6 @@ describe('collectionPlaceUpdateRequestSchema', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// #2471: price, currency, website and phone were columns the update contract did
|
||||
// not know, so the validation pipe stripped them and a saved place's cost could
|
||||
// never be edited.
|
||||
describe('collectionPlaceUpdateRequestSchema price, currency, website and phone', () => {
|
||||
it('passes all four through', () => {
|
||||
const parsed = collectionPlaceUpdateRequestSchema.parse({
|
||||
price: 12.5,
|
||||
currency: 'CHF',
|
||||
website: 'https://museum.example',
|
||||
phone: '+41 44 123 45 67',
|
||||
});
|
||||
expect(parsed).toEqual({
|
||||
price: 12.5,
|
||||
currency: 'CHF',
|
||||
website: 'https://museum.example',
|
||||
phone: '+41 44 123 45 67',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts null on each to clear it, and zero as a price', () => {
|
||||
const none = { price: null, currency: null, website: null, phone: null };
|
||||
expect(collectionPlaceUpdateRequestSchema.parse(none)).toEqual(none);
|
||||
expect(collectionPlaceUpdateRequestSchema.parse({ price: 0 }).price).toBe(0);
|
||||
});
|
||||
|
||||
it('leaves all four absent when an unrelated field changes', () => {
|
||||
const parsed = collectionPlaceUpdateRequestSchema.parse({ name: 'Kunsthaus' });
|
||||
for (const key of ['price', 'currency', 'website', 'phone']) expect(key in parsed).toBe(false);
|
||||
});
|
||||
|
||||
it('refuses a negative or non-finite price', () => {
|
||||
for (const price of [-1, -0.01, Number.NaN, Number.POSITIVE_INFINITY]) {
|
||||
expect(collectionPlaceUpdateRequestSchema.safeParse({ price }).success).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('normalises the currency to a trimmed upper-case code', () => {
|
||||
expect(collectionPlaceUpdateRequestSchema.parse({ currency: ' usd ' }).currency).toBe('USD');
|
||||
expect(collectionPlaceCurrencySchema.parse('jpy')).toBe('JPY');
|
||||
});
|
||||
|
||||
it('refuses a currency that is not three letters', () => {
|
||||
for (const currency of ['EU', 'EURO', '€', '12A', '']) {
|
||||
expect(collectionPlaceUpdateRequestSchema.safeParse({ currency }).success).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('holds the website to http or https, like the save side', () => {
|
||||
expect(collectionPlaceUpdateRequestSchema.safeParse({ website: 'javascript:alert(1)' }).success).toBe(false);
|
||||
expect(collectionPlaceUpdateRequestSchema.safeParse({ website: 'http://museum.example' }).success).toBe(true);
|
||||
});
|
||||
|
||||
it('trims the phone and caps it at 60 characters', () => {
|
||||
expect(collectionPlaceUpdateRequestSchema.parse({ phone: ' +49 30 1234 ' }).phone).toBe('+49 30 1234');
|
||||
expect(collectionPlaceUpdateRequestSchema.safeParse({ phone: '1'.repeat(60) }).success).toBe(true);
|
||||
expect(collectionPlaceUpdateRequestSchema.safeParse({ phone: '1'.repeat(61) }).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// The two ratchet schemas replace hand-rolled "array of numbers" checks — they
|
||||
// must stay exactly that permissive (empty arrays included, no int/positive).
|
||||
describe('collectionReorderRequestSchema / collectionDeleteManyRequestSchema', () => {
|
||||
|
||||
@@ -184,31 +184,10 @@ export const collectionSaveFromTripManyRequestSchema = z.object({
|
||||
});
|
||||
export type CollectionSaveFromTripManyRequest = z.infer<typeof collectionSaveFromTripManyRequestSchema>;
|
||||
|
||||
/**
|
||||
* A saved place's currency. A collection has no base currency to fall back on,
|
||||
* so a price always names its own: three letters, trimmed and upper-cased so
|
||||
* 'eur ' lands as 'EUR'.
|
||||
*/
|
||||
export const collectionPlaceCurrencySchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.regex(/^[A-Za-z]{3}$/, { message: 'must be a three-letter ISO 4217 code' })
|
||||
.toUpperCase();
|
||||
|
||||
export const collectionPlaceUpdateRequestSchema = z.object({
|
||||
name: z.string().min(1).optional(),
|
||||
description: z.string().nullable().optional(),
|
||||
notes: z.string().nullable().optional(),
|
||||
// A rough cost such as an entry fee (#2471). The columns were always there and
|
||||
// travelled along on save and on copy into a trip, but nothing could edit them.
|
||||
// Stricter than the save side on purpose (no negative amount, a real currency
|
||||
// code); the edit forms only resend a field the user changed, so an older row
|
||||
// never blocks an unrelated edit.
|
||||
price: z.number().nonnegative().nullable().optional(),
|
||||
currency: collectionPlaceCurrencySchema.nullable().optional(),
|
||||
website: placeWebsiteSchema.nullable().optional(),
|
||||
// Capped like the file import's phone, which is where most of them come from.
|
||||
phone: z.string().trim().max(60).nullable().optional(),
|
||||
// Editable coordinates so a place added by GPS can be corrected later (#1435).
|
||||
lat: z.number().nullable().optional(),
|
||||
lng: z.number().nullable().optional(),
|
||||
|
||||
Reference in New Issue
Block a user