@agent-native/core 0.98.8 → 0.98.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/mcp/build-server.ts +99 -26
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +31 -2
- package/corpus/templates/calendar/AGENTS.md +11 -0
- package/corpus/templates/calendar/README.md +2 -1
- package/corpus/templates/calendar/actions/create-event.ts +7 -6
- package/corpus/templates/calendar/actions/event-action-helpers.ts +49 -0
- package/corpus/templates/calendar/actions/update-event.ts +225 -14
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +185 -70
- package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +26 -4
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +112 -69
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +621 -524
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +275 -164
- package/corpus/templates/calendar/app/components/calendar/WorkingLocationEditor.tsx +222 -0
- package/corpus/templates/calendar/app/hooks/use-events.ts +151 -79
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +6 -0
- package/corpus/templates/calendar/app/i18n-data.ts +60 -0
- package/corpus/templates/calendar/app/lib/all-day-layout.ts +126 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +18 -1
- package/corpus/templates/calendar/app/lib/event-mutation-inputs.ts +1 -1
- package/corpus/templates/calendar/app/lib/working-location.ts +163 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +21 -2
- package/corpus/templates/calendar/changelog/2026-07-07-working-locations-from-google-calendar-now-appear-as-native-.md +6 -0
- package/corpus/templates/calendar/server/lib/calendar-availability.ts +2 -0
- package/corpus/templates/calendar/server/lib/google-api.ts +6 -1
- package/corpus/templates/calendar/server/lib/google-calendar.ts +49 -19
- package/corpus/templates/calendar/shared/api.ts +2 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +82 -28
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/secrets/routes.d.ts +9 -9
- package/package.json +1 -1
|
@@ -712,6 +712,7 @@ const enUS = {
|
|
|
712
712
|
addingMeetingLink: "Adding {{provider}} link",
|
|
713
713
|
alerts: "Alerts",
|
|
714
714
|
allDay: "All day",
|
|
715
|
+
allDays: "All days",
|
|
715
716
|
allEvents: "All events",
|
|
716
717
|
applyChangesTo: "Apply changes to",
|
|
717
718
|
applyTo: "Apply to",
|
|
@@ -796,6 +797,7 @@ const enUS = {
|
|
|
796
797
|
notifyGuestsQuestion: "Notify guests?",
|
|
797
798
|
none: "None",
|
|
798
799
|
office: "Office",
|
|
800
|
+
other: "Other",
|
|
799
801
|
openInSidebar: "Open in sidebar",
|
|
800
802
|
openInGoogleCalendar: "Open in Google Calendar",
|
|
801
803
|
optionalDescription: "Optional description",
|
|
@@ -849,6 +851,7 @@ const enUS = {
|
|
|
849
851
|
start: "Start",
|
|
850
852
|
startDate: "Start date",
|
|
851
853
|
subscribedFeeds: "Subscribed feeds",
|
|
854
|
+
thisDayOnly: "This day only",
|
|
852
855
|
thisEvent: "This event",
|
|
853
856
|
timeSelected: "Time selected",
|
|
854
857
|
timezone: "Timezone",
|
|
@@ -872,6 +875,9 @@ const enUS = {
|
|
|
872
875
|
weekly: "Weekly",
|
|
873
876
|
workingFrom: "Working from",
|
|
874
877
|
workingLocation: "Working location",
|
|
878
|
+
workingLocationDesk: "Desk {{desk}}",
|
|
879
|
+
workingLocationFloor: "Floor {{floor}}",
|
|
880
|
+
workingLocationTitle: "Working location: {{location}}",
|
|
875
881
|
yearly: "Yearly",
|
|
876
882
|
zoom: "Zoom",
|
|
877
883
|
zoomAdded: "Zoom added",
|
|
@@ -1142,6 +1148,7 @@ const calendarSurfaceTranslations = {
|
|
|
1142
1148
|
addingMeetingLink: "添加 {{provider}} 链接",
|
|
1143
1149
|
alerts: "警报",
|
|
1144
1150
|
allDay: "全天",
|
|
1151
|
+
allDays: "所有日期",
|
|
1145
1152
|
allEvents: "所有活动",
|
|
1146
1153
|
applyTo: "适用于",
|
|
1147
1154
|
askAi: "询问人工智能",
|
|
@@ -1204,6 +1211,7 @@ const calendarSurfaceTranslations = {
|
|
|
1204
1211
|
noVideo: "没有视频",
|
|
1205
1212
|
none: "无",
|
|
1206
1213
|
office: "办公室",
|
|
1214
|
+
other: "其他",
|
|
1207
1215
|
openInSidebar: "在侧边栏中打开",
|
|
1208
1216
|
optionalDescription: "可选描述",
|
|
1209
1217
|
optionalLocation: "可选位置",
|
|
@@ -1222,6 +1230,7 @@ const calendarSurfaceTranslations = {
|
|
|
1222
1230
|
showAs: "显示为",
|
|
1223
1231
|
start: "开始",
|
|
1224
1232
|
startDate: "开始日期",
|
|
1233
|
+
thisDayOnly: "仅当天",
|
|
1225
1234
|
thisEvent: "本次活动",
|
|
1226
1235
|
timeSelected: "选定时间",
|
|
1227
1236
|
timezone: "时区",
|
|
@@ -1235,6 +1244,9 @@ const calendarSurfaceTranslations = {
|
|
|
1235
1244
|
weekly: "每周",
|
|
1236
1245
|
workingFrom: "工作自",
|
|
1237
1246
|
workingLocation: "工作地点",
|
|
1247
|
+
workingLocationDesk: "工位 {{desk}}",
|
|
1248
|
+
workingLocationFloor: "楼层 {{floor}}",
|
|
1249
|
+
workingLocationTitle: "工作地点:{{location}}",
|
|
1238
1250
|
yearly: "每年",
|
|
1239
1251
|
zoom: "Zoom",
|
|
1240
1252
|
zoomAdded: "添加Zoom",
|
|
@@ -1435,6 +1447,7 @@ const calendarSurfaceTranslations = {
|
|
|
1435
1447
|
addingMeetingLink: "Añadiendo enlace {{provider}}",
|
|
1436
1448
|
alerts: "Alertas",
|
|
1437
1449
|
allDay: "Todo el día",
|
|
1450
|
+
allDays: "Todos los días",
|
|
1438
1451
|
allEvents: "Todos los eventos",
|
|
1439
1452
|
applyTo: "Referirse a",
|
|
1440
1453
|
askAi: "Pregúntale a la IA",
|
|
@@ -1500,6 +1513,7 @@ const calendarSurfaceTranslations = {
|
|
|
1500
1513
|
noVideo: "Sin vídeo",
|
|
1501
1514
|
none: "Ninguno",
|
|
1502
1515
|
office: "Oficina",
|
|
1516
|
+
other: "Otro",
|
|
1503
1517
|
openInSidebar: "Abrir en la barra lateral",
|
|
1504
1518
|
optionalDescription: "Descripción opcional",
|
|
1505
1519
|
optionalLocation: "Ubicación opcional",
|
|
@@ -1519,6 +1533,7 @@ const calendarSurfaceTranslations = {
|
|
|
1519
1533
|
showAs: "Mostrar como",
|
|
1520
1534
|
start: "Comenzar",
|
|
1521
1535
|
startDate: "Fecha de inicio",
|
|
1536
|
+
thisDayOnly: "Solo este día",
|
|
1522
1537
|
thisEvent: "este evento",
|
|
1523
1538
|
timeSelected: "Hora seleccionada",
|
|
1524
1539
|
timezone: "Zona horaria",
|
|
@@ -1532,6 +1547,9 @@ const calendarSurfaceTranslations = {
|
|
|
1532
1547
|
weekly: "Semanalmente",
|
|
1533
1548
|
workingFrom: "Trabajando desde",
|
|
1534
1549
|
workingLocation: "Lugar de trabajo",
|
|
1550
|
+
workingLocationDesk: "Escritorio {{desk}}",
|
|
1551
|
+
workingLocationFloor: "Piso {{floor}}",
|
|
1552
|
+
workingLocationTitle: "Lugar de trabajo: {{location}}",
|
|
1535
1553
|
yearly: "Anual",
|
|
1536
1554
|
zoom: "Zoom",
|
|
1537
1555
|
zoomAdded: "Zoom añadido",
|
|
@@ -1738,6 +1756,7 @@ const calendarSurfaceTranslations = {
|
|
|
1738
1756
|
addingMeetingLink: "Ajout du lien {{provider}}",
|
|
1739
1757
|
alerts: "Alertes",
|
|
1740
1758
|
allDay: "Toute la journée",
|
|
1759
|
+
allDays: "Tous les jours",
|
|
1741
1760
|
allEvents: "Tous les événements",
|
|
1742
1761
|
applyTo: "Postuler à",
|
|
1743
1762
|
askAi: "Demandez à l'IA",
|
|
@@ -1804,6 +1823,7 @@ const calendarSurfaceTranslations = {
|
|
|
1804
1823
|
noVideo: "Pas de vidéo",
|
|
1805
1824
|
none: "Aucun",
|
|
1806
1825
|
office: "Bureau",
|
|
1826
|
+
other: "Autre",
|
|
1807
1827
|
openInSidebar: "Ouvrir dans la barre latérale",
|
|
1808
1828
|
optionalDescription: "Description facultative",
|
|
1809
1829
|
optionalLocation: "Emplacement facultatif",
|
|
@@ -1823,6 +1843,7 @@ const calendarSurfaceTranslations = {
|
|
|
1823
1843
|
showAs: "Afficher comme",
|
|
1824
1844
|
start: "Commencer",
|
|
1825
1845
|
startDate: "Date de début",
|
|
1846
|
+
thisDayOnly: "Ce jour uniquement",
|
|
1826
1847
|
thisEvent: "Cet événement",
|
|
1827
1848
|
timeSelected: "Heure sélectionnée",
|
|
1828
1849
|
timezone: "Fuseau horaire",
|
|
@@ -1836,6 +1857,9 @@ const calendarSurfaceTranslations = {
|
|
|
1836
1857
|
weekly: "Hebdomadaire",
|
|
1837
1858
|
workingFrom: "Travailler à partir de",
|
|
1838
1859
|
workingLocation: "Lieu de travail",
|
|
1860
|
+
workingLocationDesk: "Bureau {{desk}}",
|
|
1861
|
+
workingLocationFloor: "Étage {{floor}}",
|
|
1862
|
+
workingLocationTitle: "Lieu de travail : {{location}}",
|
|
1839
1863
|
yearly: "Annuel",
|
|
1840
1864
|
zoom: "Zoom",
|
|
1841
1865
|
zoomAdded: "Zoom ajouté",
|
|
@@ -2041,6 +2065,7 @@ const calendarSurfaceTranslations = {
|
|
|
2041
2065
|
addingMeetingLink: "{{provider}}-Link hinzufügen",
|
|
2042
2066
|
alerts: "Warnungen",
|
|
2043
2067
|
allDay: "Den ganzen Tag",
|
|
2068
|
+
allDays: "Alle Tage",
|
|
2044
2069
|
allEvents: "Alle Veranstaltungen",
|
|
2045
2070
|
applyTo: "Bewerben Sie sich bei",
|
|
2046
2071
|
askAi: "Fragen Sie die KI",
|
|
@@ -2108,6 +2133,7 @@ const calendarSurfaceTranslations = {
|
|
|
2108
2133
|
noVideo: "Kein Video",
|
|
2109
2134
|
none: "Keine",
|
|
2110
2135
|
office: "Büro",
|
|
2136
|
+
other: "Andere",
|
|
2111
2137
|
openInSidebar: "In der Seitenleiste öffnen",
|
|
2112
2138
|
optionalDescription: "Optionale Beschreibung",
|
|
2113
2139
|
optionalLocation: "Optionaler Standort",
|
|
@@ -2127,6 +2153,7 @@ const calendarSurfaceTranslations = {
|
|
|
2127
2153
|
showAs: "Anzeigen als",
|
|
2128
2154
|
start: "Starten",
|
|
2129
2155
|
startDate: "Startdatum",
|
|
2156
|
+
thisDayOnly: "Nur dieser Tag",
|
|
2130
2157
|
thisEvent: "Dieses Ereignis",
|
|
2131
2158
|
timeSelected: "Zeit ausgewählt",
|
|
2132
2159
|
timezone: "Zeitzone",
|
|
@@ -2140,6 +2167,9 @@ const calendarSurfaceTranslations = {
|
|
|
2140
2167
|
weekly: "Wöchentlich",
|
|
2141
2168
|
workingFrom: "Arbeiten von",
|
|
2142
2169
|
workingLocation: "Arbeitsort",
|
|
2170
|
+
workingLocationDesk: "Schreibtisch {{desk}}",
|
|
2171
|
+
workingLocationFloor: "Etage {{floor}}",
|
|
2172
|
+
workingLocationTitle: "Arbeitsort: {{location}}",
|
|
2143
2173
|
yearly: "Jährlich",
|
|
2144
2174
|
zoom: "Zoom",
|
|
2145
2175
|
zoomAdded: "Zoom hinzugefügt",
|
|
@@ -2339,6 +2369,7 @@ const calendarSurfaceTranslations = {
|
|
|
2339
2369
|
addingMeetingLink: "{{provider}}リンクの追加",
|
|
2340
2370
|
alerts: "アラート",
|
|
2341
2371
|
allDay: "一日中",
|
|
2372
|
+
allDays: "すべての日",
|
|
2342
2373
|
allEvents: "すべてのイベント",
|
|
2343
2374
|
applyTo: "申請先",
|
|
2344
2375
|
askAi: "AIに聞く",
|
|
@@ -2405,6 +2436,7 @@ const calendarSurfaceTranslations = {
|
|
|
2405
2436
|
noVideo: "ビデオなし",
|
|
2406
2437
|
none: "なし",
|
|
2407
2438
|
office: "オフィス",
|
|
2439
|
+
other: "その他",
|
|
2408
2440
|
openInSidebar: "サイドバーで開く",
|
|
2409
2441
|
optionalDescription: "オプションの説明",
|
|
2410
2442
|
optionalLocation: "オプションの場所",
|
|
@@ -2424,6 +2456,7 @@ const calendarSurfaceTranslations = {
|
|
|
2424
2456
|
showAs: "として表示",
|
|
2425
2457
|
start: "スタート",
|
|
2426
2458
|
startDate: "開始日",
|
|
2459
|
+
thisDayOnly: "この日のみ",
|
|
2427
2460
|
thisEvent: "このイベント",
|
|
2428
2461
|
timeSelected: "選択された時間",
|
|
2429
2462
|
timezone: "タイムゾーン",
|
|
@@ -2437,6 +2470,9 @@ const calendarSurfaceTranslations = {
|
|
|
2437
2470
|
weekly: "毎週",
|
|
2438
2471
|
workingFrom: "から働く",
|
|
2439
2472
|
workingLocation: "勤務地",
|
|
2473
|
+
workingLocationDesk: "デスク {{desk}}",
|
|
2474
|
+
workingLocationFloor: "{{floor}}階",
|
|
2475
|
+
workingLocationTitle: "勤務地: {{location}}",
|
|
2440
2476
|
yearly: "毎年",
|
|
2441
2477
|
zoom: "Zoom",
|
|
2442
2478
|
zoomAdded: "Zoom追加",
|
|
@@ -2636,6 +2672,7 @@ const calendarSurfaceTranslations = {
|
|
|
2636
2672
|
addingMeetingLink: "{{provider}} 링크 추가",
|
|
2637
2673
|
alerts: "경고",
|
|
2638
2674
|
allDay: "하루 종일",
|
|
2675
|
+
allDays: "모든 날",
|
|
2639
2676
|
allEvents: "모든 이벤트",
|
|
2640
2677
|
applyTo: "신청 대상",
|
|
2641
2678
|
askAi: "AI에게 물어보세요",
|
|
@@ -2699,6 +2736,7 @@ const calendarSurfaceTranslations = {
|
|
|
2699
2736
|
noVideo: "영상 없음",
|
|
2700
2737
|
none: "없음",
|
|
2701
2738
|
office: "사무실",
|
|
2739
|
+
other: "기타",
|
|
2702
2740
|
openInSidebar: "사이드바에서 열기",
|
|
2703
2741
|
optionalDescription: "선택적 설명",
|
|
2704
2742
|
optionalLocation: "선택적 위치",
|
|
@@ -2718,6 +2756,7 @@ const calendarSurfaceTranslations = {
|
|
|
2718
2756
|
showAs: "다음으로 표시",
|
|
2719
2757
|
start: "시작",
|
|
2720
2758
|
startDate: "시작일",
|
|
2759
|
+
thisDayOnly: "이 날만",
|
|
2721
2760
|
thisEvent: "이번 이벤트",
|
|
2722
2761
|
timeSelected: "선택한 시간",
|
|
2723
2762
|
timezone: "시간대",
|
|
@@ -2731,6 +2770,9 @@ const calendarSurfaceTranslations = {
|
|
|
2731
2770
|
weekly: "주간",
|
|
2732
2771
|
workingFrom: "근무처:",
|
|
2733
2772
|
workingLocation: "근무 위치",
|
|
2773
|
+
workingLocationDesk: "좌석 {{desk}}",
|
|
2774
|
+
workingLocationFloor: "{{floor}}층",
|
|
2775
|
+
workingLocationTitle: "근무 위치: {{location}}",
|
|
2734
2776
|
yearly: "매년",
|
|
2735
2777
|
zoom: "Zoom",
|
|
2736
2778
|
zoomAdded: "Zoom 추가됨",
|
|
@@ -2933,6 +2975,7 @@ const calendarSurfaceTranslations = {
|
|
|
2933
2975
|
addingMeetingLink: "Adicionando link {{provider}}",
|
|
2934
2976
|
alerts: "Alertas",
|
|
2935
2977
|
allDay: "O dia todo",
|
|
2978
|
+
allDays: "Todos os dias",
|
|
2936
2979
|
allEvents: "Todos os eventos",
|
|
2937
2980
|
applyTo: "Candidatar-se a",
|
|
2938
2981
|
askAi: "Pergunte à IA",
|
|
@@ -2998,6 +3041,7 @@ const calendarSurfaceTranslations = {
|
|
|
2998
3041
|
noVideo: "Nenhum vídeo",
|
|
2999
3042
|
none: "Nenhum",
|
|
3000
3043
|
office: "Escritório",
|
|
3044
|
+
other: "Outro",
|
|
3001
3045
|
openInSidebar: "Abrir na barra lateral",
|
|
3002
3046
|
optionalDescription: "Descrição opcional",
|
|
3003
3047
|
optionalLocation: "Localização opcional",
|
|
@@ -3017,6 +3061,7 @@ const calendarSurfaceTranslations = {
|
|
|
3017
3061
|
showAs: "Mostrar como",
|
|
3018
3062
|
start: "Começar",
|
|
3019
3063
|
startDate: "Data de início",
|
|
3064
|
+
thisDayOnly: "Somente este dia",
|
|
3020
3065
|
thisEvent: "Este evento",
|
|
3021
3066
|
timeSelected: "Hora selecionada",
|
|
3022
3067
|
timezone: "Fuso horário",
|
|
@@ -3030,6 +3075,9 @@ const calendarSurfaceTranslations = {
|
|
|
3030
3075
|
weekly: "Semanalmente",
|
|
3031
3076
|
workingFrom: "Trabalhando de",
|
|
3032
3077
|
workingLocation: "Local de trabalho",
|
|
3078
|
+
workingLocationDesk: "Mesa {{desk}}",
|
|
3079
|
+
workingLocationFloor: "Andar {{floor}}",
|
|
3080
|
+
workingLocationTitle: "Local de trabalho: {{location}}",
|
|
3033
3081
|
yearly: "Anualmente",
|
|
3034
3082
|
zoom: "Zoom",
|
|
3035
3083
|
zoomAdded: "Zoom adicionado",
|
|
@@ -3227,6 +3275,7 @@ const calendarSurfaceTranslations = {
|
|
|
3227
3275
|
addingMeetingLink: "{{provider}} लिंक जोड़ा जा रहा है",
|
|
3228
3276
|
alerts: "अलर्ट",
|
|
3229
3277
|
allDay: "सारा दिन",
|
|
3278
|
+
allDays: "सभी दिन",
|
|
3230
3279
|
allEvents: "सभी घटनाएँ",
|
|
3231
3280
|
applyTo: "के लिए आवेदन करें",
|
|
3232
3281
|
askAi: "एआई से पूछें",
|
|
@@ -3290,6 +3339,7 @@ const calendarSurfaceTranslations = {
|
|
|
3290
3339
|
noVideo: "कोई वीडियो नहीं",
|
|
3291
3340
|
none: "कोई नहीं",
|
|
3292
3341
|
office: "कार्यालय",
|
|
3342
|
+
other: "अन्य",
|
|
3293
3343
|
openInSidebar: "साइडबार में खोलें",
|
|
3294
3344
|
optionalDescription: "वैकल्पिक विवरण",
|
|
3295
3345
|
optionalLocation: "वैकल्पिक स्थान",
|
|
@@ -3309,6 +3359,7 @@ const calendarSurfaceTranslations = {
|
|
|
3309
3359
|
showAs: "के रूप में दिखाएँ",
|
|
3310
3360
|
start: "प्रारंभ करें",
|
|
3311
3361
|
startDate: "आरंभ तिथि",
|
|
3362
|
+
thisDayOnly: "केवल यह दिन",
|
|
3312
3363
|
thisEvent: "यह घटना",
|
|
3313
3364
|
timeSelected: "समय चयनित",
|
|
3314
3365
|
timezone: "समयक्षेत्र",
|
|
@@ -3322,6 +3373,9 @@ const calendarSurfaceTranslations = {
|
|
|
3322
3373
|
weekly: "साप्ताहिक",
|
|
3323
3374
|
workingFrom: "से काम कर रहे हैं",
|
|
3324
3375
|
workingLocation: "कार्य स्थान",
|
|
3376
|
+
workingLocationDesk: "डेस्क {{desk}}",
|
|
3377
|
+
workingLocationFloor: "मंज़िल {{floor}}",
|
|
3378
|
+
workingLocationTitle: "कार्य स्थान: {{location}}",
|
|
3325
3379
|
yearly: "वार्षिक",
|
|
3326
3380
|
zoom: "Zoom",
|
|
3327
3381
|
zoomAdded: "Zoom जोड़ा गया",
|
|
@@ -3519,6 +3573,7 @@ const calendarSurfaceTranslations = {
|
|
|
3519
3573
|
addingMeetingLink: "إضافة رابط {{provider}}",
|
|
3520
3574
|
alerts: "التنبيهات",
|
|
3521
3575
|
allDay: "طوال اليوم",
|
|
3576
|
+
allDays: "كل الأيام",
|
|
3522
3577
|
allEvents: "جميع الأحداث",
|
|
3523
3578
|
applyTo: "تنطبق على",
|
|
3524
3579
|
askAi: "اسأل الذكاء الاصطناعي",
|
|
@@ -3583,6 +3638,7 @@ const calendarSurfaceTranslations = {
|
|
|
3583
3638
|
noVideo: "لا يوجد فيديو",
|
|
3584
3639
|
none: "لا شيء",
|
|
3585
3640
|
office: "مكتب",
|
|
3641
|
+
other: "أخرى",
|
|
3586
3642
|
openInSidebar: "فتح في الشريط الجانبي",
|
|
3587
3643
|
optionalDescription: "وصف اختياري",
|
|
3588
3644
|
optionalLocation: "الموقع الاختياري",
|
|
@@ -3602,6 +3658,7 @@ const calendarSurfaceTranslations = {
|
|
|
3602
3658
|
showAs: "إظهار كـ",
|
|
3603
3659
|
start: "ابدأ",
|
|
3604
3660
|
startDate: "تاريخ البدء",
|
|
3661
|
+
thisDayOnly: "هذا اليوم فقط",
|
|
3605
3662
|
thisEvent: "هذا الحدث",
|
|
3606
3663
|
timeSelected: "الوقت المحدد",
|
|
3607
3664
|
timezone: "المنطقة الزمنية",
|
|
@@ -3615,6 +3672,9 @@ const calendarSurfaceTranslations = {
|
|
|
3615
3672
|
weekly: "أسبوعيا",
|
|
3616
3673
|
workingFrom: "العمل من",
|
|
3617
3674
|
workingLocation: "موقع العمل",
|
|
3675
|
+
workingLocationDesk: "المكتب {{desk}}",
|
|
3676
|
+
workingLocationFloor: "الطابق {{floor}}",
|
|
3677
|
+
workingLocationTitle: "موقع العمل: {{location}}",
|
|
3618
3678
|
yearly: "سنويا",
|
|
3619
3679
|
zoom: "Zoom",
|
|
3620
3680
|
zoomAdded: "تمت إضافة Zoom",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { CalendarEvent } from "@shared/api";
|
|
2
|
+
import { addDays, parseISO, startOfDay } from "date-fns";
|
|
3
|
+
|
|
4
|
+
import { isWorkingLocationEvent } from "@/lib/working-location";
|
|
5
|
+
|
|
6
|
+
export interface AllDaySpan {
|
|
7
|
+
event: CalendarEvent;
|
|
8
|
+
startCol: number;
|
|
9
|
+
endCol: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AllDayPlacement extends AllDaySpan {
|
|
13
|
+
row: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AllDayLayout {
|
|
17
|
+
placements: AllDayPlacement[];
|
|
18
|
+
rowCount: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type AllDayPlacementGroup = AllDayPlacement[];
|
|
22
|
+
|
|
23
|
+
export function partitionAllDayEvents(events: CalendarEvent[]) {
|
|
24
|
+
const workingLocations: CalendarEvent[] = [];
|
|
25
|
+
const regularEvents: CalendarEvent[] = [];
|
|
26
|
+
|
|
27
|
+
for (const event of events) {
|
|
28
|
+
if (isWorkingLocationEvent(event)) workingLocations.push(event);
|
|
29
|
+
else regularEvents.push(event);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { workingLocations, regularEvents };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Determine which visible day columns an all-day event overlaps. */
|
|
36
|
+
export function getAllDaySpan(
|
|
37
|
+
event: CalendarEvent,
|
|
38
|
+
days: Date[],
|
|
39
|
+
): Omit<AllDaySpan, "event"> | null {
|
|
40
|
+
const eventStart = parseISO(event.start);
|
|
41
|
+
const eventEnd = event.end ? parseISO(event.end) : addDays(eventStart, 1);
|
|
42
|
+
|
|
43
|
+
let startCol = -1;
|
|
44
|
+
let endCol = -1;
|
|
45
|
+
|
|
46
|
+
for (let index = 0; index < days.length; index++) {
|
|
47
|
+
const dayStart = startOfDay(days[index]);
|
|
48
|
+
const dayEnd = addDays(dayStart, 1);
|
|
49
|
+
if (eventStart < dayEnd && eventEnd > dayStart) {
|
|
50
|
+
if (startCol === -1) startCol = index;
|
|
51
|
+
endCol = index;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return startCol === -1 ? null : { startCol, endCol };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function compareSpans(a: AllDaySpan, b: AllDaySpan): number {
|
|
59
|
+
if (a.startCol !== b.startCol) return a.startCol - b.startCol;
|
|
60
|
+
if (a.endCol !== b.endCol) return b.endCol - a.endCol;
|
|
61
|
+
return a.event.id.localeCompare(b.event.id);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function layoutAllDayEvents(
|
|
65
|
+
events: CalendarEvent[],
|
|
66
|
+
days: Date[],
|
|
67
|
+
): AllDayLayout {
|
|
68
|
+
const spans = events
|
|
69
|
+
.map((event) => {
|
|
70
|
+
const span = getAllDaySpan(event, days);
|
|
71
|
+
return span ? { event, ...span } : null;
|
|
72
|
+
})
|
|
73
|
+
.filter((span): span is AllDaySpan => span !== null)
|
|
74
|
+
.sort(compareSpans);
|
|
75
|
+
const rows: AllDaySpan[][] = [];
|
|
76
|
+
const placements: AllDayPlacement[] = [];
|
|
77
|
+
|
|
78
|
+
for (const span of spans) {
|
|
79
|
+
let row = rows.findIndex((candidate) =>
|
|
80
|
+
candidate.every(
|
|
81
|
+
(existing) =>
|
|
82
|
+
span.endCol < existing.startCol || span.startCol > existing.endCol,
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (row === -1) {
|
|
87
|
+
row = rows.length;
|
|
88
|
+
rows.push([]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
rows[row].push(span);
|
|
92
|
+
placements.push({ ...span, row });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { placements, rowCount: rows.length };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Group visually adjacent placements while preserving each event's click target. */
|
|
99
|
+
export function groupAdjacentAllDayPlacements(
|
|
100
|
+
placements: AllDayPlacement[],
|
|
101
|
+
getGroupKey: (placement: AllDayPlacement) => string,
|
|
102
|
+
): AllDayPlacementGroup[] {
|
|
103
|
+
const sorted = [...placements].sort(
|
|
104
|
+
(a, b) => a.row - b.row || a.startCol - b.startCol,
|
|
105
|
+
);
|
|
106
|
+
const groups: AllDayPlacementGroup[] = [];
|
|
107
|
+
|
|
108
|
+
for (const placement of sorted) {
|
|
109
|
+
const previousGroup = groups[groups.length - 1];
|
|
110
|
+
const previousPlacement = previousGroup?.[previousGroup.length - 1];
|
|
111
|
+
const isAdjacent =
|
|
112
|
+
previousPlacement?.row === placement.row &&
|
|
113
|
+
previousPlacement.endCol + 1 === placement.startCol;
|
|
114
|
+
const isSameGroup =
|
|
115
|
+
previousPlacement !== undefined &&
|
|
116
|
+
getGroupKey(previousPlacement) === getGroupKey(placement);
|
|
117
|
+
|
|
118
|
+
if (previousGroup && isAdjacent && isSameGroup) {
|
|
119
|
+
previousGroup.push(placement);
|
|
120
|
+
} else {
|
|
121
|
+
groups.push([placement]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return groups;
|
|
126
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CalendarEvent } from "@shared/api";
|
|
1
|
+
import type { CalendarEvent, UpdateEventScope } from "@shared/api";
|
|
2
2
|
|
|
3
3
|
export type ReminderMethod = "popup" | "email";
|
|
4
4
|
export type ReminderMode = "default" | "none" | "custom";
|
|
@@ -195,6 +195,23 @@ export function getEventEndValidationMessage({
|
|
|
195
195
|
return "End date and time must be after start date and time.";
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
export function normalizeAllDayEditEndDate(
|
|
199
|
+
singleDay: boolean,
|
|
200
|
+
startDate: string,
|
|
201
|
+
endDate: string,
|
|
202
|
+
): string {
|
|
203
|
+
return singleDay ? startDate : endDate;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function resolveTimeEditScope(
|
|
207
|
+
isRecurring: boolean,
|
|
208
|
+
isSingleDayWorkingLocation: boolean,
|
|
209
|
+
requestedScope: UpdateEventScope,
|
|
210
|
+
): UpdateEventScope {
|
|
211
|
+
if (!isRecurring || isSingleDayWorkingLocation) return "single";
|
|
212
|
+
return requestedScope;
|
|
213
|
+
}
|
|
214
|
+
|
|
198
215
|
export function getLocalTimezone() {
|
|
199
216
|
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
200
217
|
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { CalendarEvent, UpdateEventScope } from "@shared/api";
|
|
2
|
+
|
|
3
|
+
export type WorkingLocationKind =
|
|
4
|
+
| "homeOffice"
|
|
5
|
+
| "officeLocation"
|
|
6
|
+
| "customLocation";
|
|
7
|
+
|
|
8
|
+
export interface WorkingLocationSelection {
|
|
9
|
+
type: WorkingLocationKind;
|
|
10
|
+
label: string;
|
|
11
|
+
scope?: UpdateEventScope;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface WorkingLocationDisplayLabels {
|
|
15
|
+
home: string;
|
|
16
|
+
office: string;
|
|
17
|
+
workingLocation: string;
|
|
18
|
+
title: (location: string) => string;
|
|
19
|
+
floor: (floor: string) => string;
|
|
20
|
+
desk: (desk: string) => string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type WorkingLocationTranslate = (
|
|
24
|
+
key: string,
|
|
25
|
+
values?: Record<string, string>,
|
|
26
|
+
) => string;
|
|
27
|
+
|
|
28
|
+
export function createWorkingLocationDisplayLabels(
|
|
29
|
+
t: WorkingLocationTranslate,
|
|
30
|
+
): WorkingLocationDisplayLabels {
|
|
31
|
+
return {
|
|
32
|
+
home: t("eventForm.home"),
|
|
33
|
+
office: t("eventForm.office"),
|
|
34
|
+
workingLocation: t("eventForm.workingLocation"),
|
|
35
|
+
title: (location) => t("eventForm.workingLocationTitle", { location }),
|
|
36
|
+
floor: (floor) => t("eventForm.workingLocationFloor", { floor }),
|
|
37
|
+
desk: (desk) => t("eventForm.workingLocationDesk", { desk }),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isWorkingLocationEvent(
|
|
42
|
+
event: Pick<CalendarEvent, "eventType">,
|
|
43
|
+
) {
|
|
44
|
+
return event.eventType === "workingLocation";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getWorkingLocationType(
|
|
48
|
+
event: Pick<CalendarEvent, "workingLocationProperties">,
|
|
49
|
+
): WorkingLocationKind {
|
|
50
|
+
return event.workingLocationProperties?.type ?? "customLocation";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getWorkingLocationLabel(
|
|
54
|
+
event: Pick<
|
|
55
|
+
CalendarEvent,
|
|
56
|
+
"location" | "title" | "workingLocationProperties"
|
|
57
|
+
>,
|
|
58
|
+
labels: WorkingLocationDisplayLabels,
|
|
59
|
+
): string {
|
|
60
|
+
const properties = event.workingLocationProperties;
|
|
61
|
+
if (properties?.type === "homeOffice") return labels.home;
|
|
62
|
+
if (properties?.type === "officeLocation") {
|
|
63
|
+
return (
|
|
64
|
+
properties.officeLocation?.label ||
|
|
65
|
+
properties.officeLocation?.buildingId ||
|
|
66
|
+
event.location ||
|
|
67
|
+
event.title ||
|
|
68
|
+
labels.office
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return (
|
|
72
|
+
properties?.customLocation?.label ||
|
|
73
|
+
event.location ||
|
|
74
|
+
event.title ||
|
|
75
|
+
labels.workingLocation
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getWorkingLocationEditableLabel(
|
|
80
|
+
event: Pick<CalendarEvent, "location" | "workingLocationProperties">,
|
|
81
|
+
): string {
|
|
82
|
+
const properties = event.workingLocationProperties;
|
|
83
|
+
if (properties?.type === "officeLocation") {
|
|
84
|
+
return properties.officeLocation?.label || event.location || "";
|
|
85
|
+
}
|
|
86
|
+
if (properties?.type === "customLocation") {
|
|
87
|
+
return properties.customLocation?.label || event.location || "";
|
|
88
|
+
}
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function buildWorkingLocationUpdate(
|
|
93
|
+
event: Pick<CalendarEvent, "id" | "accountEmail">,
|
|
94
|
+
selection: WorkingLocationSelection,
|
|
95
|
+
) {
|
|
96
|
+
const label = selection.type === "homeOffice" ? "" : selection.label.trim();
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
id: event.id,
|
|
100
|
+
accountEmail: event.accountEmail,
|
|
101
|
+
workingLocationType: selection.type,
|
|
102
|
+
workingLocationLabel: label,
|
|
103
|
+
scope: selection.scope,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function buildWorkingLocationProperties(
|
|
108
|
+
event: Pick<CalendarEvent, "workingLocationProperties">,
|
|
109
|
+
selection: Pick<WorkingLocationSelection, "type" | "label">,
|
|
110
|
+
): NonNullable<CalendarEvent["workingLocationProperties"]> {
|
|
111
|
+
const label = selection.label.trim();
|
|
112
|
+
if (selection.type === "homeOffice") {
|
|
113
|
+
return { type: "homeOffice", homeOffice: {} };
|
|
114
|
+
}
|
|
115
|
+
if (selection.type === "officeLocation") {
|
|
116
|
+
return {
|
|
117
|
+
type: "officeLocation",
|
|
118
|
+
officeLocation: {
|
|
119
|
+
...(event.workingLocationProperties?.type === "officeLocation"
|
|
120
|
+
? event.workingLocationProperties.officeLocation
|
|
121
|
+
: {}),
|
|
122
|
+
label,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
type: "customLocation",
|
|
128
|
+
customLocation: { label },
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function getWorkingLocationChipLabel(
|
|
133
|
+
event: CalendarEvent,
|
|
134
|
+
labels: WorkingLocationDisplayLabels,
|
|
135
|
+
): string {
|
|
136
|
+
return isWorkingLocationEvent(event)
|
|
137
|
+
? getWorkingLocationLabel(event, labels)
|
|
138
|
+
: event.title;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function getWorkingLocationTitle(
|
|
142
|
+
event: CalendarEvent,
|
|
143
|
+
labels: WorkingLocationDisplayLabels,
|
|
144
|
+
): string {
|
|
145
|
+
return isWorkingLocationEvent(event)
|
|
146
|
+
? labels.title(getWorkingLocationLabel(event, labels))
|
|
147
|
+
: event.title;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function getWorkingLocationDetail(
|
|
151
|
+
event: Pick<CalendarEvent, "workingLocationProperties">,
|
|
152
|
+
labels: WorkingLocationDisplayLabels,
|
|
153
|
+
): string | undefined {
|
|
154
|
+
const office = event.workingLocationProperties?.officeLocation;
|
|
155
|
+
if (!office) return undefined;
|
|
156
|
+
const parts = [
|
|
157
|
+
office.buildingId,
|
|
158
|
+
office.floorId ? labels.floor(office.floorId) : undefined,
|
|
159
|
+
office.floorSectionId,
|
|
160
|
+
office.deskId ? labels.desk(office.deskId) : undefined,
|
|
161
|
+
].filter(Boolean);
|
|
162
|
+
return parts.length > 0 ? parts.join(" / ") : undefined;
|
|
163
|
+
}
|