@umituz/react-native-settings 4.17.14 → 4.17.16

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 (103) hide show
  1. package/package.json +16 -15
  2. package/src/domains/about/__tests__/integration.test.tsx +328 -0
  3. package/src/domains/about/__tests__/types.d.ts +5 -0
  4. package/src/domains/about/domain/entities/AppInfo.ts +74 -0
  5. package/src/domains/about/domain/entities/__tests__/AppInfo.test.ts +93 -0
  6. package/src/domains/about/domain/repositories/IAboutRepository.ts +22 -0
  7. package/src/domains/about/index.ts +10 -0
  8. package/src/domains/about/infrastructure/repositories/AboutRepository.ts +68 -0
  9. package/src/domains/about/infrastructure/repositories/__tests__/AboutRepository.test.ts +153 -0
  10. package/src/domains/about/presentation/components/AboutContent.tsx +104 -0
  11. package/src/domains/about/presentation/components/AboutHeader.tsx +79 -0
  12. package/src/domains/about/presentation/components/AboutSection.tsx +134 -0
  13. package/src/domains/about/presentation/components/AboutSettingItem.tsx +208 -0
  14. package/src/domains/about/presentation/components/__tests__/AboutContent.simple.test.tsx +178 -0
  15. package/src/domains/about/presentation/components/__tests__/AboutContent.test.tsx +293 -0
  16. package/src/domains/about/presentation/components/__tests__/AboutHeader.test.tsx +201 -0
  17. package/src/domains/about/presentation/components/__tests__/AboutSettingItem.test.tsx +71 -0
  18. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.simple.test.tsx +229 -0
  19. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.test.tsx +240 -0
  20. package/src/domains/about/presentation/hooks/useAboutInfo.ts +262 -0
  21. package/src/domains/about/presentation/screens/AboutScreen.tsx +195 -0
  22. package/src/domains/about/presentation/screens/__tests__/AboutScreen.simple.test.tsx +199 -0
  23. package/src/domains/about/presentation/screens/__tests__/AboutScreen.test.tsx +366 -0
  24. package/src/domains/about/types/global.d.ts +15 -0
  25. package/src/domains/about/utils/__tests__/index.test.ts +408 -0
  26. package/src/domains/about/utils/index.ts +160 -0
  27. package/src/domains/appearance/__tests__/components/AppearanceScreen.test.tsx +195 -0
  28. package/src/domains/appearance/__tests__/hooks/index.test.tsx +232 -0
  29. package/src/domains/appearance/__tests__/integration/index.test.tsx +207 -0
  30. package/src/domains/appearance/__tests__/services/appearanceService.test.ts +299 -0
  31. package/src/domains/appearance/__tests__/setup.ts +96 -0
  32. package/src/domains/appearance/__tests__/stores/appearanceStore.test.tsx +175 -0
  33. package/src/domains/appearance/data/colorPalettes.ts +94 -0
  34. package/src/domains/appearance/hooks/index.ts +6 -0
  35. package/src/domains/appearance/hooks/useAppearance.ts +61 -0
  36. package/src/domains/appearance/hooks/useAppearanceActions.ts +144 -0
  37. package/src/domains/appearance/index.ts +7 -0
  38. package/src/domains/appearance/infrastructure/services/appearanceService.ts +301 -0
  39. package/src/domains/appearance/infrastructure/services/systemThemeDetection.ts +79 -0
  40. package/src/domains/appearance/infrastructure/services/validation.ts +91 -0
  41. package/src/domains/appearance/infrastructure/storage/appearanceStorage.ts +120 -0
  42. package/src/domains/appearance/infrastructure/stores/appearanceStore.ts +132 -0
  43. package/src/domains/appearance/presentation/components/AppearanceHeader.tsx +67 -0
  44. package/src/domains/appearance/presentation/components/AppearancePreview.tsx +141 -0
  45. package/src/domains/appearance/presentation/components/AppearanceSection.tsx +139 -0
  46. package/src/domains/appearance/presentation/components/ColorPicker.tsx +113 -0
  47. package/src/domains/appearance/presentation/components/CustomColorsSection.tsx +186 -0
  48. package/src/domains/appearance/presentation/components/ThemeModeSection.tsx +110 -0
  49. package/src/domains/appearance/presentation/components/ThemeOption.tsx +138 -0
  50. package/src/domains/appearance/presentation/components/index.ts +6 -0
  51. package/src/domains/appearance/presentation/screens/AppearanceScreen.tsx +226 -0
  52. package/src/domains/appearance/presentation/screens/index.ts +2 -0
  53. package/src/domains/appearance/types/index.ts +54 -0
  54. package/src/domains/faqs/domain/entities/FAQEntity.ts +16 -0
  55. package/src/domains/faqs/domain/services/FAQSearchService.ts +36 -0
  56. package/src/domains/faqs/domain/services/index.ts +1 -0
  57. package/src/domains/faqs/index.ts +7 -0
  58. package/src/domains/faqs/presentation/components/FAQCategory.tsx +71 -0
  59. package/src/domains/faqs/presentation/components/FAQEmptyState.tsx +75 -0
  60. package/src/domains/faqs/presentation/components/FAQItem.tsx +103 -0
  61. package/src/domains/faqs/presentation/components/FAQSearchBar.tsx +70 -0
  62. package/src/domains/faqs/presentation/components/FAQSection.tsx +50 -0
  63. package/src/domains/faqs/presentation/components/index.ts +18 -0
  64. package/src/domains/faqs/presentation/hooks/index.ts +6 -0
  65. package/src/domains/faqs/presentation/hooks/useFAQExpansion.ts +51 -0
  66. package/src/domains/faqs/presentation/hooks/useFAQSearch.ts +33 -0
  67. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +129 -0
  68. package/src/domains/faqs/presentation/screens/index.ts +2 -0
  69. package/src/domains/feedback/domain/entities/FeedbackEntity.ts +92 -0
  70. package/src/domains/feedback/domain/repositories/IFeedbackRepository.ts +28 -0
  71. package/src/domains/feedback/index.ts +6 -0
  72. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +189 -0
  73. package/src/domains/feedback/presentation/components/FeedbackModal.tsx +111 -0
  74. package/src/domains/feedback/presentation/components/SupportSection.tsx +160 -0
  75. package/src/domains/feedback/presentation/hooks/useDeleteFeedback.ts +25 -0
  76. package/src/domains/feedback/presentation/hooks/useFeedbackForm.ts +59 -0
  77. package/src/domains/feedback/presentation/hooks/useSubmitFeedback.ts +55 -0
  78. package/src/domains/feedback/presentation/hooks/useUserFeedback.ts +29 -0
  79. package/src/domains/legal/__tests__/ContentValidationService.test.ts +195 -0
  80. package/src/domains/legal/__tests__/StyleCacheService.test.ts +110 -0
  81. package/src/domains/legal/__tests__/UrlHandlerService.test.ts +71 -0
  82. package/src/domains/legal/__tests__/setup.ts +82 -0
  83. package/src/domains/legal/domain/entities/LegalConfig.ts +26 -0
  84. package/src/domains/legal/domain/services/ContentValidationService.ts +89 -0
  85. package/src/domains/legal/domain/services/StyleCacheService.ts +97 -0
  86. package/src/domains/legal/domain/services/UrlHandlerService.ts +128 -0
  87. package/src/domains/legal/index.ts +8 -0
  88. package/src/domains/legal/presentation/components/LegalItem.tsx +177 -0
  89. package/src/domains/legal/presentation/components/LegalLinks.tsx +154 -0
  90. package/src/domains/legal/presentation/components/LegalSection.tsx +134 -0
  91. package/src/domains/legal/presentation/screens/LegalScreen.tsx +237 -0
  92. package/src/domains/legal/presentation/screens/PrivacyPolicyScreen.tsx +214 -0
  93. package/src/domains/legal/presentation/screens/TermsOfServiceScreen.tsx +214 -0
  94. package/src/index.ts +19 -0
  95. package/src/presentation/components/DevSettingsSection.tsx +2 -2
  96. package/src/presentation/components/SettingItem.tsx +2 -2
  97. package/src/presentation/components/SettingsErrorBoundary.tsx +2 -2
  98. package/src/presentation/components/SettingsFooter.tsx +2 -2
  99. package/src/presentation/components/SettingsSection.tsx +2 -2
  100. package/src/presentation/navigation/SettingsStackNavigator.tsx +2 -2
  101. package/src/presentation/screens/SettingsScreen.tsx +2 -2
  102. package/src/presentation/screens/components/SettingsContent.tsx +2 -2
  103. package/src/presentation/screens/components/SettingsHeader.tsx +2 -2
@@ -0,0 +1,129 @@
1
+ /**
2
+ * FAQ Screen Component
3
+ * Full-screen FAQ viewer with search functionality
4
+ * Uses design system tokens for theming
5
+ */
6
+
7
+ import React, { useMemo } from 'react';
8
+ import { View, ScrollView, StyleSheet, ViewStyle, TextStyle } from 'react-native';
9
+ import { useResponsiveDesignTokens, AtomicText, ScreenLayout } from '@umituz/react-native-design-system';
10
+ import { FAQCategory } from '../../domain/entities/FAQEntity';
11
+ import { useFAQSearch } from '../hooks/useFAQSearch';
12
+ import { useFAQExpansion } from '../hooks/useFAQExpansion';
13
+ import { FAQSearchBar, FAQSearchBarStyles } from '../components/FAQSearchBar';
14
+ import { FAQEmptyState, FAQEmptyStateStyles } from '../components/FAQEmptyState';
15
+ import { FAQCategoryComponent, FAQCategoryStyles } from '../components/FAQCategory';
16
+
17
+ export interface FAQScreenStyles {
18
+ container?: ViewStyle;
19
+ header?: ViewStyle;
20
+ title?: TextStyle;
21
+ content?: ViewStyle;
22
+ searchBar?: FAQSearchBarStyles;
23
+ emptyState?: FAQEmptyStateStyles;
24
+ category?: FAQCategoryStyles;
25
+ }
26
+
27
+ export interface FAQScreenProps {
28
+ categories: FAQCategory[];
29
+ searchPlaceholder: string;
30
+ emptySearchTitle: string;
31
+ emptySearchMessage: string;
32
+ headerTitle: string;
33
+ onBack?: () => void;
34
+ renderHeader?: (props: { onBack: () => void }) => React.ReactElement | null;
35
+ styles?: FAQScreenStyles;
36
+ }
37
+
38
+ export const FAQScreen: React.FC<FAQScreenProps> = ({
39
+ categories,
40
+ searchPlaceholder,
41
+ emptySearchTitle,
42
+ emptySearchMessage,
43
+ headerTitle,
44
+ onBack,
45
+ renderHeader,
46
+ styles: customStyles,
47
+ }) => {
48
+ const tokens = useResponsiveDesignTokens();
49
+ const { searchQuery, setSearchQuery, filteredCategories, hasResults } =
50
+ useFAQSearch(categories);
51
+ const { isExpanded, toggleExpansion } = useFAQExpansion();
52
+
53
+ const styles = useMemo(
54
+ () =>
55
+ StyleSheet.create({
56
+ container: {
57
+ flex: 1,
58
+ backgroundColor: tokens.colors.backgroundPrimary,
59
+ },
60
+ header: {
61
+ padding: tokens.spacing.md,
62
+ borderBottomWidth: 1,
63
+ borderBottomColor: tokens.colors.border,
64
+ },
65
+ content: {
66
+ flex: 1,
67
+ },
68
+ }),
69
+ [tokens]
70
+ );
71
+
72
+ const renderContent = () => {
73
+ if (searchQuery && !hasResults) {
74
+ return (
75
+ <FAQEmptyState
76
+ title={emptySearchTitle}
77
+ message={emptySearchMessage}
78
+ styles={customStyles?.emptyState}
79
+ />
80
+ );
81
+ }
82
+
83
+ return (
84
+ <ScrollView
85
+ style={[styles.content, customStyles?.content]}
86
+ showsVerticalScrollIndicator={false}
87
+ >
88
+ <View style={[styles.header, customStyles?.header]}>
89
+ <AtomicText type="headlineMedium" color="textPrimary" style={{ marginBottom: tokens.spacing.sm }}>
90
+ {headerTitle}
91
+ </AtomicText>
92
+ <FAQSearchBar
93
+ value={searchQuery}
94
+ onChangeText={setSearchQuery}
95
+ placeholder={searchPlaceholder}
96
+ styles={customStyles?.searchBar}
97
+ />
98
+ </View>
99
+
100
+ {filteredCategories.map((category) => (
101
+ <FAQCategoryComponent
102
+ key={category.id}
103
+ category={category}
104
+ isExpanded={isExpanded}
105
+ onToggleItem={toggleExpansion}
106
+ styles={customStyles?.category}
107
+ />
108
+ ))}
109
+ </ScrollView>
110
+ );
111
+ };
112
+
113
+ if (renderHeader) {
114
+ return (
115
+ <View style={[styles.container, customStyles?.container]}>
116
+ {renderHeader({ onBack: onBack || (() => { }) })}
117
+ {renderContent()}
118
+ </View>
119
+ );
120
+ }
121
+
122
+ return (
123
+ <ScreenLayout edges={['bottom']}>
124
+ <View style={[styles.container, customStyles?.container]}>
125
+ {renderContent()}
126
+ </View>
127
+ </ScreenLayout>
128
+ );
129
+ };
@@ -0,0 +1,2 @@
1
+ export { FAQScreen } from "./FAQScreen";
2
+ export type { FAQScreenProps } from "./FAQScreen";
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Feedback Entity
3
+ */
4
+
5
+ export type FeedbackType =
6
+ | 'general'
7
+ | 'bug_report'
8
+ | 'feature_request'
9
+ | 'improvement'
10
+ | 'other';
11
+
12
+ export type FeedbackStatus = 'pending' | 'reviewed' | 'resolved' | 'closed';
13
+
14
+ export type FeedbackRating = 1 | 2 | 3 | 4 | 5;
15
+
16
+ export interface FeedbackEntity {
17
+ id: string;
18
+ userId: string | null;
19
+ userEmail?: string | null;
20
+ type: FeedbackType;
21
+ title: string;
22
+ description: string;
23
+ rating?: FeedbackRating;
24
+ status: FeedbackStatus;
25
+ deviceInfo?: {
26
+ platform: string;
27
+ osVersion: string;
28
+ appVersion: string;
29
+ };
30
+ metadata?: Record<string, unknown>;
31
+ createdAt: string;
32
+ updatedAt: string;
33
+ }
34
+
35
+ export function createFeedback(
36
+ userId: string | null,
37
+ type: FeedbackType,
38
+ title: string,
39
+ description: string,
40
+ userEmail?: string | null,
41
+ rating?: FeedbackRating,
42
+ deviceInfo?: FeedbackEntity['deviceInfo'],
43
+ metadata?: Record<string, unknown>
44
+ ): Omit<FeedbackEntity, 'id'> {
45
+ const now = new Date().toISOString();
46
+ return {
47
+ userId,
48
+ userEmail,
49
+ type,
50
+ title,
51
+ description,
52
+ rating,
53
+ status: 'pending',
54
+ deviceInfo,
55
+ metadata,
56
+ createdAt: now,
57
+ updatedAt: now,
58
+ };
59
+ }
60
+
61
+ export function getFeedbackTypeLabel(type: FeedbackType): string {
62
+ const labels: Record<FeedbackType, string> = {
63
+ general: 'General Feedback',
64
+ bug_report: 'Bug Report',
65
+ feature_request: 'Feature Request',
66
+ improvement: 'Improvement',
67
+ other: 'Other',
68
+ };
69
+ return labels[type] || 'Unknown';
70
+ }
71
+
72
+ export function getFeedbackTypeEmoji(type: FeedbackType): string {
73
+ const emojis: Record<FeedbackType, string> = {
74
+ general: '💬',
75
+ bug_report: '🐛',
76
+ feature_request: '✨',
77
+ improvement: '🔧',
78
+ other: '📝',
79
+ };
80
+ return emojis[type] || '📝';
81
+ }
82
+
83
+ export function getFeedbackTypeIcon(type: FeedbackType): string {
84
+ const icons: Record<FeedbackType, string> = {
85
+ general: 'message-circle',
86
+ bug_report: 'bug',
87
+ feature_request: 'sparkles',
88
+ improvement: 'wrench',
89
+ other: 'file-text',
90
+ };
91
+ return icons[type] || 'file-text';
92
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Feedback Repository Interface
3
+ */
4
+
5
+ import type { FeedbackEntity } from '../entities/FeedbackEntity';
6
+
7
+ export interface FeedbackError {
8
+ message: string;
9
+ code?: 'SUBMIT_FAILED' | 'FETCH_FAILED' | 'DELETE_FAILED' | 'VALIDATION_ERROR';
10
+ }
11
+
12
+ export type FeedbackResult<T> =
13
+ | {
14
+ success: true;
15
+ data: T;
16
+ }
17
+ | {
18
+ success: false;
19
+ error: FeedbackError;
20
+ };
21
+
22
+ export interface IFeedbackRepository {
23
+ submitFeedback(
24
+ feedback: FeedbackEntity | Omit<FeedbackEntity, 'id'>
25
+ ): Promise<FeedbackResult<FeedbackEntity>>;
26
+ getUserFeedback(userId: string): Promise<FeedbackResult<FeedbackEntity[]>>;
27
+ deleteFeedback(feedbackId: string): Promise<FeedbackResult<boolean>>;
28
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Feedback Domain
3
+ * User feedback, bug reports
4
+ */
5
+
6
+ export * from './presentation/screens/FeedbackScreen';
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Feedback Form Component
3
+ * Form for submitting user feedback with type, rating, and description
4
+ */
5
+
6
+ import React, { useState } from "react";
7
+ import { View, StyleSheet, TouchableOpacity, ScrollView, TextInput } from "react-native";
8
+ import { useResponsiveDesignTokens, AtomicText, AtomicButton, AtomicIcon } from "@umituz/react-native-design-system";
9
+ import type { FeedbackType, FeedbackRating } from "../../domain/entities/FeedbackEntity";
10
+ import { useFeedbackForm } from "../hooks/useFeedbackForm";
11
+
12
+ export interface FeedbackFormProps {
13
+ onSubmit: (data: { type: FeedbackType; rating: FeedbackRating; description: string; title: string }) => Promise<void>;
14
+ texts: {
15
+ ratingLabel: string;
16
+ descriptionPlaceholder: string;
17
+ submitButton: string;
18
+ submittingButton: string;
19
+ feedbackTypes: Array<{ type: FeedbackType; label: string }>;
20
+ defaultTitle: (type: FeedbackType) => string;
21
+ };
22
+ initialType?: FeedbackType;
23
+ isSubmitting?: boolean;
24
+ }
25
+
26
+ export const FeedbackForm: React.FC<FeedbackFormProps> = ({
27
+ onSubmit,
28
+ texts,
29
+ initialType,
30
+ isSubmitting = false,
31
+ }) => {
32
+ const tokens = useResponsiveDesignTokens();
33
+ const [selectedType, setSelectedType] = useState<FeedbackType>(initialType || texts.feedbackTypes[0].type);
34
+ const [rating, setRating] = useState<FeedbackRating>(5);
35
+ const [description, setDescription] = useState("");
36
+ const [title, setTitle] = useState("");
37
+
38
+ const handleSubmit = async () => {
39
+ if (!description.trim()) return;
40
+
41
+ await onSubmit({
42
+ type: selectedType,
43
+ rating,
44
+ description,
45
+ title: title || texts.defaultTitle(selectedType),
46
+ });
47
+ };
48
+
49
+ const renderRating = () => (
50
+ <View style={styles.ratingContainer}>
51
+ <AtomicText type="bodyMedium" style={{ marginBottom: 8, color: tokens.colors.textSecondary }}>
52
+ {texts.ratingLabel}
53
+ </AtomicText>
54
+ <View style={styles.stars}>
55
+ {[1, 2, 3, 4, 5].map((star) => (
56
+ <TouchableOpacity
57
+ key={star}
58
+ onPress={() => setRating(star as FeedbackRating)}
59
+ style={styles.starButton}
60
+ >
61
+ <AtomicIcon
62
+ name={star <= rating ? "star" : "star-outline"}
63
+ customSize={32}
64
+ customColor={star <= rating ? tokens.colors.warning : tokens.colors.border}
65
+ />
66
+ </TouchableOpacity>
67
+ ))}
68
+ </View>
69
+ </View>
70
+ );
71
+
72
+ const renderTypeSelector = () => (
73
+ <ScrollView
74
+ horizontal
75
+ showsHorizontalScrollIndicator={false}
76
+ contentContainerStyle={styles.typeScroll}
77
+ style={styles.typeContainer}
78
+ >
79
+ {texts.feedbackTypes.map((item) => {
80
+ const isSelected = selectedType === item.type;
81
+
82
+ return (
83
+ <TouchableOpacity
84
+ key={item.type}
85
+ style={[
86
+ styles.typeButton,
87
+ {
88
+ backgroundColor: isSelected ? tokens.colors.primary : tokens.colors.surface,
89
+ borderColor: isSelected ? tokens.colors.primary : tokens.colors.border,
90
+ },
91
+ ]}
92
+ onPress={() => setSelectedType(item.type)}
93
+ >
94
+ <AtomicText
95
+ type="bodySmall"
96
+ style={{
97
+ color: isSelected ? tokens.colors.onPrimary : tokens.colors.textSecondary,
98
+ fontWeight: isSelected ? "600" : "400",
99
+ }}
100
+ >
101
+ {item.label}
102
+ </AtomicText>
103
+ </TouchableOpacity>
104
+ );
105
+ })}
106
+ </ScrollView>
107
+ );
108
+
109
+ return (
110
+ <View style={styles.container}>
111
+ {renderTypeSelector()}
112
+
113
+ {renderRating()}
114
+
115
+ <View style={styles.inputContainer}>
116
+ <TextInput
117
+ value={description}
118
+ onChangeText={setDescription}
119
+ placeholder={texts.descriptionPlaceholder}
120
+ placeholderTextColor={tokens.colors.textTertiary}
121
+ multiline
122
+ numberOfLines={4}
123
+ style={[
124
+ styles.textArea,
125
+ {
126
+ color: tokens.colors.textPrimary,
127
+ backgroundColor: tokens.colors.surface,
128
+ borderColor: tokens.colors.border,
129
+ }
130
+ ]}
131
+ />
132
+ </View>
133
+
134
+ <AtomicButton
135
+ onPress={handleSubmit}
136
+ disabled={isSubmitting || !description.trim()}
137
+ style={styles.submitButton}
138
+ >
139
+ {isSubmitting ? texts.submittingButton : texts.submitButton}
140
+ </AtomicButton>
141
+ </View>
142
+ );
143
+ };
144
+
145
+ const styles = StyleSheet.create({
146
+ container: {
147
+ width: "100%",
148
+ },
149
+ typeContainer: {
150
+ marginBottom: 24,
151
+ },
152
+ typeScroll: {
153
+ gap: 8,
154
+ },
155
+ typeButton: {
156
+ flexDirection: "row",
157
+ alignItems: "center",
158
+ paddingHorizontal: 16,
159
+ paddingVertical: 8,
160
+ borderRadius: 20,
161
+ borderWidth: 1,
162
+ gap: 6,
163
+ },
164
+ ratingContainer: {
165
+ alignItems: "center",
166
+ marginBottom: 24,
167
+ },
168
+ stars: {
169
+ flexDirection: "row",
170
+ gap: 8,
171
+ },
172
+ starButton: {
173
+ padding: 4,
174
+ },
175
+ inputContainer: {
176
+ marginBottom: 24,
177
+ },
178
+ textArea: {
179
+ textAlignVertical: "top",
180
+ minHeight: 120,
181
+ borderWidth: 1,
182
+ borderRadius: 8,
183
+ padding: 12,
184
+ fontSize: 16,
185
+ },
186
+ submitButton: {
187
+ width: "100%",
188
+ },
189
+ });
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Feedback Modal Component
3
+ * Modal wrapper for providing feedback
4
+ */
5
+
6
+ import React from "react";
7
+ import { Modal, View, StyleSheet, TouchableOpacity, ScrollView, KeyboardAvoidingView, Platform } from "react-native";
8
+ import { SafeAreaView } from "react-native-safe-area-context";
9
+ import { useResponsiveDesignTokens, AtomicText, AtomicIcon } from "@umituz/react-native-design-system";
10
+ import { FeedbackForm } from "./FeedbackForm";
11
+ import type { FeedbackType, FeedbackRating } from "../../domain/entities/FeedbackEntity";
12
+
13
+ export interface FeedbackModalProps {
14
+ visible: boolean;
15
+ onClose: () => void;
16
+ onSubmit: (data: { type: FeedbackType; rating: FeedbackRating; description: string; title: string }) => Promise<void>;
17
+ initialType?: FeedbackType;
18
+ isSubmitting?: boolean;
19
+ title?: string;
20
+ subtitle?: string;
21
+ texts: any; // Type should ideally be shared or imported
22
+ }
23
+
24
+ export const FeedbackModal: React.FC<FeedbackModalProps> = ({
25
+ visible,
26
+ onClose,
27
+ onSubmit,
28
+ initialType,
29
+ isSubmitting,
30
+ title,
31
+ subtitle,
32
+ texts,
33
+ }) => {
34
+ const tokens = useResponsiveDesignTokens();
35
+
36
+ return (
37
+ <Modal
38
+ visible={visible}
39
+ animationType="slide"
40
+ presentationStyle="pageSheet"
41
+ onRequestClose={onClose}
42
+ >
43
+ <SafeAreaView style={[styles.safeArea, { backgroundColor: tokens.colors.backgroundPrimary }]}>
44
+ <KeyboardAvoidingView
45
+ behavior={Platform.OS === "ios" ? "padding" : "height"}
46
+ style={styles.keyboardView}
47
+ >
48
+ <View style={[styles.header, { borderBottomColor: tokens.colors.border }]}>
49
+ <View style={styles.headerText}>
50
+ <AtomicText type="headlineSmall" style={{ color: tokens.colors.textPrimary }}>
51
+ {title}
52
+ </AtomicText>
53
+ {subtitle && (
54
+ <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary, marginTop: 4 }}>
55
+ {subtitle}
56
+ </AtomicText>
57
+ )}
58
+ </View>
59
+ <TouchableOpacity
60
+ onPress={onClose}
61
+ style={[styles.closeButton, { backgroundColor: tokens.colors.surface }]}
62
+ >
63
+ <AtomicIcon name="close" customSize={20} customColor={tokens.colors.textPrimary} />
64
+ </TouchableOpacity>
65
+ </View>
66
+
67
+ <ScrollView
68
+ contentContainerStyle={styles.content}
69
+ keyboardShouldPersistTaps="handled"
70
+ >
71
+ <FeedbackForm
72
+ onSubmit={onSubmit}
73
+ initialType={initialType}
74
+ isSubmitting={isSubmitting}
75
+ texts={texts}
76
+ />
77
+ </ScrollView>
78
+ </KeyboardAvoidingView>
79
+ </SafeAreaView>
80
+ </Modal>
81
+ );
82
+ };
83
+
84
+ const styles = StyleSheet.create({
85
+ safeArea: {
86
+ flex: 1,
87
+ },
88
+ keyboardView: {
89
+ flex: 1,
90
+ },
91
+ header: {
92
+ flexDirection: "row",
93
+ justifyContent: "space-between",
94
+ alignItems: "center",
95
+ padding: 16,
96
+ borderBottomWidth: 1,
97
+ },
98
+ headerText: {
99
+ flex: 1,
100
+ },
101
+ closeButton: {
102
+ width: 36,
103
+ height: 36,
104
+ borderRadius: 18,
105
+ justifyContent: "center",
106
+ alignItems: "center",
107
+ },
108
+ content: {
109
+ padding: 20,
110
+ },
111
+ });