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

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,33 +395,141 @@ export default function AuthScreen() {
if (isLoggedIn && currentUser) {
return (
<SafeAreaView style={styles.container}>
<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}>
<Text style={styles.infoLabel}>E-Mail:</Text>
<Text style={styles.infoValue}>{currentUser.email}</Text>
<Text style={styles.infoLabel}>Mitglied seit:</Text>
<Text style={styles.infoValue}>
{new Date(currentUser.createdAt).toLocaleDateString("de-DE")}
<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}>Wohnort:</Text>
<Text style={styles.infoValue}>
{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}
>
<Text style={styles.logoutButtonText}>Abmelden</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.submitButton, { marginTop: 20 }]}
onPress={() => router.push("/calendar")}
>
<Text style={styles.submitButtonText}>Zum Kalender</Text>
</TouchableOpacity>
</View>
<TouchableOpacity style={styles.logoutButton} onPress={handleLogout}>
<Text style={styles.logoutButtonText}>Abmelden</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.submitButton, { marginTop: 20 }]}
onPress={() => router.push("/calendar")}
>
<Text style={styles.submitButtonText}>Zum Kalender</Text>
</TouchableOpacity>
</View>
</ScrollView>
</SafeAreaView>
);
}
@@ -351,111 +540,156 @@ export default function AuthScreen() {
style={styles.keyboardView}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<View style={styles.authContainer}>
<Text style={styles.title}>
{authMode === "login" ? "Anmelden" : "Registrieren"}
</Text>
<ScrollView
contentContainerStyle={styles.scrollContent}
keyboardShouldPersistTaps="handled"
>
<View style={styles.authContainer}>
<Text style={styles.title}>
{authMode === "login" ? "Anmelden" : "Registrieren"}
</Text>
{authMode === "register" && (
<>
<TextInput
style={styles.input}
placeholder="Vorname"
value={firstName}
onChangeText={setFirstName}
autoCapitalize="words"
/>
{authMode === "register" && (
<>
<TextInput
style={styles.input}
placeholder="Vorname"
value={firstName}
onChangeText={setFirstName}
autoCapitalize="words"
/>
<TextInput
style={styles.input}
placeholder="Nachname"
value={lastName}
onChangeText={setLastName}
autoCapitalize="words"
/>
<TextInput
style={styles.input}
placeholder="Nachname"
value={lastName}
onChangeText={setLastName}
autoCapitalize="words"
/>
<TextInput
style={styles.input}
placeholder="Wohnort"
value={residence}
onChangeText={setResidence}
/>
<TextInput
style={styles.input}
placeholder="Arbeitsplatz / Uni / Schule"
value={workplace}
onChangeText={setWorkplace}
/>
<TouchableOpacity
style={[
styles.submitButton,
{ backgroundColor: "#34C759", marginBottom: 10 },
]}
onPress={handleUseLocation}
>
<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,
{ backgroundColor: "#34C759", marginBottom: 10 },
]}
onPress={handleUseLocation}
>
<Text style={styles.submitButtonText}>
Aktuellen Standort verwenden
</Text>
</TouchableOpacity>
</>
)}
<TextInput
style={styles.input}
placeholder="E-Mail"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="Passwort"
value={password}
onChangeText={setPassword}
secureTextEntry
autoCapitalize="none"
/>
<TouchableOpacity
style={styles.submitButton}
onPress={authMode === "login" ? handleLogin : handleRegister}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="white" />
) : (
<Text style={styles.submitButtonText}>
Aktuellen Standort verwenden
{authMode === "login" ? "Anmelden" : "Registrieren"}
</Text>
)}
</TouchableOpacity>
{biometricSupported && authMode === "login" && (
<TouchableOpacity
style={styles.biometricButton}
onPress={handleBiometricAuth}
>
<Text style={styles.biometricButtonText}>
Mit Face ID / Touch ID anmelden
</Text>
</TouchableOpacity>
</>
)}
<TextInput
style={styles.input}
placeholder="E-Mail"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="Passwort"
value={password}
onChangeText={setPassword}
secureTextEntry
autoCapitalize="none"
/>
<TouchableOpacity
style={styles.submitButton}
onPress={authMode === "login" ? handleLogin : handleRegister}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="white" />
) : (
<Text style={styles.submitButtonText}>
{authMode === "login" ? "Anmelden" : "Registrieren"}
</Text>
)}
</TouchableOpacity>
{biometricSupported && authMode === "login" && (
<TouchableOpacity
style={styles.biometricButton}
onPress={handleBiometricAuth}
style={styles.switchButton}
onPress={() =>
setAuthMode(authMode === "login" ? "register" : "login")
}
>
<Text style={styles.biometricButtonText}>
Mit Face ID / Touch ID anmelden
<Text style={styles.switchButtonText}>
{authMode === "login"
? "Noch kein Konto? Registrieren"
: "Bereits ein Konto? Anmelden"}
</Text>
</TouchableOpacity>
)}
<TouchableOpacity
style={styles.switchButton}
onPress={() =>
setAuthMode(authMode === "login" ? "register" : "login")
}
>
<Text style={styles.switchButtonText}>
{authMode === "login"
? "Noch kein Konto? Registrieren"
: "Bereits ein Konto? Anmelden"}
</Text>
</TouchableOpacity>
</View>
</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,