diff --git a/messages/et.json b/messages/et.json index 0ad31ec..82dd910 100644 --- a/messages/et.json +++ b/messages/et.json @@ -1,4 +1,4 @@ -{ +{ "$schema": "https://inlang.com/schema/inlang-message-format", "app_name": "Root", "nav_files": "Failid", @@ -7,28 +7,28 @@ "nav_kanban": "Kanban", "user_menu_account_settings": "Konto seaded", "user_menu_switch_org": "Vaheta organisatsiooni", - "user_menu_logout": "Logi vƤlja", + "user_menu_logout": "Logi välja", "btn_new": "Uus", "btn_create": "Loo", - "btn_cancel": "Tühista", + "btn_cancel": "Tühista", "btn_save": "Salvesta", "btn_delete": "Kustuta", "btn_edit": "Muuda", "btn_close": "Sulge", - "btn_upload": "Laadi üles", + "btn_upload": "Laadi üles", "btn_remove": "Eemalda", - "login_title": "Tere tulemast Rooti", - "login_subtitle": "Sinu meeskonna tƶƶruum dokumentide, tahvlite ja kalendrite jaoks.", + "login_title": "Tere tulemast rooti", + "login_subtitle": "Sinu meeskonna tööruum dokumentide, tahvlite ja kalendrite jaoks.", "login_tab_login": "Logi sisse", "login_tab_signup": "Registreeru", "login_email_label": "E-post", - "login_email_placeholder": "sina@nƤide.ee", + "login_email_placeholder": "sina@näide.ee", "login_password_label": "Parool", - "login_password_placeholder": "••••••••", + "login_password_placeholder": "••••••••", "login_btn_login": "Logi sisse", "login_btn_signup": "Registreeru", - "login_or_continue": "vƵi jƤtka", - "login_google": "JƤtka Google'iga", + "login_or_continue": "või jätka", + "login_google": "Jätka Google'iga", "login_signup_prompt": "Pole kontot?", "login_login_prompt": "On juba konto?", "login_signup_success_title": "Kontrolli oma e-posti", @@ -38,9 +38,9 @@ "org_selector_create_title": "Loo organisatsioon", "org_selector_name_label": "Organisatsiooni nimi", "org_selector_name_placeholder": "Minu meeskond", - "org_selector_slug_label": "URL-i lühend", + "org_selector_slug_label": "URL-i lühend", "org_selector_slug_placeholder": "minu-meeskond", - "org_overview": "Organisatsiooni ülevaade", + "org_overview": "Organisatsiooni ülevaade", "files_title": "Failid", "files_breadcrumb_home": "Avaleht", "files_create_title": "Loo uus", @@ -51,8 +51,8 @@ "files_doc_placeholder": "Dokumendi nimi", "files_folder_placeholder": "Kausta nimi", "files_kanban_placeholder": "Kanban tahvli nimi", - "files_rename_title": "Nimeta ümber", - "files_context_rename": "Nimeta ümber", + "files_rename_title": "Nimeta ümber", + "files_context_rename": "Nimeta ümber", "files_context_move": "Teisalda...", "files_context_delete": "Kustuta", "files_context_open_tab": "Ava uuel vahelehel", @@ -63,36 +63,36 @@ "kanban_board_name_label": "Tahvli nimi", "kanban_board_name_placeholder": "nt Sprint 1", "kanban_edit_board": "Muuda tahvlit", - "kanban_rename_board": "Nimeta tahvel ümber", + "kanban_rename_board": "Nimeta tahvel ümber", "kanban_delete_board": "Kustuta tahvel", "kanban_add_column": "Lisa veerg", "kanban_column_name_label": "Veeru nimi", - "kanban_column_name_placeholder": "nt Teha, Tƶƶs, Valmis", + "kanban_column_name_placeholder": "nt Teha, Töös, Valmis", "kanban_add_card": "Lisa kaart", - "kanban_card_details": "Kaardi üksikasjad", + "kanban_card_details": "Kaardi üksikasjad", "kanban_card_title_label": "Pealkiri", "kanban_card_title_placeholder": "Kaardi pealkiri", "kanban_card_desc_label": "Kirjeldus", - "kanban_card_desc_placeholder": "Lisa üksikasjalikum kirjeldus...", + "kanban_card_desc_placeholder": "Lisa üksikasjalikum kirjeldus...", "kanban_tags": "Sildid", "kanban_tag_placeholder": "Sildi nimi", "kanban_tag_add": "Lisa", - "kanban_empty": "Kanban tahvleid hallatakse nüüd Failides", + "kanban_empty": "Kanban tahvleid hallatakse nüüd Failides", "kanban_go_to_files": "Mine Failidesse", - "kanban_select_board": "Vali tahvel ülalt", + "kanban_select_board": "Vali tahvel ülalt", "calendar_title": "Kalender", "calendar_subscribe": "Telli kalender", - "calendar_refresh": "VƤrskenda sündmusi", + "calendar_refresh": "Värskenda sündmusi", "calendar_settings": "Kalendri seaded", - "calendar_create_event": "Loo sündmus", - "calendar_edit_event": "Muuda sündmust", + "calendar_create_event": "Loo sündmus", + "calendar_edit_event": "Muuda sündmust", "calendar_event_title": "Pealkiri", - "calendar_event_title_placeholder": "Sündmuse pealkiri", - "calendar_event_date": "KuupƤev", + "calendar_event_title_placeholder": "Sündmuse pealkiri", + "calendar_event_date": "Kuupäev", "calendar_event_time": "Kellaaeg", "calendar_event_desc": "Kirjeldus", "settings_title": "Seaded", - "settings_tab_general": "Üldine", + "settings_tab_general": "Üldine", "settings_tab_members": "Liikmed", "settings_tab_roles": "Rollid", "settings_tab_tags": "Sildid", @@ -101,12 +101,12 @@ "settings_general_avatar": "Avatar", "settings_general_name": "Nimi", "settings_general_name_placeholder": "Organisatsiooni nimi", - "settings_general_slug": "URL-i lühend (sait.ee/...)", + "settings_general_slug": "URL-i lühend (sait.ee/...)", "settings_general_slug_placeholder": "minu-org", "settings_general_save": "Salvesta muudatused", "settings_general_danger_zone": "Ohutsoon", "settings_general_delete_org": "Kustuta organisatsioon", - "settings_general_delete_org_desc": "Kustuta see organisatsioon ja kƵik selle andmed jƤƤdavalt.", + "settings_general_delete_org_desc": "Kustuta see organisatsioon ja kõik selle andmed jäädavalt.", "settings_general_leave_org": "Lahku organisatsioonist", "settings_general_leave_org_desc": "Lahku sellest organisatsioonist. Tagasi liitumiseks on vaja uut kutset.", "settings_general_leave_btn": "Lahku {orgName}", @@ -134,7 +134,7 @@ "settings_social_twitch_placeholder": "https://twitch.tv/sinuorg", "settings_social_save": "Salvesta lingid", "settings_tags_title": "Organisatsiooni sildid", - "settings_tags_desc": "Halda silte, mida saab kasutada kƵigil Kanban tahvlitel.", + "settings_tags_desc": "Halda silte, mida saab kasutada kõigil Kanban tahvlitel.", "settings_tags_create": "Loo silt", "settings_tags_empty": "Silte pole veel. Loo oma esimene silt Kanban kaartide korraldamiseks.", "settings_tags_name_placeholder": "Sildi nimi", @@ -149,7 +149,7 @@ "settings_members_remove": "Eemalda organisatsioonist", "settings_invite_title": "Kutsu liige", "settings_invite_email": "E-posti aadress", - "settings_invite_email_placeholder": "kolleeg@nƤide.ee", + "settings_invite_email_placeholder": "kolleeg@näide.ee", "settings_invite_role": "Roll", "settings_invite_send": "Saada kutse", "settings_invite_role_viewer": "Vaataja - Saab sisu vaadata", @@ -158,42 +158,42 @@ "settings_invite_role_admin": "Admin - Saab hallata liikmeid ja seadeid", "settings_edit_member": "Muuda liiget", "settings_roles_title": "Rollid", - "settings_roles_desc": "Loo kohandatud rolle kindlate Ƶigustega.", + "settings_roles_desc": "Loo kohandatud rolle kindlate õigustega.", "settings_roles_create": "Loo roll", "settings_roles_edit": "Muuda rolli", - "settings_roles_system": "Süsteemne", + "settings_roles_system": "Süsteemne", "settings_roles_default": "Vaikimisi", - "settings_roles_all_perms": "KƵik Ƶigused", + "settings_roles_all_perms": "Kõik õigused", "settings_roles_more": "+{count} veel", "settings_roles_name_label": "Nimi", "settings_roles_name_placeholder": "nt Moderaator", - "settings_roles_color": "VƤrv", - "settings_roles_permissions": "Ć•igused", + "settings_roles_color": "Värv", + "settings_roles_permissions": "Õigused", "settings_integrations_google_cal": "Google'i kalender", - "settings_integrations_google_cal_desc": "Jaga Google'i kalendrit kƵigi organisatsiooni liikmetega.", - "settings_integrations_connected": "Ühendatud", - "settings_integrations_disconnect": "Katkesta ühendus", - "settings_integrations_connect": "Ühenda Google'i kalender", + "settings_integrations_google_cal_desc": "Jaga Google'i kalendrit kõigi organisatsiooni liikmetega.", + "settings_integrations_connected": "Ühendatud", + "settings_integrations_disconnect": "Katkesta ühendus", + "settings_integrations_connect": "Ühenda Google'i kalender", "settings_integrations_discord": "Discord", "settings_integrations_discord_desc": "Saa teavitusi oma Discordi serveris.", "settings_integrations_slack": "Slack", - "settings_integrations_slack_desc": "Saa teavitusi oma Slacki tƶƶruumis.", + "settings_integrations_slack_desc": "Saa teavitusi oma Slacki tööruumis.", "settings_integrations_coming_soon": "Tulekul", - "settings_connect_cal_title": "Ühenda avalik Google'i kalender", - "settings_connect_cal_desc": "Kleebi oma Google'i kalendri jagamislink vƵi kalendri ID. Kalender peab olema Google'i kalendri seadetes avalikuks seatud.", + "settings_connect_cal_title": "Ühenda avalik Google'i kalender", + "settings_connect_cal_desc": "Kleebi oma Google'i kalendri jagamislink või kalendri ID. Kalender peab olema Google'i kalendri seadetes avalikuks seatud.", "settings_connect_cal_how": "Kuidas saada kalendri linki:", "settings_connect_cal_step1": "Ava Google'i kalender", - "settings_connect_cal_step2": "Kliki kalendri kƵrval 3 punkti → Seaded", - "settings_connect_cal_step3": "\"JuurdepƤƤsuƵiguste\" all mƤrgi \"Tee avalikuks\"", - "settings_connect_cal_step4": "Keri alla \"Kalendri integreerimine\" ja kopeeri kalendri ID vƵi avalik URL", - "settings_connect_cal_input_label": "Kalendri URL vƵi ID", - "settings_connect_cal_input_placeholder": "Kleebi kalendri URL vƵi ID (nt abc123@group.calendar.google.com)", - "settings_connect_cal_btn": "Ühenda", + "settings_connect_cal_step2": "Kliki kalendri kõrval 3 punkti → Seaded", + "settings_connect_cal_step3": "\"Juurdepääsuõiguste\" all märgi \"Tee avalikuks\"", + "settings_connect_cal_step4": "Keri alla \"Kalendri integreerimine\" ja kopeeri kalendri ID või avalik URL", + "settings_connect_cal_input_label": "Kalendri URL või ID", + "settings_connect_cal_input_placeholder": "Kleebi kalendri URL või ID (nt abc123@group.calendar.google.com)", + "settings_connect_cal_btn": "Ühenda", "account_title": "Konto seaded", "account_subtitle": "Halda oma isiklikku profiili ja eelistusi.", "account_profile": "Profiil", "account_photo": "Foto", - "account_sync_google": "Sünkrooni Google", + "account_sync_google": "Sünkrooni Google", "account_remove_photo": "Eemalda foto", "account_display_name": "Kuvatav nimi", "account_display_name_placeholder": "Sinu nimi", @@ -203,16 +203,16 @@ "account_discord": "Discord", "account_discord_placeholder": "kasutajanimi", "account_contact_info": "Kontakt ja suurused", - "account_shirt_size": "SƤrgi suurus", + "account_shirt_size": "Särgi suurus", "account_hoodie_size": "Pusa suurus", "account_size_placeholder": "Vali suurus", "account_save_profile": "Salvesta profiil", - "account_appearance": "VƤlimus", + "account_appearance": "Välimus", "account_theme": "Teema", "account_theme_dark": "Tume", "account_theme_light": "Hele (tulekul)", - "account_theme_system": "Süsteemne (tulekul)", - "account_accent_color": "AktsentvƤrv", + "account_theme_system": "Süsteemne (tulekul)", + "account_accent_color": "Aktsentvärv", "account_use_org_theme": "Kasuta organisatsiooni teemat", "account_use_org_theme_desc": "Asenda oma isiklik teema organisatsiooni seadetega.", "account_language": "Keel", @@ -220,11 +220,11 @@ "account_save_preferences": "Salvesta eelistused", "account_security": "Turvalisus ja seansid", "account_password": "Parool", - "account_password_desc": "Kui logisid sisse Google'iga, haldab sinu parooli Google. Muul juhul saad parooli e-posti teel lƤhtestada.", - "account_send_reset": "Saada lƤhtestamise e-kiri", + "account_password_desc": "Kui logisid sisse Google'iga, haldab sinu parooli Google. Muul juhul saad parooli e-posti teel lähtestada.", + "account_send_reset": "Saada lähtestamise e-kiri", "account_active_sessions": "Aktiivsed seansid", - "account_sessions_desc": "Logi vƤlja kƵigist teistest seanssidest, kui kahtlustad volitamata juurdepƤƤsu.", - "account_signout_others": "Logi teised seansid vƤlja", + "account_sessions_desc": "Logi välja kõigist teistest seanssidest, kui kahtlustad volitamata juurdepääsu.", + "account_signout_others": "Logi teised seansid välja", "editor_save": "Salvesta", "editor_saving": "Salvestamine...", "editor_saved": "Salvestatud", @@ -232,33 +232,33 @@ "editor_placeholder": "Alusta kirjutamist...", "editor_bold": "Paks (Ctrl+B)", "editor_italic": "Kursiiv (Ctrl+I)", - "editor_strikethrough": "LƤbikriipsutus", - "editor_bullet_list": "TƤpploend", + "editor_strikethrough": "Läbikriipsutus", + "editor_bullet_list": "Täpploend", "editor_numbered_list": "Nummerdatud loend", "editor_quote": "Tsitaat", "editor_code_block": "Koodiplokk", - "toast_error_delete_org": "Organisatsiooni kustutamine ebaƵnnestus.", - "toast_error_leave_org": "Organisatsioonist lahkumine ebaƵnnestus.", - "toast_error_invite": "Kutse saatmine ebaƵnnestus: {error}", - "toast_error_update_role": "Rolli uuendamine ebaƵnnestus.", - "toast_error_remove_member": "Liikme eemaldamine ebaƵnnestus.", - "toast_error_delete_role": "Rolli kustutamine ebaƵnnestus.", - "toast_error_disconnect_cal": "Kalendri lahtiühendamine ebaƵnnestus.", - "toast_error_reset_email": "LƤhtestamise e-kirja saatmine ebaƵnnestus.", - "toast_success_reset_email": "Parooli lƤhtestamise e-kiri saadetud.", - "toast_success_signout_others": "Teised seansid vƤlja logitud.", - "confirm_delete_role": "Kustuta roll \"{name}\"? Selle rolliga liikmed tuleb ümber mƤƤrata.", + "toast_error_delete_org": "Organisatsiooni kustutamine ebaõnnestus.", + "toast_error_leave_org": "Organisatsioonist lahkumine ebaõnnestus.", + "toast_error_invite": "Kutse saatmine ebaõnnestus: {error}", + "toast_error_update_role": "Rolli uuendamine ebaõnnestus.", + "toast_error_remove_member": "Liikme eemaldamine ebaõnnestus.", + "toast_error_delete_role": "Rolli kustutamine ebaõnnestus.", + "toast_error_disconnect_cal": "Kalendri lahtiühendamine ebaõnnestus.", + "toast_error_reset_email": "Lähtestamise e-kirja saatmine ebaõnnestus.", + "toast_success_reset_email": "Parooli lähtestamise e-kiri saadetud.", + "toast_success_signout_others": "Teised seansid välja logitud.", + "confirm_delete_role": "Kustuta roll \"{name}\"? Selle rolliga liikmed tuleb ümber määrata.", "confirm_leave_org": "Kas oled kindel, et soovid lahkuda {orgName}?", "confirm_delete_org": "Sisesta \"{orgName}\" kustutamise kinnitamiseks:", "confirm_remove_member": "Eemalda {name} organisatsioonist?", - "confirm_disconnect_cal": "Katkesta Google'i kalendri ühendus?", + "confirm_disconnect_cal": "Katkesta Google'i kalendri ühendus?", "role_viewer": "Vaataja", "role_commenter": "Kommenteerija", "role_editor": "Toimetaja", "role_admin": "Admin", "role_owner": "Omanik", - "error_owner_cant_leave": "Omanikud ei saa lahkuda. KƵigepealt anna omandiƵigus üle vƵi kustuta organisatsioon.", - "overview_title": "Organisatsiooni ülevaade", + "error_owner_cant_leave": "Omanikud ei saa lahkuda. Kõigepealt anna omandiõigus üle või kustuta organisatsioon.", + "overview_title": "Organisatsiooni ülevaade", "overview_stat_members": "Liikmed", "overview_stat_documents": "Dokumendid", "overview_stat_folders": "Kaustad", @@ -266,11 +266,11 @@ "overview_quick_links": "Kiirlingid", "activity_title": "Viimane tegevus", "activity_empty": "Viimast tegevust pole veel.", - "activity_created": "{user} lƵi {entityType} \"{name}\"", + "activity_created": "{user} lõi {entityType} \"{name}\"", "activity_updated": "{user} uuendas {entityType} \"{name}\"", "activity_deleted": "{user} kustutas {entityType} \"{name}\"", "activity_moved": "{user} teisaldas {entityType} \"{name}\"", - "activity_renamed": "{user} nimetas ümber {entityType} \"{name}\"", + "activity_renamed": "{user} nimetas ümber {entityType} \"{name}\"", "activity_just_now": "Just praegu", "activity_minutes_ago": "{count} min tagasi", "activity_hours_ago": "{count}t tagasi", @@ -283,88 +283,88 @@ "entity_member": "liikme", "entity_role": "rolli", "entity_invite": "kutse", - "entity_event": "ürituse", - "nav_events": "Üritused", + "entity_event": "ürituse", + "nav_events": "Üritused", "nav_chat": "Vestlus", "chat_title": "Vestlus", - "chat_subtitle": "Meeskonna sƵnumid ja suhtlus", - "events_title": "Üritused", - "events_subtitle": "Korralda ja halda oma üritusi", - "events_new": "Uus üritus", - "events_create": "Loo üritus", - "events_empty_title": "Üritusi pole veel", - "events_empty_desc": "Loo oma esimene üritus alustamiseks", - "events_no_dates": "KuupƤevad mƤƤramata", - "events_tab_all": "KƵik üritused", + "chat_subtitle": "Meeskonna sõnumid ja suhtlus", + "events_title": "Üritused", + "events_subtitle": "Korralda ja halda oma üritusi", + "events_new": "Uus üritus", + "events_create": "Loo üritus", + "events_empty_title": "Üritusi pole veel", + "events_empty_desc": "Loo oma esimene üritus alustamiseks", + "events_no_dates": "Kuupäevad määramata", + "events_tab_all": "Kõik üritused", "events_tab_planning": "Planeerimisel", "events_tab_active": "Aktiivne", - "events_tab_completed": "LƵpetatud", + "events_tab_completed": "Lõpetatud", "events_tab_archived": "Arhiveeritud", "events_status_planning": "Planeerimisel", "events_status_active": "Aktiivne", - "events_status_completed": "LƵpetatud", + "events_status_completed": "Lõpetatud", "events_status_archived": "Arhiveeritud", - "events_form_name": "Ürituse nimi", + "events_form_name": "Ürituse nimi", "events_form_name_placeholder": "nt Suvekonverents 2026", "events_form_description": "Kirjeldus", - "events_form_description_placeholder": "Ürituse lühikirjeldus...", - "events_form_start_date": "AlguskuupƤev", - "events_form_end_date": "LƵppkuupƤev", + "events_form_description_placeholder": "Ürituse lühikirjeldus...", + "events_form_start_date": "Alguskuupäev", + "events_form_end_date": "Lõppkuupäev", "events_form_venue": "Toimumiskoht", "events_form_venue_placeholder": "nt Konverentsikeskus", "events_form_venue_address_placeholder": "Toimumiskoha aadress", - "events_form_color": "VƤrv", - "events_form_select_color": "Vali vƤrv {color}", + "events_form_color": "Värv", + "events_form_select_color": "Vali värv {color}", "events_creating": "Loomine...", "events_saving": "Salvestamine...", "events_deleting": "Kustutamine...", - "events_updated": "Üritus uuendatud", - "events_created": "Üritus \"{name}\" loodud", - "events_deleted": "Üritus kustutatud", - "events_delete_title": "Kustuta üritus?", - "events_delete_desc": "See kustutab jƤƤdavalt ürituse {name} ja kƵik selle andmed. Seda toimingut ei saa tagasi vƵtta.", - "events_delete_confirm": "Kustuta üritus", - "events_days_ago": "{count} pƤeva tagasi", - "events_today": "TƤna!", + "events_updated": "Üritus uuendatud", + "events_created": "Üritus \"{name}\" loodud", + "events_deleted": "Üritus kustutatud", + "events_delete_title": "Kustuta üritus?", + "events_delete_desc": "See kustutab jäädavalt ürituse {name} ja kõik selle andmed. Seda toimingut ei saa tagasi võtta.", + "events_delete_confirm": "Kustuta üritus", + "events_days_ago": "{count} päeva tagasi", + "events_today": "Täna!", "events_tomorrow": "Homme", - "events_in_days": "{count} pƤeva pƤrast", - "events_overview": "Ülevaade", + "events_in_days": "{count} päeva pärast", + "events_overview": "Ülevaade", "events_modules": "Moodulid", - "events_details": "Ürituse andmed", - "events_start_date": "AlguskuupƤev", - "events_end_date": "LƵppkuupƤev", + "events_details": "Ürituse andmed", + "events_start_date": "Alguskuupäev", + "events_end_date": "Lõppkuupäev", "events_venue": "Toimumiskoht", - "events_not_set": "MƤƤramata", - "events_all_events": "KƵik üritused", + "events_not_set": "Määramata", + "events_all_events": "Kõik üritused", "events_team": "Meeskond", "events_team_count": "Meeskond ({count})", "events_team_manage": "Halda", - "events_team_empty": "Meeskonnaliikmeid pole veel mƤƤratud", + "events_team_empty": "Meeskonnaliikmeid pole veel määratud", "events_more_members": "+{count} veel", - "events_mod_tasks": "Ülesanded", - "events_mod_tasks_desc": "Halda ülesandeid, verstaposte ja edenemist", + "events_mod_tasks": "Ülesanded", + "events_mod_tasks_desc": "Halda ülesandeid, verstaposte ja edenemist", "events_mod_files": "Failid", "events_mod_files_desc": "Dokumendid, lepingud ja meedia", "events_mod_schedule": "Ajakava", - "events_mod_schedule_desc": "Ürituse ajakava ja programm", + "events_mod_schedule_desc": "Ürituse ajakava ja programm", "events_mod_budget": "Eelarve", - "events_mod_budget_desc": "Tulud, kulud ja jƤlgimine", - "events_mod_guests": "Külalised", - "events_mod_guests_desc": "Külaliste nimekiri ja registreerimine", + "events_mod_budget_desc": "Tulud, kulud ja jälgimine", + "events_mod_guests": "Külalised", + "events_mod_guests_desc": "Külaliste nimekiri ja registreerimine", "events_mod_team": "Meeskond", "events_mod_team_desc": "Meeskonnaliikmed ja vahetuste planeerimine", "events_mod_sponsors": "Sponsorid", "events_mod_sponsors_desc": "Sponsorid, partnerid ja kohustused", "module_coming_soon": "Tulekul", - "module_coming_soon_desc": "See moodul on arendamisel ja saab peagi kƤttesaadavaks.", - "team_title": "Ürituse meeskond", - "team_subtitle": "Halda meeskonnaliikmeid ja nende rolle selle ürituse jaoks.", + "module_coming_soon_desc": "See moodul on arendamisel ja saab peagi kättesaadavaks.", + "team_title": "Ürituse meeskond", + "team_subtitle": "Halda meeskonnaliikmeid ja nende rolle selle ürituse jaoks.", "team_add_member": "Lisa liige", "team_role_lead": "Juht", "team_role_manager": "Haldur", "team_role_member": "Liige", - "team_empty": "Meeskonnaliikmeid pole veel mƤƤratud. Lisa liikmeid oma organisatsioonist.", - "team_remove_confirm": "Eemalda {name} selle ürituse meeskonnast?", + "team_empty": "Meeskonnaliikmeid pole veel määratud. Lisa liikmeid oma organisatsioonist.", + "team_remove_confirm": "Eemalda {name} selle ürituse meeskonnast?", "team_remove_btn": "Eemalda", "team_added": "{name} lisatud meeskonda", "team_removed": "{name} eemaldatud meeskonnast", @@ -374,8 +374,8 @@ "team_already_assigned": "Juba meeskonnas", "team_departments": "Valdkonnad", "team_roles": "Rollid", - "team_all": "KƵik", - "team_no_department": "MƤƤramata", + "team_all": "Kõik", + "team_no_department": "Määramata", "team_add_department": "Lisa valdkond", "team_add_role": "Lisa roll", "team_edit_department": "Muuda valdkonda", @@ -390,31 +390,31 @@ "team_role_deleted": "Roll kustutatud", "team_dept_delete_confirm": "Kustuta valdkond {name}? Liikmed eemaldatakse sellest.", "team_role_delete_confirm": "Kustuta roll {name}? Liikmed kaotavad selle rolli.", - "team_view_by_dept": "Valdkondade jƤrgi", + "team_view_by_dept": "Valdkondade järgi", "team_view_list": "Nimekirja vaade", "team_member_count": "{count} liiget", - "team_assign_dept": "MƤƤra valdkonnad", - "team_notes": "MƤrkmed", - "team_notes_placeholder": "Valikulised mƤrkmed selle liikme kohta...", - "overview_subtitle": "Tere tagasi. Siin on ülevaade toimuvast.", - "overview_stat_events": "Üritused", - "overview_upcoming_events": "Tulevased üritused", - "overview_upcoming_empty": "Tulevasi üritusi pole. Loo üks alustamiseks.", - "overview_view_all_events": "Vaata kƵiki üritusi", + "team_assign_dept": "Määra valdkonnad", + "team_notes": "Märkmed", + "team_notes_placeholder": "Valikulised märkmed selle liikme kohta...", + "overview_subtitle": "Tere tagasi. Siin on ülevaade toimuvast.", + "overview_stat_events": "Üritused", + "overview_upcoming_events": "Tulevased üritused", + "overview_upcoming_empty": "Tulevasi üritusi pole. Loo üks alustamiseks.", + "overview_view_all_events": "Vaata kõiki üritusi", "overview_more_members": "+{count} veel", "chat_join_title": "Liitu vestlusega", - "chat_join_description": "Vestlus pƵhineb Matrixil - avatud standardil turvalise ja detsentraliseeritud suhtluse jaoks.", - "chat_join_consent": "Liitudes luuakse sulle Matrixi konto sinu praeguste profiiliandmete (nimi, e-post ja avatar) pƵhjal.", - "chat_join_learn_more": "Loe Matrixi kohta lƤhemalt", + "chat_join_description": "Vestlus põhineb Matrixil - avatud standardil turvalise ja detsentraliseeritud suhtluse jaoks.", + "chat_join_consent": "Liitudes luuakse sulle Matrixi konto sinu praeguste profiiliandmete (nimi, e-post ja avatar) põhjal.", + "chat_join_learn_more": "Loe Matrixi kohta lähemalt", "chat_join_button": "Liitu vestlusega", "chat_joining": "Konto seadistamine...", "chat_join_success": "Vestluskonto loodud! Tere tulemast.", - "chat_join_error": "Vestluse seadistamine ebaƵnnestus. Proovi uuesti.", - "chat_disconnect": "Katkesta vestlusühendus", + "chat_join_error": "Vestluse seadistamine ebaõnnestus. Proovi uuesti.", + "chat_disconnect": "Katkesta vestlusühendus", "dept_dashboard_no_modules": "Mooduleid pole veel seadistatud", "dept_dashboard_add_first": "Lisa oma esimene moodul", "dept_dashboard_add_module": "Lisa moodul", - "dept_dashboard_all_added": "KƵik moodulid on juba lisatud", + "dept_dashboard_all_added": "Kõik moodulid on juba lisatud", "dept_dashboard_expand": "Laienda", "dept_dashboard_remove_module": "Eemalda moodul", "dept_dashboard_coming_soon": "Tulekul", @@ -422,14 +422,14 @@ "dept_dashboard_departments": "Valdkonnad", "dept_checklist_no_items": "Kontrollnimekirju pole veel", "dept_checklist_add": "Lisa kontrollnimekiri", - "dept_checklist_add_item": "Lisa üksus...", - "dept_notes_no_notes": "MƤrkmeid pole veel", - "dept_notes_new": "Uus mƤrge", - "dept_notes_select": "Vali mƤrge", + "dept_checklist_add_item": "Lisa üksus...", + "dept_notes_no_notes": "Märkmeid pole veel", + "dept_notes_new": "Uus märge", + "dept_notes_select": "Vali märge", "dept_notes_placeholder": "Alusta kirjutamist...", - "dept_notes_title_placeholder": "MƤrkme pealkiri...", - "dept_kanban_open": "Ava ülesannete tahvel", - "dept_kanban_desc": "Selle valdkonna ülesannete tahvel", + "dept_notes_title_placeholder": "Märkme pealkiri...", + "dept_kanban_open": "Ava ülesannete tahvel", + "dept_kanban_desc": "Selle valdkonna ülesannete tahvel", "dept_files_open": "Ava failid", "dept_files_desc": "Valdkonna failid ja dokumendid", "dept_quick_add": "Kiirvalik", @@ -444,70 +444,70 @@ "dept_module_kanban": "Kanban", "dept_module_files": "Failid", "dept_module_checklist": "Kontrollnimekiri", - "dept_module_notes": "MƤrkmed", + "dept_module_notes": "Märkmed", "dept_module_schedule": "Ajakava", "dept_module_contacts": "Kontaktid", "dept_module_budget": "Eelarve", "dept_module_sponsors": "Sponsorid", "dept_module_map": "Kaart", - "toast_error_update_layout": "Paigutuse uuendamine ebaƵnnestus", - "toast_error_add_module": "Mooduli lisamine ebaƵnnestus", - "toast_error_remove_module": "Mooduli eemaldamine ebaƵnnestus", - "toast_error_reorder_modules": "Moodulite ümberjƤrjestamine ebaƵnnestus", - "toast_error_add_item": "Üksuse lisamine ebaƵnnestus", - "toast_error_update_item": "Üksuse uuendamine ebaƵnnestus", - "toast_error_delete_item": "Üksuse kustutamine ebaƵnnestus", - "toast_error_create_checklist": "Kontrollnimekirja loomine ebaƵnnestus", - "toast_error_delete_checklist": "Kontrollnimekirja kustutamine ebaƵnnestus", - "toast_error_rename_checklist": "Kontrollnimekirja ümbernimetamine ebaƵnnestus", - "toast_error_create_note": "MƤrkme loomine ebaƵnnestus", - "toast_error_update_note": "MƤrkme uuendamine ebaƵnnestus", - "toast_error_delete_note": "MƤrkme kustutamine ebaƵnnestus", - "toast_error_create_stage": "Lava loomine ebaƵnnestus", - "toast_error_delete_stage": "Lava kustutamine ebaƵnnestus", - "toast_error_create_block": "Ajakavaploki loomine ebaƵnnestus", - "toast_error_update_block": "Ajakavaploki uuendamine ebaƵnnestus", - "toast_error_delete_block": "Ajakavaploki kustutamine ebaƵnnestus", - "toast_error_create_contact": "Kontakti loomine ebaƵnnestus", - "toast_error_update_contact": "Kontakti uuendamine ebaƵnnestus", - "toast_error_delete_contact": "Kontakti kustutamine ebaƵnnestus", - "toast_error_create_category": "Kategooria loomine ebaƵnnestus", - "toast_error_delete_category": "Kategooria kustutamine ebaƵnnestus", - "toast_error_create_budget_item": "Eelarveüksuse loomine ebaƵnnestus", - "toast_error_update_budget_item": "Eelarveüksuse uuendamine ebaƵnnestus", - "toast_error_delete_budget_item": "Eelarveüksuse kustutamine ebaƵnnestus", - "toast_error_upload_receipt": "Kviitungi üleslaadimine ebaƵnnestus", + "toast_error_update_layout": "Paigutuse uuendamine ebaõnnestus", + "toast_error_add_module": "Mooduli lisamine ebaõnnestus", + "toast_error_remove_module": "Mooduli eemaldamine ebaõnnestus", + "toast_error_reorder_modules": "Moodulite ümberjärjestamine ebaõnnestus", + "toast_error_add_item": "Üksuse lisamine ebaõnnestus", + "toast_error_update_item": "Üksuse uuendamine ebaõnnestus", + "toast_error_delete_item": "Üksuse kustutamine ebaõnnestus", + "toast_error_create_checklist": "Kontrollnimekirja loomine ebaõnnestus", + "toast_error_delete_checklist": "Kontrollnimekirja kustutamine ebaõnnestus", + "toast_error_rename_checklist": "Kontrollnimekirja ümbernimetamine ebaõnnestus", + "toast_error_create_note": "Märkme loomine ebaõnnestus", + "toast_error_update_note": "Märkme uuendamine ebaõnnestus", + "toast_error_delete_note": "Märkme kustutamine ebaõnnestus", + "toast_error_create_stage": "Lava loomine ebaõnnestus", + "toast_error_delete_stage": "Lava kustutamine ebaõnnestus", + "toast_error_create_block": "Ajakavaploki loomine ebaõnnestus", + "toast_error_update_block": "Ajakavaploki uuendamine ebaõnnestus", + "toast_error_delete_block": "Ajakavaploki kustutamine ebaõnnestus", + "toast_error_create_contact": "Kontakti loomine ebaõnnestus", + "toast_error_update_contact": "Kontakti uuendamine ebaõnnestus", + "toast_error_delete_contact": "Kontakti kustutamine ebaõnnestus", + "toast_error_create_category": "Kategooria loomine ebaõnnestus", + "toast_error_delete_category": "Kategooria kustutamine ebaõnnestus", + "toast_error_create_budget_item": "Eelarveüksuse loomine ebaõnnestus", + "toast_error_update_budget_item": "Eelarveüksuse uuendamine ebaõnnestus", + "toast_error_delete_budget_item": "Eelarveüksuse kustutamine ebaõnnestus", + "toast_error_upload_receipt": "Kviitungi üleslaadimine ebaõnnestus", "toast_success_receipt_attached": "Kviitung \"{name}\" lisatud", - "toast_error_create_tier": "Taseme loomine ebaƵnnestus", - "toast_error_delete_tier": "Taseme kustutamine ebaƵnnestus", - "toast_error_create_sponsor": "Sponsori loomine ebaƵnnestus", - "toast_error_update_sponsor": "Sponsori uuendamine ebaƵnnestus", - "toast_error_delete_sponsor": "Sponsori kustutamine ebaƵnnestus", - "toast_error_create_deliverable": "Kohustuse loomine ebaƵnnestus", - "toast_error_update_deliverable": "Kohustuse uuendamine ebaƵnnestus", - "toast_error_delete_deliverable": "Kohustuse kustutamine ebaƵnnestus", - "checklist_rename": "Nimeta ümber", + "toast_error_create_tier": "Taseme loomine ebaõnnestus", + "toast_error_delete_tier": "Taseme kustutamine ebaõnnestus", + "toast_error_create_sponsor": "Sponsori loomine ebaõnnestus", + "toast_error_update_sponsor": "Sponsori uuendamine ebaõnnestus", + "toast_error_delete_sponsor": "Sponsori kustutamine ebaõnnestus", + "toast_error_create_deliverable": "Kohustuse loomine ebaõnnestus", + "toast_error_update_deliverable": "Kohustuse uuendamine ebaõnnestus", + "toast_error_delete_deliverable": "Kohustuse kustutamine ebaõnnestus", + "checklist_rename": "Nimeta ümber", "checklist_delete": "Kustuta nimekiri", - "checklist_add_item_placeholder": "Lisa üksus...", + "checklist_add_item_placeholder": "Lisa üksus...", "checklist_name_placeholder": "Nimekirja nimi...", "checklist_no_items": "Kontrollnimekirju pole veel", "checklist_add": "Lisa kontrollnimekiri", - "notes_new": "Uus mƤrge", - "notes_title_placeholder": "MƤrkme pealkiri...", + "notes_new": "Uus märge", + "notes_title_placeholder": "Märkme pealkiri...", "notes_placeholder": "Alusta kirjutamist...", - "notes_no_notes": "MƤrkmeid pole veel", - "notes_select": "Vali mƤrge", + "notes_no_notes": "Märkmeid pole veel", + "notes_select": "Vali märge", "notes_export_document": "Ekspordi dokumendina", - "notes_delete": "Kustuta mƤrge", + "notes_delete": "Kustuta märge", "notes_exported": "Eksporditud \"{title}\" dokumendina", - "notes_export_error": "MƤrkme eksportimine ebaƵnnestus", + "notes_export_error": "Märkme eksportimine ebaõnnestus", "schedule_timeline": "Ajajoon", "schedule_list": "Nimekiri", "schedule_add_block": "Lisa plokk", "schedule_manage_stages": "Halda lavasid", "schedule_no_blocks": "Ajakavaplokke pole veel", "schedule_add_first": "Lisa oma esimene plokk ajakava koostamise alustamiseks.", - "schedule_all_day": "Terve pƤev", + "schedule_all_day": "Terve päev", "schedule_no_stage": "Lava puudub", "schedule_add_stage_title": "Lisa lava / ruum", "schedule_stage_name_placeholder": "nt Peamine lava", @@ -521,19 +521,19 @@ "schedule_block_speaker_label": "Esineja / Juht", "schedule_block_speaker_placeholder": "nt Jaan Tamm", "schedule_block_start_label": "Algusaeg", - "schedule_block_end_label": "LƵpuaeg", + "schedule_block_end_label": "Lõpuaeg", "schedule_block_stage_label": "Lava / Ruum", "schedule_block_no_stage": "Lava puudub", "schedule_block_description_label": "Kirjeldus", "schedule_block_description_placeholder": "Valikuline kirjeldus...", - "schedule_block_color_label": "VƤrv", + "schedule_block_color_label": "Värv", "schedule_block_delete": "Kustuta plokk", "contacts_search_placeholder": "Otsi kontakte...", "contacts_add": "Lisa kontakt", "contacts_no_contacts": "Kontakte pole veel", "contacts_add_first": "Lisa oma esimene kontakt kataloogi loomiseks.", "contacts_no_results": "Otsingutulemusi ei leitud", - "contacts_category_all": "KƵik", + "contacts_category_all": "Kõik", "contacts_category_venue": "Toimumiskoht", "contacts_category_catering": "Toitlustus", "contacts_category_av": "AV / Tehnika", @@ -548,31 +548,31 @@ "contacts_name_label": "Nimi", "contacts_name_placeholder": "Kontakti nimi", "contacts_role_label": "Roll / Ametinimetus", - "contacts_role_placeholder": "nt Ürituse juht", - "contacts_company_label": "EttevƵte", - "contacts_company_placeholder": "EttevƵtte nimi", + "contacts_role_placeholder": "nt Ürituse juht", + "contacts_company_label": "Ettevõte", + "contacts_company_placeholder": "Ettevõtte nimi", "contacts_email_label": "E-post", - "contacts_email_placeholder": "email@nƤide.ee", + "contacts_email_placeholder": "email@näide.ee", "contacts_phone_label": "Telefon", "contacts_phone_placeholder": "+372 ...", "contacts_website_label": "Veebileht", "contacts_website_placeholder": "https://...", "contacts_category_label": "Kategooria", - "contacts_notes_label": "MƤrkmed", - "contacts_notes_placeholder": "Valikulised mƤrkmed...", + "contacts_notes_label": "Märkmed", + "contacts_notes_placeholder": "Valikulised märkmed...", "contacts_delete_confirm": "Kustuta see kontakt?", "budget_income": "Tulud", "budget_expenses": "Kulud", "budget_planned": "Planeeritud: {amount}", "budget_planned_balance": "Planeeritud saldo", "budget_actual_balance": "Tegelik saldo", - "budget_view_all": "KƵik", + "budget_view_all": "Kõik", "budget_view_income": "Tulud", "budget_view_expenses": "Kulud", "budget_add_category": "Kategooria", - "budget_add_item": "Lisa üksus", - "budget_no_items": "Eelarveüksusi pole veel", - "budget_col_type": "Tüüp", + "budget_add_item": "Lisa üksus", + "budget_no_items": "Eelarveüksusi pole veel", + "budget_col_type": "Tüüp", "budget_col_description": "Kirjeldus", "budget_col_category": "Kategooria", "budget_col_planned": "Planeeritud", @@ -581,35 +581,35 @@ "budget_col_receipt": "Kviitung", "budget_uncategorized": "Kategoriseerimata", "budget_total": "Kokku", - "budget_missing_receipt": "Arve/kviitung puudub - kliki üleslaadimiseks", + "budget_missing_receipt": "Arve/kviitung puudub - kliki üleslaadimiseks", "budget_missing_receipt_short": "Arve/kviitung puudub", "budget_receipt_attached": "Kviitung lisatud", - "budget_add_item_title": "Lisa eelarveüksus", - "budget_edit_item_title": "Muuda eelarveüksust", + "budget_add_item_title": "Lisa eelarveüksus", + "budget_edit_item_title": "Muuda eelarveüksust", "budget_description_label": "Kirjeldus", "budget_description_placeholder": "nt Ruumi rent", - "budget_type_label": "Tüüp", + "budget_type_label": "Tüüp", "budget_type_expense": "Kulu", "budget_type_income": "Tulu", "budget_category_label": "Kategooria", "budget_planned_amount_label": "Planeeritud summa", "budget_actual_amount_label": "Tegelik summa", - "budget_notes_label": "MƤrkmed", - "budget_notes_placeholder": "Valikulised mƤrkmed...", + "budget_notes_label": "Märkmed", + "budget_notes_placeholder": "Valikulised märkmed...", "budget_add_category_title": "Lisa kategooria", "budget_category_name_label": "Nimi", "budget_category_name_placeholder": "nt Toimumiskoht", - "budget_category_color_label": "VƤrv", - "budget_select_color": "Vali vƤrv {color}", + "budget_category_color_label": "Värv", + "budget_select_color": "Vali värv {color}", "budget_existing_categories": "Olemasolevad kategooriad", "sponsors_search_placeholder": "Otsi sponsoreid...", "sponsors_add_tier": "Lisa tase", "sponsors_add_sponsor": "Lisa sponsor", "sponsors_no_sponsors": "Sponsoreid pole veel", - "sponsors_add_first": "Lisa sponsoritasemed ja alusta sponsorite jƤlgimist.", + "sponsors_add_first": "Lisa sponsoritasemed ja alusta sponsorite jälgimist.", "sponsors_no_results": "Filtritele vastavaid sponsoreid ei leitud", - "sponsors_filter_all_statuses": "KƵik staatused", - "sponsors_filter_all_tiers": "KƵik tasemed", + "sponsors_filter_all_statuses": "Kõik staatused", + "sponsors_filter_all_tiers": "Kõik tasemed", "sponsors_status_prospect": "Potentsiaalne", "sponsors_status_contacted": "Kontakteeritud", "sponsors_status_confirmed": "Kinnitatud", @@ -620,18 +620,18 @@ "sponsors_deliverables_label": "Kohustused", "sponsors_deliverable_placeholder": "Lisa kohustus...", "sponsors_no_deliverables": "Kohustusi pole veel", - "sponsors_notes_label": "MƤrkmed", + "sponsors_notes_label": "Märkmed", "sponsors_add_tier_title": "Lisa sponsoritase", "sponsors_tier_name_label": "Taseme nimi", "sponsors_tier_name_placeholder": "nt Kuld", "sponsors_tier_amount_label": "Minimaalne summa", - "sponsors_tier_color_label": "VƤrv", + "sponsors_tier_color_label": "Värv", "sponsors_existing_tiers": "Olemasolevad tasemed", "sponsors_no_tiers": "Tasemeid pole veel", "sponsors_add_sponsor_title": "Lisa sponsor", "sponsors_edit_sponsor_title": "Muuda sponsorit", "sponsors_name_label": "Sponsori nimi", - "sponsors_name_placeholder": "EttevƵtte nimi", + "sponsors_name_placeholder": "Ettevõtte nimi", "sponsors_tier_label": "Tase", "sponsors_no_tier": "Tase puudub", "sponsors_status_label": "Staatus", @@ -640,18 +640,18 @@ "sponsors_contact_name_label": "Kontaktisiku nimi", "sponsors_contact_name_placeholder": "Kontaktisik", "sponsors_contact_email_label": "Kontakti e-post", - "sponsors_contact_email_placeholder": "email@nƤide.ee", + "sponsors_contact_email_placeholder": "email@näide.ee", "sponsors_contact_phone_label": "Kontakti telefon", "sponsors_contact_phone_placeholder": "+372 ...", "sponsors_website_label": "Veebileht", "sponsors_website_placeholder": "https://...", - "sponsors_notes_placeholder": "Valikulised mƤrkmed...", + "sponsors_notes_placeholder": "Valikulised märkmed...", "sponsors_delete_confirm": "Kustuta see sponsor?", - "sponsors_select_color": "Vali vƤrv {color}", + "sponsors_select_color": "Vali värv {color}", "files_widget_loading": "Failide laadimine...", "files_widget_no_folder": "Valdkonna kausta pole veel loodud", "files_widget_empty": "Faile pole veel", - "files_widget_full_view": "TƤisvaade", + "files_widget_full_view": "Täisvaade", "files_widget_create_title": "Loo uus", "files_widget_type_document": "Dokument", "files_widget_type_folder": "Kaust", @@ -660,30 +660,30 @@ "files_widget_doc_placeholder": "Dokumendi nimi", "files_widget_folder_placeholder": "Kausta nimi", "files_widget_kanban_placeholder": "Tahvli nimi", - "files_widget_drop_files": "Lohista failid üleslaadimiseks", - "files_widget_uploading": "Üleslaadimine {name}...", + "files_widget_drop_files": "Lohista failid üleslaadimiseks", + "files_widget_uploading": "Üleslaadimine {name}...", "kanban_widget_loading": "Tahvlite laadimine...", "kanban_widget_no_folder": "Valdkonna kausta pole veel loodud", "kanban_widget_no_boards": "Kanban tahvleid pole veel", "kanban_widget_create": "Loo tahvel", "kanban_widget_create_title": "Loo Kanban tahvel", "kanban_widget_name_label": "Tahvli nimi", - "kanban_widget_name_placeholder": "nt Ülesannete jƤlgija", - "finances_title": "Ürituse rahandus", - "finances_subtitle": "Eelarve ülevaade kƵigi valdkondade lƵikes", + "kanban_widget_name_placeholder": "nt Ülesannete jälgija", + "finances_title": "Ürituse rahandus", + "finances_subtitle": "Eelarve ülevaade kõigi valdkondade lõikes", "finances_total_income": "Tulud kokku", "finances_total_expenses": "Kulud kokku", "finances_net_balance": "Netosaldo", "finances_missing_receipts": "Puuduvad kviitungid", - "finances_items_without_receipts": "{count} üksust ilma kviitungita", + "finances_items_without_receipts": "{count} üksust ilma kviitungita", "finances_planned": "planeeritud", - "finances_view_by_dept": "Valdkondade jƤrgi", - "finances_view_by_category": "Kategooriate jƤrgi", - "finances_filter_all": "KƵik", + "finances_view_by_dept": "Valdkondade järgi", + "finances_view_by_category": "Kategooriate järgi", + "finances_filter_all": "Kõik", "finances_filter_income": "Tulud", "finances_filter_expenses": "Kulud", - "finances_no_items": "Eelarveüksusi pole veel", - "finances_no_items_desc": "Eelarveüksused ilmuvad siia, kui valdkonnad neid lisavad.", + "finances_no_items": "Eelarveüksusi pole veel", + "finances_no_items_desc": "Eelarveüksused ilmuvad siia, kui valdkonnad neid lisavad.", "finances_col_description": "Kirjeldus", "finances_col_department": "Valdkond", "finances_col_category": "Kategooria", diff --git a/src/lib/api/map.ts b/src/lib/api/map.ts index 7f80dea..94e313e 100644 --- a/src/lib/api/map.ts +++ b/src/lib/api/map.ts @@ -250,3 +250,50 @@ export async function deleteMapShape(supabase: SupabaseClient, shapeId: string): if (error) throw error; } + +// ── Realtime ── + +export interface RealtimeMapPayload { + event: 'INSERT' | 'UPDATE' | 'DELETE'; + new: T; + old: Partial; +} + +export function subscribeToMapLayers( + supabase: SupabaseClient, + layerIds: string[], + onPinChange: (payload: RealtimeMapPayload) => void, + onShapeChange: (payload: RealtimeMapPayload) => void, +) { + const layerIdSet = new Set(layerIds); + const channel = supabase.channel(`map:${layerIds.join(',')}`); + + channel + .on('postgres_changes', { event: '*', schema: 'public', table: 'map_pins' }, + (payload) => { + const pin = (payload.new ?? payload.old) as Partial; + const lid = pin.layer_id ?? (payload.old as Partial)?.layer_id; + if (lid && !layerIdSet.has(lid)) return; + onPinChange({ + event: payload.eventType as 'INSERT' | 'UPDATE' | 'DELETE', + new: payload.new as MapPin, + old: payload.old as Partial, + }); + } + ) + .on('postgres_changes', { event: '*', schema: 'public', table: 'map_shapes' }, + (payload) => { + const shape = (payload.new ?? payload.old) as Partial; + const lid = shape.layer_id ?? (payload.old as Partial)?.layer_id; + if (lid && !layerIdSet.has(lid)) return; + onShapeChange({ + event: payload.eventType as 'INSERT' | 'UPDATE' | 'DELETE', + new: payload.new as MapShape, + old: payload.old as Partial, + }); + } + ) + .subscribe(); + + return channel; +} diff --git a/src/lib/components/modules/MapWidget.svelte b/src/lib/components/modules/MapWidget.svelte index e90d888..84fd109 100644 --- a/src/lib/components/modules/MapWidget.svelte +++ b/src/lib/components/modules/MapWidget.svelte @@ -6,11 +6,13 @@ import * as m from "$lib/paraglide/messages"; import type { SupabaseClient } from "@supabase/supabase-js"; import type { Database } from "$lib/supabase/types"; + import type { RealtimeChannel } from "@supabase/supabase-js"; import { type MapLayerWithPins, type MapLayer, type MapPin as MapPinType, type MapShape, + type RealtimeMapPayload, createMapLayer, updateMapLayer, deleteMapLayer, @@ -20,6 +22,7 @@ createMapShape, updateMapShape, deleteMapShape, + subscribeToMapLayers, } from "$lib/api/map"; let L: any; @@ -51,6 +54,10 @@ let leafletShapes = new Map(); let leafletBoundsRects = new Map(); + // Realtime + let realtimeChannel: RealtimeChannel | null = null; + const optimisticIds = new Set(); + // svelte-ignore state_referenced_locally let layers = $state(initialLayers); let activeLayerIdx = $state(0); @@ -415,6 +422,7 @@ (s) => s.id === shapeId, ); if (shape) { + optimisticIds.add(shapeId); try { await updateMapShape(supabase, shapeId, { vertices: shape.vertices, @@ -461,6 +469,7 @@ vertices, sort_order: activeLayer.shapes?.length ?? 0, }); + optimisticIds.add(shape.id); layers = layers.map((l, i) => i === activeLayerIdx ? { ...l, shapes: [...(l.shapes ?? []), shape] } @@ -485,6 +494,7 @@ vertices: verts, sort_order: activeLayer.shapes?.length ?? 0, }); + optimisticIds.add(shape.id); layers = layers.map((l, i) => i === activeLayerIdx ? { ...l, shapes: [...(l.shapes ?? []), shape] } @@ -643,6 +653,7 @@ ); showPinModal = false; syncAllObjects(layers[activeLayerIdx]); + optimisticIds.add(editingPin.id); try { await updateMapPin(supabase, editingPin.id, updated); } catch { @@ -658,6 +669,7 @@ lng: pendingLatLng.lng, sort_order: activeLayer.pins.length, }); + optimisticIds.add(pin.id); layers = layers.map((l, i) => i === activeLayerIdx ? { ...l, pins: [...l.pins, pin] } : l, ); @@ -687,6 +699,7 @@ ); if (selectedObjectId === pinId) deselectAll(); syncAllObjects(layers[activeLayerIdx]); + optimisticIds.add(pinId); try { await deleteMapPin(supabase, pinId); } catch { @@ -720,6 +733,7 @@ ); showShapeModal = false; syncAllObjects(layers[activeLayerIdx]); + optimisticIds.add(editingShape.id); try { await updateMapShape(supabase, editingShape.id, updated); } catch { @@ -740,6 +754,7 @@ ); if (selectedObjectId === shapeId) deselectAll(); syncAllObjects(layers[activeLayerIdx]); + optimisticIds.add(shapeId); try { await deleteMapShape(supabase, shapeId); } catch { @@ -774,6 +789,7 @@ rotation: original.rotation, sort_order: activeLayer.shapes?.length ?? 0, }); + optimisticIds.add(shape.id); layers = layers.map((l, i) => i === activeLayerIdx ? { ...l, shapes: [...(l.shapes ?? []), shape] } @@ -815,6 +831,7 @@ layers = [...layers, withData]; activeLayerIdx = layers.length - 1; showLayerOnMap(withData); + setupRealtime(); } catch { toasts.error("Failed to create layer"); } @@ -827,6 +844,7 @@ if (activeLayerIdx >= layers.length) activeLayerIdx = Math.max(0, layers.length - 1); if (layers.length > 0) showLayerOnMap(layers[activeLayerIdx]); + setupRealtime(); try { await deleteMapLayer(supabase, layerId); } catch { @@ -979,6 +997,120 @@ } } + // ── Realtime handlers ── + + function handlePinRealtime(payload: RealtimeMapPayload) { + const id = payload.new?.id ?? payload.old?.id; + if (!id) return; + if (optimisticIds.has(id)) { + optimisticIds.delete(id); + return; + } + + const layerId = payload.new?.layer_id ?? payload.old?.layer_id; + if (!layerId) return; + + if (payload.event === "INSERT") { + layers = layers.map((l) => + l.id === layerId + ? { + ...l, + pins: [ + ...l.pins.filter((p) => p.id !== id), + payload.new, + ], + } + : l, + ); + } else if (payload.event === "UPDATE") { + layers = layers.map((l) => + l.id === layerId + ? { + ...l, + pins: l.pins.map((p) => + p.id === id ? payload.new : p, + ), + } + : l, + ); + } else if (payload.event === "DELETE") { + layers = layers.map((l) => + l.id === layerId + ? { ...l, pins: l.pins.filter((p) => p.id !== id) } + : l, + ); + if (selectedObjectId === id) deselectAll(); + } + + const layerIdx = layers.findIndex((l) => l.id === layerId); + if (layerIdx === activeLayerIdx) syncAllObjects(layers[layerIdx]); + } + + function handleShapeRealtime(payload: RealtimeMapPayload) { + const id = payload.new?.id ?? payload.old?.id; + if (!id) return; + if (optimisticIds.has(id)) { + optimisticIds.delete(id); + return; + } + + const layerId = payload.new?.layer_id ?? payload.old?.layer_id; + if (!layerId) return; + + if (payload.event === "INSERT") { + layers = layers.map((l) => + l.id === layerId + ? { + ...l, + shapes: [ + ...(l.shapes ?? []).filter((s) => s.id !== id), + payload.new, + ], + } + : l, + ); + } else if (payload.event === "UPDATE") { + layers = layers.map((l) => + l.id === layerId + ? { + ...l, + shapes: (l.shapes ?? []).map((s) => + s.id === id ? payload.new : s, + ), + } + : l, + ); + } else if (payload.event === "DELETE") { + layers = layers.map((l) => + l.id === layerId + ? { + ...l, + shapes: (l.shapes ?? []).filter((s) => s.id !== id), + } + : l, + ); + if (selectedObjectId === id) deselectAll(); + } + + const layerIdx = layers.findIndex((l) => l.id === layerId); + if (layerIdx === activeLayerIdx) syncAllObjects(layers[layerIdx]); + } + + function setupRealtime() { + if (realtimeChannel) { + supabase.removeChannel(realtimeChannel); + realtimeChannel = null; + } + const layerIds = layers.map((l) => l.id).filter(Boolean); + if (layerIds.length === 0) return; + realtimeChannel = subscribeToMapLayers( + supabase, + layerIds, + handlePinRealtime, + handleShapeRealtime, + ); + } + // ── Lifecycle ── onMount(async () => { @@ -1003,9 +1135,15 @@ } else { showLayerOnMap(layers[activeLayerIdx]); } + + setupRealtime(); }); onDestroy(() => { + if (realtimeChannel) { + supabase.removeChannel(realtimeChannel); + realtimeChannel = null; + } if (map) { map.remove(); map = null; diff --git a/src/routes/[orgSlug]/events/+page.svelte b/src/routes/[orgSlug]/events/+page.svelte index bc44429..59a18d7 100644 --- a/src/routes/[orgSlug]/events/+page.svelte +++ b/src/routes/[orgSlug]/events/+page.svelte @@ -59,6 +59,7 @@ let newEventStartDate = $state(""); let newEventEndDate = $state(""); let newEventVenue = $state(""); + let newEventVenueAddress = $state(""); let newEventColor = $state(data.org.default_event_color || "#00A3E0"); let creating = $state(false); @@ -111,6 +112,7 @@ start_date: newEventStartDate || null, end_date: newEventEndDate || null, venue_name: newEventVenue.trim() || null, + venue_address: newEventVenueAddress.trim() || null, color: newEventColor, created_by: userId, }) @@ -154,6 +156,7 @@ newEventStartDate = ""; newEventEndDate = ""; newEventVenue = ""; + newEventVenueAddress = ""; newEventColor = data.org.default_event_color || "#00A3E0"; } @@ -331,12 +334,19 @@ - +
+ + +
diff --git a/src/routes/[orgSlug]/events/[eventSlug]/dept/[deptId]/+page.svelte b/src/routes/[orgSlug]/events/[eventSlug]/dept/[deptId]/+page.svelte index deea32b..e2a0f01 100644 --- a/src/routes/[orgSlug]/events/[eventSlug]/dept/[deptId]/+page.svelte +++ b/src/routes/[orgSlug]/events/[eventSlug]/dept/[deptId]/+page.svelte @@ -1272,13 +1272,14 @@
{:else}
{#each dashboard.panels as panel (panel.id)}