create-gufran-expo-app 2.0.4 → 2.0.6

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 (43) hide show
  1. package/README.md +60 -321
  2. package/bin/cli.js +0 -1
  3. package/package.json +4 -3
  4. package/template/src/navigation/AuthStack.tsx +6 -25
  5. package/template/src/navigation/MainStack.tsx +0 -148
  6. package/template/src/navigation/RootNavigator.tsx +4 -26
  7. package/template/src/navigation/index.ts +0 -1
  8. package/template/src/navigation/navigationRef.ts +1 -1
  9. package/template/src/screens/HomeScreen.tsx +3 -215
  10. package/template/src/screens/auth/LoginScreen.tsx +13 -13
  11. package/template/src/screens/auth/index.ts +1 -6
  12. package/template/src/screens/index.ts +0 -35
  13. package/template/src/services/api.ts +5 -5
  14. package/template/src/services/authService.ts +3 -299
  15. package/template/src/services/mainServices.ts +19 -1914
  16. package/template/src/types/navigation.ts +5 -155
  17. package/template/src/utils/index.ts +5 -8
  18. package/template/src/navigation/MiddleStack.tsx +0 -35
  19. package/template/src/screens/auth/AddMamber.tsx +0 -428
  20. package/template/src/screens/auth/ForgotPasswordScreen.tsx +0 -176
  21. package/template/src/screens/auth/OTPVerifyScreen.tsx +0 -359
  22. package/template/src/screens/auth/RegisterScreen.tsx +0 -430
  23. package/template/src/screens/auth/SuccessScreen.tsx +0 -201
  24. package/template/src/screens/chat/ChatScreen.tsx +0 -1819
  25. package/template/src/screens/chat/ChatThreadsScreen.tsx +0 -360
  26. package/template/src/screens/chat/ReportMessageScreen.tsx +0 -238
  27. package/template/src/screens/clubs/Announcements.tsx +0 -426
  28. package/template/src/screens/clubs/BuyRaffleTicketsScreen.tsx +0 -568
  29. package/template/src/screens/clubs/ClubDeteils.tsx +0 -497
  30. package/template/src/screens/clubs/JoinClub.tsx +0 -841
  31. package/template/src/screens/events/EventScreen.tsx +0 -460
  32. package/template/src/screens/raffles/MyReferralMembersScreen.tsx +0 -758
  33. package/template/src/screens/raffles/RaffleDetailsScreen.tsx +0 -762
  34. package/template/src/screens/raffles/RafflesScreen.tsx +0 -495
  35. package/template/src/screens/raffles/SetRaffleReminderScreen.tsx +0 -390
  36. package/template/src/screens/teams/JoinTeamScreen.tsx +0 -464
  37. package/template/src/screens/teams/MyTeamDetailsScreen.tsx +0 -979
  38. package/template/src/screens/teams/MyTeamScreen.tsx +0 -568
  39. package/template/src/screens/teams/PendingRequestsScreen.tsx +0 -426
  40. package/template/src/screens/volunteerOpportunities/SetReminderScreen.tsx +0 -631
  41. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesDetailsScreen.tsx +0 -1049
  42. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesScreen.tsx +0 -608
  43. package/template/src/utils/ClubSearchManager.ts +0 -222
@@ -1,428 +0,0 @@
1
- import React, { useState, useCallback, useRef } from 'react';
2
- import {
3
- View,
4
- Text,
5
- StyleSheet,
6
- StatusBar,
7
- TouchableOpacity,
8
- FlatList,
9
- Image,
10
- Platform,
11
- Alert,
12
- } from 'react-native';
13
- import { SafeAreaView } from 'react-native-safe-area-context';
14
- import { Button } from '../../components/common';
15
- import { theme } from '../../constants';
16
- import { Strings } from '../../constants/strings';
17
- import { moderateScale } from '../../utils/scaling';
18
- import { Fonts } from '../../constants/Fonts';
19
- import SVG from '../../assets/icons';
20
- import { AddMemberScreenProps } from '../../types/navigation';
21
- import { useAuthStore } from '../../stores/authStore';
22
- import { getApiErrorInfo, MembersRequest, useMembers, useGetImageUrl } from '../../services/authService';
23
- import { useUpdateMember, UpdateMemberRequest } from '../../services/authService';
24
- import ToastManager from '../../components/common/ToastManager';
25
- import { SuccessModal } from './SuccessScreen';
26
- import { AddMemberModal } from '../../components/common/index';
27
-
28
-
29
- interface Member {
30
- id: string;
31
- name: string;
32
- profileImage: string;
33
- isOwner: boolean;
34
- memberImageUrl?: string;
35
- }
36
-
37
- export const AddMemberScreen: React.FC<AddMemberScreenProps> = ({ navigation }) => {
38
- const updateMemberMutation = useUpdateMember();
39
- const getImageUrlMutation = useGetImageUrl();
40
-
41
- const [showModal, setShowModal] = useState(false);
42
- const [addMemberSuccessClose, setAddMemberSuccessClose] = useState(false);
43
- const [showSuccessModal, setShowSuccessModal] = useState(false);
44
- const [addMemberImageUrls, setAddMemberImageUrls] = useState<Record<number, string>>({});
45
- const { setUser } = useAuthStore();
46
-
47
-
48
-
49
- const membersParams: MembersRequest = {
50
- PageNumber: 0,
51
- PageSize: 20,
52
- };
53
-
54
- const handleUpdateMember = (memberId: number, memberData: UpdateMemberRequest) => {
55
- updateMemberMutation.mutate({
56
- memberId,
57
- memberData: memberData
58
- }, {
59
- onSuccess: (response) => {
60
- if(response?.status === 202){
61
- Alert.alert("Add Member", response.data.message);
62
- }else{
63
- setAddMemberSuccessClose(true);
64
- setTimeout(() => {
65
- setShowModal(false);
66
- ToastManager.success( response.data.message);
67
- refetch()
68
- setAddMemberSuccessClose(false);
69
- }, 1000);
70
- }
71
- },
72
- onError: (error) => {
73
- const errorInfo = getApiErrorInfo(error);
74
- ToastManager.error(errorInfo?.message);
75
- }
76
- });
77
- };
78
-
79
- const { data, isLoading, isError, refetch } = useMembers(membersParams);
80
- const rawMembers = (data?.data?.data || []).filter((member: Member) => !member.isOwner);
81
-
82
-
83
- // Add memberImageUrl from state
84
- const members = rawMembers.map((member: any) => ({
85
- ...member,
86
- memberImageUrl: addMemberImageUrls[member.id] || undefined
87
- }));
88
-
89
- const ownerName = data?.data?.data?.find(member => member.isOwner === true)?.name;
90
-
91
-
92
- const handleSkip = () => {
93
- setShowSuccessModal(true);
94
- };
95
-
96
- const handleAddMember = () => {
97
- setShowModal(!showModal);
98
- };
99
-
100
- const handleSaveMember = (memberData: any) => {
101
- const newMember: UpdateMemberRequest = {
102
- birthDate: memberData.dateOfBirth,
103
- relation: memberData.relationshipType,
104
- name: memberData.name,
105
- profileImage: memberData.profileImage?.fileName || '',
106
- };
107
- handleUpdateMember(0, newMember)
108
- };
109
-
110
- const handleNext = () => {
111
- // Update user to mark that member addition is complete
112
- const currentUser = useAuthStore.getState().user;
113
- if (currentUser) {
114
- setUser({
115
- ...currentUser,
116
- isAddMember: false
117
- });
118
- }
119
- };
120
-
121
- const getAddMemberImageUrls = async (memberItem: any) => {
122
- if (memberItem?.profileImage == null) return;
123
-
124
- // Skip if already fetched
125
- if (addMemberImageUrls[memberItem.id] !== undefined) return;
126
-
127
- const [folder, blobName] = memberItem?.profileImage.split('/');
128
-
129
- try {
130
- getImageUrlMutation.mutate({
131
- containerName: folder,
132
- blobName: blobName
133
- }, {
134
- onSuccess: (response) => {
135
- const imageUrl = response?.data?.data?.url;
136
- console.log('AddMember Image URL response', response.data);
137
-
138
- // Prefetch image into React Native cache to avoid blink when scrolling back
139
- if (imageUrl) {
140
- Image.prefetch(imageUrl).catch((prefetchError) => {
141
- console.warn('AddMember Image prefetch failed:', prefetchError);
142
- });
143
- }
144
-
145
- // Simple state update instead of recreating entire query cache
146
- setAddMemberImageUrls(prev => ({
147
- ...prev,
148
- [memberItem.id]: imageUrl
149
- }));
150
- },
151
- onError: (error) => {
152
- const errorInfo = getApiErrorInfo(error);
153
- ToastManager.error(errorInfo?.message);
154
- console.error('Failed to get AddMember image URL:', error);
155
-
156
- // Simple state update for failed image fetch
157
- setAddMemberImageUrls(prev => ({
158
- ...prev,
159
- [memberItem.id]: ""
160
- }));
161
- }
162
- });
163
- } catch (err) {
164
- console.error('Error in getAddMemberImageUrls:', err);
165
- setAddMemberImageUrls(prev => ({
166
- ...prev,
167
- [memberItem.id]: ""
168
- }));
169
- }
170
- };
171
-
172
- const onAddMemberViewableItemsChanged = useCallback(
173
- ({
174
- viewableItems,
175
- }: {
176
- viewableItems: Array<{ item: any }>;
177
- }) => {
178
- const items = viewableItems.map(({ item }) => item);
179
-
180
- // On-demand image fetching for viewable member items
181
- items.forEach((item) => {
182
- if (item && !item.memberImageUrl) {
183
- console.log('AddMember onViewableItemsChanged>>>>>', JSON.stringify(item));
184
- getAddMemberImageUrls(item);
185
- }
186
- });
187
- },
188
- [getAddMemberImageUrls]
189
- );
190
-
191
- const addMemberViewabilityConfig = useRef({
192
- viewAreaCoveragePercentThreshold: 50, // Increased threshold for better on-demand loading
193
- minimumViewTime: 100, // Minimum time item must be visible before triggering
194
- waitForInteraction: false, // Don't wait for user interaction
195
- }).current;
196
-
197
- const renderMemberItem = ({ item }: { item: Member }) => {
198
- return (
199
- <View style={styles.memberItem}>
200
- {item?.profileImage ? (
201
- <Image source={{ uri: item?.profileImage }} style={styles.profileImage} />
202
- ) : (
203
- <View style={[styles.profileImage, { backgroundColor: theme.colors.surface, justifyContent: 'center', alignItems: 'center' }]}>
204
- <SVG.emptyUser width={moderateScale(20)} height={moderateScale(20)} />
205
- </View>
206
- )}
207
- <Text style={styles.memberName}>{item.name}</Text>
208
- </View>
209
- );
210
- };
211
-
212
- const keyExtractor = (item: Member, index: number) => {
213
- // Create a more robust key that combines multiple attributes to ensure uniqueness
214
- const id = item?.id?.toString() || '';
215
- const name = item?.name || '';
216
- const email = item?.profileImage || '';
217
-
218
- // Create a unique composite key using multiple attributes
219
- const keyParts = [id, name, email].filter(part => part && part.toString().trim() !== '');
220
-
221
- if (keyParts.length > 0) {
222
- return `member-${keyParts.join('-')}-${index}`;
223
- }
224
-
225
- // Ultimate fallback with index to ensure uniqueness
226
- return `member-unknown-${index}`;
227
- };
228
-
229
- return (
230
- <View style={styles.container}>
231
- <StatusBar
232
- barStyle="light-content"
233
- backgroundColor={theme.colors.blue}
234
- translucent={Platform.OS === 'android' ? true : false}
235
- />
236
-
237
- {/* Status Bar Background for iOS */}
238
- {Platform.OS === 'ios' && <View style={styles.statusBarBackground} />}
239
-
240
- {/* Header Section */}
241
- <SafeAreaView style={styles.header} edges={['top']}>
242
- <TouchableOpacity onPress={handleSkip} style={styles.skipButton}>
243
- <Text style={styles.skipButtonText}>{Strings.AUTH.SKIP}</Text>
244
- </TouchableOpacity>
245
- <Text style={styles.headerTitle}>{Strings.AUTH.ADD_MEMBER_TITLE}</Text>
246
- </SafeAreaView>
247
-
248
- {/* Add Member Button */}
249
- <View style={styles.addMemberContainer}>
250
- <TouchableOpacity style={styles.addMemberButton} onPress={handleAddMember}>
251
- <SVG.icAdd width={moderateScale(20)} height={moderateScale(20)} />
252
- <Text style={styles.addMemberButtonText}>{Strings.AUTH.ADD_MEMBER_SUBTITLE}</Text>
253
- </TouchableOpacity>
254
- </View>
255
-
256
- {/* Content Section */}
257
- <View style={styles.content}>
258
- <Text style={styles.sectionTitle}>{Strings.AUTH.ADD_MEMBER_SECTION_TITLE}</Text>
259
-
260
- <FlatList
261
- data={members}
262
- renderItem={renderMemberItem}
263
- keyExtractor={keyExtractor}
264
- style={styles.membersList}
265
- showsVerticalScrollIndicator={false}
266
- contentContainerStyle={styles.flatListContent}
267
- removeClippedSubviews={true}
268
- maxToRenderPerBatch={10}
269
- windowSize={10}
270
- initialNumToRender={10}
271
- // onViewableItemsChanged={onAddMemberViewableItemsChanged}
272
- // viewabilityConfig={addMemberViewabilityConfig}
273
- getItemLayout={(data, index) => ({
274
- length: moderateScale(70), // Approximate item height
275
- offset: moderateScale(70) * index,
276
- index,
277
- })}
278
- refreshing={isLoading}
279
- onRefresh={refetch}
280
- ListEmptyComponent={() => (
281
- <Text style={styles.emptyListText}>No members found</Text>
282
- )}
283
-
284
- />
285
- </View>
286
-
287
- <View style={styles.bottomContainer}>
288
- <Button
289
- disabled={members?.length <= 0 ? true : false}
290
- title={Strings.AUTH.NEXT}
291
- onPress={handleSkip}
292
- variant="primary"
293
- size="medium"
294
- style={styles.nextButton}
295
- />
296
- </View>
297
- <AddMemberModal
298
- visible={showModal}
299
- onClose={() => setShowModal(false)}
300
- onSave={handleSaveMember}
301
- onSuccessClose={addMemberSuccessClose}
302
- />
303
- <SuccessModal
304
- visible={showSuccessModal}
305
- userName={ownerName}
306
- onClose={() => setShowSuccessModal(false)}
307
- onGoToHome={() => handleNext()}
308
- onSkipToAddMember={() => navigation.navigate('AddMember' as never)}
309
- />
310
- </View>
311
- );
312
- };
313
-
314
- const styles = StyleSheet.create({
315
- container: {
316
- flex: 1,
317
- backgroundColor: theme.colors.blue,
318
- },
319
- statusBarBackground: {
320
- position: 'absolute',
321
- top: 0,
322
- left: 0,
323
- right: 0,
324
- height: Platform.OS === 'ios' ? 44 : 0, // Status bar height for iOS
325
- backgroundColor: theme.colors.blue,
326
- zIndex: 1000,
327
- },
328
- header: {
329
- backgroundColor: theme.colors.blue,
330
- paddingHorizontal: theme.spacing.lg,
331
- paddingVertical: theme.spacing.md,
332
- justifyContent: 'space-between',
333
- alignItems: 'center',
334
- },
335
- headerTitle: {
336
- alignSelf: 'flex-start',
337
- fontFamily: Fonts.outfitSemiBold,
338
- fontSize: moderateScale(24),
339
- color: theme.colors.white,
340
- marginTop: moderateScale(20),
341
- },
342
- skipButton: {
343
- alignSelf: 'flex-end',
344
- paddingHorizontal: theme.spacing.sm,
345
- paddingVertical: theme.spacing.xs,
346
- },
347
- skipButtonText: {
348
- fontFamily: Fonts.outfitSemiBold,
349
- fontSize: theme.typography.fontSize.md,
350
- color: theme.colors.white,
351
- },
352
- addMemberContainer: {
353
- backgroundColor: "theme.colors.blue",
354
- paddingHorizontal: theme.spacing.lg,
355
- paddingBottom: theme.spacing.md,
356
- },
357
- addMemberButton: {
358
- flexDirection: 'row',
359
- alignItems: 'center',
360
- justifyContent: 'center',
361
- backgroundColor: 'transparent',
362
- borderWidth: 1.5,
363
- borderColor: theme.colors.appleGreen,
364
- borderRadius: theme.borderRadius.xxl,
365
- paddingVertical: theme.spacing.sm,
366
- paddingHorizontal: theme.spacing.lg,
367
- gap: theme.spacing.sm,
368
- },
369
- addMemberButtonText: {
370
- fontFamily: Fonts.outfitMedium,
371
- fontSize: theme.typography.fontSize.md,
372
- color: theme.colors.appleGreen,
373
- },
374
- content: {
375
- flex: 1,
376
- backgroundColor: theme.colors.background,
377
- paddingHorizontal: theme.spacing.lg,
378
- paddingTop: theme.spacing.md,
379
- borderTopLeftRadius: moderateScale(30),
380
- borderTopRightRadius: moderateScale(30),
381
- },
382
- sectionTitle: {
383
- fontFamily: Fonts.outfitMedium,
384
- fontSize: theme.typography.fontSize.xs,
385
- color: theme.colors.blue,
386
- marginBottom: theme.spacing.md,
387
- letterSpacing: 0.5,
388
- },
389
- membersList: {
390
- flex: 1,
391
- },
392
- flatListContent: {
393
- paddingBottom: theme.spacing.sm,
394
- },
395
- memberItem: {
396
- flexDirection: 'row',
397
- alignItems: 'center',
398
- paddingVertical: theme.spacing.sm,
399
- gap: theme.spacing.md,
400
- },
401
- profileImage: {
402
- width: moderateScale(50),
403
- height: moderateScale(50),
404
- borderRadius: moderateScale(25),
405
- backgroundColor: theme.colors.surface,
406
- },
407
- memberName: {
408
- fontFamily: Fonts.outfitBold,
409
- fontSize: theme.typography.fontSize.md,
410
- color: theme.colors.text,
411
- },
412
- bottomContainer: {
413
- height: moderateScale(120),
414
- paddingHorizontal: theme.spacing.lg,
415
- paddingVertical: theme.spacing.md,
416
- backgroundColor: theme.colors.white,
417
- },
418
- nextButton: {
419
- width: '100%',
420
- marginTop: moderateScale(-10),
421
- },
422
- emptyListText: {
423
- fontFamily: Fonts.outfitMedium,
424
- fontSize: theme.typography.fontSize.md,
425
- color: theme.colors.text,
426
- textAlign: 'center',
427
- },
428
- });
@@ -1,176 +0,0 @@
1
- import React, { useState } from 'react';
2
- import {
3
- View,
4
- Text,
5
- StyleSheet,
6
- TouchableOpacity,
7
- Keyboard,
8
- Platform,
9
- } from 'react-native';
10
- import { Button, hideLoader, showLoader, TextInput } from '../../components/common';
11
- import { theme } from '../../constants';
12
- import { Strings } from '../../constants/strings';
13
- import { Validation } from '../../utils';
14
- import { ImageBackground } from 'react-native';
15
- import Images from '../../assets/images';
16
- import SVG from '../../assets/icons';
17
- import { moderateScale } from '../../utils/scaling';
18
- import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
19
- import { SafeAreaView } from 'react-native-safe-area-context';
20
- import { StatusBar } from 'react-native';
21
- import { Fonts } from '../../constants/Fonts';
22
- import ToastManager from '../../components/common/ToastManager';
23
- import { useForgotPassword } from '../../services/authService';
24
-
25
- interface ForgotPasswordScreenProps {
26
- navigation: any;
27
- }
28
-
29
- export const ForgotPasswordScreen: React.FC<ForgotPasswordScreenProps> = ({ navigation }) => {
30
- const [email, setEmail] = useState('');
31
- const forgotPasswordMutation = useForgotPassword();
32
-
33
- const handleSubmit = async () => {
34
- Keyboard.dismiss();
35
- if (!Validation.validateForgotPasswordFormAndShowErrors(email)) {
36
- return;
37
- }
38
-
39
- showLoader();
40
- forgotPasswordMutation.mutate({ email: email }
41
- , {
42
- onSuccess: (response) => {
43
- navigation.reset({
44
- index: 0,
45
- routes: [{ name: 'Login' }],
46
- });
47
- ToastManager.success(response.data.message || 'Forgot password successful!');
48
- hideLoader();
49
- },
50
- onError: (error: any) => {
51
- console.error('Forgot password error:', error);
52
- if (error.response?.data?.message) {
53
- ToastManager.error( error.response.data.message);
54
- } else if (error.message) {
55
- ToastManager.error( error.message);
56
- } else {
57
- ToastManager.error('Something went wrong. Please try again.');
58
- }
59
- hideLoader();
60
- },
61
- onSettled: () => {
62
- hideLoader();
63
- }
64
- });
65
- };
66
-
67
-
68
-
69
- const onEmailChange = (text: string) => {
70
- const trimmedText = text.trim();
71
- setEmail(trimmedText);
72
- };
73
-
74
-
75
- return (
76
- <ImageBackground
77
- source={Images.backgroundImage}
78
- style={styles.backgroundImage}
79
- resizeMode="contain"
80
- >
81
- <SafeAreaView edges={['top']} style={{ flex: 1 }}>
82
- <StatusBar
83
- barStyle="dark-content"
84
- backgroundColor={theme.colors.background}
85
- translucent={Platform.OS === 'android' ? true : false}
86
- />
87
- <KeyboardAwareScrollView
88
- enableOnAndroid={true}
89
- contentContainerStyle={styles.container}
90
- keyboardShouldPersistTaps="handled"
91
- >
92
- <TouchableOpacity style={{ marginLeft: theme.spacing.md }} onPress={() => navigation.goBack()}>
93
- <SVG.arrowLeft height={moderateScale(30)} width={moderateScale(30)} style={styles.logoIcon} />
94
- </TouchableOpacity>
95
- <View style={styles.logoSection}>
96
- <SVG.login_logo height={moderateScale(150)} width={moderateScale(150)} style={styles.logoIcon} />
97
- </View>
98
-
99
- {/* Login Form */}
100
- <View style={styles.loginSection}>
101
- <Text style={styles.loginTitle}>{Strings.AUTH.FORGOT_PASSWORD_TITLE}</Text>
102
- <Text style={styles.loginSubTitle}>{Strings.AUTH.FORGOT_PASSWORD_SUBTITLE}</Text>
103
-
104
- <View style={styles.form}>
105
- <TextInput
106
- label={Strings.COMMON.EMAIL}
107
- value={email}
108
- onChangeText={onEmailChange}
109
- placeholder={Strings.COMMON.EMAIL_PLACEHOLDER}
110
- keyboardType="email-address"
111
- autoCapitalize="none"
112
- autoCorrect={false}
113
- variant="outlined"
114
- leftIcon={SVG.Email}
115
- maxLength={50}
116
- />
117
- <Button
118
- title={Strings.COMMON.SUBMIT}
119
- onPress={handleSubmit}
120
- variant="primary"
121
- size="medium"
122
- style={styles.button}
123
- />
124
- </View>
125
- </View>
126
- </KeyboardAwareScrollView>
127
- </SafeAreaView>
128
- </ImageBackground>
129
- );
130
- };
131
-
132
- const styles = StyleSheet.create({
133
- backgroundImage: {
134
- flex: 1,
135
- width: '100%',
136
- height: '100%',
137
- },
138
- container: {
139
- flexGrow: 1,
140
- },
141
- // Logo Section
142
- logoSection: {
143
- justifyContent: 'center',
144
- alignItems: 'center',
145
- },
146
- logoIcon: {
147
- marginBottom: theme.spacing.xs,
148
- },
149
- // Login Section
150
- loginSection: {
151
- flex: 1,
152
- backgroundColor: theme.colors.background,
153
- paddingHorizontal: theme.spacing.lg,
154
- borderTopLeftRadius: moderateScale(30),
155
- borderTopRightRadius: moderateScale(30),
156
- paddingTop: moderateScale(20),
157
- },
158
- loginTitle: {
159
- fontFamily: Fonts.outfitSemiBold,
160
- fontSize: moderateScale(24),
161
- color: theme.colors.black,
162
- marginBottom: theme.spacing.sm,
163
- },
164
- loginSubTitle: {
165
- fontFamily: Fonts.outfitRegular,
166
- fontSize: moderateScale(13),
167
- color: theme.colors.black,
168
- marginBottom: theme.spacing.md,
169
- },
170
- form: {
171
- gap: theme.spacing.sm,
172
- },
173
- button: {
174
- marginTop: theme.spacing.md,
175
- }
176
- });