mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 14:36:31 +00:00
feat(collections): bulk-add selected trip places to a list (#1081)
Add a "Save to collection" action to the trip place list's selection bar (next to bulk category + delete): it opens a list picker and copies every selected place into the chosen list in one request, instead of one-by-one from each place. - Server: saveFromTripPlaces (one access check + one WS notify), POST places/from-trip-many; dedups by name/coords, skips missing ids, honours force. - Client: saveFromTripMany api + SaveTripPlacesToListModal; the selection-bar button is gated on the collections addon being enabled. - Copy count / skipped-duplicates toast; strings in all 22 locales. - Service + controller tests for the bulk path.
This commit is contained in:
@@ -138,6 +138,15 @@ export const collectionSaveFromTripRequestSchema = z.object({
|
||||
});
|
||||
export type CollectionSaveFromTripRequest = z.infer<typeof collectionSaveFromTripRequestSchema>;
|
||||
|
||||
/** Bulk: copy several selected trip places into a list at once. */
|
||||
export const collectionSaveFromTripManyRequestSchema = z.object({
|
||||
collection_id: z.number(),
|
||||
source_trip_id: z.number(),
|
||||
source_place_ids: z.array(z.number()).min(1).max(1000),
|
||||
force: z.boolean().optional(),
|
||||
});
|
||||
export type CollectionSaveFromTripManyRequest = z.infer<typeof collectionSaveFromTripManyRequestSchema>;
|
||||
|
||||
export const collectionPlaceUpdateRequestSchema = z.object({
|
||||
name: z.string().min(1).optional(),
|
||||
description: z.string().nullable().optional(),
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'حفظ {count} في قائمة',
|
||||
'collections.addedNToList': 'تم حفظ {count} في {name}',
|
||||
'collections.noOwnLists': 'ليس لديك قوائم بعد',
|
||||
'collections.saveToListHint': 'يتم تخطي التكرارات تلقائيًا',
|
||||
'collections.role.label': 'الدور',
|
||||
'collections.role.viewer': 'مشاهد',
|
||||
'collections.role.editor': 'محرر',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Salvar {count} em uma lista',
|
||||
'collections.addedNToList': '{count} salvos em {name}',
|
||||
'collections.noOwnLists': 'Você ainda não tem listas',
|
||||
'collections.saveToListHint': 'Duplicados são ignorados automaticamente',
|
||||
'collections.role.label': 'Função',
|
||||
'collections.role.viewer': 'Visualizador',
|
||||
'collections.role.editor': 'Editor',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Uložit {count} do seznamu',
|
||||
'collections.addedNToList': 'Uloženo {count} do {name}',
|
||||
'collections.noOwnLists': 'Zatím nemáte žádné seznamy',
|
||||
'collections.saveToListHint': 'Duplikáty se automaticky přeskočí',
|
||||
'collections.role.label': 'Role',
|
||||
'collections.role.viewer': 'Divák',
|
||||
'collections.role.editor': 'Editor',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': '{count} in eine Liste speichern',
|
||||
'collections.addedNToList': '{count} in {name} gespeichert',
|
||||
'collections.noOwnLists': 'Du hast noch keine Listen',
|
||||
'collections.saveToListHint': 'Duplikate werden automatisch übersprungen',
|
||||
'collections.role.label': 'Rolle',
|
||||
'collections.role.viewer': 'Betrachter',
|
||||
'collections.role.editor': 'Bearbeiter',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Save {count} to a list',
|
||||
'collections.addedNToList': 'Saved {count} to {name}',
|
||||
'collections.noOwnLists': 'You have no lists yet',
|
||||
'collections.saveToListHint': 'Duplicates are skipped automatically',
|
||||
'collections.role.label': 'Role',
|
||||
'collections.role.viewer': 'Viewer',
|
||||
'collections.role.editor': 'Editor',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Guardar {count} en una lista',
|
||||
'collections.addedNToList': '{count} guardados en {name}',
|
||||
'collections.noOwnLists': 'Aún no tienes listas',
|
||||
'collections.saveToListHint': 'Los duplicados se omiten automáticamente',
|
||||
'collections.role.label': 'Rol',
|
||||
'collections.role.viewer': 'Lector',
|
||||
'collections.role.editor': 'Editor',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Enregistrer {count} dans une liste',
|
||||
'collections.addedNToList': '{count} enregistrés dans {name}',
|
||||
'collections.noOwnLists': 'Vous n\'avez pas encore de listes',
|
||||
'collections.saveToListHint': 'Les doublons sont ignorés automatiquement',
|
||||
'collections.role.label': 'Rôle',
|
||||
'collections.role.viewer': 'Lecteur',
|
||||
'collections.role.editor': 'Éditeur',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Αποθήκευση {count} σε λίστα',
|
||||
'collections.addedNToList': 'Αποθηκεύτηκαν {count} στο {name}',
|
||||
'collections.noOwnLists': 'Δεν έχετε λίστες ακόμη',
|
||||
'collections.saveToListHint': 'Τα διπλότυπα παραλείπονται αυτόματα',
|
||||
'collections.role.label': 'Ρόλος',
|
||||
'collections.role.viewer': 'Θεατής',
|
||||
'collections.role.editor': 'Συντάκτης',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': '{count} mentése listába',
|
||||
'collections.addedNToList': '{count} mentve ide: {name}',
|
||||
'collections.noOwnLists': 'Még nincsenek listáid',
|
||||
'collections.saveToListHint': 'A duplikátumok automatikusan kimaradnak',
|
||||
'collections.role.label': 'Szerep',
|
||||
'collections.role.viewer': 'Néző',
|
||||
'collections.role.editor': 'Szerkesztő',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Simpan {count} ke daftar',
|
||||
'collections.addedNToList': '{count} disimpan ke {name}',
|
||||
'collections.noOwnLists': 'Kamu belum punya daftar',
|
||||
'collections.saveToListHint': 'Duplikat dilewati otomatis',
|
||||
'collections.role.label': 'Peran',
|
||||
'collections.role.viewer': 'Peninjau',
|
||||
'collections.role.editor': 'Editor',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Salva {count} in una lista',
|
||||
'collections.addedNToList': '{count} salvati in {name}',
|
||||
'collections.noOwnLists': 'Non hai ancora liste',
|
||||
'collections.saveToListHint': 'I duplicati vengono ignorati automaticamente',
|
||||
'collections.role.label': 'Ruolo',
|
||||
'collections.role.viewer': 'Visualizzatore',
|
||||
'collections.role.editor': 'Editore',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': '{count} 件をリストに保存',
|
||||
'collections.addedNToList': '{count} 件を {name} に保存しました',
|
||||
'collections.noOwnLists': 'リストがまだありません',
|
||||
'collections.saveToListHint': '重複は自動的にスキップされます',
|
||||
'collections.role.label': '役割',
|
||||
'collections.role.viewer': '閲覧者',
|
||||
'collections.role.editor': '編集者',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': '{count}개를 목록에 저장',
|
||||
'collections.addedNToList': '{count}개를 {name}에 저장함',
|
||||
'collections.noOwnLists': '아직 목록이 없습니다',
|
||||
'collections.saveToListHint': '중복 항목은 자동으로 건너뜁니다',
|
||||
'collections.role.label': '역할',
|
||||
'collections.role.viewer': '뷰어',
|
||||
'collections.role.editor': '편집자',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': '{count} in een lijst opslaan',
|
||||
'collections.addedNToList': '{count} opgeslagen in {name}',
|
||||
'collections.noOwnLists': 'Je hebt nog geen lijsten',
|
||||
'collections.saveToListHint': 'Duplicaten worden automatisch overgeslagen',
|
||||
'collections.role.label': 'Rol',
|
||||
'collections.role.viewer': 'Kijker',
|
||||
'collections.role.editor': 'Bewerker',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Zapisz {count} na liście',
|
||||
'collections.addedNToList': 'Zapisano {count} w {name}',
|
||||
'collections.noOwnLists': 'Nie masz jeszcze list',
|
||||
'collections.saveToListHint': 'Duplikaty są pomijane automatycznie',
|
||||
'collections.role.label': 'Rola',
|
||||
'collections.role.viewer': 'Widz',
|
||||
'collections.role.editor': 'Edytor',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Сохранить {count} в список',
|
||||
'collections.addedNToList': 'Сохранено {count} в {name}',
|
||||
'collections.noOwnLists': 'У вас пока нет списков',
|
||||
'collections.saveToListHint': 'Дубликаты пропускаются автоматически',
|
||||
'collections.role.label': 'Роль',
|
||||
'collections.role.viewer': 'Наблюдатель',
|
||||
'collections.role.editor': 'Редактор',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Spara {count} i en lista',
|
||||
'collections.addedNToList': 'Sparade {count} i {name}',
|
||||
'collections.noOwnLists': 'Du har inga listor än',
|
||||
'collections.saveToListHint': 'Dubbletter hoppas över automatiskt',
|
||||
'collections.role.label': 'Roll',
|
||||
'collections.role.viewer': 'Läsare',
|
||||
'collections.role.editor': 'Redigerare',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': '{count} öğeyi bir listeye kaydet',
|
||||
'collections.addedNToList': '{count} öğe {name} listesine kaydedildi',
|
||||
'collections.noOwnLists': 'Henüz listeniz yok',
|
||||
'collections.saveToListHint': 'Yinelenenler otomatik olarak atlanır',
|
||||
'collections.role.label': 'Rol',
|
||||
'collections.role.viewer': 'Görüntüleyici',
|
||||
'collections.role.editor': 'Editör',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Зберегти {count} до списку',
|
||||
'collections.addedNToList': 'Збережено {count} до {name}',
|
||||
'collections.noOwnLists': 'У вас ще немає списків',
|
||||
'collections.saveToListHint': 'Дублікати пропускаються автоматично',
|
||||
'collections.role.label': 'Роль',
|
||||
'collections.role.viewer': 'Глядач',
|
||||
'collections.role.editor': 'Редактор',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': 'Lưu {count} vào danh sách',
|
||||
'collections.addedNToList': 'Đã lưu {count} vào {name}',
|
||||
'collections.noOwnLists': 'Bạn chưa có danh sách nào',
|
||||
'collections.saveToListHint': 'Các mục trùng lặp được bỏ qua tự động',
|
||||
'collections.role.label': 'Vai trò',
|
||||
'collections.role.viewer': 'Người xem',
|
||||
'collections.role.editor': 'Người sửa',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': '將 {count} 個儲存到清單',
|
||||
'collections.addedNToList': '已將 {count} 個儲存到 {name}',
|
||||
'collections.noOwnLists': '你還沒有清單',
|
||||
'collections.saveToListHint': '重複項目會被自動略過',
|
||||
'collections.role.label': '角色',
|
||||
'collections.role.viewer': '檢視者',
|
||||
'collections.role.editor': '編輯者',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { TranslationStrings } from '../types';
|
||||
|
||||
const collection: TranslationStrings = {
|
||||
'collections.saveNToList': '将 {count} 个保存到列表',
|
||||
'collections.addedNToList': '已将 {count} 个保存到 {name}',
|
||||
'collections.noOwnLists': '你还没有列表',
|
||||
'collections.saveToListHint': '重复项会被自动跳过',
|
||||
'collections.role.label': '角色',
|
||||
'collections.role.viewer': '查看者',
|
||||
'collections.role.editor': '编辑者',
|
||||
|
||||
Reference in New Issue
Block a user