create-ern-boilerplate 0.0.37 → 0.0.38
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.
- package/package.json +1 -1
- package/templates/agent-generator/.gitignore-template +2 -1
- package/templates/agent-generator/examples/screen.example.tsx +3 -2
- package/templates/agent-generator/src/components/auth/ProtectedRoute.tsx +29 -44
- package/templates/agent-generator/src/components/common/Button.tsx +43 -14
- package/templates/agent-generator/src/components/common/Card.tsx +1 -1
- package/templates/agent-generator/src/components/common/Input.tsx +7 -4
- package/templates/agent-generator/src/components/common/Loading.tsx +1 -1
- package/templates/agent-generator/src/hooks/useTheme.ts +7 -1
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import React, { useState, useEffect } from 'react';
|
|
13
13
|
import { View, Text, FlatList, RefreshControl, ActivityIndicator } from 'react-native';
|
|
14
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
14
15
|
import { useThemeStore } from '@/store/themeStore';
|
|
15
16
|
import { Card } from '@/components/common/Card';
|
|
16
17
|
import { Button } from '@/components/common/Button';
|
|
@@ -123,7 +124,7 @@ export default function NewsListScreen() {
|
|
|
123
124
|
|
|
124
125
|
// Main content
|
|
125
126
|
return (
|
|
126
|
-
<
|
|
127
|
+
<SafeAreaView className="flex-1" style={{ backgroundColor: isDark ? '#000' : '#F9FAFB' }}>
|
|
127
128
|
<FlatList
|
|
128
129
|
data={news}
|
|
129
130
|
renderItem={renderItem}
|
|
@@ -133,6 +134,6 @@ export default function NewsListScreen() {
|
|
|
133
134
|
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor="#3b82f6" />
|
|
134
135
|
}
|
|
135
136
|
/>
|
|
136
|
-
</
|
|
137
|
+
</SafeAreaView>
|
|
137
138
|
);
|
|
138
139
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// components/auth/ProtectedRoute.tsx
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { View, Text,
|
|
3
|
+
import { View, Text, ActivityIndicator } from 'react-native';
|
|
4
4
|
import { Redirect } from 'expo-router';
|
|
5
5
|
import { useAuth } from '@/hooks/useAuth';
|
|
6
6
|
import { useTheme } from '@/hooks/useTheme';
|
|
@@ -18,9 +18,15 @@ export function ProtectedRoute({ children, requireAdmin = false }: ProtectedRout
|
|
|
18
18
|
// Show loading state while checking authentication
|
|
19
19
|
if (isLoading) {
|
|
20
20
|
return (
|
|
21
|
-
<View
|
|
21
|
+
<View
|
|
22
|
+
className="flex-1 justify-center items-center p-6"
|
|
23
|
+
style={{ backgroundColor: colors.background }}
|
|
24
|
+
>
|
|
22
25
|
<ActivityIndicator size="large" color={colors.primary} />
|
|
23
|
-
<Text
|
|
26
|
+
<Text
|
|
27
|
+
className="mt-4 text-base font-semibold"
|
|
28
|
+
style={{ color: colors.text }}
|
|
29
|
+
>
|
|
24
30
|
Checking authentication...
|
|
25
31
|
</Text>
|
|
26
32
|
</View>
|
|
@@ -35,16 +41,31 @@ export function ProtectedRoute({ children, requireAdmin = false }: ProtectedRout
|
|
|
35
41
|
// Check admin access if required
|
|
36
42
|
if (requireAdmin && user?.role !== 'admin') {
|
|
37
43
|
return (
|
|
38
|
-
<View
|
|
39
|
-
|
|
44
|
+
<View
|
|
45
|
+
className="flex-1 justify-center items-center p-6"
|
|
46
|
+
style={{ backgroundColor: colors.background }}
|
|
47
|
+
>
|
|
48
|
+
<View
|
|
49
|
+
className="p-8 rounded-3xl items-center max-w-md"
|
|
50
|
+
style={{ backgroundColor: `${colors.error}10` }}
|
|
51
|
+
>
|
|
40
52
|
<ShieldAlert size={64} color={colors.error} />
|
|
41
|
-
<Text
|
|
53
|
+
<Text
|
|
54
|
+
className="text-2xl font-extrabold mt-4 mb-2"
|
|
55
|
+
style={{ color: colors.error }}
|
|
56
|
+
>
|
|
42
57
|
Access Denied
|
|
43
58
|
</Text>
|
|
44
|
-
<Text
|
|
59
|
+
<Text
|
|
60
|
+
className="text-base text-center mb-2"
|
|
61
|
+
style={{ color: colors.textSecondary }}
|
|
62
|
+
>
|
|
45
63
|
You don't have permission to access this page.
|
|
46
64
|
</Text>
|
|
47
|
-
<Text
|
|
65
|
+
<Text
|
|
66
|
+
className="text-sm text-center italic"
|
|
67
|
+
style={{ color: colors.textSecondary }}
|
|
68
|
+
>
|
|
48
69
|
This page is only accessible to administrators.
|
|
49
70
|
</Text>
|
|
50
71
|
</View>
|
|
@@ -55,42 +76,6 @@ export function ProtectedRoute({ children, requireAdmin = false }: ProtectedRout
|
|
|
55
76
|
return <>{children}</>;
|
|
56
77
|
}
|
|
57
78
|
|
|
58
|
-
const styles = StyleSheet.create({
|
|
59
|
-
container: {
|
|
60
|
-
flex: 1,
|
|
61
|
-
justifyContent: 'center',
|
|
62
|
-
alignItems: 'center',
|
|
63
|
-
padding: 24,
|
|
64
|
-
},
|
|
65
|
-
loadingText: {
|
|
66
|
-
marginTop: 16,
|
|
67
|
-
fontSize: 16,
|
|
68
|
-
fontWeight: '600',
|
|
69
|
-
},
|
|
70
|
-
errorContainer: {
|
|
71
|
-
padding: 32,
|
|
72
|
-
borderRadius: 24,
|
|
73
|
-
alignItems: 'center',
|
|
74
|
-
maxWidth: 400,
|
|
75
|
-
},
|
|
76
|
-
errorTitle: {
|
|
77
|
-
fontSize: 24,
|
|
78
|
-
fontWeight: '800',
|
|
79
|
-
marginTop: 16,
|
|
80
|
-
marginBottom: 8,
|
|
81
|
-
},
|
|
82
|
-
errorMessage: {
|
|
83
|
-
fontSize: 16,
|
|
84
|
-
textAlign: 'center',
|
|
85
|
-
marginBottom: 8,
|
|
86
|
-
},
|
|
87
|
-
errorHint: {
|
|
88
|
-
fontSize: 14,
|
|
89
|
-
textAlign: 'center',
|
|
90
|
-
fontStyle: 'italic',
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
|
|
94
79
|
// Usage Example in Settings Screen:
|
|
95
80
|
// import { ProtectedRoute } from '@/components/auth/ProtectedRoute';
|
|
96
81
|
//
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { TouchableOpacity, Text, ActivityIndicator, TouchableOpacityProps } from 'react-native';
|
|
3
|
+
import { useTheme } from '@/hooks/useTheme';
|
|
3
4
|
|
|
4
5
|
interface ButtonProps extends TouchableOpacityProps {
|
|
5
6
|
title: string;
|
|
@@ -19,12 +20,7 @@ export function Button({
|
|
|
19
20
|
className = '',
|
|
20
21
|
...props
|
|
21
22
|
}: ButtonProps) {
|
|
22
|
-
const
|
|
23
|
-
primary: 'bg-primary-600 active:bg-primary-700',
|
|
24
|
-
secondary: 'bg-gray-600 active:bg-gray-700',
|
|
25
|
-
outline: 'border-2 border-primary-600 bg-transparent active:bg-primary-50',
|
|
26
|
-
ghost: 'bg-transparent active:bg-gray-100',
|
|
27
|
-
};
|
|
23
|
+
const { colors } = useTheme();
|
|
28
24
|
|
|
29
25
|
const sizeClasses = {
|
|
30
26
|
sm: 'px-3 py-2',
|
|
@@ -38,30 +34,63 @@ export function Button({
|
|
|
38
34
|
lg: 'text-lg',
|
|
39
35
|
};
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
// Get dynamic colors based on variant
|
|
38
|
+
const getButtonStyles = () => {
|
|
39
|
+
switch (variant) {
|
|
40
|
+
case 'primary':
|
|
41
|
+
return { backgroundColor: colors.primary };
|
|
42
|
+
case 'secondary':
|
|
43
|
+
return { backgroundColor: colors.textSecondary };
|
|
44
|
+
case 'outline':
|
|
45
|
+
return {
|
|
46
|
+
backgroundColor: 'transparent',
|
|
47
|
+
borderWidth: 2,
|
|
48
|
+
borderColor: colors.primary
|
|
49
|
+
};
|
|
50
|
+
case 'ghost':
|
|
51
|
+
return { backgroundColor: 'transparent' };
|
|
52
|
+
default:
|
|
53
|
+
return { backgroundColor: colors.primary };
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getTextColor = () => {
|
|
58
|
+
switch (variant) {
|
|
59
|
+
case 'primary':
|
|
60
|
+
case 'secondary':
|
|
61
|
+
return '#ffffff';
|
|
62
|
+
case 'outline':
|
|
63
|
+
case 'ghost':
|
|
64
|
+
return colors.primary;
|
|
65
|
+
default:
|
|
66
|
+
return '#ffffff';
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getLoaderColor = () => {
|
|
71
|
+
return variant === 'outline' || variant === 'ghost' ? colors.primary : '#ffffff';
|
|
46
72
|
};
|
|
47
73
|
|
|
48
74
|
return (
|
|
49
75
|
<TouchableOpacity
|
|
50
76
|
className={`
|
|
51
|
-
${variantClasses[variant]}
|
|
52
77
|
${sizeClasses[size]}
|
|
53
78
|
${fullWidth ? 'w-full' : ''}
|
|
54
79
|
${disabled || loading ? 'opacity-50' : ''}
|
|
55
80
|
rounded-lg items-center justify-center
|
|
56
81
|
${className}
|
|
57
82
|
`}
|
|
83
|
+
style={getButtonStyles()}
|
|
58
84
|
disabled={disabled || loading}
|
|
59
85
|
{...props}
|
|
60
86
|
>
|
|
61
87
|
{loading ? (
|
|
62
|
-
<ActivityIndicator color={
|
|
88
|
+
<ActivityIndicator color={getLoaderColor()} />
|
|
63
89
|
) : (
|
|
64
|
-
<Text
|
|
90
|
+
<Text
|
|
91
|
+
className={`${textSizeClasses[size]} font-semibold`}
|
|
92
|
+
style={{ color: getTextColor() }}
|
|
93
|
+
>
|
|
65
94
|
{title}
|
|
66
95
|
</Text>
|
|
67
96
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { View, TextInput, Text, TouchableOpacity, TextInputProps } from 'react-native';
|
|
3
|
-
import {
|
|
3
|
+
import { Eye, EyeOff } from 'lucide-react-native';
|
|
4
|
+
import { useTheme } from '@/hooks/useTheme';
|
|
4
5
|
|
|
5
6
|
interface InputProps extends TextInputProps {
|
|
6
7
|
label?: string;
|
|
@@ -52,9 +53,11 @@ export function Input({
|
|
|
52
53
|
|
|
53
54
|
{secureTextEntry && (
|
|
54
55
|
<TouchableOpacity onPress={() => setShowPassword(!showPassword)} className="ml-2">
|
|
55
|
-
|
|
56
|
-
{
|
|
57
|
-
|
|
56
|
+
{showPassword ? (
|
|
57
|
+
<EyeOff size={20} color={colors.textSecondary} />
|
|
58
|
+
) : (
|
|
59
|
+
<Eye size={20} color={colors.textSecondary} />
|
|
60
|
+
)}
|
|
58
61
|
</TouchableOpacity>
|
|
59
62
|
)}
|
|
60
63
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useThemeStore } from '@/store/themeStore';
|
|
2
|
+
import { colors as themeColors } from '@/theme/colors';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Hook for accessing theme state and actions
|
|
@@ -8,11 +9,16 @@ export function useTheme() {
|
|
|
8
9
|
const colorScheme = useThemeStore((state) => state.colorScheme);
|
|
9
10
|
const setColorScheme = useThemeStore((state) => state.setColorScheme);
|
|
10
11
|
const toggleColorScheme = useThemeStore((state) => state.toggleColorScheme);
|
|
12
|
+
const isDark = colorScheme === 'dark';
|
|
13
|
+
|
|
14
|
+
// Get colors based on current color scheme
|
|
15
|
+
const colors = isDark ? themeColors.dark : themeColors.light;
|
|
11
16
|
|
|
12
17
|
return {
|
|
13
18
|
colorScheme,
|
|
14
19
|
setColorScheme,
|
|
15
20
|
toggleColorScheme,
|
|
16
|
-
isDark
|
|
21
|
+
isDark,
|
|
22
|
+
colors,
|
|
17
23
|
};
|
|
18
24
|
}
|