create-ern-boilerplate 0.0.39 → 0.0.40

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ern-boilerplate",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "Expo React Native boilerplate generator",
5
5
  "bin": {
6
6
  "create-ern-boilerplate": "./create.js",
@@ -2,19 +2,17 @@ import React, { useState } from 'react';
2
2
  import { View, Text, TextInput, TouchableOpacity, KeyboardAvoidingView, Platform } from 'react-native';
3
3
  import { Link } from 'expo-router';
4
4
  import { useAuthStore } from '@/store/authStore';
5
- import { useThemeStore } from '@/store/themeStore';
5
+ import { useTheme } from '@/hooks/useTheme';
6
6
  import { Button } from '@/components/common/Button';
7
7
 
8
8
  export default function LoginScreen() {
9
9
  const login = useAuthStore((state) => state.login);
10
- const colorScheme = useThemeStore((state) => state.colorScheme);
10
+ const { colors, isDark } = useTheme();
11
11
 
12
12
  const [email, setEmail] = useState('');
13
13
  const [password, setPassword] = useState('');
14
14
  const [loading, setLoading] = useState(false);
15
15
 
16
- const isDark = colorScheme === 'dark';
17
-
18
16
  const handleLogin = async () => {
19
17
  if (!email || !password) return;
20
18
 
@@ -1,20 +1,18 @@
1
1
  import React, { useState } from 'react';
2
2
  import { View, Text, TextInput, TouchableOpacity, KeyboardAvoidingView, Platform } from 'react-native';
3
3
  import { Link, useRouter } from 'expo-router';
4
- import { useThemeStore } from '@/store/themeStore';
4
+ import { useTheme } from '@/hooks/useTheme';
5
5
  import { Button } from '@/components/common/Button';
6
6
 
7
7
  export default function RegisterScreen() {
8
8
  const router = useRouter();
9
- const colorScheme = useThemeStore((state) => state.colorScheme);
9
+ const { colors, isDark } = useTheme();
10
10
 
11
11
  const [name, setName] = useState('');
12
12
  const [email, setEmail] = useState('');
13
13
  const [password, setPassword] = useState('');
14
14
  const [loading, setLoading] = useState(false);
15
15
 
16
- const isDark = colorScheme === 'dark';
17
-
18
16
  const handleRegister = async () => {
19
17
  if (!name || !email || !password) return;
20
18
 
@@ -1,15 +1,13 @@
1
1
  import React from 'react';
2
2
  import { View, Text, ScrollView } from 'react-native';
3
- import { useThemeStore } from '@/store/themeStore';
3
+ import { useTheme } from '@/hooks/useTheme';
4
4
  import { useAuthStore } from '@/store/authStore';
5
5
  import { Card } from '@/components/common/Card';
6
6
 
7
7
  export default function HomeScreen() {
8
- const colorScheme = useThemeStore((state) => state.colorScheme);
8
+ const { colors, isDark } = useTheme();
9
9
  const user = useAuthStore((state) => state.user);
10
10
 
11
- const isDark = colorScheme === 'dark';
12
-
13
11
  return (
14
12
  <ScrollView
15
13
  className="flex-1"
@@ -11,7 +11,7 @@
11
11
 
12
12
  import React from 'react';
13
13
  import { View, Text, TouchableOpacity } from 'react-native';
14
- import { useThemeStore } from '@/store/themeStore';
14
+ import { useTheme } from '@/hooks/useTheme';
15
15
 
16
16
  // Example: Item Card Component
17
17
  interface ItemCardProps {
@@ -31,8 +31,7 @@ export function ItemCard({
31
31
  variant = 'default',
32
32
  className = '',
33
33
  }: ItemCardProps) {
34
- const colorScheme = useThemeStore((state) => state.colorScheme);
35
- const isDark = colorScheme === 'dark';
34
+ const { colors, isDark } = useTheme();
36
35
 
37
36
  const baseClasses = `rounded-lg p-4 ${className}`;
38
37
  const variantClasses = variant === 'highlighted'
@@ -25,7 +25,7 @@ import {
25
25
  Image,
26
26
  Alert,
27
27
  } from 'react-native';
28
- import { useThemeStore } from '@/store/themeStore';
28
+ import { useTheme } from '@/hooks/useTheme';
29
29
  import { useRouter, useLocalSearchParams } from 'expo-router';
30
30
  import Toast from 'react-native-toast-message';
31
31
 
@@ -62,10 +62,9 @@ const mockProductService = {
62
62
  };
63
63
 
64
64
  export default function ProductDetailScreen() {
65
- const colorScheme = useThemeStore((state) => state.colorScheme);
65
+ const { colors, isDark } = useTheme();
66
66
  const router = useRouter();
67
67
  const params = useLocalSearchParams();
68
- const isDark = colorScheme === 'dark';
69
68
 
70
69
  // Get product ID from params
71
70
  const productId = params.id as string;
@@ -26,7 +26,7 @@ import {
26
26
  ScrollView,
27
27
  ActivityIndicator,
28
28
  } from 'react-native';
29
- import { useThemeStore } from '@/store/themeStore';
29
+ import { useTheme } from '@/hooks/useTheme';
30
30
  import { useRouter, useLocalSearchParams } from 'expo-router';
31
31
  import Toast from 'react-native-toast-message';
32
32
 
@@ -67,10 +67,9 @@ const mockProductService = {
67
67
  };
68
68
 
69
69
  export default function ProductFormScreen() {
70
- const colorScheme = useThemeStore((state) => state.colorScheme);
70
+ const { colors, isDark } = useTheme();
71
71
  const router = useRouter();
72
72
  const params = useLocalSearchParams();
73
- const isDark = colorScheme === 'dark';
74
73
 
75
74
  // Check if we're in edit mode
76
75
  const isEditMode = !!params.id;
@@ -24,7 +24,7 @@ import {
24
24
  TouchableOpacity,
25
25
  Image,
26
26
  } from 'react-native';
27
- import { useThemeStore } from '@/store/themeStore';
27
+ import { useTheme } from '@/hooks/useTheme';
28
28
  import { useRouter } from 'expo-router';
29
29
 
30
30
  // Example: News interface
@@ -61,9 +61,8 @@ const mockNewsService = {
61
61
  };
62
62
 
63
63
  export default function NewsListScreen() {
64
- const colorScheme = useThemeStore((state) => state.colorScheme);
64
+ const { colors, isDark } = useTheme();
65
65
  const router = useRouter();
66
- const isDark = colorScheme === 'dark';
67
66
 
68
67
  // State management
69
68
  const [news, setNews] = useState<NewsItem[]>([]);
@@ -24,7 +24,7 @@ import {
24
24
  RefreshControl,
25
25
  TouchableOpacity,
26
26
  } from 'react-native';
27
- import { useThemeStore } from '@/store/themeStore';
27
+ import { useTheme } from '@/hooks/useTheme';
28
28
  import { useAuthStore } from '@/store/authStore';
29
29
  import { useRouter } from 'expo-router';
30
30
 
@@ -65,10 +65,9 @@ const mockTaskService = {
65
65
  };
66
66
 
67
67
  export default function TaskListScreen() {
68
- const colorScheme = useThemeStore((state) => state.colorScheme);
68
+ const { colors, isDark } = useTheme();
69
69
  const { user, isAuthenticated } = useAuthStore();
70
70
  const router = useRouter();
71
- const isDark = colorScheme === 'dark';
72
71
 
73
72
  // State management
74
73
  const [tasks, setTasks] = useState<Task[]>([]);
@@ -1,7 +1,8 @@
1
1
  // components/shared/ConfirmDialog.tsx
2
2
  import React from 'react';
3
- import { View, Text, Modal, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
3
+ import { View, Text, Modal, TouchableOpacity, ActivityIndicator } from 'react-native';
4
4
  import { AlertTriangle } from 'lucide-react-native';
5
+ import { useTheme } from '@/hooks/useTheme';
5
6
 
6
7
  interface ConfirmDialogProps {
7
8
  visible: boolean;
@@ -12,7 +13,6 @@ interface ConfirmDialogProps {
12
13
  onConfirm: () => void;
13
14
  onCancel: () => void;
14
15
  loading?: boolean;
15
- colors: any;
16
16
  destructive?: boolean;
17
17
  }
18
18
 
@@ -25,9 +25,10 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
25
25
  onConfirm,
26
26
  onCancel,
27
27
  loading = false,
28
- colors,
29
28
  destructive = false,
30
29
  }) => {
30
+ const { colors } = useTheme();
31
+
31
32
  return (
32
33
  <Modal
33
34
  visible={visible}
@@ -35,39 +36,54 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
35
36
  animationType="fade"
36
37
  onRequestClose={onCancel}
37
38
  >
38
- <View style={styles.dialogOverlay}>
39
- <View style={[styles.dialogContent, { backgroundColor: colors.background }]}>
39
+ <View className="flex-1 justify-center items-center px-6" style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }}>
40
+ <View
41
+ className="w-full max-w-[360px] rounded-2xl p-6 shadow-xl"
42
+ style={{
43
+ backgroundColor: colors.background,
44
+ shadowColor: '#000',
45
+ shadowOffset: { width: 0, height: 4 },
46
+ shadowOpacity: 0.2,
47
+ shadowRadius: 12,
48
+ elevation: 8,
49
+ }}
50
+ >
40
51
  {destructive && (
41
- <View style={styles.iconContainer}>
52
+ <View className="items-center mb-4">
42
53
  <AlertTriangle size={48} color="#ef4444" />
43
54
  </View>
44
55
  )}
45
-
46
- <Text style={[styles.dialogTitle, { color: colors.text }]}>{title}</Text>
47
- <Text style={[styles.dialogMessage, { color: colors.textSecondary }]}>
56
+
57
+ <Text className="text-xl font-extrabold mb-3 text-center" style={{ color: colors.text }}>
58
+ {title}
59
+ </Text>
60
+ <Text className="text-sm font-medium leading-5 text-center mb-6" style={{ color: colors.textSecondary }}>
48
61
  {message}
49
62
  </Text>
50
63
 
51
- <View style={styles.dialogActions}>
64
+ <View className="flex-row gap-3">
52
65
  <TouchableOpacity
53
- style={[styles.dialogButton, styles.cancelButton, { borderColor: colors.border }]}
66
+ className="flex-1 py-3.5 rounded-xl items-center justify-center border-2"
67
+ style={{ borderColor: colors.border }}
54
68
  onPress={onCancel}
55
69
  activeOpacity={0.8}
56
70
  disabled={loading}
57
71
  >
58
- <Text style={[styles.dialogButtonText, { color: colors.text }]}>
72
+ <Text className="text-base font-bold" style={{ color: colors.text }}>
59
73
  {cancelText}
60
74
  </Text>
61
75
  </TouchableOpacity>
62
76
  <TouchableOpacity
63
- style={[
64
- styles.dialogButton,
65
- styles.confirmButton,
66
- {
67
- backgroundColor: destructive ? '#ef4444' : colors.primary,
68
- opacity: loading ? 0.7 : 1,
69
- },
70
- ]}
77
+ className="flex-1 py-3.5 rounded-xl items-center justify-center shadow-md"
78
+ style={{
79
+ backgroundColor: destructive ? '#ef4444' : colors.primary,
80
+ opacity: loading ? 0.7 : 1,
81
+ shadowColor: '#000',
82
+ shadowOffset: { width: 0, height: 2 },
83
+ shadowOpacity: 0.15,
84
+ shadowRadius: 4,
85
+ elevation: 3,
86
+ }}
71
87
  onPress={onConfirm}
72
88
  activeOpacity={0.8}
73
89
  disabled={loading}
@@ -75,7 +91,7 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
75
91
  {loading ? (
76
92
  <ActivityIndicator size="small" color="#FFFFFF" />
77
93
  ) : (
78
- <Text style={[styles.dialogButtonText, { color: '#FFFFFF' }]}>
94
+ <Text className="text-base font-bold text-white">
79
95
  {confirmText}
80
96
  </Text>
81
97
  )}
@@ -86,66 +102,3 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
86
102
  </Modal>
87
103
  );
88
104
  };
89
-
90
- const styles = StyleSheet.create({
91
- dialogOverlay: {
92
- flex: 1,
93
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
94
- justifyContent: 'center',
95
- alignItems: 'center',
96
- paddingHorizontal: 24,
97
- },
98
- dialogContent: {
99
- width: '100%',
100
- maxWidth: 360,
101
- borderRadius: 20,
102
- padding: 24,
103
- elevation: 8,
104
- shadowColor: '#000',
105
- shadowOffset: { width: 0, height: 4 },
106
- shadowOpacity: 0.2,
107
- shadowRadius: 12,
108
- },
109
- iconContainer: {
110
- alignItems: 'center',
111
- marginBottom: 16,
112
- },
113
- dialogTitle: {
114
- fontSize: 20,
115
- fontWeight: '800',
116
- marginBottom: 12,
117
- textAlign: 'center',
118
- },
119
- dialogMessage: {
120
- fontSize: 14,
121
- fontWeight: '500',
122
- lineHeight: 20,
123
- textAlign: 'center',
124
- marginBottom: 24,
125
- },
126
- dialogActions: {
127
- flexDirection: 'row',
128
- gap: 12,
129
- },
130
- dialogButton: {
131
- flex: 1,
132
- paddingVertical: 14,
133
- borderRadius: 12,
134
- alignItems: 'center',
135
- justifyContent: 'center',
136
- },
137
- cancelButton: {
138
- borderWidth: 2,
139
- },
140
- confirmButton: {
141
- shadowColor: '#000',
142
- shadowOffset: { width: 0, height: 2 },
143
- shadowOpacity: 0.15,
144
- shadowRadius: 4,
145
- elevation: 3,
146
- },
147
- dialogButtonText: {
148
- fontSize: 15,
149
- fontWeight: '700',
150
- },
151
- });
@@ -1,33 +1,37 @@
1
1
  // components/shared/FormInput.tsx
2
2
  import React from 'react';
3
- import { View, Text, TextInput, StyleSheet, TextInputProps } from 'react-native';
3
+ import { View, Text, TextInput, TextInputProps } from 'react-native';
4
4
  import { User, Mail, Lock } from 'lucide-react-native';
5
+ import { useTheme } from '@/hooks/useTheme';
5
6
 
6
7
  interface FormInputProps extends TextInputProps {
7
8
  label: string;
8
9
  icon?: 'user' | 'mail' | 'lock';
9
- colors: any;
10
10
  required?: boolean;
11
11
  }
12
12
 
13
13
  export const FormInput: React.FC<FormInputProps> = ({
14
14
  label,
15
15
  icon,
16
- colors,
17
16
  required = false,
18
17
  ...textInputProps
19
18
  }) => {
19
+ const { colors } = useTheme();
20
20
  const IconComponent = icon === 'user' ? User : icon === 'mail' ? Mail : Lock;
21
21
 
22
22
  return (
23
- <View style={styles.inputGroup}>
24
- <Text style={[styles.inputLabel, { color: colors.text }]}>
25
- {label} {required && <Text style={styles.required}>*</Text>}
23
+ <View className="gap-2">
24
+ <Text className="text-sm font-semibold mb-1" style={{ color: colors.text }}>
25
+ {label} {required && <Text className="text-red-500">*</Text>}
26
26
  </Text>
27
- <View style={[styles.inputContainer, { backgroundColor: `${colors.primary}08` }]}>
27
+ <View
28
+ className="flex-row items-center px-4 py-3 rounded-xl gap-2.5"
29
+ style={{ backgroundColor: `${colors.primary}08` }}
30
+ >
28
31
  {icon && <IconComponent size={18} color={colors.textSecondary} />}
29
32
  <TextInput
30
- style={[styles.input, { color: colors.text }]}
33
+ className="flex-1 text-base font-medium"
34
+ style={{ color: colors.text }}
31
35
  placeholderTextColor={colors.textSecondary}
32
36
  {...textInputProps}
33
37
  />
@@ -36,32 +40,5 @@ export const FormInput: React.FC<FormInputProps> = ({
36
40
  );
37
41
  };
38
42
 
39
- const styles = StyleSheet.create({
40
- inputGroup: {
41
- gap: 8,
42
- },
43
- inputLabel: {
44
- fontSize: 14,
45
- fontWeight: '600',
46
- marginBottom: 4,
47
- },
48
- required: {
49
- color: '#ef4444',
50
- },
51
- inputContainer: {
52
- flexDirection: 'row',
53
- alignItems: 'center',
54
- paddingHorizontal: 16,
55
- paddingVertical: 12,
56
- borderRadius: 12,
57
- gap: 10,
58
- },
59
- input: {
60
- flex: 1,
61
- fontSize: 15,
62
- fontWeight: '500',
63
- },
64
- });
65
-
66
43
 
67
44
 
@@ -1,42 +1,39 @@
1
1
  // components/shared/RoleSelector.tsx
2
2
  import React from 'react';
3
- import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
3
+ import { View, Text, TouchableOpacity } from 'react-native';
4
+ import { useTheme } from '@/hooks/useTheme';
4
5
 
5
6
  interface RoleSelectorProps {
6
7
  selectedRole: 'admin' | 'user' | any;
7
8
  onSelectRole: (role: 'admin' | 'user') => void;
8
- colors: any;
9
9
  }
10
10
 
11
11
  export const RoleSelector: React.FC<RoleSelectorProps> = ({
12
12
  selectedRole,
13
13
  onSelectRole,
14
- colors,
15
14
  }) => {
15
+ const { colors } = useTheme();
16
+
16
17
  return (
17
- <View style={styles.roleSelector}>
18
+ <View className="flex-row gap-3">
18
19
  {(['user', 'admin'] as const).map((role) => (
19
20
  <TouchableOpacity
20
21
  key={role}
21
- style={[
22
- styles.roleOption,
23
- {
24
- backgroundColor: selectedRole === role
25
- ? colors.primary
26
- : `${colors.primary}10`,
27
- },
28
- ]}
22
+ className="flex-1 py-3 rounded-xl items-center"
23
+ style={{
24
+ backgroundColor: selectedRole === role
25
+ ? colors.primary
26
+ : `${colors.primary}10`,
27
+ }}
29
28
  onPress={() => onSelectRole(role)}
30
29
  activeOpacity={0.7}
31
30
  >
32
31
  <Text
33
- style={[
34
- styles.roleOptionText,
35
- {
36
- color: selectedRole === role ? '#FFFFFF' : colors.text,
37
- fontWeight: selectedRole === role ? '700' : '500',
38
- },
39
- ]}
32
+ className="text-sm"
33
+ style={{
34
+ color: selectedRole === role ? '#FFFFFF' : colors.text,
35
+ fontWeight: selectedRole === role ? '700' : '500',
36
+ }}
40
37
  >
41
38
  {role.charAt(0).toUpperCase() + role.slice(1)}
42
39
  </Text>
@@ -45,19 +42,3 @@ export const RoleSelector: React.FC<RoleSelectorProps> = ({
45
42
  </View>
46
43
  );
47
44
  };
48
-
49
- const styles = StyleSheet.create({
50
- roleSelector: {
51
- flexDirection: 'row',
52
- gap: 12,
53
- },
54
- roleOption: {
55
- flex: 1,
56
- paddingVertical: 12,
57
- borderRadius: 12,
58
- alignItems: 'center',
59
- },
60
- roleOptionText: {
61
- fontSize: 14,
62
- },
63
- });