docs: README.md and docs

This commit is contained in:
Ismail Ali
2025-07-15 22:51:37 +02:00
parent 007dedb09a
commit 209b712175
5 changed files with 680 additions and 156 deletions

140
README.md
View File

@@ -1,50 +1,118 @@
# Welcome to your Expo app 👋
# 📱 Heval Light Personal Assistant App
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
**Heval Light** ist eine persönliche Assistenten-App für iOS/Android, entwickelt mit **React Native + Expo**.
Die App soll langfristig ein smarter Begleiter für Studierende, Arbeitnehmer und Privatpersonen werden mit **Kalender-, Erinnerungs- und KI-Funktionen**.
## Get started
---
1. Install dependencies
## ✨ Features (aktueller Stand)
```bash
npm install
```
**Benutzerregistrierung & Login**
2. Start the app
- Vorname, Nachname, E-Mail, Passwort
- Speicherung in **SQLite**
- **Biometrische Anmeldung** via Face ID/Touch ID
```bash
npx expo start
```
**Kalender & Erinnerungen**
In the output, you'll find options to open the app in a
- Zugriff auf **iOS Kalender/Erinnerungen** via `expo-calendar`
- Speicherung von Events in **lokaler SQLite-Datenbank**
- **Modal-Formular** für manuelles Hinzufügen von Terminen
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
**Lokale Speicherung & Sessions**
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
- Benutzer-Session wird sicher in **AsyncStorage** gehalten
- Passwort & Benutzerdaten lokal in SQLite gespeichert
## Get a fresh project
**Zukunftsorientierte Funktionen (geplant)**
When you're ready, run:
- **GPS-Standort** automatisch übernehmen (Wohnort, Arbeitsplatz/Uni)
- **Sprachbasierte Eingabe** (Speech-to-Text für Erinnerungen)
- **KI-Assistent (ChatGPT)** zur Organisation
- **Push-Notifications** für Erinnerungen
---
## 🗺️ Roadmap
- [ ] **Registrierung erweitern**: Wohnort/Arbeitsort per GPS automatisch erkennen
- [ ] **Speech-to-Text** für neue Erinnerungen/Termine
- [ ] **Push-Notifications** für wichtige Events
- [ ] **AI-Integration** (ChatGPT für intelligente Terminplanung)
- [ ] **Mehrsprachigkeit** (Deutsch/Englisch)
---
## 🛠️ Technologie-Stack
- **React Native + Expo (Managed Workflow)**
- **SQLite** für lokale Benutzer- & Eventdaten
- **AsyncStorage** für Sitzungen
- **expo-local-authentication** für Face ID/Touch ID
- **expo-calendar** für Kalender- und Erinnerungszugriff
- **expo-location** (geplant) für GPS-Standort
- **react-native-voice** (geplant) für Spracheingabe
---
## 📂 Projektstruktur
app/
├─ index.tsx # AuthScreen: Registrierung, Login, FaceID
├─ calendar.tsx # Kalender & Erinnerungen, SQLite-Speicherung
├─ (geplant) reminders.tsx # Sprachbasierte Erinnerungen
└─ ...
yaml
Copy
Edit
---
## 🚀 Installation & Start
### Voraussetzungen
- Node.js (>= 18)
- Expo CLI
- Git
### Setup
```bash
npm run reset-project
# Repository klonen
git clone <repo-url>
cd heval-light
# Abhängigkeiten installieren
npm install
# App starten
npx expo start
Auf iOS: Expo Go App installieren und QR-Code scannen
Auf Android: ebenfalls Expo Go oder Emulator verwenden
🔐 Sicherheit
Benutzer- und Kalenderdaten bleiben lokal auf dem Gerät
Kein externer Server nötig
Geplant: Verschlüsselte Speicherung (SecureStore)
🧭 Ziel der App
Heval Light soll zu einem intelligenten Assistenten werden, der dir hilft bei:
Kalender- & Erinnerungsverwaltung
Standortbasierten Aufgaben (Wohnort, Arbeitsplatz, Uni)
Sprach- und KI-gestützter Organisation
🤝 Mitwirken
Dieses Projekt ist noch in der aktiven Entwicklung.
Ideen, Feedback oder Pull Requests sind willkommen!
📜 Lizenz
MIT License frei nutzbar für private & kommerzielle Zwecke.
```
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
## Learn more
To learn more about developing your project with Expo, look at the following resources:
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
## Join the community
Join our community of developers creating universal apps.
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.

View File

@@ -1,4 +1,5 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import { Picker } from "@react-native-picker/picker";
import * as LocalAuthentication from "expo-local-authentication";
import * as Location from "expo-location";
import { useRouter } from "expo-router";
@@ -10,6 +11,7 @@ import {
KeyboardAvoidingView,
Platform,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
TextInput,
@@ -24,7 +26,12 @@ interface User {
firstName: string;
lastName: string;
residence: string;
residenceStreet: string;
residenceNumber: string;
workplace: string;
workplaceStreet: string;
workplaceNumber: string;
transportMode: string;
latitude: string;
longitude: string;
createdAt: string;
@@ -43,11 +50,18 @@ export default function AuthScreen() {
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [residence, setResidence] = useState("");
const [residenceStreet, setResidenceStreet] = useState("");
const [residenceNumber, setResidenceNumber] = useState("");
const [workplace, setWorkplace] = useState("");
const [workplaceStreet, setWorkplaceStreet] = useState("");
const [workplaceNumber, setWorkplaceNumber] = useState("");
const [latitude, setLatitude] = useState<string | null>(null);
const [longitude, setLongitude] = useState<string | null>(null);
const [biometricSupported, setBiometricSupported] = useState(false);
const [showCalendar, setShowCalendar] = useState(false);
const [transportMode, setTransportMode] = useState("auto");
const [editMode, setEditMode] = useState(false);
const [showOnlyName, setShowOnlyName] = useState(false);
// Database
const [db, setDb] = useState<SQLite.SQLiteDatabase | null>(null);
@@ -71,7 +85,12 @@ export default function AuthScreen() {
lastName TEXT NOT NULL,
password TEXT NOT NULL,
residence TEXT,
residenceStreet TEXT,
residenceNumber TEXT,
workplace TEXT,
workplaceStreet TEXT,
workplaceNumber TEXT,
transportMode TEXT,
latitude TEXT,
longitude TEXT,
createdAt TEXT NOT NULL
@@ -104,7 +123,11 @@ export default function AuthScreen() {
!firstName ||
!lastName ||
!residence ||
!workplace
!residenceStreet ||
!residenceNumber ||
!workplace ||
!workplaceStreet ||
!workplaceNumber
) {
Alert.alert("Fehler", "Bitte füllen Sie alle Felder aus.");
return;
@@ -125,14 +148,19 @@ export default function AuthScreen() {
const createdAt = new Date().toISOString();
const result = await db.runAsync(
"INSERT INTO users (email, firstName, lastName, password, residence, workplace, latitude, longitude, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
"INSERT INTO users (email, firstName, lastName, password, residence, residenceStreet, residenceNumber, workplace, workplaceStreet, workplaceNumber, transportMode, latitude, longitude, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
[
email,
firstName,
lastName,
password,
residence,
residenceStreet,
residenceNumber,
workplace,
workplaceStreet,
workplaceNumber,
transportMode,
latitude ?? "",
longitude ?? "",
createdAt,
@@ -145,7 +173,12 @@ export default function AuthScreen() {
firstName,
lastName,
residence,
residenceStreet,
residenceNumber,
workplace,
workplaceStreet,
workplaceNumber,
transportMode,
latitude: latitude ?? "",
longitude: longitude ?? "",
createdAt,
@@ -201,6 +234,11 @@ export default function AuthScreen() {
latitude: result.latitude,
longitude: result.longitude,
createdAt: result.createdAt,
residenceStreet: result.residenceStreet,
residenceNumber: result.residenceNumber,
workplaceStreet: result.workplaceStreet,
workplaceNumber: result.workplaceNumber,
transportMode: result.transportMode,
};
// Save session
@@ -245,6 +283,11 @@ export default function AuthScreen() {
latitude: user.latitude,
longitude: user.longitude,
createdAt: user.createdAt,
residenceStreet: user.residenceStreet,
residenceNumber: user.residenceNumber,
workplaceStreet: user.workplaceStreet,
workplaceNumber: user.workplaceNumber,
transportMode: user.transportMode,
};
await AsyncStorage.setItem(
@@ -300,6 +343,44 @@ export default function AuthScreen() {
}
};
const handleSaveEdit = async () => {
if (!currentUser || !db) return;
await db.runAsync(
`UPDATE users SET firstName=?, lastName=?, residence=?, residenceStreet=?, residenceNumber=?, workplace=?, workplaceStreet=?, workplaceNumber=?, transportMode=?, latitude=?, longitude=? WHERE id=?`,
[
firstName,
lastName,
residence,
residenceStreet,
residenceNumber,
workplace,
workplaceStreet,
workplaceNumber,
transportMode,
latitude ?? "",
longitude ?? "",
currentUser.id,
]
);
setEditMode(false);
setShowOnlyName(true);
setCurrentUser({
...currentUser,
firstName,
lastName,
residence,
residenceStreet,
residenceNumber,
workplace,
workplaceStreet,
workplaceNumber,
transportMode,
latitude: latitude ?? "",
longitude: longitude ?? "",
});
Alert.alert("Erfolg", "Daten gespeichert.");
};
if (loading) {
return (
<SafeAreaView style={styles.container}>
@@ -314,26 +395,133 @@ export default function AuthScreen() {
if (isLoggedIn && currentUser) {
return (
<SafeAreaView style={styles.container}>
<ScrollView
contentContainerStyle={styles.scrollContent}
keyboardShouldPersistTaps="handled"
>
<View style={styles.welcomeContainer}>
<Text style={styles.welcomeTitle}>Willkommen zurück!</Text>
<Text style={styles.userName}>
{currentUser.firstName} {currentUser.lastName}
</Text>
<View style={styles.userInfo}>
{showOnlyName ? (
<></>
) : editMode ? (
<>
<TextInput
style={styles.input}
value={firstName}
onChangeText={setFirstName}
placeholder="Vorname"
/>
<TextInput
style={styles.input}
value={lastName}
onChangeText={setLastName}
placeholder="Nachname"
/>
<TextInput
style={styles.input}
value={residenceStreet}
onChangeText={setResidenceStreet}
placeholder="Straße (Wohnort)"
/>
<TextInput
style={styles.input}
value={residenceNumber}
onChangeText={setResidenceNumber}
placeholder="Hausnummer (Wohnort)"
/>
<TextInput
style={styles.input}
value={residence}
onChangeText={setResidence}
placeholder="Wohnort"
/>
<TextInput
style={styles.input}
value={workplaceStreet}
onChangeText={setWorkplaceStreet}
placeholder="Straße (Arbeit/Schule/Uni)"
/>
<TextInput
style={styles.input}
value={workplaceNumber}
onChangeText={setWorkplaceNumber}
placeholder="Hausnummer (Arbeit/Schule/Uni)"
/>
<TextInput
style={styles.input}
value={workplace}
onChangeText={setWorkplace}
placeholder="Arbeitsplatz / Uni / Schule"
/>
<View style={{ marginBottom: 15 }}>
<Text style={{ marginBottom: 5, fontWeight: "600" }}>
Verkehrsmittel
</Text>
<Picker
selectedValue={transportMode}
onValueChange={setTransportMode}
style={{ backgroundColor: "#fff" }}
>
<Picker.Item label="Auto" value="auto" />
<Picker.Item label="Zug" value="zug" />
<Picker.Item label="Bus" value="bus" />
<Picker.Item label="Fahrrad" value="fahrrad" />
<Picker.Item label="Zu Fuß" value="zufuss" />
</Picker>
</View>
<TouchableOpacity
style={[styles.submitButton, { marginBottom: 10 }]}
onPress={handleSaveEdit}
>
<Text style={styles.submitButtonText}>Speichern</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.logoutButton}
onPress={() => setEditMode(false)}
>
<Text style={styles.logoutButtonText}>Abbrechen</Text>
</TouchableOpacity>
</>
) : (
<>
<Text style={styles.infoLabel}>E-Mail:</Text>
<Text style={styles.infoValue}>{currentUser.email}</Text>
<Text style={styles.infoLabel}>Mitglied seit:</Text>
<Text style={styles.infoLabel}>Wohnort:</Text>
<Text style={styles.infoValue}>
{new Date(currentUser.createdAt).toLocaleDateString("de-DE")}
{currentUser.residenceStreet} {currentUser.residenceNumber},{" "}
{currentUser.residence}
</Text>
<Text style={styles.infoLabel}>Arbeit/Schule/Uni:</Text>
<Text style={styles.infoValue}>
{currentUser.workplaceStreet} {currentUser.workplaceNumber},{" "}
{currentUser.workplace}
</Text>
<Text style={styles.infoLabel}>Verkehrsmittel:</Text>
<Text style={styles.infoValue}>
{currentUser.transportMode}
</Text>
<TouchableOpacity
style={[
styles.submitButton,
{ marginTop: 10, marginBottom: 10 },
]}
onPress={() => setEditMode(true)}
>
<Text style={styles.submitButtonText}>Bearbeiten</Text>
</TouchableOpacity>
</>
)}
</View>
<TouchableOpacity style={styles.logoutButton} onPress={handleLogout}>
<TouchableOpacity
style={styles.logoutButton}
onPress={handleLogout}
>
<Text style={styles.logoutButtonText}>Abmelden</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.submitButton, { marginTop: 20 }]}
onPress={() => router.push("/calendar")}
@@ -341,6 +529,7 @@ export default function AuthScreen() {
<Text style={styles.submitButtonText}>Zum Kalender</Text>
</TouchableOpacity>
</View>
</ScrollView>
</SafeAreaView>
);
}
@@ -350,6 +539,10 @@ export default function AuthScreen() {
<KeyboardAvoidingView
style={styles.keyboardView}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<ScrollView
contentContainerStyle={styles.scrollContent}
keyboardShouldPersistTaps="handled"
>
<View style={styles.authContainer}>
<Text style={styles.title}>
@@ -374,18 +567,58 @@ export default function AuthScreen() {
autoCapitalize="words"
/>
<TextInput
style={styles.input}
placeholder="Straße (Wohnort)"
value={residenceStreet}
onChangeText={setResidenceStreet}
/>
<TextInput
style={styles.input}
placeholder="Hausnummer (Wohnort)"
value={residenceNumber}
onChangeText={setResidenceNumber}
/>
<TextInput
style={styles.input}
placeholder="Wohnort"
value={residence}
onChangeText={setResidence}
/>
<TextInput
style={styles.input}
placeholder="Straße (Arbeit/Schule/Uni)"
value={workplaceStreet}
onChangeText={setWorkplaceStreet}
/>
<TextInput
style={styles.input}
placeholder="Hausnummer (Arbeit/Schule/Uni)"
value={workplaceNumber}
onChangeText={setWorkplaceNumber}
/>
<TextInput
style={styles.input}
placeholder="Arbeitsplatz / Uni / Schule"
value={workplace}
onChangeText={setWorkplace}
/>
<View style={{ marginBottom: 15 }}>
<Text style={{ marginBottom: 5, fontWeight: "600" }}>
Verkehrsmittel
</Text>
<Picker
selectedValue={transportMode}
onValueChange={setTransportMode}
style={{ backgroundColor: "#fff" }}
>
<Picker.Item label="Auto" value="auto" />
<Picker.Item label="Zug" value="zug" />
<Picker.Item label="Bus" value="bus" />
<Picker.Item label="Fahrrad" value="fahrrad" />
<Picker.Item label="Zu Fuß" value="zufuss" />
</Picker>
</View>
<TouchableOpacity
style={[
styles.submitButton,
@@ -456,6 +689,7 @@ export default function AuthScreen() {
</Text>
</TouchableOpacity>
</View>
</ScrollView>
</KeyboardAvoidingView>
</SafeAreaView>
);
@@ -469,10 +703,16 @@ const styles = StyleSheet.create({
keyboardView: {
flex: 1,
},
scrollContent: {
flexGrow: 1,
justifyContent: "center",
paddingVertical: 30,
},
authContainer: {
flex: 1,
justifyContent: "center",
paddingHorizontal: 30,
paddingBottom: 30,
},
loadingContainer: {
flex: 1,

140
docs/requirements.md Normal file
View File

@@ -0,0 +1,140 @@
# 📄 Heval Light Requirements Document
## 1. Überblick
**Heval Light** ist eine persönliche Assistenten-App für iOS und Android, die Nutzern hilft, ihren Alltag zu organisieren.
Die App bietet **Kalender- und Erinnerungsfunktionen**, erkennt **Wohn- und Arbeitsorte**, und soll langfristig durch **Sprachsteuerung und KI-Integration** zu einem intelligenten Begleiter werden.
---
## 2. Zielgruppe
- **Studierende** → Verwaltung von Uni-Terminen, Vorlesungen, Deadlines
- **Arbeitnehmer:innen** → Meetings, Arbeitswege, Standortinformationen
- **Privatpersonen** → Erinnerungen, Einkaufslisten, persönliche Termine
---
## 3. Ziele der App
- Einen **zentralen Assistenten** für Termine, Erinnerungen & Standortinfos bieten
- Daten **lokal & sicher** speichern (kein Zwang zu Cloud-Diensten)
- Einfacher Login & **biometrische Authentifizierung**
- Integration von **Standort, Spracheingabe & KI-Unterstützung**
---
## 4. Benutzerrollen
1. **Registrierter Nutzer**
- Kann ein Profil erstellen (Name, E-Mail, Wohnort, Arbeitsplatz/Schule/Uni)
- Kann Kalender & Erinnerungen verwalten
- Kann optional **Sprachbefehle** nutzen
- Kann Face ID / Touch ID für Login verwenden
2. **Später: KI-Assistent / Hintergrundlogik**
- Erkennt wiederkehrende Muster (z.B. Standort, Termine)
- Schlägt automatische Aktionen vor
---
## 5. Muss-Features (Minimum Viable Product)
-**Registrierung & Login**
- Vorname, Nachname, E-Mail, Passwort
- Speicherung in **SQLite**
- Biometrische Authentifizierung (Face ID/Touch ID)
-**Kalender & Erinnerungen**
- Zugriff auf **iOS Kalender/Erinnerungen**
- Manuelles Hinzufügen von Terminen in SQLite
- Anzeige in einer FlatList
-**Benutzer-Session**
- Speicherung via **AsyncStorage**
---
## 6. Soll-/Kann-Features (geplante Erweiterungen)
- **GPS-Integration (expo-location)**
- Automatische Erkennung von Wohnort & Arbeitsplatz
- Reverse Geocoding → Adresse automatisch eintragen
- **Sprachsteuerung (react-native-voice)**
- Termine & Erinnerungen per Sprache hinzufügen
- Sprachausgabe für wichtige Benachrichtigungen
- **Push-Notifications**
- Erinnerungen & Kalenderbenachrichtigungen
- **AI-Integration (OpenAI API)**
- Chatbot für Terminorganisation & Erinnerungen
- Intelligente Vorschläge (z.B. „Soll ich dich morgen an den Termin erinnern?“)
- **Mehrsprachigkeit (Deutsch/Englisch)**
---
## 7. Use Cases
### UC1: Registrierung mit Standort
1. Nutzer öffnet die App und wählt _Registrieren_
2. Gibt Name, E-Mail, Passwort ein
3. Optional → Klickt auf **„Aktuellen Standort übernehmen“** → App füllt Wohnort automatisch
4. Optional → Gibt Arbeitsplatz/Uni/Schule ein
5. Daten werden in SQLite gespeichert
### UC2: Termin/Erinnerung hinzufügen
1. Nutzer öffnet den Kalender
2. Klickt auf „+“ → Modal öffnet sich
3. Trägt Titel, Start-/Endzeit ein → Speichern in SQLite
4. Termin wird in Liste angezeigt
### UC3: Termin per Spracheingabe
1. Nutzer klickt auf Mikrofon-Icon
2. Spricht „Meeting mit Max am Freitag 15 Uhr“
3. App erkennt Text & speichert automatisch als neuen Termin
### UC4: Login mit Face ID
1. Nutzer öffnet die App
2. App fragt automatisch Face ID/Touch ID an
3. Nach erfolgreicher Authentifizierung wird der Nutzer direkt eingeloggt
---
## 8. Technische Anforderungen
- **Framework:** React Native + Expo (Managed Workflow)
- **Datenbank:** SQLite (lokal)
- **Geräte-Features:**
- Face ID / Touch ID → `expo-local-authentication`
- Kalenderzugriff → `expo-calendar`
- Standort → `expo-location`
- Spracheingabe → `react-native-voice`
- **Speicher:** AsyncStorage für Sessions
- **Geplant:** KI-Integration über OpenAI API
---
## 9. Sicherheits- und Datenschutzaspekte
- Alle Daten bleiben **lokal auf dem Gerät**
- Keine Cloud-Verbindung notwendig
- Geplant: Verschlüsselte Speicherung für sensible Daten (SecureStore)
---
## 10. Erfolgskriterien
- Nutzer kann ohne Internet registrieren & anmelden
- Termine & Erinnerungen lassen sich manuell hinzufügen
- Standort kann automatisch erfasst werden
- Biometrische Anmeldung funktioniert zuverlässig
- Später: Spracheingabe & KI-Assistent integrierbar
---

View File

@@ -0,0 +1,23 @@
# 📊 Heval Light Sequence Diagram: Registrierung mit Standort
```mermaid
sequenceDiagram
participant U as Benutzer
participant App as Heval Light App
participant GPS as GPS/Location API
participant DB as SQLite
U->>App: Öffnet Registrierungsformular
U->>App: Klickt "Aktuellen Standort übernehmen"
App->>GPS: Anfrage Standortberechtigung
GPS-->>App: Liefert Koordinaten & Adresse
App->>U: Zeigt automatisch erkannten Wohnort
U->>App: Gibt Name, E-Mail, Passwort, Arbeitsplatz ein
U->>App: Klickt "Registrieren"
App->>DB: Speichert Benutzer + Wohnort/Arbeitsort
DB-->>App: Bestätigung
App->>U: Zeigt "Registrierung erfolgreich"
```

53
docs/usecase-diagram.md Normal file
View File

@@ -0,0 +1,53 @@
# 📊 Heval Light Use Case Diagram
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart LR
%% Akteure %%
user((👤 Benutzer))
%% Systemcontainer %%
subgraph System [Heval Light App]
direction TB
UC1([Registrieren mit Name, E-Mail, Passwort])
UC2([Wohnort automatisch per GPS übernehmen])
UC3([Arbeitsort/Schule/Uni eintragen])
UC4([Anmelden mit Face ID / Touch ID])
UC5([Kalender-Events aus iPhone lesen])
UC6([Neue Erinnerung/Termin speichern])
UC7([Erinnerung per Sprache hinzufügen])
UC8([Push-Notification senden])
UC9([Später: KI-Assistent nutzen])
end
%% Externe Systeme %%
gps[(📍 GPS / Location API)]
sqlite[(🗄 SQLite Datenbank)]
iosCal[(📅 iOS Calendar API)]
biometrics[(🔐 Face ID / Touch ID)]
voiceApi[(🎤 Speech-to-Text API)]
pushApi[(🔔 Push Notification Service)]
aiApi[(🤖 KI-API OpenAI)]
%% Beziehungen Benutzer -> Use Cases %%
user --> UC1
user --> UC2
user --> UC3
user --> UC4
user --> UC5
user --> UC6
user --> UC7
user --> UC8
user --> UC9
%% Beziehungen Use Cases -> Externe Systeme %%
UC2 --> gps
UC4 --> biometrics
UC5 --> iosCal
UC6 --> sqlite
UC7 --> voiceApi
UC8 --> pushApi
UC9 --> aiApi
```