diff --git a/components/calendar/__tests__/calendar-app.test.tsx b/components/calendar/__tests__/calendar-app.test.tsx index b4a9e6ed0..4ef1094e5 100644 --- a/components/calendar/__tests__/calendar-app.test.tsx +++ b/components/calendar/__tests__/calendar-app.test.tsx @@ -339,3 +339,39 @@ describe('CalendarApp read-only events', () => { expect(screen.getAllByRole('button', { name: /Standup/ })[0]).toHaveAttribute('draggable', 'true'); }); }); + +function dropOnMonthDay(day: RegExp, event: CalendarEvent) { + const cell = screen.getAllByRole('gridcell').find((c) => day.test(c.getAttribute('aria-label') ?? '')); + if (!cell) throw new Error(`no cell ${day}`); + const json = JSON.stringify({ type: 'calendar-event', eventId: event.id, originalStart: event.start, duration: event.duration }); + fireEvent.drop(cell, { dataTransfer: { types: ['application/x-calendar-event'], getData: () => json } }); +} + +describe('CalendarApp dragging', () => { + it('asks which occurrences a drag of a series moves', async () => { + setUp([weekly], 'month'); + renderApp(); + dropOnMonthDay(/October 14, 2026/, weekly); + const scope = await screen.findByRole('dialog', { name: 'edit_title' }); + expect(store.updateEvent).not.toHaveBeenCalled(); + fireEvent.click(within(scope).getByRole('button', { name: 'cancel' })); + await waitFor(() => expect(screen.queryByRole('dialog', { name: 'edit_title' })).toBeNull()); + expect(store.updateEvent).not.toHaveBeenCalled(); + }); + + it('moves one event at once and can move it back', async () => { + setUp([makeEvent()], 'month'); + renderApp(); + dropOnMonthDay(/October 14, 2026/, makeEvent()); + await waitFor(() => expect(store.updateEvent).toHaveBeenCalledTimes(1)); + expect(store.updateEvent.mock.calls[0][2]).toMatchObject({ start: '2026-10-14T10:00:00' }); + const moved = await waitFor(() => { + const found = useToastStore.getState().toasts.find((t) => t.title === 'notifications.event_moved'); + expect(found).toBeTruthy(); + return found!; + }); + moved.action!.onClick(); + await waitFor(() => expect(store.updateEvent).toHaveBeenCalledTimes(2)); + expect(store.updateEvent.mock.calls[1][2]).toMatchObject({ start: '2026-10-12T10:00:00', duration: 'PT1H' }); + }); +}); diff --git a/components/calendar/calendar-app.tsx b/components/calendar/calendar-app.tsx index cad627be8..fb1c4912a 100644 --- a/components/calendar/calendar-app.tsx +++ b/components/calendar/calendar-app.tsx @@ -59,7 +59,7 @@ import { deleteOccurrence, saveOccurrenceEdit, type EventEditorState } from "@/l import { useContactNameResolver } from "@/hooks/use-contact-name-resolver"; import { getClientByLocalAccountId } from "@/stores/client-registry"; import { useCalendarErrorMessage } from "@/hooks/use-calendar-error-message"; -import { getEventStartDate } from "@/lib/calendar-utils"; +import { getEventDisplayEndDate, getEventEndDate, getEventStartDate } from "@/lib/calendar-utils"; import { displayNow, getEffectiveTimeZone } from "@/lib/timezone"; import { canCreateEventsIn, getEventEditability, type EditabilityContext } from "@/lib/calendar-editability"; import { QuickCreatePopover, type QuickCreateDraft } from "@/components/calendar/quick-create-popover"; @@ -73,7 +73,8 @@ import { useConfirmDialog } from "@/hooks/use-confirm-dialog"; import { SchedulingDeniedError } from "@/lib/jmap/scheduling-error"; import { CreateCalendarModal } from "@/components/calendar/create-calendar-modal"; import { CalendarSidebarHeader } from "@/components/calendar/calendar-sidebar-header"; -import { getUserParticipantId, collectUserCalendarAddresses, userOrganizes } from "@/lib/calendar-participants"; +import { getUserParticipantId, collectUserCalendarAddresses, userOrganizes, getParticipantList } from "@/lib/calendar-participants"; +import type { MoveEventHandler } from "@/hooks/use-time-grid-interactions"; import { generateBirthdayEvents, createBirthdayCalendar, BIRTHDAY_CALENDAR_ID } from "@/lib/birthday-calendar"; import { sharedCalendarColorKey, pickUnusedCalendarColor } from "@/lib/shared-calendar-colors"; import { debug } from "@/lib/debug"; @@ -1303,6 +1304,53 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps = } }, [client, pendingScopeAction, updateEvent, deleteEvent, createEvent, findMasterEvent, currentUserEmails, resolveContactName, refetchCurrentRange, submitRsvp, describeError, t]); + // A drag or resize. A series asks which occurrences it covers, as the + // editor does; one event moves at once and can be moved back. + const handleMoveEvent = useCallback(async (event, updates, sendScheduling, kind) => { + if (!client) { toast.error(t("notifications.event_move_error")); return; } + if (isRecurringEvent(event)) { + const moved = { ...event, ...updates }; + const allDay = !!event.showWithoutTime; + // The attendees stay as they are; the save works out the rest from + // where the occurrence went, as for a time changed in the editor. + const attendees = getParticipantList(event, { resolveName: resolveContactName }) + .filter((p) => !p.isOrganizer && p.email) + .map((p) => ({ name: p.name, email: p.email.trim() })); + setPendingScopeAction({ + type: "edit", + event, + updates, + sendScheduling, + editor: { + initialAttendees: attendees, + attendees, + when: { allDay, start: getEventStartDate(moved), end: allDay ? getEventDisplayEndDate(moved) : getEventEndDate(moved) }, + ruleChanged: false, + }, + }); + return; + } + try { + // false: a plugin stopped it and said why. + if (!(await updateEvent(client, event.id, updates, sendScheduling))) return; + } catch (error) { + toast.error(describeError(error, t(kind === "resize" ? "notifications.event_resize_error" : "notifications.event_move_error"))); + return; + } + const before: Partial = { start: event.start, duration: event.duration }; + if (updates.timeZone !== undefined) before.timeZone = event.timeZone; + toast.success(t(kind === "resize" ? "notifications.event_updated" : "notifications.event_moved"), { + action: { + label: t("notifications.undo"), + onClick: () => { + void updateEvent(client, event.id, before, sendScheduling).catch((error: unknown) => { + toast.error(describeError(error, t("notifications.event_move_error"))); + }); + }, + }, + }); + }, [client, updateEvent, resolveContactName, describeError, t]); + const handleRsvp = useCallback(async (eventId: string, participantId: string, status: CalendarParticipant['participationStatus']) => { if (!client) return; // An answer on one occurrence of a series asks whether it covers just @@ -1665,6 +1713,7 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps = onSelectTask={openEditTaskModal} currentUserEmails={currentUserEmails} canEditEvent={canEditEvent} + onMoveEvent={handleMoveEvent} /> ); case "week": @@ -1691,6 +1740,7 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps = onSelectTask={openEditTaskModal} currentUserEmails={currentUserEmails} canEditEvent={canEditEvent} + onMoveEvent={handleMoveEvent} /> ); case "day": @@ -1714,6 +1764,7 @@ export function CalendarApp({ linkSegments: routeSegments }: CalendarAppProps = onSelectTask={openEditTaskModal} currentUserEmails={currentUserEmails} canEditEvent={canEditEvent} + onMoveEvent={handleMoveEvent} /> ); case "agenda": diff --git a/components/calendar/calendar-day-view.tsx b/components/calendar/calendar-day-view.tsx index 7d238439e..63b1451ef 100644 --- a/components/calendar/calendar-day-view.tsx +++ b/components/calendar/calendar-day-view.tsx @@ -15,7 +15,7 @@ import { displayNow, isDisplayToday } from "@/lib/timezone"; import { groupTasksByDueDay } from "@/lib/calendar-tasks"; import type { CalendarEvent, Calendar, CalendarTask } from "@/lib/jmap/types"; import { useDisplayHours } from "@/hooks/use-display-hours"; -import { useTimeGridInteractions } from "@/hooks/use-time-grid-interactions"; +import { useTimeGridInteractions, type MoveEventHandler } from "@/hooks/use-time-grid-interactions"; import { useScrollWindow, getScrollStart, setScrollStart, scrollToStart } from "@/hooks/use-scroll-window"; import { dayKey, type ScrollWindowViewProps } from "@/lib/calendar-scroll-window"; import type { PendingEventPreview } from "./event-modal"; @@ -46,6 +46,11 @@ interface CalendarDayViewProps extends ScrollWindowViewProps { currentUserEmails?: string[]; /** Whether the user may change an event: only those can be dragged or resized. */ canEditEvent?: (event: CalendarEvent) => boolean; + /** + * Save a drag or resize. The page asks which occurrences a change to a + * series covers; without it the change goes straight to the store. + */ + onMoveEvent?: MoveEventHandler; } const HOUR_HEIGHT = 64; @@ -78,6 +83,7 @@ export function CalendarDayView({ onSelectTask, currentUserEmails, canEditEvent = () => true, + onMoveEvent, }: CalendarDayViewProps) { const t = useTranslations("calendar"); const describeError = useCalendarErrorMessage(); @@ -280,6 +286,7 @@ export function CalendarDayView({ describeError, currentUserEmails, isMobile, + onMoveEvent, }); const formatHour = (h: number): string => { diff --git a/components/calendar/calendar-month-view.tsx b/components/calendar/calendar-month-view.tsx index 4c0698952..f343a5fe7 100644 --- a/components/calendar/calendar-month-view.tsx +++ b/components/calendar/calendar-month-view.tsx @@ -21,6 +21,7 @@ import { useScrollWindow } from "@/hooks/use-scroll-window"; import { dayKey, parseDayKey, type ScrollWindowViewProps } from "@/lib/calendar-scroll-window"; import { useCalendarErrorMessage } from "@/hooks/use-calendar-error-message"; import { PluginDayLabel } from "./plugin-day-label"; +import type { MoveEventHandler } from "@/hooks/use-time-grid-interactions"; interface CalendarMonthViewProps extends ScrollWindowViewProps { selectedDate: Date; @@ -48,6 +49,11 @@ interface CalendarMonthViewProps extends ScrollWindowViewProps { currentUserEmails?: string[]; /** Whether the user may change an event: only those can be dragged or resized. */ canEditEvent?: (event: CalendarEvent) => boolean; + /** + * Save a drag or resize. The page asks which occurrences a change to a + * series covers; without it the change goes straight to the store. + */ + onMoveEvent?: MoveEventHandler; } /** Fraction of the viewport height at which the "current month" is sampled. */ @@ -80,6 +86,7 @@ export function CalendarMonthView({ onSelectTask, currentUserEmails, canEditEvent = () => true, + onMoveEvent, }: CalendarMonthViewProps) { const t = useTranslations("calendar"); const describeError = useCalendarErrorMessage(); @@ -290,11 +297,12 @@ export function CalendarMonthView({ const client = useAuthStore.getState().client; if (!client) return; const moved = movedEventUpdates(event, { start: newStartISO }, currentUserEmails ?? []); - await useCalendarStore.getState().updateEvent(client, data.eventId, moved.updates, moved.sendSchedulingMessages); + if (event && onMoveEvent) await onMoveEvent(event, moved.updates, moved.sendSchedulingMessages, "move"); + else await useCalendarStore.getState().updateEvent(client, data.eventId, moved.updates, moved.sendSchedulingMessages); } catch (error) { toast.error(describeError(error, t("notifications.event_move_error"))); } - }, [t, describeError, currentUserEmails]); + }, [t, describeError, currentUserEmails, onMoveEvent]); return (
diff --git a/components/calendar/calendar-week-view.tsx b/components/calendar/calendar-week-view.tsx index bf391fd20..d4c9c1a9f 100644 --- a/components/calendar/calendar-week-view.tsx +++ b/components/calendar/calendar-week-view.tsx @@ -18,7 +18,7 @@ import { displayNow, isDisplayToday } from "@/lib/timezone"; import type { CalendarEvent, Calendar, CalendarTask } from "@/lib/jmap/types"; import { useSettingsStore } from "@/stores/settings-store"; import { useDisplayHours } from "@/hooks/use-display-hours"; -import { useTimeGridInteractions } from "@/hooks/use-time-grid-interactions"; +import { useTimeGridInteractions, type MoveEventHandler } from "@/hooks/use-time-grid-interactions"; import { useScrollWindow, getScrollStart, setScrollStart, scrollToStart } from "@/hooks/use-scroll-window"; import { dayKey, type ScrollWindowViewProps } from "@/lib/calendar-scroll-window"; import type { PendingEventPreview } from "./event-modal"; @@ -53,6 +53,11 @@ interface CalendarWeekViewProps extends ScrollWindowViewProps { currentUserEmails?: string[]; /** Whether the user may change an event: only those can be dragged or resized. */ canEditEvent?: (event: CalendarEvent) => boolean; + /** + * Save a drag or resize. The page asks which occurrences a change to a + * series covers; without it the change goes straight to the store. + */ + onMoveEvent?: MoveEventHandler; } const HOUR_HEIGHT = 60; @@ -89,6 +94,7 @@ export function CalendarWeekView({ onSelectTask, currentUserEmails, canEditEvent = () => true, + onMoveEvent, }: CalendarWeekViewProps) { const t = useTranslations("calendar"); const describeError = useCalendarErrorMessage(); @@ -373,6 +379,7 @@ export function CalendarWeekView({ describeError, currentUserEmails, isMobile, + onMoveEvent, }); const formatHour = (h: number): string => { diff --git a/hooks/use-time-grid-interactions.ts b/hooks/use-time-grid-interactions.ts index 1bb4bb0b4..3994d3087 100644 --- a/hooks/use-time-grid-interactions.ts +++ b/hooks/use-time-grid-interactions.ts @@ -7,7 +7,15 @@ import { debug } from "@/lib/debug"; import { formatIsoInTimeZone, getEventStartDate } from "@/lib/calendar-utils"; import { fromDisplayDate, getEffectiveTimeZone } from "@/lib/timezone"; import { movedEventUpdates } from "@/lib/calendar-participants"; -import type { Calendar } from "@/lib/jmap/types"; +import type { Calendar, CalendarEvent } from "@/lib/jmap/types"; + +/** Saves a dragged or resized event; `kind` says which, for the message. */ +export type MoveEventHandler = ( + event: CalendarEvent, + updates: Partial, + sendSchedulingMessages: boolean | undefined, + kind: "move" | "resize", +) => Promise | void; interface DragCreateState { dayKey: string; @@ -59,6 +67,8 @@ interface UseTimeGridInteractionsOptions { */ gridStartMinutes?: number; gridEndMinutes?: number; + /** Save a drop or resize (the page asks the scope of a series); else the store directly. */ + onMoveEvent?: MoveEventHandler; } /** `date` moved by `minutes` on the local clock, not by elapsed time. */ @@ -78,6 +88,7 @@ export function useTimeGridInteractions({ isMobile, gridStartMinutes = 0, gridEndMinutes = 1440, + onMoveEvent, }: UseTimeGridInteractionsOptions) { const snapToMinutes = useCallback((clientY: number, containerTop: number): number => { const raw = gridStartMinutes + ((clientY - containerTop) / hourHeight) * 60; @@ -275,14 +286,15 @@ export function useTimeGridInteractions({ const updates: { start?: string; duration: string } = { duration: dur }; if (start) updates.start = start; const moved = movedEventUpdates(event, updates, currentUserEmails ?? []); - await useCalendarStore.getState().updateEvent(client, resize.eventId, moved.updates, moved.sendSchedulingMessages); + if (event && onMoveEvent) await onMoveEvent(event, moved.updates, moved.sendSchedulingMessages, "resize"); + else await useCalendarStore.getState().updateEvent(client, resize.eventId, moved.updates, moved.sendSchedulingMessages); } catch (error) { debug.error("Failed to resize event:", resize.eventId, error); toast.error(describeError ? describeError(error, errorMessages.resize) : errorMessages.resize); } finally { setResizeVisual(null); } - }, [computeResize, errorMessages.resize, describeError, currentUserEmails]); + }, [computeResize, errorMessages.resize, describeError, currentUserEmails, onMoveEvent]); // --- Click / Double-click / Quick-create --- const clickTimerRef = useRef | null>(null); @@ -410,11 +422,12 @@ export function useTimeGridInteractions({ return; } const moved = movedEventUpdates(event, { start: newStartISO }, currentUserEmails ?? []); - await useCalendarStore.getState().updateEvent(client, data.eventId, moved.updates, moved.sendSchedulingMessages); + if (event && onMoveEvent) await onMoveEvent(event, moved.updates, moved.sendSchedulingMessages, "move"); + else await useCalendarStore.getState().updateEvent(client, data.eventId, moved.updates, moved.sendSchedulingMessages); } catch (error) { toast.error(describeError ? describeError(error, errorMessages.move) : errorMessages.move); } - }, [snapDragMinutes, errorMessages.move, describeError, currentUserEmails]); + }, [snapDragMinutes, errorMessages.move, describeError, currentUserEmails, onMoveEvent]); return { dragCreate, diff --git a/locales/ar/common.json b/locales/ar/common.json index 72ac934aa..7754ea944 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "تم إنشاء الحدث", "event_updated": "تم تحديث الحدث", + "event_moved": "تم نقل الحدث", + "undo": "تراجع", "event_deleted": "تم حذف الحدث", "event_delete_error": "تعذّر حذف الحدث", "task_deleted": "تم حذف المهمة", diff --git a/locales/ca/common.json b/locales/ca/common.json index f445f2c41..1fc73f655 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Esdeveniment creat", "event_updated": "Esdeveniment actualitzat", + "event_moved": "Esdeveniment mogut", + "undo": "Desfés", "event_deleted": "Esdeveniment suprimit", "event_delete_error": "No s'ha pogut suprimir l'esdeveniment", "task_deleted": "Tasca suprimida", diff --git a/locales/cs/common.json b/locales/cs/common.json index 4621a1202..a400812d8 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "Událost byla vytvořena", "event_updated": "Událost byla aktualizována", + "event_moved": "Událost byla přesunuta", + "undo": "Zpět", "event_deleted": "Událost byla odstraněna", "event_delete_error": "Událost se nepodařilo odstranit", "task_deleted": "Úkol byl odstraněn", diff --git a/locales/da/common.json b/locales/da/common.json index 3c29dbf32..bd2351aa8 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Begivenhed oprettet", "event_updated": "Begivenhed opdateret", + "event_moved": "Begivenhed flyttet", + "undo": "Fortryd", "event_deleted": "Begivenhed slettet", "event_delete_error": "Begivenheden kunne ikke slettes", "task_deleted": "Opgave slettet", diff --git a/locales/de/common.json b/locales/de/common.json index 3d3d9db3e..14ffd0d1e 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "Termin erstellt", "event_updated": "Termin aktualisiert", + "event_moved": "Termin verschoben", + "undo": "Rückgängig", "event_deleted": "Termin gelöscht", "event_delete_error": "Termin konnte nicht gelöscht werden", "task_deleted": "Aufgabe gelöscht", diff --git a/locales/en/common.json b/locales/en/common.json index 34a2ce488..30c1a0c7d 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Event created", "event_updated": "Event updated", + "event_moved": "Event moved", + "undo": "Undo", "event_deleted": "Event deleted", "event_delete_error": "Couldn't delete event", "task_deleted": "Task deleted", diff --git a/locales/es/common.json b/locales/es/common.json index 92a1eacd0..e05a0d6c5 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "Evento creado", "event_updated": "Evento actualizado", + "event_moved": "Evento movido", + "undo": "Deshacer", "event_deleted": "Evento eliminado", "event_delete_error": "No se pudo eliminar el evento", "task_deleted": "Tarea eliminada", diff --git a/locales/et/common.json b/locales/et/common.json index 21bde0de4..b195b5889 100644 --- a/locales/et/common.json +++ b/locales/et/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Sündmus loodud", "event_updated": "Sündmus uuendatud", + "event_moved": "Sündmus liigutatud", + "undo": "Võta tagasi", "event_deleted": "Sündmus kustutatud", "event_delete_error": "Sündmust ei saanud kustutada", "task_deleted": "Ülesanne kustutatud", diff --git a/locales/fa/common.json b/locales/fa/common.json index a391bb71f..6b1e57d23 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "رویداد ایجاد شد", "event_updated": "رویداد به‌روزرسانی شد", + "event_moved": "رویداد منتقل شد", + "undo": "بازگردانی", "event_deleted": "رویداد حذف شد", "event_delete_error": "حذف رویداد ممکن نشد", "task_deleted": "وظیفه حذف شد", diff --git a/locales/fr/common.json b/locales/fr/common.json index 143dde491..87ee721fd 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Événement créé", "event_updated": "Événement mis à jour", + "event_moved": "Événement déplacé", + "undo": "Annuler", "event_deleted": "Événement supprimé", "event_delete_error": "Impossible de supprimer l'événement", "task_deleted": "Tâche supprimée", diff --git a/locales/he/common.json b/locales/he/common.json index c21c59f07..94d3a855f 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -3436,6 +3436,8 @@ "notifications": { "event_created": "האירוע נוצר", "event_updated": "האירוע עודכן", + "event_moved": "האירוע הועבר", + "undo": "בטל", "event_deleted": "האירוע נמחק", "event_delete_error": "לא ניתן היה למחוק את האירוע", "task_deleted": "המשימה נמחקה", diff --git a/locales/hu/common.json b/locales/hu/common.json index 5a34f300e..55d510052 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Esemény létrehozva", "event_updated": "Esemény frissítve", + "event_moved": "Esemény áthelyezve", + "undo": "Visszavonás", "event_deleted": "Esemény törölve", "event_delete_error": "Nem sikerült törölni az eseményt", "task_deleted": "Feladat törölve", diff --git a/locales/it/common.json b/locales/it/common.json index fd36a834b..bfd0bb452 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "Evento creato", "event_updated": "Evento aggiornato", + "event_moved": "Evento spostato", + "undo": "Annulla", "event_deleted": "Evento eliminato", "event_delete_error": "Impossibile eliminare l'evento", "task_deleted": "Attività eliminata", diff --git a/locales/ja/common.json b/locales/ja/common.json index 3744afb1b..893209285 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "予定を作成しました", "event_updated": "予定を更新しました", + "event_moved": "予定を移動しました", + "undo": "元に戻す", "event_deleted": "予定を削除しました", "event_delete_error": "予定を削除できませんでした", "task_deleted": "タスクを削除しました", diff --git a/locales/ko/common.json b/locales/ko/common.json index 347bf4d5c..3c34df655 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "일정이 만들어졌어요", "event_updated": "일정이 업데이트되었어요", + "event_moved": "일정을 이동했어요", + "undo": "실행 취소", "event_deleted": "일정이 삭제되었어요", "event_delete_error": "일정을 삭제하지 못했습니다", "task_deleted": "할 일이 삭제되었습니다", diff --git a/locales/lv/common.json b/locales/lv/common.json index fc33a5161..84ea0c31e 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -3517,6 +3517,8 @@ "notifications": { "event_created": "Pasākums izveidots", "event_updated": "Pasākums atjaunināts", + "event_moved": "Pasākums pārvietots", + "undo": "Atsaukt", "event_deleted": "Pasākums izdzēsts", "event_delete_error": "Neizdevās dzēst notikumu", "task_deleted": "Uzdevums izdzēsts", diff --git a/locales/mn/common.json b/locales/mn/common.json index e3f2d8d73..ce1c5b55f 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Үйл явдал үүсгэсэн", "event_updated": "Үйл явдал шинэчлэгдсэн", + "event_moved": "Үйл явдлыг зөөсөн", + "undo": "Цуцлах", "event_deleted": "Үйл явдал устгагдсан", "event_delete_error": "Арга хэмжээг устгаж чадсангүй", "task_deleted": "Даалгавар устгагдсан", diff --git a/locales/nb/common.json b/locales/nb/common.json index d1a901695..fcef8f91e 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Hendelsen er opprettet", "event_updated": "Hendelsen er oppdatert", + "event_moved": "Hendelsen er flyttet", + "undo": "Angre", "event_deleted": "Hendelsen er slettet", "event_delete_error": "Kunne ikke slette hendelsen", "task_deleted": "Oppgaven er slettet", diff --git a/locales/nl/common.json b/locales/nl/common.json index 48625b98f..90d0304b2 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "Evenement aangemaakt", "event_updated": "Evenement bijgewerkt", + "event_moved": "Evenement verplaatst", + "undo": "Ongedaan maken", "event_deleted": "Evenement verwijderd", "event_delete_error": "Kan afspraak niet verwijderen", "task_deleted": "Taak verwijderd", diff --git a/locales/pl/common.json b/locales/pl/common.json index ce1fd841e..4f62c4aa8 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "Wydarzenie utworzono", "event_updated": "Wydarzenie zaktualizowano", + "event_moved": "Wydarzenie przeniesiono", + "undo": "Cofnij", "event_deleted": "Wydarzenie usunięto", "event_delete_error": "Nie udało się usunąć wydarzenia", "task_deleted": "Zadanie usunięto", diff --git a/locales/pt/common.json b/locales/pt/common.json index 5610e9c6a..37209001f 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Evento criado", "event_updated": "Evento atualizado", + "event_moved": "Evento movido", + "undo": "Desfazer", "event_deleted": "Evento excluído", "event_delete_error": "Não foi possível excluir o evento", "task_deleted": "Tarefa excluída", diff --git a/locales/ro/common.json b/locales/ro/common.json index 0b1eee02f..4a3a4ae83 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Eveniment creat", "event_updated": "Eveniment actualizat", + "event_moved": "Eveniment mutat", + "undo": "Anulează", "event_deleted": "Eveniment șters", "event_delete_error": "Evenimentul nu a putut fi șters", "task_deleted": "Sarcină ștearsă", diff --git a/locales/ru/common.json b/locales/ru/common.json index ae26c0a46..f81f2700b 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "Событие создано", "event_updated": "Событие обновлено", + "event_moved": "Событие перемещено", + "undo": "Отменить", "event_deleted": "Событие удалено", "event_delete_error": "Не удалось удалить событие", "task_deleted": "Задача удалена", diff --git a/locales/sk/common.json b/locales/sk/common.json index 941ef155a..f87ab38a7 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Udalosť bola vytvorená", "event_updated": "Udalosť bola aktualizovaná", + "event_moved": "Udalosť bola presunutá", + "undo": "Späť", "event_deleted": "Udalosť bola odstránená", "event_delete_error": "Udalosť sa nepodarilo odstrániť", "task_deleted": "Úloha bola odstránená", diff --git a/locales/th/common.json b/locales/th/common.json index d87454d99..7a4fb16f0 100644 --- a/locales/th/common.json +++ b/locales/th/common.json @@ -2858,6 +2858,8 @@ "notifications": { "event_created": "สร้างกิจกรรมแล้ว", "event_updated": "อัปเดตกิจกรรมแล้ว", + "event_moved": "ย้ายกิจกรรมแล้ว", + "undo": "เลิกทำ", "event_deleted": "ลบกิจกรรมแล้ว", "event_delete_error": "ไม่สามารถลบกิจกรรมได้", "task_deleted": "ลบงานแล้ว", diff --git a/locales/tr/common.json b/locales/tr/common.json index b9f86f969..888649d1f 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "Etkinlik oluşturuldu", "event_updated": "Etkinlik güncellendi", + "event_moved": "Etkinlik taşındı", + "undo": "Geri al", "event_deleted": "Etkinlik silindi", "event_delete_error": "Etkinlik silinemedi", "task_deleted": "Görev silindi", diff --git a/locales/uk/common.json b/locales/uk/common.json index 47512abe8..07e316390 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "Подія створена", "event_updated": "Подію оновлено", + "event_moved": "Подію переміщено", + "undo": "Скасувати", "event_deleted": "Подію видалено", "event_delete_error": "Не вдалося видалити подію", "task_deleted": "Завдання видалено", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index b8e71463c..c6a8ef322 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -3544,6 +3544,8 @@ "notifications": { "event_created": "活動已建立", "event_updated": "活動已更新", + "event_moved": "活動已移動", + "undo": "復原", "event_deleted": "活動已刪除", "event_delete_error": "無法刪除活動", "task_deleted": "任務已刪除", diff --git a/locales/zh/common.json b/locales/zh/common.json index 29f0c0e26..85b606f2f 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -3518,6 +3518,8 @@ "notifications": { "event_created": "活动已创建", "event_updated": "活动已更新", + "event_moved": "活动已移动", + "undo": "撤销", "event_deleted": "活动已删除", "event_delete_error": "无法删除事件", "task_deleted": "任务已删除",