add ai and todo example templates for native frontends (#293)

This commit is contained in:
Aman Varshney
2025-06-02 16:30:53 +05:30
committed by GitHub
parent 9dbeea8983
commit 7851d0636d
42 changed files with 1606 additions and 536 deletions

View File

@@ -10,23 +10,28 @@ export default function TabLayout() {
<Tabs
screenOptions={{
headerShown: false,
tabBarActiveTintColor: theme.colors.primary,
tabBarInactiveTintColor: theme.colors.mutedForeground,
tabBarStyle: {
backgroundColor: theme.colors.background,
borderTopColor: theme.colors.border,
},
}}
>
<Tabs.Screen
name="index"
options={{
title: "Tab One",
tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
title: "Home",
tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />,
}}
/>
<Tabs.Screen
name="two"
options={{
title: "Tab Two",
tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
title: "Explore",
tabBarIcon: ({ color }) => (
<TabBarIcon name="compass" color={color} />
),
}}
/>
</Tabs>

View File

@@ -1,29 +1,37 @@
import { Stack } from "expo-router";
import { StyleSheet } from "react-native-unistyles";
import { Container } from "@/components/container";
import { Text, View } from "react-native";
import { ScrollView, Text, View } from "react-native";
import { StyleSheet } from "react-native-unistyles";
export default function Home() {
return (
<>
<Stack.Screen options={{ title: "Tab One" }} />
<Container>
<View style={styles.container}>
<Text style={styles.text}>Tab One</Text>
<Container>
<ScrollView contentContainerStyle={styles.container}>
<View style={styles.headerSection}>
<Text style={styles.title}>Tab One</Text>
<Text style={styles.subtitle}>
Explore the first section of your app
</Text>
</View>
</Container>
</>
</ScrollView>
</Container>
);
}
const styles = StyleSheet.create((theme) => ({
text: {
color: theme.colors.typography,
},
container: {
flex: 1,
paddingBottom: 100,
justifyContent: "center",
alignItems: "center",
padding: theme.spacing.lg,
},
headerSection: {
paddingVertical: theme.spacing.xl,
},
title: {
fontSize: theme.fontSize["3xl"],
fontWeight: "bold",
color: theme.colors.foreground,
marginBottom: theme.spacing.sm,
},
subtitle: {
fontSize: theme.fontSize.lg,
color: theme.colors.mutedForeground,
},
}));

View File

@@ -1,29 +1,37 @@
import { Stack } from "expo-router";
import { StyleSheet } from "react-native-unistyles";
import { Container } from "@/components/container";
import { Text, View } from "react-native";
import { ScrollView, Text, View } from "react-native";
import { StyleSheet } from "react-native-unistyles";
export default function Home() {
export default function TabTwo() {
return (
<>
<Stack.Screen options={{ title: "Tab Two" }} />
<Container>
<View style={styles.container}>
<Text style={styles.text}>Tab Two</Text>
<Container>
<ScrollView contentContainerStyle={styles.container}>
<View style={styles.headerSection}>
<Text style={styles.title}>Tab Two</Text>
<Text style={styles.subtitle}>
Discover more features and content
</Text>
</View>
</Container>
</>
</ScrollView>
</Container>
);
}
const styles = StyleSheet.create((theme) => ({
text: {
color: theme.colors.typography,
},
container: {
flex: 1,
paddingBottom: 100,
justifyContent: "center",
alignItems: "center",
padding: theme.spacing.lg,
},
headerSection: {
paddingVertical: theme.spacing.xl,
},
title: {
fontSize: theme.fontSize["3xl"],
fontWeight: "bold",
color: theme.colors.foreground,
marginBottom: theme.spacing.sm,
},
subtitle: {
fontSize: theme.fontSize.lg,
color: theme.colors.mutedForeground,
},
}));

View File

@@ -10,26 +10,26 @@ const DrawerLayout = () => {
return (
<Drawer
screenOptions={{
screenOptions=\{{
headerStyle: {
backgroundColor: theme.colors.background,
},
headerTitleStyle: {
color: theme.colors.typography,
color: theme.colors.foreground,
},
headerTintColor: theme.colors.typography,
headerTintColor: theme.colors.foreground,
drawerStyle: {
backgroundColor: theme.colors.background,
},
drawerLabelStyle: {
color: theme.colors.typography,
color: theme.colors.foreground,
},
drawerInactiveTintColor: theme.colors.typography,
drawerInactiveTintColor: theme.colors.mutedForeground,
}}
>
<Drawer.Screen
name="index"
options={{
options=\{{
headerTitle: "Home",
drawerLabel: "Home",
drawerIcon: ({ size, color }) => (
@@ -39,7 +39,7 @@ const DrawerLayout = () => {
/>
<Drawer.Screen
name="(tabs)"
options={{
options=\{{
headerTitle: "Tabs",
drawerLabel: "Tabs",
drawerIcon: ({ size, color }) => (
@@ -52,6 +52,34 @@ const DrawerLayout = () => {
),
}}
/>
{{#if (includes examples "todo")}}
<Drawer.Screen
name="todos"
options=\{{
headerTitle: "Todos",
drawerLabel: "Todos",
drawerIcon: ({ size, color }) => (
<Ionicons name="checkbox-outline" size={size} color={color} />
),
}}
/>
{{/if}}
{{#if (includes examples "ai")}}
<Drawer.Screen
name="ai"
options=\{{
headerTitle: "AI",
drawerLabel: "AI",
drawerIcon: ({ size, color }) => (
<Ionicons
name="chatbubble-ellipses-outline"
size={size}
color={color}
/>
),
}}
/>
{{/if}}
</Drawer>
);
};

View File

@@ -28,56 +28,79 @@ export default function Home() {
return (
<Container>
<ScrollView contentContainerStyle={styles.pageContainer}>
<Text style={styles.headerTitle}>BETTER T STACK</Text>
<ScrollView
contentContainerStyle={styles.container}
showsVerticalScrollIndicator={false}
>
<Text className="font-mono text-foreground text-3xl font-bold mb-4">
BETTER T STACK
</Text>
<View style={styles.statusCard}>
<View style={styles.statusHeader}>
<Text style={styles.statusTitle}>System Status</Text>
<View style={styles.statusBadge}>
<Text style={styles.statusBadgeText}>LIVE</Text>
</View>
</View>
<View style={styles.apiStatusCard}>
<Text style={styles.cardTitle}>API Status</Text>
{{#if (eq backend "convex")}}
<View style={styles.apiStatusRow}>
<View style={styles.statusRow}>
<View
style={[
styles.statusIndicatorDot,
styles.statusDot,
healthCheck === "OK"
? styles.statusIndicatorGreen
: styles.statusIndicatorRed,
? styles.statusDotSuccess
: styles.statusDotWarning,
]}
/>
<Text style={styles.statusText}>
{healthCheck === undefined
? "Checking..."
: healthCheck === "OK"
? "Connected"
: "Error"}
</Text>
<View style={styles.statusContent}>
<Text style={styles.statusLabel}>Convex</Text>
<Text style={styles.statusDescription}>
{healthCheck === undefined
? "Checking connection..."
: healthCheck === "OK"
? "All systems operational"
: "Service unavailable"}
</Text>
</View>
</View>
{{else}}
{{#unless (eq api "none")}}
<View style={styles.apiStatusRow}>
<View style={styles.statusRow}>
<View
style={[
styles.statusIndicatorDot,
styles.statusDot,
healthCheck.data
? styles.statusIndicatorGreen
: styles.statusIndicatorRed,
? styles.statusDotSuccess
: styles.statusDotWarning,
]}
/>
<Text style={styles.statusText}>
{{#if (eq api "orpc")}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
{{#if (eq api "trpc")}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
</Text>
<View style={styles.statusContent}>
<Text style={styles.statusLabel}>
{{#if (eq api "orpc")}}
ORPC
{{/if}}
{{#if (eq api "trpc")}}
TRPC
{{/if}}
</Text>
<Text style={styles.statusDescription}>
{{#if (eq api "orpc")}}
{healthCheck.isLoading
? "Checking connection..."
: healthCheck.data
? "All systems operational"
: "Service unavailable"}
{{/if}}
{{#if (eq api "trpc")}}
{healthCheck.isLoading
? "Checking connection..."
: healthCheck.data
? "All systems operational"
: "Service unavailable"}
{{/if}}
</Text>
</View>
</View>
{{/unless}}
{{/if}}
@@ -88,44 +111,84 @@ export default function Home() {
}
const styles = StyleSheet.create((theme) => ({
pageContainer: {
paddingHorizontal: 8,
container: {
paddingHorizontal: theme.spacing.md,
},
headerTitle: {
color: theme?.colors?.typography,
fontSize: 30,
heroSection: {
paddingVertical: theme.spacing.xl,
},
heroTitle: {
fontSize: theme.fontSize["4xl"],
fontWeight: "bold",
marginBottom: 16,
color: theme.colors.foreground,
marginBottom: theme.spacing.sm,
},
apiStatusCard: {
marginBottom: 24,
borderRadius: 8,
heroSubtitle: {
fontSize: theme.fontSize.lg,
color: theme.colors.mutedForeground,
lineHeight: 28,
},
statusCard: {
backgroundColor: theme.colors.card,
borderWidth: 1,
borderColor: theme?.colors?.border,
padding: 16,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.xl,
padding: theme.spacing.lg,
marginBottom: theme.spacing.lg,
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 3,
elevation: 2,
},
cardTitle: {
marginBottom: 12,
fontWeight: "500",
color: theme?.colors?.typography,
},
apiStatusRow: {
statusHeader: {
flexDirection: "row",
alignItems: "center",
gap: 8,
justifyContent: "space-between",
marginBottom: theme.spacing.md,
},
statusIndicatorDot: {
height: 12,
width: 12,
statusTitle: {
fontSize: theme.fontSize.lg,
fontWeight: "600",
color: theme.colors.cardForeground,
},
statusBadge: {
backgroundColor: theme.colors.secondary,
paddingHorizontal: theme.spacing.sm + 4,
paddingVertical: theme.spacing.xs,
borderRadius: 9999,
},
statusIndicatorGreen: {
statusBadgeText: {
fontSize: theme.fontSize.xs,
fontWeight: "500",
color: theme.colors.secondaryForeground,
},
statusRow: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing.sm + 4,
},
statusDot: {
height: 12,
width: 12,
borderRadius: 6,
},
statusDotSuccess: {
backgroundColor: theme.colors.success,
},
statusIndicatorRed: {
backgroundColor: theme.colors.destructive,
statusDotWarning: {
backgroundColor: "#F59E0B",
},
statusText: {
color: theme?.colors?.typography,
statusContent: {
flex: 1,
},
statusLabel: {
fontSize: theme.fontSize.sm,
fontWeight: "500",
color: theme.colors.cardForeground,
},
statusDescription: {
fontSize: theme.fontSize.xs,
color: theme.colors.mutedForeground,
}
}));

View File

@@ -1,34 +1,65 @@
import { Link, Stack } from "expo-router";
import { Text } from "react-native";
import { StyleSheet } from "react-native-unistyles";
import { Container } from "@/components/container";
import { Link, Stack } from "expo-router";
import { Text, View } from "react-native";
import { StyleSheet } from "react-native-unistyles";
export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: "Oops!" }} />
<Container>
<Text style={styles.title}>This screen doesn't exist.</Text>
<Link href="/" style={styles.link}>
<Text style={styles.linkText}>Go to home screen!</Text>
</Link>
<View style={styles.container}>
<View style={styles.content}>
<Text style={styles.emoji}>🤔</Text>
<Text style={styles.title}>Page Not Found</Text>
<Text style={styles.description}>
Sorry, the page you're looking for doesn't exist.
</Text>
<Link href="/" style={styles.button}>
<Text style={styles.buttonText}>Go to Home</Text>
</Link>
</View>
</View>
</Container>
</>
);
}
const styles = StyleSheet.create((theme) => ({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: theme.spacing.lg,
},
content: {
alignItems: "center",
},
emoji: {
fontSize: 64,
marginBottom: theme.spacing.md,
},
title: {
fontSize: 20,
fontSize: theme.fontSize["2xl"],
fontWeight: "bold",
color: theme.colors.typography,
color: theme.colors.foreground,
marginBottom: theme.spacing.sm,
textAlign: "center",
},
link: {
marginTop: 16,
paddingVertical: 16,
description: {
color: theme.colors.mutedForeground,
textAlign: "center",
marginBottom: theme.spacing.xl,
maxWidth: 280,
},
linkText: {
fontSize: 14,
button: {
backgroundColor: `${theme.colors.primary}1A`, // 10% opacity
paddingHorizontal: theme.spacing.lg,
paddingVertical: theme.spacing.sm + 4,
borderRadius: theme.borderRadius.lg,
},
buttonText: {
color: theme.colors.primary,
fontWeight: "500",
},
}));

View File

@@ -1,3 +1,6 @@
{{#if (includes examples "ai")}}
import "@/polyfills";
{{/if}}
{{#if (eq api "trpc")}}
import { queryClient } from "@/utils/trpc";
{{/if}}
@@ -39,9 +42,9 @@ export default function RootLayout() {
backgroundColor: theme.colors.background,
},
headerTitleStyle: {
color: theme.colors.typography,
color: theme.colors.foreground,
},
headerTintColor: theme.colors.typography,
headerTintColor: theme.colors.foreground,
}}
>
<Stack.Screen name="(drawer)" options=\{{ headerShown: false }} />
@@ -62,9 +65,9 @@ export default function RootLayout() {
backgroundColor: theme.colors.background,
},
headerTitleStyle: {
color: theme.colors.typography,
color: theme.colors.foreground,
},
headerTintColor: theme.colors.typography,
headerTintColor: theme.colors.foreground,
}}
>
<Stack.Screen name="(drawer)" options=\{{ headerShown: false }} />
@@ -83,9 +86,9 @@ export default function RootLayout() {
backgroundColor: theme.colors.background,
},
headerTitleStyle: {
color: theme.colors.typography,
color: theme.colors.foreground,
},
headerTintColor: theme.colors.typography,
headerTintColor: theme.colors.foreground,
}}
>
<Stack.Screen name="(drawer)" options=\{{ headerShown: false }} />

View File

@@ -1,29 +1,33 @@
import { Container } from "@/components/container";
import { StatusBar } from "expo-status-bar";
import { Platform, Text, View } from "react-native";
import { Text, View } from "react-native";
import { StyleSheet } from "react-native-unistyles";
export default function Modal() {
return (
<>
<StatusBar style={Platform.OS === "ios" ? "light" : "auto"} />
<Container>
<View style={styles.container}>
<Text style={styles.text}>Model</Text>
<Container>
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Modal</Text>
</View>
</Container>
</>
</View>
</Container>
);
}
const styles = StyleSheet.create((theme) => ({
text: {
color: theme.colors.typography,
},
container: {
flex: 1,
paddingBottom: 100,
justifyContent: "center",
padding: theme.spacing.lg,
},
header: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: theme.spacing.xl,
},
title: {
fontSize: theme.fontSize["2xl"],
fontWeight: "bold",
color: theme.colors.foreground,
},
}));