wip: tracking plan
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
+16
-3
@@ -1,13 +1,15 @@
|
||||
/**
|
||||
* App.tsx – root component for the DocuElevate mobile app.
|
||||
*
|
||||
* Wraps the entire app in the AuthProvider and renders either the login
|
||||
* screen (unauthenticated) or the main tab navigator (authenticated).
|
||||
* Wraps the entire app in the AuthProvider and renders either the
|
||||
* authenticated tab navigator or an unauthenticated auth stack that takes
|
||||
* the user through WelcomeScreen → LoginScreen.
|
||||
* Push notification registration is handled by the usePushNotifications hook.
|
||||
*/
|
||||
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import React from "react";
|
||||
import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
|
||||
@@ -18,8 +20,19 @@ import FilesScreen from "./src/screens/FilesScreen";
|
||||
import LoginScreen from "./src/screens/LoginScreen";
|
||||
import ProfileScreen from "./src/screens/ProfileScreen";
|
||||
import UploadScreen from "./src/screens/UploadScreen";
|
||||
import WelcomeScreen, { type AuthStackParamList } from "./src/screens/WelcomeScreen";
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
||||
const AuthStack = createNativeStackNavigator<AuthStackParamList>();
|
||||
|
||||
function AuthNavigator() {
|
||||
return (
|
||||
<AuthStack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<AuthStack.Screen name="Welcome" component={WelcomeScreen} />
|
||||
<AuthStack.Screen name="Login" component={LoginScreen} />
|
||||
</AuthStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function TabNavigator() {
|
||||
const { isAuthenticated } = useAuth();
|
||||
@@ -97,7 +110,7 @@ function AppContent() {
|
||||
|
||||
return (
|
||||
<NavigationContainer>
|
||||
{isAuthenticated ? <TabNavigator /> : <LoginScreen />}
|
||||
{isAuthenticated ? <TabNavigator /> : <AuthNavigator />}
|
||||
</NavigationContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* LoginScreen – entry point for unauthenticated users.
|
||||
* LoginScreen – server URL entry and SSO sign-in.
|
||||
*
|
||||
* Renders a server URL input and a "Sign in with SSO" button that opens the
|
||||
* DocuElevate web login page in the system browser. On success the
|
||||
* AuthContext stores the API token and navigates to the main app.
|
||||
*/
|
||||
|
||||
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
@@ -20,8 +21,13 @@ import {
|
||||
View,
|
||||
} from "react-native";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
import type { AuthStackParamList } from "./WelcomeScreen";
|
||||
|
||||
export default function LoginScreen() {
|
||||
type LoginScreenProps = {
|
||||
navigation: NativeStackNavigationProp<AuthStackParamList, "Login">;
|
||||
};
|
||||
|
||||
export default function LoginScreen({ navigation }: LoginScreenProps) {
|
||||
const { signIn } = useAuth();
|
||||
const [serverUrl, setServerUrl] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -97,6 +103,15 @@ export default function LoginScreen() {
|
||||
<Text style={styles.hint}>
|
||||
You will be redirected to your organisation's sign-in page.
|
||||
</Text>
|
||||
|
||||
<Pressable
|
||||
onPress={() => navigation.navigate("Welcome")}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Back to welcome screen"
|
||||
style={styles.backLink}
|
||||
>
|
||||
<Text style={styles.backLinkText}>← Back</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
@@ -179,4 +194,14 @@ const styles = StyleSheet.create({
|
||||
color: "#9ca3af",
|
||||
textAlign: "center",
|
||||
},
|
||||
backLink: {
|
||||
marginTop: 20,
|
||||
alignItems: "center",
|
||||
minHeight: 44,
|
||||
justifyContent: "center",
|
||||
},
|
||||
backLinkText: {
|
||||
fontSize: 13,
|
||||
color: "#6b7280",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* WelcomeScreen – first screen shown to unauthenticated users on first launch.
|
||||
*
|
||||
* Presents the DocuElevate brand, a short description of what the app does,
|
||||
* and a "Get Started" button that navigates to the LoginScreen.
|
||||
*/
|
||||
|
||||
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import React from "react";
|
||||
import {
|
||||
Image,
|
||||
Pressable,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
|
||||
export type AuthStackParamList = {
|
||||
Welcome: undefined;
|
||||
Login: undefined;
|
||||
};
|
||||
|
||||
type WelcomeScreenProps = {
|
||||
navigation: NativeStackNavigationProp<AuthStackParamList, "Welcome">;
|
||||
};
|
||||
|
||||
const FEATURES: { icon: string; title: string; description: string }[] = [
|
||||
{
|
||||
icon: "🔍",
|
||||
title: "OCR & Text Extraction",
|
||||
description: "Convert scanned PDFs and images into fully searchable text automatically.",
|
||||
},
|
||||
{
|
||||
icon: "🤖",
|
||||
title: "AI Metadata Extraction",
|
||||
description: "AI classifies documents and pulls out key fields like dates, amounts, and subjects.",
|
||||
},
|
||||
{
|
||||
icon: "☁️",
|
||||
title: "Multi-Cloud Storage",
|
||||
description: "Route processed files to Dropbox, Google Drive, OneDrive, S3, Nextcloud, and more.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function WelcomeScreen({ navigation }: WelcomeScreenProps) {
|
||||
return (
|
||||
<SafeAreaView style={styles.safe}>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scroll}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
{/* Hero */}
|
||||
<View style={styles.hero}>
|
||||
<View style={styles.logoContainer}>
|
||||
<Image
|
||||
source={require("../../../assets/logo.png")}
|
||||
style={styles.logoImage}
|
||||
resizeMode="contain"
|
||||
accessibilityLabel="DocuElevate logo"
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.appName}>DocuElevate</Text>
|
||||
<Text style={styles.tagline}>Intelligent Document Processing</Text>
|
||||
<Text style={styles.heroDescription}>
|
||||
Ingest documents, run OCR, extract metadata with AI, and route files
|
||||
to your cloud storage — all in one seamless pipeline.
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Feature highlights */}
|
||||
<View style={styles.features}>
|
||||
{FEATURES.map((feature) => (
|
||||
<View key={feature.title} style={styles.featureRow}>
|
||||
<Text style={styles.featureIcon} aria-hidden>{feature.icon}</Text>
|
||||
<View style={styles.featureText}>
|
||||
<Text style={styles.featureTitle}>{feature.title}</Text>
|
||||
<Text style={styles.featureDescription}>{feature.description}</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* CTA */}
|
||||
<Pressable
|
||||
style={({ pressed }) => [styles.button, pressed && styles.buttonPressed]}
|
||||
onPress={() => navigation.navigate("Login")}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Get started — connect to your DocuElevate server"
|
||||
>
|
||||
<Text style={styles.buttonText}>Get Started</Text>
|
||||
</Pressable>
|
||||
|
||||
<Text style={styles.hint}>
|
||||
Connect to your self-hosted or cloud DocuElevate server.
|
||||
</Text>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
safe: {
|
||||
flex: 1,
|
||||
backgroundColor: "#1e40af",
|
||||
},
|
||||
scroll: {
|
||||
flexGrow: 1,
|
||||
paddingHorizontal: 28,
|
||||
paddingTop: 48,
|
||||
paddingBottom: 40,
|
||||
},
|
||||
hero: {
|
||||
alignItems: "center",
|
||||
marginBottom: 40,
|
||||
},
|
||||
logoContainer: {
|
||||
width: 96,
|
||||
height: 96,
|
||||
borderRadius: 24,
|
||||
backgroundColor: "rgba(255,255,255,0.15)",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginBottom: 16,
|
||||
},
|
||||
logoImage: {
|
||||
width: 72,
|
||||
height: 72,
|
||||
},
|
||||
appName: {
|
||||
fontSize: 36,
|
||||
fontWeight: "800",
|
||||
color: "#ffffff",
|
||||
textAlign: "center",
|
||||
marginBottom: 6,
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
tagline: {
|
||||
fontSize: 14,
|
||||
fontWeight: "600",
|
||||
color: "rgba(255,255,255,0.75)",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: 1.5,
|
||||
textAlign: "center",
|
||||
marginBottom: 16,
|
||||
},
|
||||
heroDescription: {
|
||||
fontSize: 16,
|
||||
color: "rgba(255,255,255,0.85)",
|
||||
textAlign: "center",
|
||||
lineHeight: 24,
|
||||
maxWidth: 320,
|
||||
},
|
||||
features: {
|
||||
backgroundColor: "rgba(255,255,255,0.1)",
|
||||
borderRadius: 16,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 16,
|
||||
marginBottom: 36,
|
||||
},
|
||||
featureRow: {
|
||||
flexDirection: "row",
|
||||
alignItems: "flex-start",
|
||||
paddingVertical: 14,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: "rgba(255,255,255,0.2)",
|
||||
},
|
||||
featureIcon: {
|
||||
fontSize: 24,
|
||||
marginRight: 14,
|
||||
marginTop: 1,
|
||||
},
|
||||
featureText: {
|
||||
flex: 1,
|
||||
},
|
||||
featureTitle: {
|
||||
fontSize: 15,
|
||||
fontWeight: "600",
|
||||
color: "#ffffff",
|
||||
marginBottom: 2,
|
||||
},
|
||||
featureDescription: {
|
||||
fontSize: 13,
|
||||
color: "rgba(255,255,255,0.75)",
|
||||
lineHeight: 18,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: "#ffffff",
|
||||
borderRadius: 12,
|
||||
paddingVertical: 16,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
minHeight: 52,
|
||||
shadowColor: "#000",
|
||||
shadowOpacity: 0.15,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowRadius: 8,
|
||||
elevation: 4,
|
||||
marginBottom: 16,
|
||||
},
|
||||
buttonPressed: {
|
||||
opacity: 0.9,
|
||||
transform: [{ scale: 0.98 }],
|
||||
},
|
||||
buttonText: {
|
||||
color: "#1e40af",
|
||||
fontSize: 17,
|
||||
fontWeight: "700",
|
||||
letterSpacing: 0.3,
|
||||
},
|
||||
hint: {
|
||||
fontSize: 12,
|
||||
color: "rgba(255,255,255,0.55)",
|
||||
textAlign: "center",
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user