@cachly-dev/mcp-server 0.10.64 → 0.10.66

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.
Files changed (270) hide show
  1. package/dist/src/handlers/brain.js +1 -1
  2. package/dist/src/index.js +1 -1
  3. package/package.json +7 -1
  4. package/server.json +2 -2
  5. package/.env.test.example +0 -23
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -50
  7. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  8. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -25
  9. package/.github/workflows/eas-build-android.yml +0 -58
  10. package/.github/workflows/eas-update.yml +0 -55
  11. package/.github/workflows/publish.yml +0 -72
  12. package/BENCH.md +0 -91
  13. package/CHANGELOG.md +0 -604
  14. package/CHANGELOG.md.tmp +0 -0
  15. package/Dockerfile +0 -16
  16. package/LAUNCH_TEXTS.md +0 -182
  17. package/PROGRESS.md +0 -137
  18. package/STRATEGY.md +0 -228
  19. package/VISION_10X.md +0 -133
  20. package/apps/travel-chaos-organizer/.env.example +0 -49
  21. package/apps/travel-chaos-organizer/DEPLOY.md +0 -328
  22. package/apps/travel-chaos-organizer/Makefile +0 -57
  23. package/apps/travel-chaos-organizer/README.md +0 -159
  24. package/apps/travel-chaos-organizer/backend/.env.example +0 -10
  25. package/apps/travel-chaos-organizer/backend/Dockerfile +0 -25
  26. package/apps/travel-chaos-organizer/backend/alembic/env.py +0 -56
  27. package/apps/travel-chaos-organizer/backend/alembic/script.py.mako +0 -24
  28. package/apps/travel-chaos-organizer/backend/alembic/versions/.gitkeep +0 -0
  29. package/apps/travel-chaos-organizer/backend/alembic/versions/0001_stripe_customer_id.py +0 -24
  30. package/apps/travel-chaos-organizer/backend/alembic/versions/0002_telegram_chat_id.py +0 -24
  31. package/apps/travel-chaos-organizer/backend/alembic.ini +0 -39
  32. package/apps/travel-chaos-organizer/backend/app/__init__.py +0 -0
  33. package/apps/travel-chaos-organizer/backend/app/auth/__init__.py +0 -0
  34. package/apps/travel-chaos-organizer/backend/app/auth/keycloak.py +0 -81
  35. package/apps/travel-chaos-organizer/backend/app/config.py +0 -66
  36. package/apps/travel-chaos-organizer/backend/app/db/__init__.py +0 -0
  37. package/apps/travel-chaos-organizer/backend/app/db/database.py +0 -20
  38. package/apps/travel-chaos-organizer/backend/app/db/migrations/001_init.sql +0 -71
  39. package/apps/travel-chaos-organizer/backend/app/db/migrations/002_telemetry_freemium.sql +0 -31
  40. package/apps/travel-chaos-organizer/backend/app/db/migrations/003_telegram_chat_id.sql +0 -4
  41. package/apps/travel-chaos-organizer/backend/app/db/migrations/004_stripe_and_email.sql +0 -5
  42. package/apps/travel-chaos-organizer/backend/app/limiter.py +0 -4
  43. package/apps/travel-chaos-organizer/backend/app/logging_config.py +0 -25
  44. package/apps/travel-chaos-organizer/backend/app/main.py +0 -77
  45. package/apps/travel-chaos-organizer/backend/app/models/__init__.py +0 -0
  46. package/apps/travel-chaos-organizer/backend/app/models/schemas.py +0 -142
  47. package/apps/travel-chaos-organizer/backend/app/routers/__init__.py +0 -0
  48. package/apps/travel-chaos-organizer/backend/app/routers/admin.py +0 -237
  49. package/apps/travel-chaos-organizer/backend/app/routers/cache.py +0 -13
  50. package/apps/travel-chaos-organizer/backend/app/routers/events.py +0 -29
  51. package/apps/travel-chaos-organizer/backend/app/routers/export.py +0 -122
  52. package/apps/travel-chaos-organizer/backend/app/routers/inbox.py +0 -122
  53. package/apps/travel-chaos-organizer/backend/app/routers/items.py +0 -130
  54. package/apps/travel-chaos-organizer/backend/app/routers/mail.py +0 -108
  55. package/apps/travel-chaos-organizer/backend/app/routers/parse.py +0 -303
  56. package/apps/travel-chaos-organizer/backend/app/routers/payments.py +0 -179
  57. package/apps/travel-chaos-organizer/backend/app/routers/trips.py +0 -134
  58. package/apps/travel-chaos-organizer/backend/app/routers/users.py +0 -169
  59. package/apps/travel-chaos-organizer/backend/app/routers/waitlist.py +0 -37
  60. package/apps/travel-chaos-organizer/backend/app/services/__init__.py +0 -0
  61. package/apps/travel-chaos-organizer/backend/app/services/cache.py +0 -131
  62. package/apps/travel-chaos-organizer/backend/app/services/drip.py +0 -128
  63. package/apps/travel-chaos-organizer/backend/app/services/email.py +0 -61
  64. package/apps/travel-chaos-organizer/backend/app/services/errors.py +0 -44
  65. package/apps/travel-chaos-organizer/backend/app/services/notifier.py +0 -10
  66. package/apps/travel-chaos-organizer/backend/app/services/ollama.py +0 -101
  67. package/apps/travel-chaos-organizer/backend/app/services/parser.py +0 -22
  68. package/apps/travel-chaos-organizer/backend/app/services/quota.py +0 -85
  69. package/apps/travel-chaos-organizer/backend/app/services/sentry.py +0 -11
  70. package/apps/travel-chaos-organizer/backend/app/services/telemetry.py +0 -35
  71. package/apps/travel-chaos-organizer/backend/pytest.ini +0 -4
  72. package/apps/travel-chaos-organizer/backend/requirements-dev.txt +0 -6
  73. package/apps/travel-chaos-organizer/backend/requirements.txt +0 -25
  74. package/apps/travel-chaos-organizer/backend/tests/__init__.py +0 -0
  75. package/apps/travel-chaos-organizer/backend/tests/conftest.py +0 -223
  76. package/apps/travel-chaos-organizer/backend/tests/test_admin.py +0 -99
  77. package/apps/travel-chaos-organizer/backend/tests/test_cache.py +0 -142
  78. package/apps/travel-chaos-organizer/backend/tests/test_drip.py +0 -32
  79. package/apps/travel-chaos-organizer/backend/tests/test_e2e_chain.py +0 -121
  80. package/apps/travel-chaos-organizer/backend/tests/test_export.py +0 -48
  81. package/apps/travel-chaos-organizer/backend/tests/test_inbox.py +0 -107
  82. package/apps/travel-chaos-organizer/backend/tests/test_items.py +0 -138
  83. package/apps/travel-chaos-organizer/backend/tests/test_mail.py +0 -113
  84. package/apps/travel-chaos-organizer/backend/tests/test_ollama.py +0 -145
  85. package/apps/travel-chaos-organizer/backend/tests/test_parse_url.py +0 -98
  86. package/apps/travel-chaos-organizer/backend/tests/test_parser.py +0 -72
  87. package/apps/travel-chaos-organizer/backend/tests/test_payments.py +0 -163
  88. package/apps/travel-chaos-organizer/backend/tests/test_quota.py +0 -46
  89. package/apps/travel-chaos-organizer/backend/tests/test_search.py +0 -67
  90. package/apps/travel-chaos-organizer/backend/tests/test_trips.py +0 -123
  91. package/apps/travel-chaos-organizer/backend/tests/test_users.py +0 -52
  92. package/apps/travel-chaos-organizer/backups/.gitkeep +0 -0
  93. package/apps/travel-chaos-organizer/caddy/Caddyfile +0 -27
  94. package/apps/travel-chaos-organizer/docker-compose.node1.yml +0 -31
  95. package/apps/travel-chaos-organizer/docker-compose.override.yml +0 -14
  96. package/apps/travel-chaos-organizer/docker-compose.yml +0 -128
  97. package/apps/travel-chaos-organizer/frontend/.env.example +0 -13
  98. package/apps/travel-chaos-organizer/frontend/app/(app)/_layout.tsx +0 -80
  99. package/apps/travel-chaos-organizer/frontend/app/(app)/inbox.tsx +0 -166
  100. package/apps/travel-chaos-organizer/frontend/app/(app)/search.tsx +0 -130
  101. package/apps/travel-chaos-organizer/frontend/app/(app)/settings.tsx +0 -381
  102. package/apps/travel-chaos-organizer/frontend/app/(app)/trips/[id].tsx +0 -346
  103. package/apps/travel-chaos-organizer/frontend/app/(app)/trips.tsx +0 -363
  104. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/cancel.tsx +0 -34
  105. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/success.tsx +0 -35
  106. package/apps/travel-chaos-organizer/frontend/app/(auth)/login.tsx +0 -91
  107. package/apps/travel-chaos-organizer/frontend/app/_layout.tsx +0 -71
  108. package/apps/travel-chaos-organizer/frontend/app/auth.tsx +0 -29
  109. package/apps/travel-chaos-organizer/frontend/app/index.tsx +0 -16
  110. package/apps/travel-chaos-organizer/frontend/app/onboarding.tsx +0 -119
  111. package/apps/travel-chaos-organizer/frontend/app.config.ts +0 -54
  112. package/apps/travel-chaos-organizer/frontend/assets/adaptive-icon.svg +0 -66
  113. package/apps/travel-chaos-organizer/frontend/assets/icon.svg +0 -79
  114. package/apps/travel-chaos-organizer/frontend/assets/splash.svg +0 -15
  115. package/apps/travel-chaos-organizer/frontend/components/BottomSheet.tsx +0 -102
  116. package/apps/travel-chaos-organizer/frontend/components/ConfettiCannon.tsx +0 -107
  117. package/apps/travel-chaos-organizer/frontend/components/ConfirmDialog.tsx +0 -109
  118. package/apps/travel-chaos-organizer/frontend/components/ErrorBoundary.tsx +0 -45
  119. package/apps/travel-chaos-organizer/frontend/components/FileUploadButton.tsx +0 -19
  120. package/apps/travel-chaos-organizer/frontend/components/ImportSheet.tsx +0 -155
  121. package/apps/travel-chaos-organizer/frontend/components/OfflineBadge.tsx +0 -55
  122. package/apps/travel-chaos-organizer/frontend/components/QRCodeViewer.tsx +0 -80
  123. package/apps/travel-chaos-organizer/frontend/components/ScoreRing.tsx +0 -39
  124. package/apps/travel-chaos-organizer/frontend/components/Screen.tsx +0 -39
  125. package/apps/travel-chaos-organizer/frontend/components/Skeleton.tsx +0 -70
  126. package/apps/travel-chaos-organizer/frontend/components/SwipeableInboxItem.tsx +0 -121
  127. package/apps/travel-chaos-organizer/frontend/components/SwipeableTimelineItem.tsx +0 -70
  128. package/apps/travel-chaos-organizer/frontend/components/TimelineItem.tsx +0 -73
  129. package/apps/travel-chaos-organizer/frontend/components/TimelineItemDetail.tsx +0 -130
  130. package/apps/travel-chaos-organizer/frontend/components/Toast.tsx +0 -60
  131. package/apps/travel-chaos-organizer/frontend/components/ToastContext.tsx +0 -42
  132. package/apps/travel-chaos-organizer/frontend/components/TripCard.tsx +0 -146
  133. package/apps/travel-chaos-organizer/frontend/components/TripQRModal.tsx +0 -70
  134. package/apps/travel-chaos-organizer/frontend/components/UpgradeBanner.tsx +0 -44
  135. package/apps/travel-chaos-organizer/frontend/eas.json +0 -22
  136. package/apps/travel-chaos-organizer/frontend/hooks/useInbox.ts +0 -24
  137. package/apps/travel-chaos-organizer/frontend/hooks/useNetworkSync.ts +0 -60
  138. package/apps/travel-chaos-organizer/frontend/hooks/useTrips.ts +0 -112
  139. package/apps/travel-chaos-organizer/frontend/lib/api.ts +0 -223
  140. package/apps/travel-chaos-organizer/frontend/lib/auth.ts +0 -93
  141. package/apps/travel-chaos-organizer/frontend/lib/db.ts +0 -86
  142. package/apps/travel-chaos-organizer/frontend/lib/fileCache.ts +0 -54
  143. package/apps/travel-chaos-organizer/frontend/lib/haptics.ts +0 -13
  144. package/apps/travel-chaos-organizer/frontend/lib/notifications.ts +0 -112
  145. package/apps/travel-chaos-organizer/frontend/lib/offlineQueue.ts +0 -145
  146. package/apps/travel-chaos-organizer/frontend/lib/quota.tsx +0 -43
  147. package/apps/travel-chaos-organizer/frontend/lib/sentry.ts +0 -28
  148. package/apps/travel-chaos-organizer/frontend/lib/shareIntent.ts +0 -44
  149. package/apps/travel-chaos-organizer/frontend/package-lock.json +0 -13221
  150. package/apps/travel-chaos-organizer/frontend/package.json +0 -44
  151. package/apps/travel-chaos-organizer/frontend/tsconfig.json +0 -23
  152. package/apps/travel-chaos-organizer/keycloak/realm-export.json +0 -59
  153. package/apps/travel-chaos-organizer/landing/index.html +0 -773
  154. package/apps/travel-chaos-organizer/scripts/backup.sh +0 -14
  155. package/apps/travel-chaos-organizer/scripts/configure-env.sh +0 -82
  156. package/apps/travel-chaos-organizer/scripts/deploy.sh +0 -54
  157. package/apps/travel-chaos-organizer/scripts/health-check.sh +0 -36
  158. package/apps/travel-chaos-organizer/scripts/migrate.sh +0 -7
  159. package/apps/travel-chaos-organizer/scripts/server-setup.sh +0 -85
  160. package/apps/travel-chaos-organizer/scripts/smoke-test.sh +0 -29
  161. package/apps/travel-chaos-organizer/scripts/verify-tracking.sh +0 -71
  162. package/apps/travel-chaos-organizer/telegram-bot/.env.example +0 -3
  163. package/apps/travel-chaos-organizer/telegram-bot/Dockerfile +0 -6
  164. package/apps/travel-chaos-organizer/telegram-bot/bot.py +0 -504
  165. package/apps/travel-chaos-organizer/telegram-bot/requirements.txt +0 -3
  166. package/dist/src/__tests__/auth.test.d.ts +0 -1
  167. package/dist/src/__tests__/auth.test.js +0 -107
  168. package/dist/src/__tests__/auth.test.js.map +0 -1
  169. package/dist/src/__tests__/brain-flow.test.d.ts +0 -12
  170. package/dist/src/__tests__/brain-flow.test.js +0 -1241
  171. package/dist/src/__tests__/brain-flow.test.js.map +0 -1
  172. package/dist/src/__tests__/confidence.test.d.ts +0 -13
  173. package/dist/src/__tests__/confidence.test.js +0 -233
  174. package/dist/src/__tests__/confidence.test.js.map +0 -1
  175. package/dist/src/__tests__/e2e/api-auth.test.d.ts +0 -7
  176. package/dist/src/__tests__/e2e/api-auth.test.js +0 -162
  177. package/dist/src/__tests__/e2e/api-auth.test.js.map +0 -1
  178. package/dist/src/__tests__/e2e/api-health.test.d.ts +0 -7
  179. package/dist/src/__tests__/e2e/api-health.test.js +0 -95
  180. package/dist/src/__tests__/e2e/api-health.test.js.map +0 -1
  181. package/dist/src/__tests__/e2e/cli.test.d.ts +0 -10
  182. package/dist/src/__tests__/e2e/cli.test.js +0 -204
  183. package/dist/src/__tests__/e2e/cli.test.js.map +0 -1
  184. package/dist/src/__tests__/e2e/config.d.ts +0 -35
  185. package/dist/src/__tests__/e2e/config.js +0 -53
  186. package/dist/src/__tests__/e2e/config.js.map +0 -1
  187. package/dist/src/__tests__/e2e/funnel.test.d.ts +0 -13
  188. package/dist/src/__tests__/e2e/funnel.test.js +0 -90
  189. package/dist/src/__tests__/e2e/funnel.test.js.map +0 -1
  190. package/dist/src/__tests__/e2e/mcp-tools.test.d.ts +0 -7
  191. package/dist/src/__tests__/e2e/mcp-tools.test.js +0 -129
  192. package/dist/src/__tests__/e2e/mcp-tools.test.js.map +0 -1
  193. package/dist/src/__tests__/e2e/provisioning.test.d.ts +0 -15
  194. package/dist/src/__tests__/e2e/provisioning.test.js +0 -123
  195. package/dist/src/__tests__/e2e/provisioning.test.js.map +0 -1
  196. package/dist/src/__tests__/handlers-unit.test.d.ts +0 -12
  197. package/dist/src/__tests__/handlers-unit.test.js +0 -505
  198. package/dist/src/__tests__/handlers-unit.test.js.map +0 -1
  199. package/dist/src/__tests__/namespace.test.d.ts +0 -1
  200. package/dist/src/__tests__/namespace.test.js +0 -114
  201. package/dist/src/__tests__/namespace.test.js.map +0 -1
  202. package/dist/src/__tests__/rerank.test.d.ts +0 -1
  203. package/dist/src/__tests__/rerank.test.js +0 -115
  204. package/dist/src/__tests__/rerank.test.js.map +0 -1
  205. package/dist/src/__tests__/utils.test.d.ts +0 -1
  206. package/dist/src/__tests__/utils.test.js +0 -83
  207. package/dist/src/__tests__/utils.test.js.map +0 -1
  208. package/dist/src/bench/cachly-bench.d.ts +0 -35
  209. package/dist/src/bench/cachly-bench.js +0 -182
  210. package/dist/src/bench/cachly-bench.js.map +0 -1
  211. package/dist/src/bench/fixtures.d.ts +0 -37
  212. package/dist/src/bench/fixtures.js +0 -225
  213. package/dist/src/bench/fixtures.js.map +0 -1
  214. package/dist/src/search.test.d.ts +0 -1
  215. package/dist/src/search.test.js +0 -713
  216. package/dist/src/search.test.js.map +0 -1
  217. package/eslint.config.js +0 -16
  218. package/glama.json +0 -36
  219. package/llms.txt +0 -155
  220. package/packages/telegram-notify/SPLIT_PLAN.md +0 -106
  221. package/packages/telegram-notify/client.ts +0 -88
  222. package/packages/telegram-notify/emojis.json +0 -22
  223. package/packages/telegram-notify/pyproject.toml +0 -17
  224. package/packages/telegram-notify/telegram_notify/__init__.py +0 -3
  225. package/packages/telegram-notify/telegram_notify/client.py +0 -87
  226. package/packages/telegram-notify/telegram_notify/emojis.json +0 -22
  227. package/scripts/e2e-run.sh +0 -160
  228. package/scripts/generate-icons.js +0 -28
  229. package/smithery.yaml +0 -45
  230. package/src/__tests__/auth.test.ts +0 -147
  231. package/src/__tests__/brain-flow.test.ts +0 -1472
  232. package/src/__tests__/confidence.test.ts +0 -285
  233. package/src/__tests__/e2e/api-auth.test.ts +0 -182
  234. package/src/__tests__/e2e/api-health.test.ts +0 -103
  235. package/src/__tests__/e2e/cli.test.ts +0 -228
  236. package/src/__tests__/e2e/config.ts +0 -53
  237. package/src/__tests__/e2e/funnel.test.ts +0 -99
  238. package/src/__tests__/e2e/mcp-tools.test.ts +0 -191
  239. package/src/__tests__/e2e/provisioning.test.ts +0 -128
  240. package/src/__tests__/handlers-unit.test.ts +0 -677
  241. package/src/__tests__/namespace.test.ts +0 -126
  242. package/src/__tests__/rerank.test.ts +0 -130
  243. package/src/__tests__/utils.test.ts +0 -81
  244. package/src/auth.ts +0 -46
  245. package/src/bench/cachly-bench.ts +0 -217
  246. package/src/bench/fixtures.ts +0 -256
  247. package/src/ckg.ts +0 -103
  248. package/src/confidence.ts +0 -38
  249. package/src/embeddings.ts +0 -217
  250. package/src/handlers/advanced.ts +0 -647
  251. package/src/handlers/brain.ts +0 -2622
  252. package/src/handlers/cache.ts +0 -633
  253. package/src/handlers/context.ts +0 -222
  254. package/src/handlers/fedbrain.ts +0 -1232
  255. package/src/handlers/instances.ts +0 -180
  256. package/src/handlers/roadmap.ts +0 -201
  257. package/src/handlers/syndicate.ts +0 -460
  258. package/src/handlers/tco.ts +0 -345
  259. package/src/handlers/team.ts +0 -1249
  260. package/src/index.ts +0 -2815
  261. package/src/namespace.ts +0 -128
  262. package/src/notifier.ts +0 -5
  263. package/src/rerank.ts +0 -148
  264. package/src/search.test.ts +0 -848
  265. package/src/search.ts +0 -1781
  266. package/src/tools.ts +0 -2056
  267. package/src/utils.ts +0 -90
  268. package/test-features.cjs +0 -23
  269. package/tsconfig.json +0 -16
  270. package/vitest.config.ts +0 -14
@@ -1,363 +0,0 @@
1
- import { useEffect, useRef, useState } from "react";
2
- import {
3
- ActivityIndicator, Alert, FlatList,
4
- RefreshControl, StyleSheet, Text, TextInput,
5
- TouchableOpacity, View,
6
- } from "react-native";
7
- import { useRouter } from "expo-router";
8
- import { useSafeAreaInsets } from "react-native-safe-area-context";
9
- import { useTrips } from "../../hooks/useTrips";
10
- import { ApiError, Trip, tripsApi } from "../../lib/api";
11
- import TripCard from "../../components/TripCard";
12
- import BottomSheet from "../../components/BottomSheet";
13
- import { TripCardSkeleton } from "../../components/Skeleton";
14
- import { haptics } from "../../lib/haptics";
15
- import { scheduleAllTripReminders } from "../../lib/notifications";
16
- import { useToast } from "../../components/ToastContext";
17
- import { UpgradeBanner } from "../../components/UpgradeBanner";
18
- import { TripQRModal } from "../../components/TripQRModal";
19
- import { getLocalItems } from "../../lib/db";
20
-
21
- function tripScore(tripId: string, hasDates: boolean): number {
22
- const items = getLocalItems(tripId);
23
- const types = items.map(i => i.type?.toLowerCase() ?? "");
24
- const hasFlight = types.some(t => t.includes("flight") || t.includes("flug"));
25
- const hasHotel = types.some(t => t.includes("hotel") || t.includes("accommodation") || t.includes("unterkunft"));
26
- return (hasDates ? 50 : 0) + (hasFlight ? 25 : 0) + (hasHotel ? 25 : 0);
27
- }
28
-
29
- type SheetMode = "create" | "edit";
30
-
31
- export default function TripsScreen() {
32
- const router = useRouter();
33
- const insets = useSafeAreaInsets();
34
- const { showToast } = useToast();
35
- const { trips, loading, error, refresh, createOffline, updateOffline } = useTrips();
36
-
37
- const [sheetVisible, setSheetVisible] = useState(false);
38
- const [sheetMode, setSheetMode] = useState<SheetMode>("create");
39
- const [editingTrip, setEditingTrip] = useState<Trip | null>(null);
40
- const [name, setName] = useState("");
41
- const [startDate, setStartDate] = useState("");
42
- const [endDate, setEndDate] = useState("");
43
- const [startDateError, setStartDateError] = useState<string | null>(null);
44
- const [endDateError, setEndDateError] = useState<string | null>(null);
45
- const [saving, setSaving] = useState(false);
46
- const submittingRef = useRef<boolean>(false);
47
-
48
- const [searchQuery, setSearchQuery] = useState("");
49
- const [searchResults, setSearchResults] = useState<Trip[] | null>(null);
50
- const [searching, setSearching] = useState(false);
51
- const [qrTrip, setQrTrip] = useState<{ id: string; name: string } | null>(null);
52
-
53
- useEffect(() => {
54
- if (trips.length > 0) scheduleAllTripReminders(trips).catch(() => {});
55
- }, [trips]);
56
-
57
- useEffect(() => {
58
- if (!searchQuery.trim()) {
59
- setSearchResults(null);
60
- return;
61
- }
62
- const t = setTimeout(async () => {
63
- setSearching(true);
64
- try {
65
- const results = await tripsApi.search(searchQuery);
66
- setSearchResults(results);
67
- } catch { setSearchResults([]); } finally { setSearching(false); }
68
- }, 300);
69
- return () => clearTimeout(t);
70
- }, [searchQuery]);
71
-
72
- const displayedTrips = searchResults ?? trips;
73
-
74
- function openCreate() {
75
- haptics.tap();
76
- setSheetMode("create");
77
- setEditingTrip(null);
78
- setName("");
79
- setStartDate("");
80
- setEndDate("");
81
- setSheetVisible(true);
82
- }
83
-
84
- function openEdit(trip: Trip) {
85
- setSheetMode("edit");
86
- setEditingTrip(trip);
87
- setName(trip.name);
88
- setStartDate(trip.start_date ?? "");
89
- setEndDate(trip.end_date ?? "");
90
- setSheetVisible(true);
91
- }
92
-
93
- function closeSheet() {
94
- setSheetVisible(false);
95
- setName("");
96
- setStartDate("");
97
- setEndDate("");
98
- setStartDateError(null);
99
- setEndDateError(null);
100
- setEditingTrip(null);
101
- }
102
-
103
- function parsedDate(s: string): string | null {
104
- if (!s.trim()) return null;
105
- // Accept YYYY-MM-DD; pass through if valid, null otherwise
106
- return /^\d{4}-\d{2}-\d{2}$/.test(s.trim()) ? s.trim() : null;
107
- }
108
-
109
- function handleStartDateChange(val: string) {
110
- setStartDate(val);
111
- if (val.length > 0 && !/^\d{4}-\d{2}-\d{2}$/.test(val.trim())) {
112
- setStartDateError("Format: JJJJ-MM-TT (z.B. 2025-06-15)");
113
- } else {
114
- setStartDateError(null);
115
- }
116
- }
117
-
118
- function handleEndDateChange(val: string) {
119
- setEndDate(val);
120
- if (val.length > 0 && !/^\d{4}-\d{2}-\d{2}$/.test(val.trim())) {
121
- setEndDateError("Format: JJJJ-MM-TT (z.B. 2025-06-15)");
122
- } else {
123
- setEndDateError(null);
124
- }
125
- }
126
-
127
- const hasDateError = startDateError !== null || endDateError !== null;
128
-
129
- async function save() {
130
- if (!name.trim()) return;
131
- if (hasDateError) return;
132
- if (submittingRef.current) return;
133
- submittingRef.current = true;
134
- setSaving(true);
135
- try {
136
- const start = parsedDate(startDate);
137
- const end = parsedDate(endDate);
138
- if (sheetMode === "create") {
139
- await createOffline({ name: name.trim(), description: null, start_date: start, end_date: end });
140
- } else if (editingTrip) {
141
- await updateOffline(editingTrip.id, { name: name.trim(), start_date: start, end_date: end });
142
- }
143
- await haptics.success();
144
- closeSheet();
145
- } catch (err) {
146
- await haptics.error();
147
- if (err instanceof ApiError && err.status === 402) {
148
- closeSheet();
149
- showToast("Trip-Limit erreicht – jetzt upgraden 🚀", "warning");
150
- } else {
151
- showToast("Trip konnte nicht erstellt werden", "error");
152
- }
153
- } finally {
154
- setSaving(false);
155
- submittingRef.current = false;
156
- }
157
- }
158
-
159
- if (loading && trips.length === 0) {
160
- return (
161
- <View style={s.container}>
162
- <View style={s.list}>
163
- {[1, 2, 3].map((k) => <TripCardSkeleton key={k} />)}
164
- </View>
165
- </View>
166
- );
167
- }
168
-
169
- return (
170
- <View style={s.container}>
171
- <UpgradeBanner />
172
- {error && <Text style={s.errorBanner}>Offline — zeige gespeicherte Daten</Text>}
173
-
174
- <TextInput
175
- style={searchInputStyle}
176
- placeholder="Trips durchsuchen..."
177
- placeholderTextColor="#3a3a5e"
178
- value={searchQuery}
179
- onChangeText={setSearchQuery}
180
- clearButtonMode="while-editing"
181
- />
182
- {searchQuery.trim() !== "" && searching && (
183
- <ActivityIndicator
184
- style={{ marginBottom: 8 }}
185
- color="#4f46e5"
186
- accessibilityLabel="Suche läuft"
187
- />
188
- )}
189
-
190
- <FlatList
191
- data={displayedTrips}
192
- keyExtractor={(t) => t.id}
193
- contentContainerStyle={[
194
- displayedTrips.length === 0 ? s.emptyContainer : s.list,
195
- { paddingBottom: insets.bottom + 80 },
196
- ]}
197
- refreshControl={<RefreshControl refreshing={loading} onRefresh={refresh} tintColor="#4f46e5" />}
198
- ListEmptyComponent={
199
- <View style={s.empty}>
200
- <Text style={s.emptyIcon}>🗺️</Text>
201
- <Text style={s.emptyTitle}>Noch keine Reisen</Text>
202
- <Text style={s.emptySubtitle}>Erstelle deinen ersten Trip und wirf deine Buchungen rein.</Text>
203
- </View>
204
- }
205
- renderItem={({ item }) => (
206
- <View>
207
- <TripCard
208
- trip={item}
209
- onPress={() => router.push(`/(app)/trips/${item.id}`)}
210
- onLongPress={() => openEdit(item)}
211
- score={tripScore(item.id, !!(item.start_date && item.end_date))}
212
- />
213
- <TouchableOpacity
214
- style={s.qrBtn}
215
- onPress={() => setQrTrip({ id: item.id, name: item.name })}
216
- accessibilityLabel={`QR-Code für ${item.name}`}
217
- accessibilityRole="button"
218
- >
219
- <Text style={s.qrBtnText}>⊞</Text>
220
- </TouchableOpacity>
221
- </View>
222
- )}
223
- />
224
-
225
- {qrTrip && (
226
- <TripQRModal
227
- tripId={qrTrip.id}
228
- tripName={qrTrip.name}
229
- visible={!!qrTrip}
230
- onClose={() => setQrTrip(null)}
231
- />
232
- )}
233
-
234
- <TouchableOpacity
235
- style={[s.fab, { bottom: insets.bottom + 24 }]}
236
- onPress={openCreate}
237
- activeOpacity={0.85}
238
- accessibilityLabel="Neuen Trip erstellen"
239
- accessibilityRole="button"
240
- >
241
- <Text style={s.fabText}>+</Text>
242
- </TouchableOpacity>
243
-
244
- <BottomSheet visible={sheetVisible} onClose={closeSheet} heightFraction={0.65} hasInputs>
245
- <View style={{ flex: 1 }}>
246
- <Text style={s.sheetTitle}>
247
- {sheetMode === "create" ? "Neuer Trip" : "Trip bearbeiten"}
248
- </Text>
249
-
250
- <Text style={s.fieldLabel}>Name *</Text>
251
- <TextInput
252
- style={s.input}
253
- placeholder="z.B. Barcelona Sommer 2025"
254
- placeholderTextColor="#6666aa"
255
- value={name}
256
- onChangeText={setName}
257
- autoFocus
258
- returnKeyType="next"
259
- accessibilityLabel="Trip-Name"
260
- />
261
-
262
- <Text style={s.fieldLabel}>Startdatum</Text>
263
- <TextInput
264
- style={s.input}
265
- placeholder="2025-06-15"
266
- placeholderTextColor="#6666aa"
267
- value={startDate}
268
- onChangeText={handleStartDateChange}
269
- keyboardType="numbers-and-punctuation"
270
- returnKeyType="next"
271
- accessibilityLabel="Startdatum"
272
- />
273
- {startDateError && (
274
- <Text style={{ color: "#ef4444", fontSize: 12 }}>{startDateError}</Text>
275
- )}
276
-
277
- <Text style={s.fieldLabel}>Enddatum</Text>
278
- <TextInput
279
- style={s.input}
280
- placeholder="2025-06-15"
281
- placeholderTextColor="#6666aa"
282
- value={endDate}
283
- onChangeText={handleEndDateChange}
284
- keyboardType="numbers-and-punctuation"
285
- returnKeyType="done"
286
- onSubmitEditing={save}
287
- accessibilityLabel="Enddatum"
288
- />
289
- {endDateError && (
290
- <Text style={{ color: "#ef4444", fontSize: 12 }}>{endDateError}</Text>
291
- )}
292
-
293
- <View style={s.sheetButtons}>
294
- <TouchableOpacity
295
- style={s.cancelBtn}
296
- onPress={closeSheet}
297
- accessibilityLabel="Abbrechen"
298
- accessibilityRole="button"
299
- >
300
- <Text style={s.cancelText}>Abbrechen</Text>
301
- </TouchableOpacity>
302
- <TouchableOpacity
303
- style={[s.saveBtn, (!name.trim() || saving || hasDateError) && s.saveBtnDisabled]}
304
- onPress={save}
305
- disabled={saving || !name.trim() || hasDateError}
306
- accessibilityLabel={sheetMode === "create" ? "Trip erstellen" : "Änderungen speichern"}
307
- accessibilityRole="button"
308
- >
309
- {saving
310
- ? <ActivityIndicator color="#fff" />
311
- : <Text style={s.saveText}>{sheetMode === "create" ? "Erstellen" : "Speichern"}</Text>}
312
- </TouchableOpacity>
313
- </View>
314
- </View>
315
- </BottomSheet>
316
- </View>
317
- );
318
- }
319
-
320
- const searchInputStyle = {
321
- backgroundColor: "#1a1a2e",
322
- borderRadius: 12,
323
- paddingHorizontal: 16,
324
- paddingVertical: 12,
325
- color: "#fff",
326
- fontSize: 15,
327
- marginHorizontal: 16,
328
- marginBottom: 12,
329
- borderWidth: 1,
330
- borderColor: "#2a2a4a",
331
- } as const;
332
-
333
- const s = StyleSheet.create({
334
- container: { flex: 1, backgroundColor: "#0f0f1a" },
335
- list: { padding: 16, gap: 12 },
336
- emptyContainer: { flex: 1 },
337
- errorBanner: { backgroundColor: "#7c3aed33", color: "#c4b5fd", padding: 8, textAlign: "center", fontSize: 13 },
338
- empty: { flex: 1, alignItems: "center", justifyContent: "center", padding: 48 },
339
- emptyIcon: { fontSize: 64, marginBottom: 16 },
340
- emptyTitle: { fontSize: 22, fontWeight: "700", color: "#fff", marginBottom: 8 },
341
- emptySubtitle: { fontSize: 15, color: "#6666aa", textAlign: "center", lineHeight: 22 },
342
- fab: {
343
- position: "absolute", right: 24, width: 56, height: 56,
344
- borderRadius: 28, backgroundColor: "#4f46e5",
345
- alignItems: "center", justifyContent: "center",
346
- elevation: 6, shadowColor: "#4f46e5", shadowOpacity: 0.4, shadowRadius: 12, shadowOffset: { width: 0, height: 4 },
347
- },
348
- fabText: { color: "#fff", fontSize: 28, lineHeight: 32 },
349
- sheetTitle: { fontSize: 20, fontWeight: "700", color: "#fff", marginBottom: 16 },
350
- fieldLabel: { color: "#6666aa", fontSize: 12, fontWeight: "600", letterSpacing: 0.5, marginBottom: 6, marginTop: 4 },
351
- input: {
352
- backgroundColor: "#0f0f1a", borderRadius: 12, padding: 14,
353
- color: "#fff", fontSize: 16, borderWidth: 1, borderColor: "#2a2a4e", marginBottom: 10,
354
- },
355
- sheetButtons: { flexDirection: "row", gap: 12, justifyContent: "flex-end", marginTop: 8 },
356
- cancelBtn: { paddingVertical: 12, paddingHorizontal: 20, borderRadius: 10, borderWidth: 1, borderColor: "#2a2a4e" },
357
- cancelText: { color: "#8888aa", fontSize: 15 },
358
- saveBtn: { paddingVertical: 12, paddingHorizontal: 28, borderRadius: 10, backgroundColor: "#4f46e5", minWidth: 100, alignItems: "center" },
359
- saveBtnDisabled: { opacity: 0.45 },
360
- saveText: { color: "#fff", fontWeight: "600", fontSize: 15 },
361
- qrBtn: { position: "absolute", bottom: 10, right: 14, padding: 6 },
362
- qrBtnText: { color: "#4f46e5", fontSize: 16 },
363
- });
@@ -1,34 +0,0 @@
1
- import { useEffect } from "react";
2
- import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
3
- import { useRouter } from "expo-router";
4
- import { useSafeAreaInsets } from "react-native-safe-area-context";
5
-
6
- export default function UpgradeCancelScreen() {
7
- const router = useRouter();
8
- const insets = useSafeAreaInsets();
9
-
10
- useEffect(() => {
11
- const t = setTimeout(() => router.replace("/(app)/trips"), 4000);
12
- return () => clearTimeout(t);
13
- }, []);
14
-
15
- return (
16
- <View style={[s.container, { paddingTop: insets.top + 32 }]}>
17
- <Text style={s.icon}>←</Text>
18
- <Text style={s.title}>Abgebrochen</Text>
19
- <Text style={s.sub}>Kein Problem — du kannst jederzeit upgraden.</Text>
20
- <TouchableOpacity style={s.btn} onPress={() => router.replace("/(app)/trips")}>
21
- <Text style={s.btnText}>Zurück</Text>
22
- </TouchableOpacity>
23
- </View>
24
- );
25
- }
26
-
27
- const s = StyleSheet.create({
28
- container: { flex: 1, backgroundColor: "#0f0f1a", alignItems: "center", justifyContent: "center", padding: 32 },
29
- icon: { fontSize: 48, color: "#6666aa", marginBottom: 24 },
30
- title: { fontSize: 24, fontWeight: "700", color: "#fff", marginBottom: 12 },
31
- sub: { fontSize: 15, color: "#6666aa", textAlign: "center", lineHeight: 22, marginBottom: 40 },
32
- btn: { backgroundColor: "#2a2a4e", paddingVertical: 14, paddingHorizontal: 40, borderRadius: 12 },
33
- btnText: { color: "#a5b4fc", fontWeight: "600", fontSize: 15 },
34
- });
@@ -1,35 +0,0 @@
1
- import { useEffect } from "react";
2
- import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
3
- import { useRouter, useLocalSearchParams } from "expo-router";
4
- import { useSafeAreaInsets } from "react-native-safe-area-context";
5
-
6
- export default function UpgradeSuccessScreen() {
7
- const router = useRouter();
8
- const insets = useSafeAreaInsets();
9
- const { session_id } = useLocalSearchParams<{ session_id?: string }>();
10
-
11
- useEffect(() => {
12
- const t = setTimeout(() => router.replace("/(app)/trips"), 3000);
13
- return () => clearTimeout(t);
14
- }, []);
15
-
16
- return (
17
- <View style={[s.container, { paddingTop: insets.top + 32 }]}>
18
- <Text style={s.icon}>✦</Text>
19
- <Text style={s.title}>Willkommen bei Pro!</Text>
20
- <Text style={s.sub}>Dein Upgrade war erfolgreich. Alle Features sind jetzt freigeschaltet.</Text>
21
- <TouchableOpacity style={s.btn} onPress={() => router.replace("/(app)/trips")}>
22
- <Text style={s.btnText}>Los geht's</Text>
23
- </TouchableOpacity>
24
- </View>
25
- );
26
- }
27
-
28
- const s = StyleSheet.create({
29
- container: { flex: 1, backgroundColor: "#0f0f1a", alignItems: "center", justifyContent: "center", padding: 32 },
30
- icon: { fontSize: 64, color: "#4f46e5", marginBottom: 24 },
31
- title: { fontSize: 28, fontWeight: "800", color: "#fff", marginBottom: 12, textAlign: "center" },
32
- sub: { fontSize: 16, color: "#6666aa", textAlign: "center", lineHeight: 24, marginBottom: 40 },
33
- btn: { backgroundColor: "#4f46e5", paddingVertical: 16, paddingHorizontal: 48, borderRadius: 14 },
34
- btnText: { color: "#fff", fontWeight: "700", fontSize: 16 },
35
- });
@@ -1,91 +0,0 @@
1
- import { useState } from "react";
2
- import {
3
- ActivityIndicator, StyleSheet, Text, TouchableOpacity, View,
4
- } from "react-native";
5
- import { useSafeAreaInsets } from "react-native-safe-area-context";
6
- import { useRouter } from "expo-router";
7
- import { login } from "../../lib/auth";
8
- import { haptics } from "../../lib/haptics";
9
-
10
- export default function LoginScreen() {
11
- const router = useRouter();
12
- const insets = useSafeAreaInsets();
13
- const [loading, setLoading] = useState(false);
14
- const [error, setError] = useState<string | null>(null);
15
-
16
- async function handleLogin() {
17
- setLoading(true);
18
- setError(null);
19
- await haptics.tap();
20
- try {
21
- const tokens = await login();
22
- if (tokens) {
23
- await haptics.success();
24
- router.replace("/(app)/trips");
25
- } else {
26
- setError("Login abgebrochen.");
27
- }
28
- } catch {
29
- await haptics.error();
30
- setError("Login fehlgeschlagen. Bitte erneut versuchen.");
31
- } finally {
32
- setLoading(false);
33
- }
34
- }
35
-
36
- return (
37
- <View style={[s.container, { paddingTop: insets.top + 16, paddingBottom: insets.bottom + 16 }]}>
38
- {/* Top spacer for visual balance */}
39
- <View style={{ flex: 1 }} />
40
-
41
- <View style={s.hero}>
42
- <Text style={s.logo}>✈️</Text>
43
- <Text style={s.title}>Travel Chaos{"\n"}Organizer</Text>
44
- <Text style={s.subtitle}>Deine Reisen. Endlich organisiert.</Text>
45
- </View>
46
-
47
- <View style={{ flex: 1.5 }} />
48
-
49
- <View style={s.bottom}>
50
- {error && (
51
- <View style={s.errorBox}>
52
- <Text style={s.errorText}>⚠️ {error}</Text>
53
- </View>
54
- )}
55
-
56
- <TouchableOpacity
57
- style={[s.button, loading && s.buttonLoading]}
58
- onPress={handleLogin}
59
- disabled={loading}
60
- activeOpacity={0.85}
61
- >
62
- {loading
63
- ? <ActivityIndicator color="#fff" />
64
- : <Text style={s.buttonText}>Mit Keycloak anmelden</Text>}
65
- </TouchableOpacity>
66
-
67
- <Text style={s.hint}>Dein Account wird von deinem selbst-gehosteten{"\n"}Keycloak verwaltet.</Text>
68
- </View>
69
- </View>
70
- );
71
- }
72
-
73
- const s = StyleSheet.create({
74
- container: { flex: 1, backgroundColor: "#0f0f1a", paddingHorizontal: 32 },
75
- hero: { alignItems: "center", gap: 12 },
76
- logo: { fontSize: 80 },
77
- title: { fontSize: 34, fontWeight: "800", color: "#fff", textAlign: "center", lineHeight: 42, letterSpacing: -0.5 },
78
- subtitle: { fontSize: 16, color: "#6666aa", textAlign: "center", lineHeight: 24 },
79
- bottom: { gap: 16 },
80
- errorBox: { backgroundColor: "#ff6b6b22", borderRadius: 12, padding: 12, borderWidth: 1, borderColor: "#ff6b6b44" },
81
- errorText: { color: "#ff8888", textAlign: "center", fontSize: 14, lineHeight: 20 },
82
- button: {
83
- backgroundColor: "#4f46e5", paddingVertical: 18, paddingHorizontal: 48,
84
- borderRadius: 16, alignItems: "center",
85
- shadowColor: "#4f46e5", shadowOpacity: 0.4, shadowRadius: 16, shadowOffset: { width: 0, height: 6 },
86
- elevation: 8,
87
- },
88
- buttonLoading: { opacity: 0.7 },
89
- buttonText: { color: "#fff", fontSize: 17, fontWeight: "700", letterSpacing: 0.2 },
90
- hint: { color: "#3a3a5e", fontSize: 12, textAlign: "center", lineHeight: 18 },
91
- });
@@ -1,71 +0,0 @@
1
- import { Stack, useRouter } from "expo-router";
2
- import { useEffect } from "react";
3
- import { initDb } from "../lib/db";
4
- import { initQueue } from "../lib/offlineQueue";
5
- import { useNetworkSync } from "../hooks/useNetworkSync";
6
- import { requestNotificationPermission } from "../lib/notifications";
7
- import { onShareIntent, getInitialShareIntent } from "../lib/shareIntent";
8
- import { ToastProvider, useToast } from "../components/ToastContext";
9
- import { QuotaProvider } from "../lib/quota";
10
- import { initSentry } from "../lib/sentry";
11
- import { ErrorBoundary } from "../components/ErrorBoundary";
12
- import { setAuthFailureCallback } from "../lib/auth";
13
-
14
- initSentry();
15
-
16
- function SyncManager() {
17
- const { showToast } = useToast();
18
- useNetworkSync(({ succeeded }) => {
19
- if (succeeded > 0) showToast(`${succeeded} Änderung${succeeded > 1 ? "en" : ""} synchronisiert`, "success");
20
- });
21
- return null;
22
- }
23
-
24
- function ShareIntentHandler() {
25
- const router = useRouter();
26
-
27
- useEffect(() => {
28
- // Handle share intent that launched the app
29
- getInitialShareIntent().then((file) => {
30
- if (file) router.push({ pathname: "/(app)/trips", params: { sharedUri: file.uri, sharedMime: file.mimeType, sharedName: file.name } });
31
- });
32
-
33
- // Handle share intents while app is running
34
- const unsub = onShareIntent((file) => {
35
- router.push({ pathname: "/(app)/trips", params: { sharedUri: file.uri, sharedMime: file.mimeType, sharedName: file.name } });
36
- });
37
- return unsub;
38
- }, []);
39
-
40
- return null;
41
- }
42
-
43
- export default function RootLayout() {
44
- const router = useRouter();
45
-
46
- useEffect(() => {
47
- initDb();
48
- initQueue();
49
- requestNotificationPermission().catch(() => {});
50
- setAuthFailureCallback(() => {
51
- router.replace('/(auth)/login');
52
- });
53
- }, []);
54
-
55
- return (
56
- <ErrorBoundary>
57
- <ToastProvider>
58
- <QuotaProvider>
59
- <SyncManager />
60
- <ShareIntentHandler />
61
- <Stack screenOptions={{ headerShown: false }}>
62
- <Stack.Screen name="index" />
63
- <Stack.Screen name="auth" />
64
- <Stack.Screen name="(auth)" />
65
- <Stack.Screen name="(app)" />
66
- </Stack>
67
- </QuotaProvider>
68
- </ToastProvider>
69
- </ErrorBoundary>
70
- );
71
- }
@@ -1,29 +0,0 @@
1
- import * as WebBrowser from "expo-web-browser";
2
- import { useEffect } from "react";
3
- import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
4
-
5
- // This screen is the OAuth callback target.
6
- // When Keycloak redirects to exp://u.expo.dev/.../~/auth (or tco://auth in production),
7
- // Expo Router renders this component. maybeCompleteAuthSession() signals the
8
- // in-app browser opened by expo-auth-session to close and pass the auth code
9
- // back to the login() call that initiated the flow.
10
- WebBrowser.maybeCompleteAuthSession();
11
-
12
- export default function AuthCallbackScreen() {
13
- useEffect(() => {
14
- // maybeCompleteAuthSession already handles closing the browser window.
15
- // This screen should never be visible to the user for more than a flash.
16
- }, []);
17
-
18
- return (
19
- <View style={s.container}>
20
- <ActivityIndicator size="large" color="#4f46e5" />
21
- <Text style={s.text}>Anmeldung wird abgeschlossen…</Text>
22
- </View>
23
- );
24
- }
25
-
26
- const s = StyleSheet.create({
27
- container: { flex: 1, backgroundColor: "#0f0f1a", alignItems: "center", justifyContent: "center", gap: 16 },
28
- text: { color: "#6666aa", fontSize: 15 },
29
- });
@@ -1,16 +0,0 @@
1
- import { useEffect } from "react";
2
- import { useRouter } from "expo-router";
3
- import { getAccessToken } from "../lib/auth";
4
- import { isOnboardingDone } from "./onboarding";
5
-
6
- export default function Root() {
7
- const router = useRouter();
8
- useEffect(() => {
9
- Promise.all([getAccessToken(), isOnboardingDone()]).then(([token, onboarded]) => {
10
- if (!onboarded) router.replace("/onboarding");
11
- else if (token) router.replace("/(app)/trips");
12
- else router.replace("/(auth)/login");
13
- });
14
- }, []);
15
- return null;
16
- }