create-stackr 0.2.0 → 0.3.0

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.
Files changed (127) hide show
  1. package/README.md +10 -0
  2. package/dist/prompts/features.d.ts +1 -1
  3. package/dist/prompts/features.d.ts.map +1 -1
  4. package/dist/prompts/features.js +34 -25
  5. package/dist/prompts/features.js.map +1 -1
  6. package/dist/prompts/index.js +33 -6
  7. package/dist/prompts/index.js.map +1 -1
  8. package/dist/prompts/preset.d.ts.map +1 -1
  9. package/dist/prompts/preset.js +69 -34
  10. package/dist/prompts/preset.js.map +1 -1
  11. package/dist/utils/template.js +1 -1
  12. package/dist/utils/template.js.map +1 -1
  13. package/dist/utils/validation.d.ts.map +1 -1
  14. package/dist/utils/validation.js +43 -1
  15. package/dist/utils/validation.js.map +1 -1
  16. package/package.json +1 -1
  17. package/templates/base/backend/controllers/rest-api/routes/auth.ts.ejs +33 -1
  18. package/templates/base/backend/controllers/rest-api/routes/oauth-web.ts.ejs +6 -0
  19. package/templates/base/backend/domain/user/repository.drizzle.ts +28 -1
  20. package/templates/base/backend/domain/user/repository.prisma.ts +25 -0
  21. package/templates/base/backend/drizzle/{schema.drizzle.ts → schema.drizzle.ts.ejs} +25 -0
  22. package/templates/base/backend/lib/auth.drizzle.ts.ejs +27 -18
  23. package/templates/base/backend/lib/auth.prisma.ts.ejs +24 -18
  24. package/templates/base/backend/package.json.ejs +29 -23
  25. package/templates/base/backend/prisma/schema.prisma.ejs +20 -0
  26. package/templates/base/mobile/app/+not-found.tsx +1 -1
  27. package/templates/base/mobile/app/_layout.tsx.ejs +95 -10
  28. package/templates/base/mobile/package.json.ejs +21 -13
  29. package/templates/base/mobile/src/components/ui/Button.tsx +5 -3
  30. package/templates/base/mobile/src/components/ui/Card.tsx +1 -1
  31. package/templates/base/mobile/src/components/ui/Input.tsx +1 -1
  32. package/templates/base/mobile/src/components/ui/Skeleton.tsx +1 -1
  33. package/templates/base/mobile/src/components/ui/Toast.tsx +106 -0
  34. package/templates/base/mobile/src/components/ui/{IconSymbol.tsx → icon-symbol.tsx} +7 -0
  35. package/templates/base/mobile/src/components/ui/{LoadingSpinner.tsx → loading-spinner.tsx} +1 -1
  36. package/templates/base/mobile/src/components/ui/{OnboardingLayout.tsx → onboarding-layout.tsx} +2 -2
  37. package/templates/base/mobile/src/components/ui/{PaywallLayout.tsx → paywall-layout.tsx} +3 -3
  38. package/templates/base/mobile/src/constants/Theme.ts +3 -3
  39. package/templates/base/mobile/src/context/{ThemeContext.tsx → theme-context.tsx} +2 -2
  40. package/templates/base/mobile/src/lib/auth-client.ts.ejs +18 -0
  41. package/templates/base/mobile/src/services/{sdkInitializer.ts.ejs → sdk-initializer.ts.ejs} +4 -4
  42. package/templates/base/web/.prettierignore +6 -0
  43. package/templates/base/web/.prettierrc +8 -0
  44. package/templates/base/web/eslint.config.mjs +31 -7
  45. package/templates/base/web/next.config.ts +50 -1
  46. package/templates/base/web/package.json.ejs +14 -2
  47. package/templates/base/web/src/app/globals.css +1 -1
  48. package/templates/base/web/src/app/layout.tsx.ejs +2 -0
  49. package/templates/base/web/src/components/auth/protected-route.tsx.ejs +32 -5
  50. package/templates/base/web/src/components/providers/device-session-setup.tsx.ejs +1 -1
  51. package/templates/base/web/src/hooks/use-device-session.ts.ejs +2 -2
  52. package/templates/base/web/src/lib/auth/actions.ts.ejs +438 -15
  53. package/templates/base/web/src/lib/auth/config.ts.ejs +13 -0
  54. package/templates/base/web/src/lib/auth/cookies.ts.ejs +61 -0
  55. package/templates/base/web/src/lib/device/actions.ts.ejs +2 -10
  56. package/templates/base/web/src/lib/device/types.ts +37 -0
  57. package/templates/base/web/src/proxy.ts.ejs +12 -2
  58. package/templates/base/web/src/store/{deviceSession.store.ts.ejs → device-session-store.ts.ejs} +1 -1
  59. package/templates/features/mobile/auth/app/(auth)/{_layout.tsx → _layout.tsx.ejs} +7 -0
  60. package/templates/features/mobile/auth/app/(auth)/{login.tsx → login.tsx.ejs} +9 -8
  61. package/templates/features/mobile/auth/app/(auth)/{register.tsx → register.tsx.ejs} +9 -8
  62. package/templates/features/mobile/auth/app/(auth)/two-factor-expired.tsx.ejs +88 -0
  63. package/templates/features/mobile/auth/app/(auth)/two-factor.tsx.ejs +259 -0
  64. package/templates/features/mobile/auth/app/(public)/_layout.tsx +9 -0
  65. package/templates/features/mobile/{tabs/app/(tabs)/index.tsx → auth/app/(public)/index.tsx.ejs} +76 -257
  66. package/templates/features/mobile/auth/app/(tabs)/settings/_layout.tsx.ejs +20 -0
  67. package/templates/features/mobile/auth/app/(tabs)/settings/index.tsx.ejs +137 -0
  68. package/templates/features/mobile/auth/app/(tabs)/settings/security/_layout.tsx.ejs +35 -0
  69. package/templates/features/mobile/auth/app/(tabs)/settings/security/index.tsx.ejs +147 -0
  70. package/templates/features/mobile/auth/app/(tabs)/settings/security/set-password.tsx.ejs +140 -0
  71. package/templates/features/mobile/auth/app/(tabs)/settings/security/two-factor-manage.tsx.ejs +366 -0
  72. package/templates/features/mobile/auth/app/(tabs)/settings/security/two-factor-setup.tsx.ejs +317 -0
  73. package/templates/features/mobile/auth/app/account.tsx.ejs +331 -0
  74. package/templates/features/mobile/auth/app/verify-email.tsx.ejs +315 -0
  75. package/templates/features/mobile/auth/components/auth/{LoginForm.tsx.ejs → login-form.tsx.ejs} +15 -3
  76. package/templates/features/mobile/auth/components/auth/protected-screen.tsx.ejs +56 -0
  77. package/templates/features/mobile/auth/components/auth/{RegisterForm.tsx.ejs → register-form.tsx.ejs} +5 -3
  78. package/templates/features/mobile/auth/hooks/{useAuth.ts.ejs → auth.ts.ejs} +255 -1
  79. package/templates/features/mobile/auth/hooks/two-factor-timeout.ts.ejs +56 -0
  80. package/templates/features/mobile/auth/services/{deviceSession.ts → device-session.ts} +2 -10
  81. package/templates/features/mobile/auth/store/{deviceSession.store.ts → device-session-store.ts} +14 -5
  82. package/templates/features/mobile/auth/types/device-session.ts +37 -0
  83. package/templates/features/mobile/onboarding/app/(onboarding)/page-1.tsx.ejs +3 -1
  84. package/templates/features/mobile/onboarding/app/(onboarding)/page-2.tsx.ejs +3 -1
  85. package/templates/features/mobile/onboarding/app/(onboarding)/page-3.tsx.ejs +6 -1
  86. package/templates/features/mobile/paywall/app/paywall.tsx +4 -4
  87. package/templates/features/mobile/tabs/app/(tabs)/_layout.tsx +0 -6
  88. package/templates/features/mobile/tabs/app/(tabs)/_layout.tsx.ejs +60 -0
  89. package/templates/features/mobile/tabs/app/(tabs)/index.tsx.ejs +264 -0
  90. package/templates/features/web/auth/app/(app)/layout.tsx.ejs +8 -22
  91. package/templates/features/web/auth/app/(auth)/login/page.tsx.ejs +19 -3
  92. package/templates/features/web/auth/app/(auth)/login/two-factor/page.tsx.ejs +24 -0
  93. package/templates/features/web/auth/app/(auth)/verify-email/page.tsx.ejs +117 -152
  94. package/templates/features/web/auth/app/{(app) → (protected)}/dashboard/dashboard-client.tsx.ejs +41 -1
  95. package/templates/features/web/auth/app/{(app) → (protected)}/dashboard/page.tsx.ejs +3 -1
  96. package/templates/features/web/auth/app/(protected)/layout.tsx.ejs +67 -0
  97. package/templates/features/web/auth/app/(protected)/settings/security/page.tsx.ejs +19 -0
  98. package/templates/features/web/auth/app/(protected)/settings/security/security-settings.tsx.ejs +73 -0
  99. package/templates/features/web/auth/app/{(app) → (protected)}/settings/sessions/page.tsx.ejs +2 -6
  100. package/templates/features/web/auth/app/auth/callback/route.ts.ejs +5 -1
  101. package/templates/features/web/auth/app/auth/session-expired/route.ts.ejs +39 -0
  102. package/templates/features/web/auth/app/auth/two-factor-expired/route.ts.ejs +22 -0
  103. package/templates/features/web/auth/components/auth/login-form.tsx.ejs +18 -0
  104. package/templates/features/web/auth/components/auth/two-factor-verify.tsx.ejs +173 -0
  105. package/templates/features/web/auth/components/settings/set-password-form.tsx.ejs +123 -0
  106. package/templates/features/web/auth/components/settings/two-factor-manage.tsx.ejs +253 -0
  107. package/templates/features/web/auth/components/settings/two-factor-setup.tsx.ejs +249 -0
  108. package/templates/features/web/auth/components/ui/checkbox.tsx +32 -0
  109. package/templates/features/web/auth/components/ui/dialog.tsx +143 -0
  110. package/templates/features/web/auth/components/ui/input-otp.tsx +71 -0
  111. package/templates/integrations/mobile/adjust/store/{adjust.store.ts → adjust-store.ts} +3 -3
  112. package/templates/integrations/mobile/att/store/{att.store.ts → att-store.ts} +2 -2
  113. package/templates/integrations/mobile/revenuecat/store/{revenuecat.store.ts → revenuecat-store.ts} +1 -1
  114. package/templates/integrations/mobile/scate/store/{scate.store.ts → scate-store.ts} +1 -1
  115. package/templates/base/mobile/src/components/ui/index.ts +0 -6
  116. package/templates/base/mobile/src/store/index.ts.ejs +0 -18
  117. package/templates/base/web/src/lib/auth/index.ts.ejs +0 -40
  118. package/templates/features/mobile/auth/components/auth/index.ts +0 -2
  119. package/templates/features/mobile/auth/hooks/index.ts.ejs +0 -1
  120. /package/templates/base/mobile/src/services/{errorService.ts → error-service.ts} +0 -0
  121. /package/templates/base/mobile/src/store/{ui.store.ts → ui-store.ts} +0 -0
  122. /package/templates/features/web/auth/app/{(app) → (protected)}/settings/sessions/sessions-client.tsx.ejs +0 -0
  123. /package/templates/integrations/mobile/adjust/services/{adjustService.ts.ejs → adjust-service.ts.ejs} +0 -0
  124. /package/templates/integrations/mobile/att/services/{attService.ts → att-service.ts} +0 -0
  125. /package/templates/integrations/mobile/att/services/{trackingPermissions.ts → tracking-permissions.ts} +0 -0
  126. /package/templates/integrations/mobile/revenuecat/services/{revenuecatService.ts.ejs → revenuecat-service.ts.ejs} +0 -0
  127. /package/templates/integrations/mobile/scate/services/{scateService.ts.ejs → scate-service.ts.ejs} +0 -0
@@ -1,69 +1,34 @@
1
- import React, { useState, useMemo } from 'react';
1
+ import React, { useMemo<% if (features.sessionManagement) { %>, useState<% } %> } from 'react';
2
2
  import {
3
3
  View,
4
4
  Text,
5
5
  StyleSheet,
6
6
  SafeAreaView,
7
7
  ScrollView,
8
- Modal,
8
+ <% if (features.sessionManagement) { %> Modal,
9
9
  Pressable,
10
- Alert,
11
10
  KeyboardAvoidingView,
12
11
  Platform,
13
- } from 'react-native';
12
+ <% } %>} from 'react-native';
14
13
  import { router } from 'expo-router';
15
- import Constants from 'expo-constants';
16
- import { LinearGradient } from 'expo-linear-gradient';
17
- import { useAuth } from '../../src/hooks';
18
- import { useSessionActions } from '../../src/store/deviceSession.store';
19
- import { Button, Input } from '../../src/components/ui';
20
- import { IconSymbol } from '../../src/components/ui/IconSymbol';
21
- import { formatDisplayName } from '../../src/utils/formatters';
22
- import { useAppTheme, AppTheme } from '@/context/ThemeContext';
23
-
24
- export default function HomeScreen() {
14
+ <% if (features.sessionManagement) { %>import Constants from 'expo-constants';
15
+ <% } %>import { LinearGradient } from 'expo-linear-gradient';
16
+ import { Button } from '@/components/ui/button';
17
+ <% if (features.sessionManagement) { %>import { Input } from '@/components/ui/input';
18
+ <% } %>
19
+ import { IconSymbol } from '@/components/ui/icon-symbol';
20
+ import { useAppTheme, AppTheme } from '@/context/theme-context';
21
+ <% if (features.sessionManagement) { %>import { useSessionActions } from '@/store/device-session-store';
22
+ <% } %>
23
+
24
+ export default function LandingScreen() {
25
25
  const theme = useAppTheme();
26
26
  const styles = useMemo(() => createStyles(theme), [theme]);
27
-
28
- const { user, signOut, deleteAccount, isLoading, isAuthenticated } = useAuth();
29
- const { deleteSession, initializeSession } = useSessionActions();
30
- const [showDeleteModal, setShowDeleteModal] = useState(false);
31
- const [deleteConfirmText, setDeleteConfirmText] = useState('');
32
- const [isDeleting, setIsDeleting] = useState(false);
27
+ <% if (features.sessionManagement) { %> const { deleteSession, initializeSession } = useSessionActions();
33
28
  const [showDeleteSessionModal, setShowDeleteSessionModal] = useState(false);
34
29
  const [deleteSessionConfirmText, setDeleteSessionConfirmText] = useState('');
35
30
  const [isDeletingSession, setIsDeletingSession] = useState(false);
36
31
 
37
- // Gradient colors based on theme
38
- const gradientColors = theme.mode === 'dark'
39
- ? [theme.colors.card, 'transparent'] as const
40
- : ['#ffffff', '#f8fafc'] as const; // Subtle fade for light mode
41
-
42
- const handleLogout = async () => {
43
- const result = await signOut();
44
- if (!result.success) {
45
- Alert.alert('Error', 'Failed to sign out. Please try again.');
46
- }
47
- };
48
-
49
- const handleDeleteAccount = async () => {
50
- if (deleteConfirmText !== 'DELETE') {
51
- return;
52
- }
53
-
54
- setIsDeleting(true);
55
- const result = await deleteAccount();
56
- setIsDeleting(false);
57
-
58
- if (result.success) {
59
- await signOut();
60
- setShowDeleteModal(false);
61
- setDeleteConfirmText('');
62
- } else {
63
- Alert.alert('Error', result.error || 'Failed to delete account. Please try again.');
64
- }
65
- };
66
-
67
32
  const handleDeleteSession = async () => {
68
33
  if (deleteSessionConfirmText !== 'DELETE') {
69
34
  return;
@@ -76,10 +41,10 @@ export default function HomeScreen() {
76
41
  setShowDeleteSessionModal(false);
77
42
  setDeleteSessionConfirmText('');
78
43
 
79
- if (!onboardingEnabled) {
80
- await initializeSession();
81
- } else {
44
+ if (onboardingEnabled) {
82
45
  router.replace('/(onboarding)/page-1');
46
+ } else {
47
+ await initializeSession();
83
48
  }
84
49
  } catch (error) {
85
50
  console.error('Failed to delete/recreate session:', error);
@@ -89,8 +54,12 @@ export default function HomeScreen() {
89
54
  setIsDeletingSession(false);
90
55
  }
91
56
  };
57
+ <% } %>
92
58
 
93
- const isDeleteButtonEnabled = deleteConfirmText === 'DELETE' && !isDeleting;
59
+ // Gradient colors based on theme
60
+ const gradientColors = theme.mode === 'dark'
61
+ ? [theme.colors.card, 'transparent'] as const
62
+ : ['#ffffff', '#f8fafc'] as const;
94
63
 
95
64
  const features = [
96
65
  'Authentication system',
@@ -104,32 +73,24 @@ export default function HomeScreen() {
104
73
  <SafeAreaView style={styles.container}>
105
74
  <ScrollView contentContainerStyle={styles.scrollContent}>
106
75
  <View style={styles.content}>
107
- <Text style={styles.title}>Welcome to Your App!</Text>
108
-
109
- {user && (
110
- <LinearGradient
111
- colors={gradientColors}
112
- style={styles.userInfo}
113
- start={{ x: 0, y: 0 }}
114
- end={{ x: 0, y: 1 }}
115
- >
116
- <Text style={styles.greeting}>
117
- Hello, {formatDisplayName(user.name)}!
118
- </Text>
119
- <Text style={styles.email}>{user.email}</Text>
120
- <Text style={styles.userId}>User ID: {user.id}</Text>
121
- </LinearGradient>
122
- )}
76
+ {/* Hero Section */}
77
+ <View style={styles.heroSection}>
78
+ <Text style={styles.title}>Welcome to Your App!</Text>
79
+ <Text style={styles.subtitle}>
80
+ Get started by creating an account or signing in
81
+ </Text>
82
+ </View>
123
83
 
84
+ {/* Features Section */}
124
85
  <LinearGradient
125
86
  colors={gradientColors}
126
87
  style={styles.section}
127
88
  start={{ x: 0, y: 0 }}
128
89
  end={{ x: 0, y: 1 }}
129
90
  >
130
- <Text style={styles.sectionTitle}>Getting Started</Text>
91
+ <Text style={styles.sectionTitle}>What's Included</Text>
131
92
  <Text style={styles.sectionText}>
132
- This is your main app screen. You can now start building your amazing features!
93
+ This app comes with everything you need to get started quickly.
133
94
  </Text>
134
95
 
135
96
  <View style={styles.featureList}>
@@ -148,124 +109,34 @@ export default function HomeScreen() {
148
109
  </View>
149
110
  </LinearGradient>
150
111
 
112
+ {/* CTA Section */}
151
113
  <View style={styles.actions}>
152
- {isAuthenticated ? (
153
- <>
154
- <Button
155
- title="Logout"
156
- variant="outline"
157
- onPress={handleLogout}
158
- loading={isLoading}
159
- style={styles.logoutButton}
160
- />
161
-
162
- <Button
163
- title="Delete Account"
164
- variant="outline"
165
- onPress={() => setShowDeleteModal(true)}
166
- style={styles.deleteButton}
167
- textStyle={styles.deleteButtonText}
168
- />
169
- </>
170
- ) : (
171
- <>
172
- <Button
173
- title="Sign In"
174
- variant="primary"
175
- onPress={() => router.push('/(auth)/login')}
176
- style={styles.signInButton}
177
- />
178
-
179
- <Button
180
- title="Create Account"
181
- variant="outline"
182
- onPress={() => router.push('/(auth)/register')}
183
- style={styles.createAccountButton}
184
- />
185
-
186
- <Button
187
- title="Delete Session"
188
- variant="outline"
189
- onPress={() => setShowDeleteSessionModal(true)}
190
- style={styles.deleteSessionButton}
191
- textStyle={styles.deleteSessionButtonText}
192
- />
193
- </>
194
- )}
114
+ <Button
115
+ title="Sign In"
116
+ variant="primary"
117
+ onPress={() => router.push('/(auth)/login')}
118
+ style={styles.signInButton}
119
+ />
120
+
121
+ <Button
122
+ title="Create Account"
123
+ variant="outline"
124
+ onPress={() => router.push('/(auth)/register')}
125
+ style={styles.createAccountButton}
126
+ />
127
+ <% if (features.sessionManagement) { %>
128
+ <Button
129
+ title="Delete Session"
130
+ variant="outline"
131
+ onPress={() => setShowDeleteSessionModal(true)}
132
+ style={styles.deleteSessionButton}
133
+ textStyle={styles.deleteSessionButtonText}
134
+ />
135
+ <% } %>
195
136
  </View>
196
137
  </View>
197
138
  </ScrollView>
198
-
199
- {/* Delete Account Confirmation Modal */}
200
- <Modal
201
- visible={showDeleteModal}
202
- transparent
203
- animationType="fade"
204
- onRequestClose={() => setShowDeleteModal(false)}
205
- statusBarTranslucent
206
- >
207
- <KeyboardAvoidingView
208
- behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
209
- style={styles.keyboardAvoidingModal}
210
- >
211
- <Pressable
212
- style={styles.modalOverlay}
213
- onPress={() => setShowDeleteModal(false)}
214
- >
215
- <Pressable
216
- style={styles.modalContent}
217
- onPress={(e) => e.stopPropagation()}
218
- >
219
- <Text style={styles.modalTitle}>Delete Account</Text>
220
-
221
- <View style={styles.warningContainer}>
222
- <Text style={styles.warningTitle}>Warning</Text>
223
- <Text style={styles.warningText}>
224
- This action is permanent and cannot be undone. All your account data will be deleted immediately.
225
- </Text>
226
- </View>
227
-
228
- <View style={styles.gdprNotice}>
229
- <Text style={styles.gdprTitle}>Third-Party Data Notice</Text>
230
- <Text style={styles.gdprText}>
231
- Please note that data stored by third-party services (RevenueCat, Adjust, Scate) may persist on their servers.
232
- </Text>
233
- </View>
234
-
235
- <Input
236
- label="Type DELETE to confirm"
237
- value={deleteConfirmText}
238
- onChangeText={setDeleteConfirmText}
239
- placeholder="DELETE"
240
- autoCapitalize="characters"
241
- containerStyle={styles.confirmInput}
242
- />
243
-
244
- <View style={styles.modalActions}>
245
- <Button
246
- title="Cancel"
247
- variant="outline"
248
- onPress={() => {
249
- setShowDeleteModal(false);
250
- setDeleteConfirmText('');
251
- }}
252
- style={styles.modalButton}
253
- />
254
-
255
- <Button
256
- title={isDeleting ? "Deleting..." : "Delete"}
257
- variant="primary"
258
- onPress={handleDeleteAccount}
259
- disabled={!isDeleteButtonEnabled}
260
- loading={isDeleting}
261
- style={StyleSheet.flatten([styles.modalButton, styles.deleteConfirmButton])}
262
- />
263
- </View>
264
- </Pressable>
265
- </Pressable>
266
- </KeyboardAvoidingView>
267
- </Modal>
268
-
139
+ <% if (features.sessionManagement) { %>
269
140
  {/* Delete Session Confirmation Modal */}
270
141
  <Modal
271
142
  visible={showDeleteSessionModal}
@@ -328,6 +199,7 @@ export default function HomeScreen() {
328
199
  </Pressable>
329
200
  </KeyboardAvoidingView>
330
201
  </Modal>
202
+ <% } %>
331
203
  </SafeAreaView>
332
204
  );
333
205
  }
@@ -347,45 +219,28 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
347
219
  padding: theme.spacing[5],
348
220
  },
349
221
 
222
+ heroSection: {
223
+ paddingVertical: theme.spacing[8],
224
+ alignItems: 'center',
225
+ },
226
+
350
227
  title: {
351
228
  fontSize: theme.typography.fontSize['3xl'],
352
229
  fontWeight: '800',
353
230
  color: theme.colors.text,
354
231
  textAlign: 'center',
355
- marginBottom: theme.spacing[8],
232
+ marginBottom: theme.spacing[3],
356
233
  letterSpacing: -1,
357
234
  },
358
235
 
359
- userInfo: {
360
- // Removed solid background
361
- borderRadius: theme.borderRadius.xl,
362
- padding: theme.spacing[5],
363
- marginBottom: theme.spacing[6],
364
- alignItems: 'center',
365
- borderWidth: 1,
366
- borderColor: theme.colors.borderLight,
367
- },
368
-
369
- greeting: {
236
+ subtitle: {
370
237
  fontSize: theme.typography.fontSize.lg,
371
- fontWeight: '700',
372
- color: theme.colors.text,
373
- marginBottom: theme.spacing[2],
374
- },
375
-
376
- email: {
377
- fontSize: theme.typography.fontSize.base,
378
238
  color: theme.colors.textSecondary,
379
- marginBottom: theme.spacing[1],
380
- },
381
-
382
- userId: {
383
- fontSize: theme.typography.fontSize.sm,
384
- color: theme.colors.textMuted,
239
+ textAlign: 'center',
240
+ lineHeight: 28,
385
241
  },
386
242
 
387
243
  section: {
388
- // Removed solid background
389
244
  borderRadius: theme.borderRadius.xl,
390
245
  padding: theme.spacing[6],
391
246
  marginBottom: theme.spacing[6],
@@ -437,29 +292,15 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
437
292
  paddingTop: theme.spacing[5],
438
293
  },
439
294
 
440
- logoutButton: {
441
- marginTop: theme.spacing[4],
442
- },
443
-
444
- deleteButton: {
445
- marginTop: theme.spacing[3],
446
- borderColor: theme.colors.error,
447
- },
448
-
449
- deleteButtonText: {
450
- color: theme.colors.error,
451
- },
452
-
453
295
  signInButton: {
454
- marginTop: theme.spacing[4],
296
+ marginBottom: theme.spacing[3],
455
297
  },
456
298
 
457
299
  createAccountButton: {
458
- marginTop: theme.spacing[3],
459
- },
460
-
300
+ <% if (features.sessionManagement) { %> marginBottom: theme.spacing[3],
301
+ <% } %> },
302
+ <% if (features.sessionManagement) { %>
461
303
  deleteSessionButton: {
462
- marginTop: theme.spacing[3],
463
304
  borderColor: theme.colors.error,
464
305
  },
465
306
 
@@ -501,13 +342,13 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
501
342
  },
502
343
 
503
344
  warningContainer: {
504
- backgroundColor: 'transparent', // No more solid gray block
345
+ backgroundColor: 'transparent',
505
346
  borderRadius: theme.borderRadius.lg,
506
347
  padding: theme.spacing[4],
507
348
  marginBottom: theme.spacing[5],
508
349
  borderWidth: 1,
509
- borderColor: theme.colors.warning, // Outline instead
510
- borderStyle: 'dashed', // Optional stylish touch
350
+ borderColor: theme.colors.warning,
351
+ borderStyle: 'dashed',
511
352
  },
512
353
 
513
354
  warningTitle: {
@@ -523,28 +364,6 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
523
364
  lineHeight: 20,
524
365
  },
525
366
 
526
- gdprNotice: {
527
- backgroundColor: 'transparent',
528
- borderRadius: theme.borderRadius.lg,
529
- padding: theme.spacing[4],
530
- marginBottom: theme.spacing[5],
531
- borderWidth: 1,
532
- borderColor: theme.colors.info,
533
- },
534
-
535
- gdprTitle: {
536
- fontSize: theme.typography.fontSize.sm,
537
- fontWeight: '600',
538
- color: theme.colors.info,
539
- marginBottom: theme.spacing[2],
540
- },
541
-
542
- gdprText: {
543
- fontSize: theme.typography.fontSize.xs,
544
- color: theme.colors.textMuted,
545
- lineHeight: 18,
546
- },
547
-
548
367
  confirmInput: {
549
368
  marginBottom: theme.spacing[6],
550
369
  },
@@ -560,6 +379,6 @@ const createStyles = (theme: AppTheme) => StyleSheet.create({
560
379
 
561
380
  deleteConfirmButton: {
562
381
  backgroundColor: theme.colors.error,
563
- borderWidth: 0, // Solid button
382
+ borderWidth: 0,
564
383
  },
565
- });
384
+ <% } %>});
@@ -0,0 +1,20 @@
1
+ import { Stack } from 'expo-router';
2
+ import { useAppTheme } from '@/context/theme-context';
3
+
4
+ export default function SettingsLayout() {
5
+ const theme = useAppTheme();
6
+
7
+ return (
8
+ <Stack
9
+ screenOptions={{
10
+ headerStyle: { backgroundColor: theme.colors.background },
11
+ headerTintColor: theme.colors.text,
12
+ headerShadowVisible: false,
13
+ headerBackTitleVisible: false,
14
+ }}
15
+ >
16
+ <Stack.Screen name="index" options={{ headerShown: false, title: 'Settings' }} />
17
+ <Stack.Screen name="security" options={{ headerShown: false }} />
18
+ </Stack>
19
+ );
20
+ }
@@ -0,0 +1,137 @@
1
+ import React, { useMemo } from 'react';
2
+ import { View, Text, StyleSheet, ScrollView, TouchableOpacity, SafeAreaView } from 'react-native';
3
+ import { useRouter } from 'expo-router';
4
+ import { useAuth } from '../../../src/hooks/auth';
5
+ import { IconSymbol } from '../../../src/components/ui/icon-symbol';
6
+ import { useAppTheme, AppTheme } from '@/context/theme-context';
7
+
8
+ export default function SettingsScreen() {
9
+ const router = useRouter();
10
+ const theme = useAppTheme();
11
+ const styles = useMemo(() => createStyles(theme), [theme]);
12
+ const { user } = useAuth();
13
+
14
+ return (
15
+ <SafeAreaView style={styles.container}>
16
+ <ScrollView contentContainerStyle={styles.scrollContent}>
17
+ <View style={styles.header}>
18
+ <Text style={styles.headerTitle}>Settings</Text>
19
+ <Text style={styles.headerSubtitle}>{user?.email}</Text>
20
+ </View>
21
+
22
+ <View style={styles.section}>
23
+ <Text style={styles.sectionTitle}>Account</Text>
24
+
25
+ <TouchableOpacity
26
+ style={styles.settingsItem}
27
+ onPress={() => router.push('/account')}
28
+ activeOpacity={0.7}
29
+ >
30
+ <View style={styles.settingsItemLeft}>
31
+ <View style={[styles.iconContainer, { backgroundColor: theme.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)' }]}>
32
+ <IconSymbol name="person.fill" size={20} color={theme.colors.text} />
33
+ </View>
34
+ <View style={styles.settingsItemContent}>
35
+ <Text style={styles.settingsItemText}>Account Details</Text>
36
+ <Text style={styles.settingsItemSubtext}>Manage your profile information</Text>
37
+ </View>
38
+ </View>
39
+ <IconSymbol name="chevron.right" size={18} color={theme.colors.textMuted} />
40
+ </TouchableOpacity>
41
+
42
+ <TouchableOpacity
43
+ style={styles.settingsItem}
44
+ onPress={() => router.push('/(tabs)/settings/security')}
45
+ activeOpacity={0.7}
46
+ >
47
+ <View style={styles.settingsItemLeft}>
48
+ <View style={[styles.iconContainer, { backgroundColor: theme.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)' }]}>
49
+ <IconSymbol name="lock.shield.fill" size={20} color={theme.colors.text} />
50
+ </View>
51
+ <View style={styles.settingsItemContent}>
52
+ <Text style={styles.settingsItemText}>Security</Text>
53
+ <Text style={styles.settingsItemSubtext}>Two-factor authentication & more</Text>
54
+ </View>
55
+ </View>
56
+ <IconSymbol name="chevron.right" size={18} color={theme.colors.textMuted} />
57
+ </TouchableOpacity>
58
+ </View>
59
+ </ScrollView>
60
+ </SafeAreaView>
61
+ );
62
+ }
63
+
64
+ const createStyles = (theme: AppTheme) => StyleSheet.create({
65
+ container: {
66
+ flex: 1,
67
+ backgroundColor: theme.colors.background,
68
+ },
69
+ scrollContent: {
70
+ paddingBottom: theme.spacing[8],
71
+ },
72
+ header: {
73
+ padding: theme.spacing[6],
74
+ paddingTop: theme.spacing[4],
75
+ paddingBottom: theme.spacing[6],
76
+ },
77
+ headerTitle: {
78
+ fontSize: 32,
79
+ fontWeight: '700',
80
+ color: theme.colors.text,
81
+ letterSpacing: -0.5,
82
+ },
83
+ headerSubtitle: {
84
+ fontSize: theme.typography.fontSize.sm,
85
+ color: theme.colors.textSecondary,
86
+ marginTop: theme.spacing[1],
87
+ },
88
+ section: {
89
+ paddingHorizontal: theme.spacing[5],
90
+ marginBottom: theme.spacing[6],
91
+ },
92
+ sectionTitle: {
93
+ fontSize: 13,
94
+ fontWeight: '600',
95
+ color: theme.colors.textSecondary,
96
+ textTransform: 'uppercase',
97
+ letterSpacing: 0.5,
98
+ marginBottom: theme.spacing[3],
99
+ marginLeft: theme.spacing[1],
100
+ },
101
+ settingsItem: {
102
+ flexDirection: 'row',
103
+ alignItems: 'center',
104
+ justifyContent: 'space-between',
105
+ padding: theme.spacing[4],
106
+ backgroundColor: theme.colors.backgroundSecondary,
107
+ borderRadius: 16,
108
+ marginBottom: theme.spacing[3],
109
+ ...theme.shadows.small,
110
+ },
111
+ settingsItemLeft: {
112
+ flexDirection: 'row',
113
+ alignItems: 'center',
114
+ flex: 1,
115
+ },
116
+ iconContainer: {
117
+ width: 40,
118
+ height: 40,
119
+ borderRadius: 10,
120
+ justifyContent: 'center',
121
+ alignItems: 'center',
122
+ },
123
+ settingsItemContent: {
124
+ marginLeft: theme.spacing[3],
125
+ flex: 1,
126
+ },
127
+ settingsItemText: {
128
+ fontSize: theme.typography.fontSize.base,
129
+ fontWeight: '500',
130
+ color: theme.colors.text,
131
+ },
132
+ settingsItemSubtext: {
133
+ fontSize: theme.typography.fontSize.xs,
134
+ color: theme.colors.textSecondary,
135
+ marginTop: 2,
136
+ },
137
+ });
@@ -0,0 +1,35 @@
1
+ import { Stack, useRouter } from 'expo-router';
2
+ import { TouchableOpacity } from 'react-native';
3
+ import { useAppTheme } from '@/context/theme-context';
4
+ import { IconSymbol } from '@/components/ui/icon-symbol';
5
+
6
+ export default function SecurityLayout() {
7
+ const theme = useAppTheme();
8
+ const router = useRouter();
9
+
10
+ const BackButton = () => (
11
+ <TouchableOpacity
12
+ onPress={() => router.back()}
13
+ style={{ marginRight: 16 }}
14
+ >
15
+ <IconSymbol name="chevron.left" size={24} color={theme.colors.text} />
16
+ </TouchableOpacity>
17
+ );
18
+
19
+ return (
20
+ <Stack
21
+ screenOptions={{
22
+ headerStyle: { backgroundColor: theme.colors.background },
23
+ headerTintColor: theme.colors.text,
24
+ headerShadowVisible: false,
25
+ headerBackTitleVisible: false,
26
+ headerLeft: () => <BackButton />,
27
+ }}
28
+ >
29
+ <Stack.Screen name="index" options={{ title: 'Security' }} />
30
+ <Stack.Screen name="two-factor-setup" options={{ title: 'Enable 2FA' }} />
31
+ <Stack.Screen name="two-factor-manage" options={{ title: 'Manage 2FA' }} />
32
+ <Stack.Screen name="set-password" options={{ title: 'Set Password' }} />
33
+ </Stack>
34
+ );
35
+ }