@umituz/react-native-settings 5.2.34 → 5.2.36

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 (70) hide show
  1. package/package.json +2 -4
  2. package/src/domains/about/presentation/screens/AboutScreenContent.tsx +87 -63
  3. package/src/domains/appearance/data/colorPalettes.ts +0 -23
  4. package/src/domains/appearance/presentation/components/CustomColorsSection.tsx +2 -4
  5. package/src/domains/appearance/presentation/components/ThemeOption.tsx +2 -2
  6. package/src/domains/dev/presentation/components/DevSettingsSection.tsx +5 -2
  7. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +19 -25
  8. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +160 -81
  9. package/src/domains/gamification/components/GamificationScreen/GamificationScreenWithConfig.tsx +11 -11
  10. package/src/domains/localization/infrastructure/components/LanguageSwitcher.tsx +0 -2
  11. package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +1 -1
  12. package/src/domains/localization/presentation/screens/LanguageSelectionScreen.tsx +85 -48
  13. package/src/domains/localization/presentation/screens/__tests__/LanguageSelectionScreen.test.tsx +0 -15
  14. package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +1 -3
  15. package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +0 -4
  16. package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +69 -31
  17. package/src/domains/rating/presentation/components/StarRating.tsx +7 -13
  18. package/src/infrastructure/utils/configFactory.ts +0 -26
  19. package/src/infrastructure/utils/constants/textLimits.ts +0 -2
  20. package/src/infrastructure/utils/sanitizers.ts +1 -25
  21. package/src/infrastructure/utils/validation/core.ts +0 -33
  22. package/src/infrastructure/utils/validation/formValidators.ts +7 -1
  23. package/src/infrastructure/utils/validation/index.ts +2 -33
  24. package/src/infrastructure/utils/validators.ts +0 -6
  25. package/src/presentation/navigation/utils/index.ts +1 -7
  26. package/src/presentation/navigation/utils/navigationHelpers.ts +2 -87
  27. package/src/presentation/screens/components/SettingsContent.tsx +4 -19
  28. package/src/presentation/screens/components/sections/CustomSettingsList.tsx +3 -8
  29. package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +0 -4
  30. package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +0 -4
  31. package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +0 -4
  32. package/src/presentation/utils/screenFactory.ts +0 -25
  33. package/src/utils/appUtils.ts +0 -18
  34. package/src/utils/devUtils.ts +0 -10
  35. package/src/utils/errorUtils.ts +0 -22
  36. package/src/domains/about/utils/index.ts +0 -156
  37. package/src/domains/faqs/domain/services/index.ts +0 -1
  38. package/src/domains/faqs/presentation/screens/index.ts +0 -2
  39. package/src/domains/gamification/components/GamificationScreen/Header.tsx +0 -30
  40. package/src/domains/legal/presentation/components/LegalLinks.tsx +0 -137
  41. package/src/domains/legal/presentation/components/index.ts +0 -5
  42. package/src/domains/localization/infrastructure/config/languagesData.ts +0 -26
  43. package/src/domains/localization/infrastructure/hooks/TranslationHook.ts +0 -37
  44. package/src/domains/localization/infrastructure/storage/AsyncStorageWrapper.ts +0 -34
  45. package/src/infrastructure/storage/storeConfig.ts +0 -114
  46. package/src/infrastructure/types/commonComponentTypes.ts +0 -142
  47. package/src/infrastructure/utils/async/core.ts +0 -110
  48. package/src/infrastructure/utils/async/debounceAndBatch.ts +0 -69
  49. package/src/infrastructure/utils/async/index.ts +0 -8
  50. package/src/infrastructure/utils/async/retryAndTimeout.ts +0 -65
  51. package/src/infrastructure/utils/dateUtils.ts +0 -61
  52. package/src/infrastructure/utils/errorHandlers.ts +0 -250
  53. package/src/infrastructure/utils/index.ts +0 -12
  54. package/src/infrastructure/utils/memoComparisonUtils.ts +0 -66
  55. package/src/infrastructure/utils/memoUtils.ts +0 -167
  56. package/src/infrastructure/utils/styleTokens.ts +0 -145
  57. package/src/infrastructure/utils/styles/componentStyles.ts +0 -90
  58. package/src/infrastructure/utils/styles/index.ts +0 -9
  59. package/src/infrastructure/utils/styles/layoutStyles.ts +0 -56
  60. package/src/infrastructure/utils/styles/spacingStyles.ts +0 -33
  61. package/src/infrastructure/utils/styles/styleHelpers.ts +0 -22
  62. package/src/infrastructure/utils/translationHelpers.ts +0 -81
  63. package/src/infrastructure/utils/validation/numericValidators.ts +0 -66
  64. package/src/infrastructure/utils/validation/passwordValidator.ts +0 -53
  65. package/src/infrastructure/utils/validation/textValidators.ts +0 -118
  66. package/src/presentation/components/ErrorBoundary/SettingsErrorBoundary.tsx +0 -105
  67. package/src/presentation/components/ErrorBoundary/index.ts +0 -12
  68. package/src/presentation/components/ErrorBoundary/withErrorBoundary.tsx +0 -45
  69. package/src/utils/hooks/index.ts +0 -6
  70. package/src/utils/index.ts +0 -3
@@ -38,40 +38,40 @@ export const GamificationScreenWithConfig: React.FC<GamificationConfigProps> = (
38
38
  type: a.type
39
39
  }));
40
40
 
41
- const translations = config.translations || {};
41
+ const t = config.translations ?? {};
42
42
  const screenProps: GamificationScreenProps = {
43
- title: translations.title || 'Gamification',
44
- statsTitle: translations.statsTitle || 'Stats',
45
- achievementsTitle: translations.achievementsTitle || 'Achievements',
46
- streakTitle: translations.streakTitle || 'Streak',
43
+ title: t.title ?? 'Gamification',
44
+ statsTitle: t.statsTitle ?? 'Stats',
45
+ achievementsTitle: t.achievementsTitle ?? 'Achievements',
46
+ streakTitle: t.streakTitle ?? 'Streak',
47
47
  levelProps: {
48
48
  level: level.currentLevel,
49
49
  points: level.currentPoints,
50
50
  pointsToNext: level.pointsToNext,
51
51
  progress: level.progress,
52
- levelTitle: translations.levelTitle || 'Level',
52
+ levelTitle: t.levelTitle ?? 'Level',
53
53
  showPoints: true,
54
54
  },
55
55
  streakProps: {
56
56
  current: streak.current,
57
57
  longest: streak.longest,
58
- bestLabel: translations.bestStreak || 'Best',
59
- daysLabel: translations.days || 'days',
58
+ bestLabel: t.bestStreak ?? 'Best',
59
+ daysLabel: t.days ?? 'days',
60
60
  },
61
61
  stats: [
62
62
  {
63
- label: translations.pointsLabel || 'Points',
63
+ label: t.pointsLabel ?? 'Points',
64
64
  value: points,
65
65
  icon: "⭐",
66
66
  },
67
67
  {
68
- label: translations.totalCompletedLabel || 'Completed',
68
+ label: t.totalCompletedLabel ?? 'Completed',
69
69
  value: totalTasksCompleted,
70
70
  icon: "✅",
71
71
  },
72
72
  ],
73
73
  achievements: achievementItems,
74
- emptyAchievementsText: translations.emptyAchievements || 'No achievements yet',
74
+ emptyAchievementsText: t.emptyAchievements ?? 'No achievements yet',
75
75
  };
76
76
 
77
77
  return <GamificationScreenInner {...screenProps} />;
@@ -83,5 +83,3 @@ export const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({
83
83
  );
84
84
  };
85
85
 
86
- export default LanguageSwitcher;
87
-
@@ -8,7 +8,7 @@ import { LanguageSwitcher } from "./LanguageSwitcher";
8
8
  import { languageRepository } from "../repository/LanguageRepository";
9
9
  import { isDev } from "../../../../utils/devUtils";
10
10
 
11
- export const LANGUAGE_SWITCH_DEBOUNCE_MS = 300;
11
+ const LANGUAGE_SWITCH_DEBOUNCE_MS = 300;
12
12
 
13
13
  /**
14
14
  * Manages localization initialization state
@@ -12,13 +12,78 @@ import {
12
12
  NavigationHeader,
13
13
  useAppNavigation,
14
14
  } from '@umituz/react-native-design-system';
15
- import { isDev } from '../../../../utils/devUtils';
16
15
  import { useLanguageSelection } from '../../infrastructure/hooks/useLanguageSelection';
17
16
  import { LanguageItem } from '../components/LanguageItem';
18
17
  import type { Language } from '../../infrastructure/storage/types/Language';
19
18
  import type { LanguageSelectionScreenProps } from './LanguageSelectionScreen.types';
20
19
  import { styles } from './LanguageSelectionScreen.styles';
21
20
 
21
+ interface LanguageListItemProps {
22
+ item: Language;
23
+ selectedCode: string;
24
+ onSelect: (code: string) => void;
25
+ renderLanguageItem?: LanguageSelectionScreenProps['renderLanguageItem'];
26
+ customStyles?: LanguageSelectionScreenProps['styles'];
27
+ }
28
+
29
+ const LanguageListItem: React.FC<LanguageListItemProps> = ({
30
+ item,
31
+ selectedCode,
32
+ onSelect,
33
+ renderLanguageItem,
34
+ customStyles,
35
+ }) => {
36
+ const isSelected = selectedCode === item.code;
37
+
38
+ if (renderLanguageItem) {
39
+ return <>{renderLanguageItem(item, isSelected, onSelect)}</>;
40
+ }
41
+
42
+ return (
43
+ <LanguageItem
44
+ item={item}
45
+ isSelected={isSelected}
46
+ onSelect={onSelect}
47
+ customStyles={customStyles}
48
+ />
49
+ );
50
+ };
51
+
52
+ interface LanguageSearchComponentProps {
53
+ searchQuery: string;
54
+ setSearchQuery: (query: string) => void;
55
+ searchPlaceholder?: string;
56
+ renderSearchInput?: LanguageSelectionScreenProps['renderSearchInput'];
57
+ customStyles?: LanguageSelectionScreenProps['styles'];
58
+ }
59
+
60
+ const LanguageSearchComponent: React.FC<LanguageSearchComponentProps> = ({
61
+ searchQuery,
62
+ setSearchQuery,
63
+ searchPlaceholder,
64
+ renderSearchInput,
65
+ customStyles,
66
+ }) => {
67
+ const tokens = useAppDesignTokens();
68
+
69
+ if (renderSearchInput) {
70
+ return renderSearchInput(searchQuery, setSearchQuery, searchPlaceholder || '');
71
+ }
72
+
73
+ return (
74
+ <SearchBar
75
+ value={searchQuery}
76
+ onChangeText={setSearchQuery}
77
+ placeholder={searchPlaceholder}
78
+ containerStyle={[
79
+ { marginBottom: tokens.spacing.md },
80
+ customStyles?.searchContainer
81
+ ]}
82
+ inputStyle={customStyles?.searchInput}
83
+ />
84
+ );
85
+ };
86
+
22
87
  export const LanguageSelectionScreen: React.FC<LanguageSelectionScreenProps> = ({
23
88
  renderLanguageItem,
24
89
  renderSearchInput,
@@ -39,52 +104,20 @@ export const LanguageSelectionScreen: React.FC<LanguageSelectionScreenProps> = (
39
104
  } = useLanguageSelection();
40
105
 
41
106
  const onSelect = async (code: string) => {
42
- if (isDev()) {
43
- }
44
107
  await handleLanguageSelect(code, () => {
45
- if (isDev()) {
46
- }
47
108
  navigation.goBack();
48
109
  });
49
- if (isDev()) {
50
- }
51
- };
52
-
53
- const renderItem = ({ item }: { item: Language }) => {
54
- const isSelected = selectedCode === item.code;
55
-
56
- if (renderLanguageItem) {
57
- return <>{renderLanguageItem(item, isSelected, onSelect)}</>;
58
- }
59
-
60
- return (
61
- <LanguageItem
62
- item={item}
63
- isSelected={isSelected}
64
- onSelect={onSelect}
65
- customStyles={customStyles}
66
- />
67
- );
68
110
  };
69
111
 
70
- const renderSearchComponent = () => {
71
- if (renderSearchInput) {
72
- return renderSearchInput(searchQuery, setSearchQuery, searchPlaceholder || '');
73
- }
74
-
75
- return (
76
- <SearchBar
77
- value={searchQuery}
78
- onChangeText={setSearchQuery}
79
- placeholder={searchPlaceholder}
80
- containerStyle={[
81
- { marginBottom: tokens.spacing.md },
82
- customStyles?.searchContainer
83
- ]}
84
- inputStyle={customStyles?.searchInput}
85
- />
86
- );
87
- };
112
+ const renderItem = ({ item }: { item: Language }) => (
113
+ <LanguageListItem
114
+ item={item}
115
+ selectedCode={selectedCode}
116
+ onSelect={onSelect}
117
+ renderLanguageItem={renderLanguageItem}
118
+ customStyles={customStyles}
119
+ />
120
+ );
88
121
 
89
122
  const handleBack = () => {
90
123
  if (onBackPress) {
@@ -101,14 +134,20 @@ export const LanguageSelectionScreen: React.FC<LanguageSelectionScreenProps> = (
101
134
  edges={['top', 'bottom', 'left', 'right']}
102
135
  backgroundColor={tokens.colors.backgroundPrimary}
103
136
  header={
104
- <NavigationHeader
105
- title={headerTitle || ""}
106
- onBackPress={handleBack}
137
+ <NavigationHeader
138
+ title={headerTitle || ""}
139
+ onBackPress={handleBack}
107
140
  />
108
141
  }
109
142
  containerStyle={customStyles?.container}
110
143
  >
111
- {renderSearchComponent()}
144
+ <LanguageSearchComponent
145
+ searchQuery={searchQuery}
146
+ setSearchQuery={setSearchQuery}
147
+ searchPlaceholder={searchPlaceholder}
148
+ renderSearchInput={renderSearchInput}
149
+ customStyles={customStyles}
150
+ />
112
151
  <FlatList
113
152
  data={filteredLanguages}
114
153
  renderItem={renderItem}
@@ -134,5 +173,3 @@ export const LanguageSelectionScreen: React.FC<LanguageSelectionScreenProps> = (
134
173
  );
135
174
  };
136
175
 
137
- export default LanguageSelectionScreen;
138
-
@@ -121,21 +121,6 @@ describe('LanguageSelectionScreen', () => {
121
121
  );
122
122
  });
123
123
 
124
- it('should use custom container when provided', () => {
125
- const CustomContainer = ({ children }: { children: React.ReactNode }) => (
126
- <div testID="custom-container">{children}</div>
127
- );
128
-
129
- const { getByTestId } = render(
130
- <LanguageSelectionScreen
131
- searchPlaceholder="Search languages..."
132
- containerComponent={CustomContainer}
133
- />
134
- );
135
-
136
- expect(getByTestId('custom-container')).toBeTruthy();
137
- });
138
-
139
124
  it('should apply custom styles', () => {
140
125
  const customStyles = {
141
126
  container: { backgroundColor: 'red' },
@@ -129,6 +129,4 @@ const getStyles = (tokens: DesignTokens) => StyleSheet.create({
129
129
  flex: 1,
130
130
  marginRight: BASE_TOKENS.spacing.md,
131
131
  },
132
- });
133
-
134
- export default NotificationsScreen;
132
+ });
@@ -6,10 +6,6 @@ export const DEFAULT_WEEKDAY = 2; // Tuesday
6
6
  export const MAX_TITLE_LENGTH = 100;
7
7
  export const MAX_BODY_LENGTH = 500;
8
8
 
9
- export const VALID_HOUR_RANGE = { min: 0, max: 23 } as const;
10
- export const VALID_MINUTE_RANGE = { min: 0, max: 59 } as const;
11
- export const VALID_WEEKDAY_RANGE = { min: 0, max: 6 } as const;
12
-
13
9
  export interface ReminderFormTranslations {
14
10
  titleLabel: string;
15
11
  titlePlaceholder: string;
@@ -3,7 +3,7 @@
3
3
  * Form for creating and editing reminders
4
4
  */
5
5
 
6
- import React, { useState, useMemo, useCallback } from 'react';
6
+ import React, { useReducer, useMemo, useCallback } from 'react';
7
7
  import { View, TextInput, ScrollView } from 'react-native';
8
8
  import { AtomicText } from '@umituz/react-native-design-system';
9
9
  import { useAppDesignTokens } from '@umituz/react-native-design-system';
@@ -24,6 +24,53 @@ import { createReminderFormStyles as createStyles } from './ReminderForm.styles'
24
24
  import { DEFAULT_TIME_PRESETS, FREQUENCY_OPTIONS } from '../../infrastructure/config/reminderPresets';
25
25
  import type { ReminderFrequency, TimePreset } from '../../../infrastructure/services/types';
26
26
 
27
+ interface ReminderFormState {
28
+ title: string;
29
+ body: string;
30
+ frequency: ReminderFrequency;
31
+ selectedPresetId: string | undefined;
32
+ hour: number;
33
+ minute: number;
34
+ weekday: number;
35
+ isCustomTime: boolean;
36
+ error: string | null;
37
+ }
38
+
39
+ type ReminderFormAction =
40
+ | { type: 'SET_TITLE'; payload: string }
41
+ | { type: 'SET_BODY'; payload: string }
42
+ | { type: 'SET_FREQUENCY'; payload: ReminderFrequency }
43
+ | { type: 'SET_WEEKDAY'; payload: number }
44
+ | { type: 'SELECT_PRESET'; payload: TimePreset }
45
+ | { type: 'SELECT_CUSTOM' }
46
+ | { type: 'SET_ERROR'; payload: string | null };
47
+
48
+ function reminderFormReducer(state: ReminderFormState, action: ReminderFormAction): ReminderFormState {
49
+ switch (action.type) {
50
+ case 'SET_TITLE':
51
+ return { ...state, title: action.payload, error: null };
52
+ case 'SET_BODY':
53
+ return { ...state, body: action.payload, error: null };
54
+ case 'SET_FREQUENCY':
55
+ return { ...state, frequency: action.payload };
56
+ case 'SET_WEEKDAY':
57
+ return { ...state, weekday: action.payload };
58
+ case 'SELECT_PRESET':
59
+ return {
60
+ ...state,
61
+ selectedPresetId: action.payload.id,
62
+ hour: action.payload.hour,
63
+ minute: action.payload.minute,
64
+ isCustomTime: false,
65
+ error: null,
66
+ };
67
+ case 'SELECT_CUSTOM':
68
+ return { ...state, selectedPresetId: undefined, isCustomTime: true };
69
+ case 'SET_ERROR':
70
+ return { ...state, error: action.payload };
71
+ }
72
+ }
73
+
27
74
  export const ReminderForm: React.FC<ReminderFormProps> = ({
28
75
  initialData,
29
76
  translations,
@@ -34,36 +81,32 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
34
81
  const tokens = useAppDesignTokens();
35
82
  const styles = useMemo(() => createStyles(tokens), [tokens]);
36
83
 
37
- const [title, setTitle] = useState(initialData?.title || '');
38
- const [body, setBody] = useState(initialData?.body || '');
39
- const [frequency, setFrequency] = useState<ReminderFrequency>(initialData?.frequency || 'daily');
40
- const [selectedPresetId, setSelectedPresetId] = useState<string | undefined>(initialData?.timePresetId);
41
- const [hour, setHour] = useState(initialData?.hour ?? DEFAULT_HOUR);
42
- const [minute, setMinute] = useState(initialData?.minute ?? DEFAULT_MINUTE);
43
- const [weekday, setWeekday] = useState(initialData?.weekday ?? DEFAULT_WEEKDAY);
44
- const [isCustomTime, setIsCustomTime] = useState(!initialData?.timePresetId);
45
- // FIXED: Add error state for user feedback
46
- const [error, setError] = useState<string | null>(null);
84
+ const [state, dispatch] = useReducer(reminderFormReducer, {
85
+ title: initialData?.title || '',
86
+ body: initialData?.body || '',
87
+ frequency: initialData?.frequency || 'daily',
88
+ selectedPresetId: initialData?.timePresetId,
89
+ hour: initialData?.hour ?? DEFAULT_HOUR,
90
+ minute: initialData?.minute ?? DEFAULT_MINUTE,
91
+ weekday: initialData?.weekday ?? DEFAULT_WEEKDAY,
92
+ isCustomTime: !initialData?.timePresetId,
93
+ error: null,
94
+ });
95
+
96
+ const { title, body, frequency, selectedPresetId, hour, minute, weekday, isCustomTime, error } = state;
47
97
 
48
98
  const handlePresetSelect = useCallback((preset: TimePreset) => {
49
- setSelectedPresetId(preset.id);
50
- setHour(preset.hour);
51
- setMinute(preset.minute);
52
- setIsCustomTime(false);
53
- // Clear error when user changes something
54
- setError(null);
99
+ dispatch({ type: 'SELECT_PRESET', payload: preset });
55
100
  }, []);
56
101
 
57
102
  const handleCustomSelect = useCallback(() => {
58
- setSelectedPresetId(undefined);
59
- setIsCustomTime(true);
103
+ dispatch({ type: 'SELECT_CUSTOM' });
60
104
  }, []);
61
105
 
62
106
  const handleSave = useCallback(() => {
63
107
  const trimmedTitle = title.trim();
64
108
  const trimmedBody = body.trim();
65
109
 
66
- // Validate using centralized validation
67
110
  const validationResult = validateReminderForm({
68
111
  title: trimmedTitle,
69
112
  body: trimmedBody,
@@ -76,15 +119,12 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
76
119
  });
77
120
 
78
121
  if (!validationResult.isValid) {
79
- // FIXED: Show error to user
80
- setError(validationResult.error || "Validation failed");
122
+ dispatch({ type: 'SET_ERROR', payload: validationResult.error || "Validation failed" });
81
123
  return;
82
124
  }
83
125
 
84
- // Clear error and proceed
85
- setError(null);
126
+ dispatch({ type: 'SET_ERROR', payload: null });
86
127
 
87
- // Sanitize input (React Native handles XSS, but we trim extra whitespace)
88
128
  const sanitizedTitle = trimmedTitle.replace(/\s+/g, ' ').trim();
89
129
  const sanitizedBody = trimmedBody.replace(/\s+/g, ' ').trim();
90
130
 
@@ -108,8 +148,7 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
108
148
  style={styles.input}
109
149
  value={title}
110
150
  onChangeText={(text) => {
111
- setTitle(text);
112
- setError(null); // Clear error on input
151
+ dispatch({ type: 'SET_TITLE', payload: text });
113
152
  }}
114
153
  placeholder={translations.titlePlaceholder}
115
154
  placeholderTextColor={tokens.colors.textSecondary}
@@ -122,8 +161,7 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
122
161
  style={[styles.input, styles.multilineInput]}
123
162
  value={body}
124
163
  onChangeText={(text) => {
125
- setBody(text);
126
- setError(null); // Clear error on input
164
+ dispatch({ type: 'SET_BODY', payload: text });
127
165
  }}
128
166
  placeholder={translations.bodyPlaceholder}
129
167
  placeholderTextColor={tokens.colors.textSecondary}
@@ -137,7 +175,7 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
137
175
  <FrequencySelector
138
176
  options={FREQUENCY_OPTIONS}
139
177
  selectedFrequency={frequency}
140
- onSelect={setFrequency}
178
+ onSelect={(freq: ReminderFrequency) => dispatch({ type: 'SET_FREQUENCY', payload: freq })}
141
179
  getLabel={translations.getFrequencyLabel}
142
180
  />
143
181
  </View>
@@ -159,7 +197,7 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
159
197
  {frequency === 'weekly' && (
160
198
  <View style={styles.section}>
161
199
  <AtomicText type="bodyMedium" style={styles.label}>{translations.weekdayLabel}</AtomicText>
162
- <WeekdaySelector selectedWeekday={weekday} onSelect={setWeekday} getLabel={translations.getWeekdayLabel} />
200
+ <WeekdaySelector selectedWeekday={weekday} onSelect={(day: number) => dispatch({ type: 'SET_WEEKDAY', payload: day })} getLabel={translations.getWeekdayLabel} />
163
201
  </View>
164
202
  )}
165
203
 
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from "react";
1
+ import React, { useState } from "react";
2
2
  import { View, StyleSheet, TouchableOpacity, ViewStyle } from "react-native";
3
3
  import { useAppDesignTokens, AtomicIcon } from "@umituz/react-native-design-system";
4
4
 
@@ -25,23 +25,17 @@ export const StarRating: React.FC<StarRatingProps> = ({
25
25
  }) => {
26
26
  const tokens = useAppDesignTokens();
27
27
  const styles = getStyles(tokens);
28
- const [internalRating, setInternalRating] = useState(rating);
28
+ const [selectedRating, setSelectedRating] = useState<number | null>(null);
29
29
 
30
- // Sync internal state with rating prop changes
31
- useEffect(() => {
32
- setInternalRating(rating);
33
- }, [rating]);
30
+ const displayRating = selectedRating ?? rating;
34
31
 
35
32
  const filledColor = activeColor || tokens.colors.warning;
36
33
  const emptyColor = inactiveColor || tokens.colors.borderLight;
37
-
38
- // Scale the size
39
- const responsiveSize = size;
40
34
 
41
35
  const handlePress = (index: number) => {
42
36
  if (disabled) return;
43
37
  const newRating = index + 1;
44
- setInternalRating(newRating);
38
+ setSelectedRating(newRating);
45
39
  onRatingChange?.(newRating);
46
40
  };
47
41
 
@@ -56,9 +50,9 @@ export const StarRating: React.FC<StarRatingProps> = ({
56
50
  style={styles.starContainer}
57
51
  >
58
52
  <AtomicIcon
59
- name={index < (onRatingChange ? internalRating : rating) ? "star" : "star-outline"}
60
- customSize={responsiveSize}
61
- customColor={index < (onRatingChange ? internalRating : rating) ? filledColor : emptyColor}
53
+ name={index < (onRatingChange ? displayRating : rating) ? "star" : "star-outline"}
54
+ customSize={size}
55
+ customColor={index < (onRatingChange ? displayRating : rating) ? filledColor : emptyColor}
62
56
  />
63
57
  </TouchableOpacity>
64
58
  ))}
@@ -62,29 +62,3 @@ export const createConfigWithExtensions = <T extends BaseConfigType>(
62
62
  return { ...baseConfig, ...extensions };
63
63
  };
64
64
 
65
- /**
66
- * Create a disabled configuration
67
- */
68
- export const createDisabledConfig = <T extends BaseConfigType>(
69
- params: Omit<ConfigCreatorParams, "routeOrOnPress" | "defaultRoute">
70
- ): T => {
71
- const baseConfig = createBaseConfig<T>(params);
72
- return { ...baseConfig, enabled: false } as T;
73
- };
74
-
75
- /**
76
- * Batch create configurations
77
- */
78
- export const createBatchConfigs = <T extends BaseConfigType>(
79
- items: Array<{
80
- icon: string;
81
- routeOrOnPress?: string | (() => void);
82
- }>
83
- ): T[] => {
84
- return items.map((item) =>
85
- createBaseConfig<T>({
86
- icon: item.icon,
87
- routeOrOnPress: item.routeOrOnPress,
88
- })
89
- );
90
- };
@@ -19,5 +19,3 @@ export const TEXT_LENGTH_LIMITS = {
19
19
  /** User ID maximum length (128 characters) */
20
20
  USER_ID: 128,
21
21
  } as const;
22
-
23
- export type TextLengthLimit = typeof TEXT_LENGTH_LIMITS[keyof typeof TEXT_LENGTH_LIMITS];
@@ -6,10 +6,7 @@
6
6
 
7
7
  import { TEXT_LENGTH_LIMITS } from './constants/textLimits';
8
8
 
9
- /**
10
- * Sanitize string by trimming and limiting length
11
- */
12
- export function sanitizeString(str: string | undefined, maxLength: number): string {
9
+ function sanitizeString(str: string | undefined, maxLength: number): string {
13
10
  if (!str) return "";
14
11
  return str.trim().slice(0, maxLength);
15
12
  }
@@ -28,24 +25,3 @@ export function sanitizeDescription(description: string | undefined): string | u
28
25
  return sanitizeString(description || "", TEXT_LENGTH_LIMITS.DESCRIPTION);
29
26
  }
30
27
 
31
- /**
32
- * Truncate text with ellipsis
33
- */
34
- export function truncateText(text: string, maxLength: number): string {
35
- if (text.length <= maxLength) return text;
36
- return text.slice(0, maxLength - 3) + "...";
37
- }
38
-
39
- /**
40
- * Escape HTML entities (for security)
41
- */
42
- export function escapeHtml(text: string): string {
43
- const htmlEntities: Record<string, string> = {
44
- "&": "&amp;",
45
- "<": "&lt;",
46
- ">": "&gt;",
47
- '"': "&quot;",
48
- "'": "&#39;",
49
- };
50
- return text.replace(/[&<>"']/g, (char) => htmlEntities[char] || char);
51
- }
@@ -3,40 +3,7 @@
3
3
  * Base validation interfaces and types
4
4
  */
5
5
 
6
- /**
7
- * Validation result interface
8
- */
9
6
  export interface ValidationResult {
10
7
  isValid: boolean;
11
8
  error?: string;
12
9
  }
13
-
14
- /**
15
- * Text validation options
16
- */
17
- export interface TextValidationOptions {
18
- minLength?: number;
19
- maxLength?: number;
20
- required?: boolean;
21
- pattern?: RegExp;
22
- customValidator?: (value: string) => string | null;
23
- }
24
-
25
- /**
26
- * Email validation options
27
- */
28
- export interface EmailValidationOptions {
29
- required?: boolean;
30
- allowEmpty?: boolean;
31
- }
32
-
33
- /**
34
- * Password validation options
35
- */
36
- export interface PasswordValidationOptions {
37
- minLength?: number;
38
- requireUppercase?: boolean;
39
- requireLowercase?: boolean;
40
- requireNumber?: boolean;
41
- requireSpecialChar?: boolean;
42
- }
@@ -4,9 +4,15 @@
4
4
  */
5
5
 
6
6
  import type { ValidationResult } from "./core";
7
- import { validateRating } from "./numericValidators";
8
7
  import { TEXT_LENGTH_LIMITS } from "../constants/textLimits";
9
8
 
9
+ const validateRating = (rating: number): ValidationResult => {
10
+ if (rating < 1 || rating > 5) {
11
+ return { isValid: false, error: "Rating must be between 1 and 5" };
12
+ }
13
+ return { isValid: true };
14
+ };
15
+
10
16
  /**
11
17
  * Feedback form validation
12
18
  */
@@ -1,37 +1,6 @@
1
1
  /**
2
2
  * Validation Utilities
3
- * Barrel export for all validation modules
4
3
  */
5
4
 
6
- export * from "./core";
7
- export * from "./textValidators";
8
- export * from "./passwordValidator";
9
- export * from "./numericValidators";
10
- export * from "./formValidators";
11
-
12
- import type { ValidationResult } from "./core";
13
-
14
- /**
15
- * Form field validator factory
16
- * Creates a validator function for a specific field
17
- */
18
- export const createFieldValidator = <T>(
19
- validator: (value: T) => ValidationResult
20
- ) => {
21
- return (value: T): ValidationResult => validator(value);
22
- };
23
-
24
- /**
25
- * Multi-field validator
26
- * Validates multiple fields and returns the first error
27
- */
28
- export const validateMultipleFields = (
29
- fields: Record<string, ValidationResult>
30
- ): ValidationResult => {
31
- for (const result of Object.values(fields)) {
32
- if (!result.isValid) {
33
- return result;
34
- }
35
- }
36
- return { isValid: true };
37
- };
5
+ export type { ValidationResult } from "./core";
6
+ export { validateFeedbackForm, validateReminderForm } from "./formValidators";