mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
feat(collections): edit a saved place's provider ids, website, phone and price (#2646)
The update contract takes google_place_id, google_ftid, osm_id, website, phone, price and currency like saving does, website with the same normalisation and refusal. REST and update_collection_place share it, so an agent can correct a place instead of re-saving it and losing its labels, ratings, position and id. The assistant's change card names the new fields.
This commit is contained in:
@@ -32,6 +32,11 @@ export const COLLECTION_CARD_FIELDS = [
|
||||
'category',
|
||||
'link',
|
||||
'photo',
|
||||
'website',
|
||||
'phone',
|
||||
'price',
|
||||
/** The place's Google or OpenStreetMap entry (its provider ids). */
|
||||
'mapEntry',
|
||||
] as const;
|
||||
|
||||
/** What a change does to a label of a list; `none` on an answer. */
|
||||
@@ -75,7 +80,7 @@ export const collectionCardDeltaSchema = z.object({
|
||||
rating: z.object({ to: z.number().int().min(1).max(5).nullable() }).nullable(),
|
||||
labelsAdded: z.array(idSchema).max(10),
|
||||
labelsRemoved: z.array(idSchema).max(10),
|
||||
fields: z.array(z.enum(COLLECTION_CARD_FIELDS)).max(8),
|
||||
fields: z.array(z.enum(COLLECTION_CARD_FIELDS)).max(COLLECTION_CARD_FIELDS.length),
|
||||
movedTo: collectionCardListSchema.nullable(),
|
||||
removed: z.boolean(),
|
||||
});
|
||||
|
||||
@@ -86,7 +86,7 @@ describe('collection cards', () => {
|
||||
const delta = deltaOf({ status: { from: 'idea', to: 'visited' }, rating: { to: null }, fields: ['photo'] });
|
||||
const card = { ...placesCard, places: [{ ...place, delta }] };
|
||||
expect(collectionPlacesCardSchema.parse(card).places[0]?.delta).toEqual(delta);
|
||||
const unknownField = { ...placesCard, places: [{ ...place, delta: deltaOf({ fields: ['phone'] }) }] };
|
||||
const unknownField = { ...placesCard, places: [{ ...place, delta: deltaOf({ fields: ['email'] }) }] };
|
||||
expect(collectionPlacesCardSchema.safeParse(unknownField).success).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -28,6 +28,36 @@ describe('collectionStatusSchema', () => {
|
||||
});
|
||||
|
||||
describe('collectionPlaceUpdateRequestSchema', () => {
|
||||
// #2646: the fields saving takes, with the same checks, so a place is corrected in place.
|
||||
it('takes the provider ids, website, phone and price, and null clears each', () => {
|
||||
const set = {
|
||||
google_place_id: 'ChIJseptime',
|
||||
google_ftid: '0x47e6:0x1b2',
|
||||
osm_id: 'node/42',
|
||||
website: 'https://septime-charonne.fr',
|
||||
phone: '+33 1 43 67 38 29',
|
||||
price: 55,
|
||||
currency: 'EUR',
|
||||
};
|
||||
expect(collectionPlaceUpdateRequestSchema.parse(set)).toEqual(set);
|
||||
const cleared = Object.fromEntries(Object.keys(set).map((key) => [key, null]));
|
||||
expect(collectionPlaceUpdateRequestSchema.parse(cleared)).toEqual(cleared);
|
||||
});
|
||||
|
||||
it('normalises a bare domain as website and refuses a script link, as saving does', () => {
|
||||
expect(collectionPlaceUpdateRequestSchema.parse({ website: 'septime-charonne.fr' }).website).toBe(
|
||||
'https://septime-charonne.fr',
|
||||
);
|
||||
expect(collectionPlaceUpdateRequestSchema.safeParse({ website: 'javascript:alert(1)' }).success).toBe(false);
|
||||
});
|
||||
|
||||
it('leaves the new fields absent when an update does not name them', () => {
|
||||
const parsed = collectionPlaceUpdateRequestSchema.parse({ name: 'Septime' });
|
||||
for (const key of ['google_place_id', 'google_ftid', 'osm_id', 'website', 'phone', 'price', 'currency']) {
|
||||
expect(key in parsed, key).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Regression for #1437: an update that omits `status` must NOT inject a default
|
||||
// of 'idea', otherwise the server overwrites the stored status on every edit-save.
|
||||
it('does not inject a status when the field is absent', () => {
|
||||
|
||||
@@ -223,6 +223,16 @@ export const collectionPlaceUpdateRequestSchema = z.object({
|
||||
label_ids: z.array(z.number()).optional(),
|
||||
// Custom thumbnail (#1136): null clears it (falls back to the auto-fetched photo).
|
||||
image_url: placeImageUrlSchema.nullable().optional(),
|
||||
// The fields saving accepts but editing did not (#2646), with the same checks, so
|
||||
// a place can be corrected in place instead of re-saved, which loses its labels,
|
||||
// ratings, position and id. Each one is left alone when absent; null clears it.
|
||||
google_place_id: z.string().nullable().optional(),
|
||||
google_ftid: z.string().nullable().optional(),
|
||||
osm_id: z.string().nullable().optional(),
|
||||
website: placeWebsiteSchema.nullable().optional(),
|
||||
phone: z.string().nullable().optional(),
|
||||
price: z.number().nullable().optional(),
|
||||
currency: z.string().nullable().optional(),
|
||||
});
|
||||
export type CollectionPlaceUpdateRequest = z.infer<typeof collectionPlaceUpdateRequestSchema>;
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'نقل إلى {name}',
|
||||
'assistant.card.collection.removed': 'أُزيل من القائمة',
|
||||
'assistant.card.collection.photo': 'صورة',
|
||||
'assistant.card.collection.mapEntry': 'سجل الخريطة',
|
||||
'assistant.card.collection.places': 'الأماكن المحفوظة في {name}',
|
||||
'assistant.card.collection.saved': 'الأماكن المحفوظة',
|
||||
'assistant.card.collection.statusSet': 'تغيّر {count} مكان محفوظ',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': '{name} siyahısına köçür',
|
||||
'assistant.card.collection.removed': 'Siyahıdan çıxarıldı',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Xəritə qeydi',
|
||||
'assistant.card.collection.places': '{name} siyahısındakı saxlanılmış məkanlar',
|
||||
'assistant.card.collection.saved': 'Saxlanılmış məkanlar',
|
||||
'assistant.card.collection.statusSet': '{count} saxlanılmış məkan dəyişdi',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Mover para {name}',
|
||||
'assistant.card.collection.removed': 'Removido da lista',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Registro no mapa',
|
||||
'assistant.card.collection.places': 'Lugares salvos em {name}',
|
||||
'assistant.card.collection.saved': 'Lugares salvos',
|
||||
'assistant.card.collection.statusSet': '{count} lugares salvos alterados',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Mou a {name}',
|
||||
'assistant.card.collection.removed': 'Tret de la llista',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Fitxa del mapa',
|
||||
'assistant.card.collection.places': 'Llocs desats a {name}',
|
||||
'assistant.card.collection.saved': 'Llocs desats',
|
||||
'assistant.card.collection.statusSet': '{count} llocs desats canviats',
|
||||
|
||||
@@ -34,6 +34,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Přesunout do {name}',
|
||||
'assistant.card.collection.removed': 'Odebráno ze seznamu',
|
||||
'assistant.card.collection.photo': 'Fotka',
|
||||
'assistant.card.collection.mapEntry': 'Záznam na mapě',
|
||||
'assistant.card.collection.places': 'Uložená místa v {name}',
|
||||
'assistant.card.collection.saved': 'Uložená místa',
|
||||
'assistant.card.collection.statusSet': '{count} uložených míst změněno',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Nach {name} verschieben',
|
||||
'assistant.card.collection.removed': 'Aus der Liste entfernt',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Karteneintrag',
|
||||
'assistant.card.collection.places': 'Gespeicherte Orte in {name}',
|
||||
'assistant.card.collection.saved': 'Gespeicherte Orte',
|
||||
'assistant.card.collection.statusSet': '{count} gespeicherte Orte geändert',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Move to {name}',
|
||||
'assistant.card.collection.removed': 'Removed from the list',
|
||||
'assistant.card.collection.photo': 'Photo',
|
||||
'assistant.card.collection.mapEntry': 'Map entry',
|
||||
'assistant.card.collection.places': 'Saved places in {name}',
|
||||
'assistant.card.collection.saved': 'Saved places',
|
||||
'assistant.card.collection.statusSet': '{count} saved places changed',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Mover a {name}',
|
||||
'assistant.card.collection.removed': 'Quitado de la lista',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Ficha del mapa',
|
||||
'assistant.card.collection.places': 'Lugares guardados en {name}',
|
||||
'assistant.card.collection.saved': 'Lugares guardados',
|
||||
'assistant.card.collection.statusSet': '{count} lugares guardados cambiados',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Teisalda nimekirja {name}',
|
||||
'assistant.card.collection.removed': 'Nimekirjast eemaldatud',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Kaardikirje',
|
||||
'assistant.card.collection.places': 'Salvestatud kohad nimekirjas {name}',
|
||||
'assistant.card.collection.saved': 'Salvestatud kohad',
|
||||
'assistant.card.collection.statusSet': '{count} salvestatud kohta muudetud',
|
||||
|
||||
@@ -28,6 +28,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.removed': 'Retiré de la liste',
|
||||
'assistant.card.collection.photo': 'Photo', // same-as-en
|
||||
'assistant.card.collection.places': 'Lieux enregistrés dans {name}',
|
||||
'assistant.card.collection.mapEntry': 'Fiche sur la carte',
|
||||
'assistant.card.collection.saved': 'Lieux enregistrés',
|
||||
'assistant.card.collection.statusSet': '{count} lieux enregistrés modifiés',
|
||||
'assistant.card.collection.statusSet.one': '{count} lieu enregistré modifié',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Μετακίνηση στη λίστα {name}',
|
||||
'assistant.card.collection.removed': 'Αφαιρέθηκε από τη λίστα',
|
||||
'assistant.card.collection.photo': 'Φωτογραφία',
|
||||
'assistant.card.collection.mapEntry': 'Καταχώριση χάρτη',
|
||||
'assistant.card.collection.places': 'Αποθηκευμένα μέρη στη λίστα {name}',
|
||||
'assistant.card.collection.saved': 'Αποθηκευμένα μέρη',
|
||||
'assistant.card.collection.statusSet': '{count} αποθηκευμένα μέρη άλλαξαν',
|
||||
|
||||
@@ -34,6 +34,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'העברה ל-{name}',
|
||||
'assistant.card.collection.removed': 'הוסר מהרשימה',
|
||||
'assistant.card.collection.photo': 'תמונה',
|
||||
'assistant.card.collection.mapEntry': 'רשומה במפה',
|
||||
'assistant.card.collection.places': 'מקומות שמורים ב-{name}',
|
||||
'assistant.card.collection.saved': 'מקומות שמורים',
|
||||
'assistant.card.collection.statusSet': '{count} מקומות שמורים עודכנו',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Áthelyezés ide: {name}',
|
||||
'assistant.card.collection.removed': 'Eltávolítva a listából',
|
||||
'assistant.card.collection.photo': 'Fotó',
|
||||
'assistant.card.collection.mapEntry': 'Térképes bejegyzés',
|
||||
'assistant.card.collection.places': 'Mentett helyek itt: {name}',
|
||||
'assistant.card.collection.saved': 'Mentett helyek',
|
||||
'assistant.card.collection.statusSet': '{count} mentett hely módosítva',
|
||||
|
||||
@@ -20,6 +20,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Pindahkan ke {name}',
|
||||
'assistant.card.collection.removed': 'Dihapus dari daftar',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Entri peta',
|
||||
'assistant.card.collection.places': 'Tempat tersimpan di {name}',
|
||||
'assistant.card.collection.saved': 'Tempat tersimpan',
|
||||
'assistant.card.collection.statusSet': '{count} tempat tersimpan diubah',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Sposta in {name}',
|
||||
'assistant.card.collection.removed': 'Rimosso dalla lista',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Scheda sulla mappa',
|
||||
'assistant.card.collection.places': 'Luoghi salvati in {name}',
|
||||
'assistant.card.collection.saved': 'Luoghi salvati',
|
||||
'assistant.card.collection.statusSet': '{count} luoghi salvati modificati',
|
||||
|
||||
@@ -20,6 +20,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': '{name} に移動',
|
||||
'assistant.card.collection.removed': 'リストから削除しました',
|
||||
'assistant.card.collection.photo': '写真',
|
||||
'assistant.card.collection.mapEntry': '地図の項目',
|
||||
'assistant.card.collection.places': '{name} の保存した場所',
|
||||
'assistant.card.collection.saved': '保存した場所',
|
||||
'assistant.card.collection.statusSet': '保存した場所 {count} 件を変更',
|
||||
|
||||
@@ -20,6 +20,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': '{name}(으)로 이동',
|
||||
'assistant.card.collection.removed': '목록에서 제거됨',
|
||||
'assistant.card.collection.photo': '사진',
|
||||
'assistant.card.collection.mapEntry': '지도 항목',
|
||||
'assistant.card.collection.places': '{name}에 저장한 장소',
|
||||
'assistant.card.collection.saved': '저장한 장소',
|
||||
'assistant.card.collection.statusSet': '저장한 장소 {count}곳 변경됨',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Verplaatsen naar {name}',
|
||||
'assistant.card.collection.removed': 'Uit de lijst verwijderd',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Kaartvermelding',
|
||||
'assistant.card.collection.places': 'Bewaarde plaatsen in {name}',
|
||||
'assistant.card.collection.saved': 'Bewaarde plaatsen',
|
||||
'assistant.card.collection.statusSet': '{count} bewaarde plaatsen gewijzigd',
|
||||
|
||||
@@ -41,6 +41,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Przenieś do {name}',
|
||||
'assistant.card.collection.removed': 'Usunięto z listy',
|
||||
'assistant.card.collection.photo': 'Zdjęcie',
|
||||
'assistant.card.collection.mapEntry': 'Wpis na mapie',
|
||||
'assistant.card.collection.places': 'Zapisane miejsca na liście {name}',
|
||||
'assistant.card.collection.saved': 'Zapisane miejsca',
|
||||
'assistant.card.collection.statusSet': 'Zmieniono {count} zapisanego miejsca',
|
||||
|
||||
@@ -41,6 +41,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Переместить в «{name}»',
|
||||
'assistant.card.collection.removed': 'Удалено из списка',
|
||||
'assistant.card.collection.photo': 'Фото',
|
||||
'assistant.card.collection.mapEntry': 'Запись на карте',
|
||||
'assistant.card.collection.places': 'Сохранённые места в «{name}»',
|
||||
'assistant.card.collection.saved': 'Сохранённые места',
|
||||
'assistant.card.collection.statusSet': 'Изменено сохранённых мест: {count}',
|
||||
|
||||
@@ -34,6 +34,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Presunúť do {name}',
|
||||
'assistant.card.collection.removed': 'Odstránené zo zoznamu',
|
||||
'assistant.card.collection.photo': 'Fotka',
|
||||
'assistant.card.collection.mapEntry': 'Záznam na mape',
|
||||
'assistant.card.collection.places': 'Uložené miesta v {name}',
|
||||
'assistant.card.collection.saved': 'Uložené miesta',
|
||||
'assistant.card.collection.statusSet': '{count} uložených miest zmenených',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Flytta till {name}',
|
||||
'assistant.card.collection.removed': 'Borttagen från listan',
|
||||
'assistant.card.collection.photo': 'Foto',
|
||||
'assistant.card.collection.mapEntry': 'Kartpost',
|
||||
'assistant.card.collection.places': 'Sparade platser i {name}',
|
||||
'assistant.card.collection.saved': 'Sparade platser',
|
||||
'assistant.card.collection.statusSet': '{count} sparade platser ändrade',
|
||||
|
||||
@@ -20,6 +20,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'ย้ายไปที่ {name}',
|
||||
'assistant.card.collection.removed': 'นำออกจากรายการแล้ว',
|
||||
'assistant.card.collection.photo': 'รูปภาพ',
|
||||
'assistant.card.collection.mapEntry': 'รายการบนแผนที่',
|
||||
'assistant.card.collection.places': 'สถานที่ที่บันทึกไว้ใน {name}',
|
||||
'assistant.card.collection.saved': 'สถานที่ที่บันทึกไว้',
|
||||
'assistant.card.collection.statusSet': 'เปลี่ยนสถานที่ที่บันทึกไว้ {count} แห่ง',
|
||||
|
||||
@@ -27,6 +27,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': '{name} listesine taşı',
|
||||
'assistant.card.collection.removed': 'Listeden çıkarıldı',
|
||||
'assistant.card.collection.photo': 'Fotoğraf',
|
||||
'assistant.card.collection.mapEntry': 'Harita kaydı',
|
||||
'assistant.card.collection.places': '{name} listesindeki kayıtlı yerler',
|
||||
'assistant.card.collection.saved': 'Kayıtlı yerler',
|
||||
'assistant.card.collection.statusSet': '{count} kayıtlı yer değişti',
|
||||
|
||||
@@ -41,6 +41,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Перемістити в «{name}»',
|
||||
'assistant.card.collection.removed': 'Прибрано зі списку',
|
||||
'assistant.card.collection.photo': 'Фото',
|
||||
'assistant.card.collection.mapEntry': 'Запис на мапі',
|
||||
'assistant.card.collection.places': 'Збережені місця в «{name}»',
|
||||
'assistant.card.collection.saved': 'Збережені місця',
|
||||
'assistant.card.collection.statusSet': 'Змінено збережених місць: {count}',
|
||||
|
||||
@@ -20,6 +20,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': 'Chuyển sang {name}',
|
||||
'assistant.card.collection.removed': 'Đã xoá khỏi danh sách',
|
||||
'assistant.card.collection.photo': 'Ảnh',
|
||||
'assistant.card.collection.mapEntry': 'Mục trên bản đồ',
|
||||
'assistant.card.collection.places': 'Địa điểm đã lưu trong {name}',
|
||||
'assistant.card.collection.saved': 'Địa điểm đã lưu',
|
||||
'assistant.card.collection.statusSet': 'Đã thay đổi {count} địa điểm đã lưu',
|
||||
|
||||
@@ -20,6 +20,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': '移動到 {name}',
|
||||
'assistant.card.collection.removed': '已從清單中移除',
|
||||
'assistant.card.collection.photo': '照片',
|
||||
'assistant.card.collection.mapEntry': '地圖項目',
|
||||
'assistant.card.collection.places': '{name} 中儲存的地點',
|
||||
'assistant.card.collection.saved': '儲存的地點',
|
||||
'assistant.card.collection.statusSet': '已變更 {count} 個儲存的地點',
|
||||
|
||||
@@ -20,6 +20,7 @@ const assistantCollection: TranslationStrings = {
|
||||
'assistant.card.collection.moveTo': '移动到 {name}',
|
||||
'assistant.card.collection.removed': '已从列表中移除',
|
||||
'assistant.card.collection.photo': '照片',
|
||||
'assistant.card.collection.mapEntry': '地图条目',
|
||||
'assistant.card.collection.places': '{name} 中保存的地点',
|
||||
'assistant.card.collection.saved': '保存的地点',
|
||||
'assistant.card.collection.statusSet': '已更改 {count} 个保存的地点',
|
||||
|
||||
Reference in New Issue
Block a user