@shaykec/app-agent 1.0.4 → 1.0.5

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 (184) hide show
  1. package/.claude/skills/ui-tester/SKILL.md +59 -0
  2. package/.cursor/skills/android-customizer/SKILL.md +50 -16
  3. package/.cursor/skills/appconfig-customization/SKILL.md +15 -0
  4. package/.cursor/skills/customization-planner/SKILL.md +17 -1
  5. package/.cursor/skills/ios-customizer/SKILL.md +54 -13
  6. package/.cursor/skills/mock-data-update/SKILL.md +11 -3
  7. package/.cursor/skills/ui-tester/SKILL.md +59 -0
  8. package/dist/cli.d.ts +18 -4
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +134 -14
  11. package/dist/cli.js.map +1 -1
  12. package/dist/github.d.ts +5 -0
  13. package/dist/github.d.ts.map +1 -1
  14. package/dist/github.js +27 -0
  15. package/dist/github.js.map +1 -1
  16. package/dist/index.js +218 -24
  17. package/dist/index.js.map +1 -1
  18. package/dist/preview-generator.d.ts +35 -0
  19. package/dist/preview-generator.d.ts.map +1 -0
  20. package/dist/preview-generator.js +546 -0
  21. package/dist/preview-generator.js.map +1 -0
  22. package/package.json +4 -1
  23. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/booking/BookingScreen.kt +5 -3
  24. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/bookings/BookingsScreen.kt +7 -5
  25. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/discovery/DiscoveryViewModel.kt +3 -2
  26. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/profile/ProfileScreen.kt +4 -2
  27. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/AppConfig.kt +9 -0
  28. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/data/DataRepository.kt +23 -0
  29. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/data/DataSourceResolver.kt +23 -0
  30. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/calls/CallsScreen.kt +4 -3
  31. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/contacts/ContactsScreen.kt +3 -2
  32. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ChatScreen.kt +11 -10
  33. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ConversationsScreen.kt +5 -5
  34. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/navigation/Navigation.kt +4 -2
  35. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/settings/SettingsScreen.kt +6 -4
  36. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/mock/MockDataProvider.kt +7 -7
  37. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/data/DataRepository.kt +46 -0
  38. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/data/DataSourceResolver.kt +23 -0
  39. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/alerts/AlertsScreen.kt +4 -3
  40. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/dashboard/DashboardScreen.kt +3 -2
  41. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/datasources/DataSourcesScreen.kt +3 -2
  42. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/reports/ReportsScreen.kt +3 -2
  43. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/mock/MockDataProvider.kt +16 -15
  44. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/AppConfig.kt +9 -0
  45. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/calendar/CalendarScreen.kt +5 -5
  46. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/chores/ChoreBoardScreen.kt +3 -3
  47. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/familyhub/FamilyHubScreen.kt +4 -4
  48. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/gallery/GalleryScreen.kt +3 -3
  49. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/members/MembersListScreen.kt +5 -5
  50. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/rewards/RewardsScreen.kt +5 -5
  51. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/settings/SettingsScreen.kt +3 -3
  52. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/mock/MockDataProvider.kt +29 -32
  53. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/AppConfig.kt +9 -0
  54. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/data/DataRepository.kt +70 -0
  55. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/data/DataSourceResolver.kt +23 -0
  56. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/budgets/BudgetsScreen.kt +3 -2
  57. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/goals/GoalsScreen.kt +5 -3
  58. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/home/HomeScreen.kt +8 -7
  59. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/settings/SettingsScreen.kt +0 -1
  60. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/transactions/TransactionsScreen.kt +9 -4
  61. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/mock/MockDataProvider.kt +103 -11
  62. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/data/DataRepository.kt +19 -0
  63. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/leaderboard/LeaderboardScreen.kt +2 -1
  64. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/results/ResultsScreen.kt +3 -2
  65. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/data/DataRepository.kt +46 -0
  66. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/data/DataSourceResolver.kt +23 -0
  67. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/dashboard/DashboardScreen.kt +3 -2
  68. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/profile/ProfileScreen.kt +3 -2
  69. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/workouts/WorkoutListScreen.kt +3 -2
  70. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/mock/MockDataProvider.kt +13 -13
  71. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/AppConfig.kt +9 -0
  72. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/data/DataRepository.kt +48 -0
  73. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/data/DataSourceResolver.kt +23 -0
  74. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/courses/CourseListScreen.kt +4 -3
  75. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/leaderboard/LeaderboardScreen.kt +3 -2
  76. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/profile/ProfileScreen.kt +3 -2
  77. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/progress/ProgressDashboardScreen.kt +3 -2
  78. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/mock/MockDataProvider.kt +41 -11
  79. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/data/DataSourceResolver.kt +11 -6
  80. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/di/AppModule.kt +11 -2
  81. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/favorites/FavoritesViewModel.kt +4 -4
  82. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/map/MapViewModel.kt +4 -4
  83. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/place/PlaceDetailViewModel.kt +5 -5
  84. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/place/PlaceListViewModel.kt +4 -4
  85. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/profile/ProfileViewModel.kt +5 -5
  86. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/route/RouteViewModel.kt +3 -3
  87. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/search/SearchViewModel.kt +3 -3
  88. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/settings/SettingsViewModel.kt +3 -3
  89. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/mock/MockDataProvider.kt +54 -41
  90. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/AppConfig.kt +9 -0
  91. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/browse/BrowseScreen.kt +2 -1
  92. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/detail/DetailScreen.kt +3 -1
  93. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/home/HomeScreen.kt +3 -2
  94. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/mock/MockDataProvider.kt +63 -132
  95. package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/AppConfig.kt +9 -0
  96. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/home/HomeViewModel.kt +2 -1
  97. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/products/ProductViewModels.kt +12 -7
  98. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/mock/MockDataProvider.kt +3 -3
  99. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/data/DataRepository.kt +0 -1
  100. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/createpost/CreatePostScreen.kt +3 -2
  101. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/feed/CommentsScreen.kt +4 -3
  102. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/feed/FeedScreen.kt +3 -2
  103. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/messages/MessagesScreen.kt +9 -8
  104. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/notifications/NotificationsScreen.kt +3 -2
  105. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/profile/ProfileScreen.kt +5 -4
  106. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/search/SearchScreen.kt +3 -2
  107. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/AppConfig.kt +9 -0
  108. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/data/DataRepository.kt +38 -0
  109. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/data/DataSourceResolver.kt +23 -0
  110. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/calendar/CalendarScreen.kt +0 -1
  111. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/inbox/InboxScreen.kt +4 -2
  112. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/kanban/KanbanBoardScreen.kt +5 -3
  113. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/projects/ProjectDetailScreen.kt +9 -5
  114. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/projects/ProjectListScreen.kt +6 -4
  115. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/search/SearchScreen.kt +1 -1
  116. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/tasks/TaskDetailScreen.kt +0 -1
  117. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/mock/MockDataProvider.kt +13 -12
  118. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/AppConfig.kt +9 -0
  119. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/data/DataRepository.kt +34 -0
  120. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/data/DataSourceResolver.kt +23 -0
  121. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/achievements/AchievementsViewModel.kt +4 -2
  122. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/dashboard/TodayViewModel.kt +4 -2
  123. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/habits/HabitDetailViewModel.kt +4 -2
  124. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/habits/HabitsListViewModel.kt +4 -2
  125. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/mock/MockDataProvider.kt +13 -11
  126. package/templates/ios/ChatTemplate/ChatTemplate/App/AppConfig.swift +9 -0
  127. package/templates/ios/ChatTemplate/ChatTemplate/Data/DataRepository.swift +43 -0
  128. package/templates/ios/ChatTemplate/ChatTemplate/Data/DataSourceResolver.swift +20 -0
  129. package/templates/ios/DashTemplate/DashTemplate/App/AppConfig.swift +9 -0
  130. package/templates/ios/DashTemplate/DashTemplate/Data/DataRepository.swift +53 -0
  131. package/templates/ios/DashTemplate/DashTemplate/MockData/MockDataProvider.swift +1 -1
  132. package/templates/ios/FamilyTemplate/FamilyTemplate/App/AppConfig.swift +9 -0
  133. package/templates/ios/FamilyTemplate/FamilyTemplate/App/FamilyTemplateApp.swift +4 -2
  134. package/templates/ios/FamilyTemplate/FamilyTemplate/MockData/MockDataProvider.swift +1 -1
  135. package/templates/ios/FinanceTemplate/FinanceTemplate/App/AppConfig.swift +9 -0
  136. package/templates/ios/FinanceTemplate/FinanceTemplate/Data/DataRepository.swift +75 -0
  137. package/templates/ios/FinanceTemplate/FinanceTemplate/Data/DataSourceResolver.swift +20 -0
  138. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Budgets/BudgetsView.swift +3 -1
  139. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Goals/GoalsView.swift +3 -1
  140. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Home/HomeView.swift +13 -6
  141. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Settings/SettingsView.swift +6 -2
  142. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Transactions/AddTransactionView.swift +4 -2
  143. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Transactions/TransactionsView.swift +3 -1
  144. package/templates/ios/FinanceTemplate/FinanceTemplate/MockData/MockDataProvider.swift +1 -1
  145. package/templates/ios/GameTemplate/GameTemplate/App/AppConfig.swift +9 -0
  146. package/templates/ios/GameTemplate/GameTemplate/Data/DataRepository.swift +30 -0
  147. package/templates/ios/GameTemplate/GameTemplate/Data/DataSourceResolver.swift +20 -0
  148. package/templates/ios/GameTemplate/GameTemplate/MockData/MockDataProvider.swift +1 -1
  149. package/templates/ios/HealthTemplate/HealthTemplate/App/AppConfig.swift +9 -0
  150. package/templates/ios/HealthTemplate/HealthTemplate/Data/DataRepository.swift +60 -0
  151. package/templates/ios/HealthTemplate/HealthTemplate/MockData/MockDataProvider.swift +1 -1
  152. package/templates/ios/LearnTemplate/LearnTemplate/App/AppConfig.swift +9 -0
  153. package/templates/ios/LearnTemplate/LearnTemplate/Features/Courses/CourseDetailView.swift +3 -2
  154. package/templates/ios/LearnTemplate/LearnTemplate/Features/Courses/CourseListView.swift +3 -2
  155. package/templates/ios/LearnTemplate/LearnTemplate/Features/Flashcards/FlashcardView.swift +2 -1
  156. package/templates/ios/LearnTemplate/LearnTemplate/Features/Leaderboard/LeaderboardView.swift +2 -1
  157. package/templates/ios/LearnTemplate/LearnTemplate/Features/Lessons/LessonPlayerView.swift +4 -2
  158. package/templates/ios/LearnTemplate/LearnTemplate/Features/Profile/ProfileView.swift +4 -2
  159. package/templates/ios/LearnTemplate/LearnTemplate/Features/Progress/ProgressDashboardView.swift +3 -2
  160. package/templates/ios/LearnTemplate/LearnTemplate/Features/Quiz/QuizView.swift +2 -1
  161. package/templates/ios/LearnTemplate/LearnTemplate/MockData/MockDataProvider.swift +1 -1
  162. package/templates/ios/MediaTemplate/MediaTemplate/App/AppConfig.swift +9 -0
  163. package/templates/ios/MediaTemplate/MediaTemplate/Data/DataRepository.swift +52 -0
  164. package/templates/ios/MediaTemplate/MediaTemplate/Data/DataSourceResolver.swift +20 -0
  165. package/templates/ios/MediaTemplate/MediaTemplate/MockData/MockDataProvider.swift +1 -1
  166. package/templates/ios/ReferenceTemplate/ReferenceTemplate/App/AppConfig.swift +9 -0
  167. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Data/DataRepository.swift +28 -0
  168. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Data/DataSourceResolver.swift +20 -0
  169. package/templates/ios/ReferenceTemplate/ReferenceTemplate/MockData/MockDataProvider.swift +1 -1
  170. package/templates/ios/TaskTemplate/TaskTemplate/App/AppConfig.swift +9 -0
  171. package/templates/ios/TaskTemplate/TaskTemplate/Data/DataRepository.swift +38 -0
  172. package/templates/ios/TaskTemplate/TaskTemplate/Data/DataSourceResolver.swift +20 -0
  173. package/templates/ios/TaskTemplate/TaskTemplate/Features/Calendar/CalendarView.swift +6 -2
  174. package/templates/ios/TaskTemplate/TaskTemplate/Features/Inbox/InboxView.swift +2 -1
  175. package/templates/ios/TaskTemplate/TaskTemplate/Features/Kanban/KanbanBoardView.swift +9 -3
  176. package/templates/ios/TaskTemplate/TaskTemplate/Features/Projects/ProjectDetailView.swift +3 -1
  177. package/templates/ios/TaskTemplate/TaskTemplate/Features/Projects/ProjectListView.swift +3 -1
  178. package/templates/ios/TaskTemplate/TaskTemplate/Features/Search/SearchView.swift +3 -1
  179. package/templates/ios/TaskTemplate/TaskTemplate/MockData/MockDataProvider.swift +1 -1
  180. package/templates/ios/TrackTemplate/TrackTemplate/App/AppConfig.swift +9 -0
  181. package/templates/ios/TrackTemplate/TrackTemplate/Data/DataRepository.swift +29 -0
  182. package/templates/ios/TrackTemplate/TrackTemplate/Data/DataSourceResolver.swift +20 -0
  183. package/templates/ios/TrackTemplate/TrackTemplate/MockData/MockDataProvider.swift +1 -1
  184. package/templates/ios/TrackTemplate/TrackTemplate/Services/HabitManager.swift +1 -1
@@ -11,12 +11,12 @@ import androidx.compose.ui.Modifier
11
11
  import androidx.compose.ui.platform.testTag
12
12
  import androidx.compose.ui.unit.dp
13
13
  import com.appship.family.data.Chore
14
- import com.appship.family.mock.MockDataProvider
14
+ import com.appship.family.data.DataSourceResolver
15
15
 
16
16
  @OptIn(ExperimentalMaterial3Api::class)
17
17
  @Composable
18
18
  fun ChoreBoardScreen() {
19
- val dataProvider = MockDataProvider.shared
19
+ val repository = remember { DataSourceResolver.repository }
20
20
  val chores by dataProvider.chores.collectAsState()
21
21
  val members by dataProvider.members.collectAsState()
22
22
  var showingAddChore by remember { mutableStateOf(false) }
@@ -63,7 +63,7 @@ fun ChoreBoardScreen() {
63
63
  AddChoreDialog(
64
64
  onDismiss = { showingAddChore = false },
65
65
  onSave = { chore ->
66
- dataProvider.createChore(chore)
66
+ repository.createChore(chore)
67
67
  showingAddChore = false
68
68
  }
69
69
  )
@@ -13,15 +13,15 @@ import androidx.compose.ui.Modifier
13
13
  import androidx.compose.ui.platform.testTag
14
14
  import androidx.compose.ui.unit.dp
15
15
  import androidx.navigation.NavController
16
+ import com.appship.family.data.DataSourceResolver
16
17
  import com.appship.family.data.FamilyMember
17
- import com.appship.family.mock.MockDataProvider
18
18
  import com.appship.family.features.members.MemberProfileScreen
19
19
 
20
20
  @Composable
21
21
  fun FamilyHubScreen(navController: NavController) {
22
- val dataProvider = MockDataProvider.shared
23
- val members by dataProvider.members.collectAsState()
24
- val chores by dataProvider.chores.collectAsState()
22
+ val repository = remember { DataSourceResolver.repository }
23
+ val members by repository.members.collectAsState()
24
+ val chores by repository.chores.collectAsState()
25
25
  var selectedMember by remember { mutableStateOf<FamilyMember?>(null) }
26
26
 
27
27
  Column(
@@ -11,14 +11,14 @@ import androidx.compose.runtime.*
11
11
  import androidx.compose.ui.Modifier
12
12
  import androidx.compose.ui.platform.testTag
13
13
  import androidx.compose.ui.unit.dp
14
+ import com.appship.family.data.DataSourceResolver
14
15
  import com.appship.family.data.Photo
15
- import com.appship.family.mock.MockDataProvider
16
16
 
17
17
  @OptIn(ExperimentalMaterial3Api::class)
18
18
  @Composable
19
19
  fun GalleryScreen() {
20
- val dataProvider = MockDataProvider.shared
21
- val photos by dataProvider.photos.collectAsState()
20
+ val repository = remember { DataSourceResolver.repository }
21
+ val photos by repository.photos.collectAsState()
22
22
  var showingAddPhoto by remember { mutableStateOf(false) }
23
23
 
24
24
  Scaffold(
@@ -10,14 +10,14 @@ import androidx.compose.ui.Modifier
10
10
  import androidx.compose.ui.platform.testTag
11
11
  import androidx.compose.ui.unit.dp
12
12
  import androidx.navigation.NavController
13
+ import com.appship.family.data.DataSourceResolver
13
14
  import com.appship.family.data.FamilyMember
14
- import com.appship.family.mock.MockDataProvider
15
15
 
16
16
  @OptIn(ExperimentalMaterial3Api::class)
17
17
  @Composable
18
18
  fun MembersListScreen(navController: NavController) {
19
- val dataProvider = MockDataProvider.shared
20
- val members by dataProvider.members.collectAsState()
19
+ val repository = remember { DataSourceResolver.repository }
20
+ val members by repository.members.collectAsState()
21
21
  var selectedMember by remember { mutableStateOf<FamilyMember?>(null) }
22
22
 
23
23
  Scaffold(
@@ -68,8 +68,8 @@ fun MemberRow(member: FamilyMember, onClick: () -> Unit) {
68
68
 
69
69
  @Composable
70
70
  fun MemberProfileScreen(member: FamilyMember, onDismiss: () -> Unit) {
71
- val dataProvider = MockDataProvider.shared
72
- val chores = dataProvider.choresForMember(member.id)
71
+ val repository = remember { DataSourceResolver.repository }
72
+ val chores = repository.choresForMember(member.id)
73
73
 
74
74
  AlertDialog(
75
75
  onDismissRequest = onDismiss,
@@ -9,16 +9,16 @@ import androidx.compose.runtime.*
9
9
  import androidx.compose.ui.Modifier
10
10
  import androidx.compose.ui.platform.testTag
11
11
  import androidx.compose.ui.unit.dp
12
+ import com.appship.family.data.DataSourceResolver
12
13
  import com.appship.family.data.Reward
13
- import com.appship.family.mock.MockDataProvider
14
14
 
15
15
  @OptIn(ExperimentalMaterial3Api::class)
16
16
  @Composable
17
17
  fun RewardsScreen() {
18
- val dataProvider = MockDataProvider.shared
19
- val allRewards by dataProvider.rewards.collectAsState()
18
+ val repository = remember { DataSourceResolver.repository }
19
+ val allRewards by repository.rewards.collectAsState()
20
20
  val rewards = allRewards.filter { !it.isRedeemed }
21
- val members by dataProvider.members.collectAsState()
21
+ val members by repository.members.collectAsState()
22
22
  val currentMember = members.firstOrNull()
23
23
 
24
24
  Scaffold(
@@ -62,7 +62,7 @@ fun RewardsScreen() {
62
62
  canAfford = currentMember?.points ?: 0 >= reward.pointsCost,
63
63
  onRedeem = {
64
64
  currentMember?.let {
65
- dataProvider.redeemReward(reward, it.id)
65
+ repository.redeemReward(reward, it.id)
66
66
  }
67
67
  },
68
68
  modifier = Modifier.testTag("reward_${index}_card")
@@ -9,12 +9,12 @@ import androidx.compose.ui.Modifier
9
9
  import androidx.compose.ui.unit.dp
10
10
  import androidx.navigation.NavController
11
11
  import com.appship.family.AppConfig
12
- import com.appship.family.mock.MockDataProvider
12
+ import com.appship.family.data.DataSourceResolver
13
13
 
14
14
  @OptIn(ExperimentalMaterial3Api::class)
15
15
  @Composable
16
16
  fun SettingsScreen(navController: NavController) {
17
- val dataProvider = MockDataProvider.shared
17
+ val repository = remember { DataSourceResolver.repository }
18
18
  var isChildMode by remember { mutableStateOf(false) }
19
19
  var showingResetAlert by remember { mutableStateOf(false) }
20
20
 
@@ -121,7 +121,7 @@ fun SettingsScreen(navController: NavController) {
121
121
  confirmButton = {
122
122
  TextButton(
123
123
  onClick = {
124
- dataProvider.reset()
124
+ repository.reset()
125
125
  showingResetAlert = false
126
126
  }
127
127
  ) {
@@ -1,20 +1,17 @@
1
1
  package com.appship.family.mock
2
2
 
3
3
  import com.appship.family.data.*
4
+ import com.appship.family.data.DataRepository
4
5
  import kotlinx.coroutines.flow.MutableStateFlow
5
6
  import kotlinx.coroutines.flow.StateFlow
6
7
  import kotlinx.coroutines.flow.asStateFlow
7
8
  import java.util.*
8
9
 
9
10
  // CUSTOMIZE:API - Replace mock data with real data source
10
- class MockDataProvider {
11
- companion object {
12
- val shared = MockDataProvider()
13
- }
14
-
11
+ object MockDataProvider : DataRepository {
15
12
  // MARK: - State Flows
16
13
  private val _members = MutableStateFlow<List<FamilyMember>>(emptyList())
17
- val members: StateFlow<List<FamilyMember>> = _members.asStateFlow()
14
+ override val members: StateFlow<List<FamilyMember>> = _members.asStateFlow()
18
15
 
19
16
  private val _chores = MutableStateFlow<List<Chore>>(emptyList())
20
17
  val chores: StateFlow<List<Chore>> = _chores.asStateFlow()
@@ -53,32 +50,32 @@ class MockDataProvider {
53
50
  _members.value = _members.value + member
54
51
  }
55
52
 
56
- fun updateMember(member: FamilyMember) {
53
+ override fun updateMember(member: FamilyMember) {
57
54
  _members.value = _members.value.map { if (it.id == member.id) member else it }
58
55
  }
59
56
 
60
- fun deleteMember(member: FamilyMember) {
57
+ override fun deleteMember(member: FamilyMember) {
61
58
  _members.value = _members.value.filter { it.id != member.id }
62
59
  }
63
60
 
64
- fun memberById(id: String): FamilyMember? {
61
+ override fun memberById(id: String): FamilyMember? {
65
62
  return _members.value.firstOrNull { it.id == id }
66
63
  }
67
64
 
68
65
  // MARK: - CRUD Operations - Chores
69
- fun createChore(chore: Chore) {
66
+ override fun createChore(chore: Chore) {
70
67
  _chores.value = _chores.value + chore
71
68
  }
72
69
 
73
- fun updateChore(chore: Chore) {
70
+ override fun updateChore(chore: Chore) {
74
71
  _chores.value = _chores.value.map { if (it.id == chore.id) chore else it }
75
72
  }
76
73
 
77
- fun deleteChore(chore: Chore) {
74
+ override fun deleteChore(chore: Chore) {
78
75
  _chores.value = _chores.value.filter { it.id != chore.id }
79
76
  }
80
77
 
81
- fun completeChore(chore: Chore, memberId: String) {
78
+ override fun completeChore(chore: Chore, memberId: String) {
82
79
  val updated = chore.copy(
83
80
  isCompleted = true,
84
81
  completedAt = Date(),
@@ -92,28 +89,28 @@ class MockDataProvider {
92
89
  }
93
90
  }
94
91
 
95
- fun choresForMember(memberId: String): List<Chore> {
92
+ override fun choresForMember(memberId: String): List<Chore> {
96
93
  return _chores.value.filter { it.assignedTo == memberId }
97
94
  }
98
95
 
99
- fun pendingChores(): List<Chore> {
96
+ override fun pendingChores(): List<Chore> {
100
97
  return _chores.value.filter { !it.isCompleted }
101
98
  }
102
99
 
103
100
  // MARK: - CRUD Operations - Rewards
104
- fun createReward(reward: Reward) {
101
+ override fun createReward(reward: Reward) {
105
102
  _rewards.value = _rewards.value + reward
106
103
  }
107
104
 
108
- fun updateReward(reward: Reward) {
105
+ override fun updateReward(reward: Reward) {
109
106
  _rewards.value = _rewards.value.map { if (it.id == reward.id) reward else it }
110
107
  }
111
108
 
112
- fun deleteReward(reward: Reward) {
109
+ override fun deleteReward(reward: Reward) {
113
110
  _rewards.value = _rewards.value.filter { it.id != reward.id }
114
111
  }
115
112
 
116
- fun redeemReward(reward: Reward, memberId: String): Boolean {
113
+ override fun redeemReward(reward: Reward, memberId: String): Boolean {
117
114
  val member = memberById(memberId) ?: return false
118
115
  if (member.points < reward.pointsCost || reward.isRedeemed) return false
119
116
 
@@ -128,24 +125,24 @@ class MockDataProvider {
128
125
  return true
129
126
  }
130
127
 
131
- fun availableRewards(): List<Reward> {
128
+ override fun availableRewards(): List<Reward> {
132
129
  return _rewards.value.filter { !it.isRedeemed }
133
130
  }
134
131
 
135
132
  // MARK: - CRUD Operations - Calendar Events
136
- fun createEvent(event: CalendarEvent) {
133
+ override fun createEvent(event: CalendarEvent) {
137
134
  _calendarEvents.value = _calendarEvents.value + event
138
135
  }
139
136
 
140
- fun updateEvent(event: CalendarEvent) {
137
+ override fun updateEvent(event: CalendarEvent) {
141
138
  _calendarEvents.value = _calendarEvents.value.map { if (it.id == event.id) event else it }
142
139
  }
143
140
 
144
- fun deleteEvent(event: CalendarEvent) {
141
+ override fun deleteEvent(event: CalendarEvent) {
145
142
  _calendarEvents.value = _calendarEvents.value.filter { it.id != event.id }
146
143
  }
147
144
 
148
- fun eventsForDate(date: Date): List<CalendarEvent> {
145
+ override fun eventsForDate(date: Date): List<CalendarEvent> {
149
146
  val calendar = Calendar.getInstance()
150
147
  calendar.time = date
151
148
  val dayOfYear = calendar.get(Calendar.DAY_OF_YEAR)
@@ -160,37 +157,37 @@ class MockDataProvider {
160
157
  }
161
158
 
162
159
  // MARK: - CRUD Operations - Milestones
163
- fun createMilestone(milestone: Milestone) {
160
+ override fun createMilestone(milestone: Milestone) {
164
161
  _milestones.value = _milestones.value + milestone
165
162
  }
166
163
 
167
- fun updateMilestone(milestone: Milestone) {
164
+ override fun updateMilestone(milestone: Milestone) {
168
165
  _milestones.value = _milestones.value.map { if (it.id == milestone.id) milestone else it }
169
166
  }
170
167
 
171
- fun deleteMilestone(milestone: Milestone) {
168
+ override fun deleteMilestone(milestone: Milestone) {
172
169
  _milestones.value = _milestones.value.filter { it.id != milestone.id }
173
170
  }
174
171
 
175
- fun milestonesForMember(memberId: String): List<Milestone> {
172
+ override fun milestonesForMember(memberId: String): List<Milestone> {
176
173
  return _milestones.value.filter { it.memberId == memberId }
177
174
  }
178
175
 
179
176
  // MARK: - CRUD Operations - Photos
180
- fun createPhoto(photo: Photo) {
177
+ override fun createPhoto(photo: Photo) {
181
178
  _photos.value = _photos.value + photo
182
179
  }
183
180
 
184
- fun updatePhoto(photo: Photo) {
181
+ override fun updatePhoto(photo: Photo) {
185
182
  _photos.value = _photos.value.map { if (it.id == photo.id) photo else it }
186
183
  }
187
184
 
188
- fun deletePhoto(photo: Photo) {
185
+ override fun deletePhoto(photo: Photo) {
189
186
  _photos.value = _photos.value.filter { it.id != photo.id }
190
187
  }
191
188
 
192
189
  // MARK: - Reset
193
- fun reset() {
190
+ override fun reset() {
194
191
  loadData()
195
192
  }
196
193
 
@@ -41,6 +41,15 @@ object AppConfig {
41
41
  const val TERMS_URL = "https://example.com/terms"
42
42
  }
43
43
 
44
+ // CUSTOMIZE:DATASOURCE - Data source configuration
45
+ object DataSource {
46
+ enum class SourceType {
47
+ LOCAL_STORAGE, // Room — real local database (DEFAULT)
48
+ MOCK // In-memory seed data — for development/testing only
49
+ }
50
+ val active: SourceType = SourceType.MOCK
51
+ }
52
+
44
53
  // CUSTOMIZE:THEME — colors are defined in core/theme/Theme.kt
45
54
  object Theme {
46
55
  val primaryColor get() = Primary
@@ -0,0 +1,70 @@
1
+ package com.appship.finance.data
2
+
3
+ import kotlinx.coroutines.flow.StateFlow
4
+ import java.util.Date
5
+
6
+ /**
7
+ * Abstracts the data layer. Both MockDataProvider and LocalStorageProvider
8
+ * implement this, allowing the app to swap data sources via AppConfig.DataSource.active.
9
+ *
10
+ * Screens and ViewModels depend on this interface rather than on a concrete implementation.
11
+ * Use [DataSourceResolver.repository] to get the active provider.
12
+ */
13
+ interface DataRepository {
14
+ // Observable state
15
+ val accounts: StateFlow<List<Account>>
16
+ val transactions: StateFlow<List<Transaction>>
17
+ val budgets: StateFlow<List<Budget>>
18
+ val goals: StateFlow<List<Goal>>
19
+ val categories: StateFlow<List<Category>>
20
+ val bills: StateFlow<List<Bill>>
21
+ val insights: StateFlow<List<SpendingInsight>>
22
+
23
+ // Computed properties
24
+ val totalBalance: Double
25
+ val monthlyIncome: Double
26
+ val monthlyExpenses: Double
27
+
28
+ // Account operations
29
+ fun getAccounts(): List<Account>
30
+ fun getAccount(id: String): Account?
31
+
32
+ // Transaction operations
33
+ fun getTransactions(): List<Transaction>
34
+ fun getTransactions(accountId: String): List<Transaction>
35
+ fun createTransaction(transaction: Transaction)
36
+ fun updateTransaction(transaction: Transaction)
37
+ fun deleteTransaction(id: String)
38
+
39
+ // Budget operations
40
+ fun getBudgets(): List<Budget>
41
+ fun getBudget(id: String): Budget?
42
+ fun createBudget(budget: Budget)
43
+ fun updateBudget(budget: Budget)
44
+ fun deleteBudget(id: String)
45
+
46
+ // Goal operations
47
+ fun getGoals(): List<Goal>
48
+ fun getGoal(id: String): Goal?
49
+ fun createGoal(goal: Goal)
50
+ fun updateGoal(goal: Goal)
51
+ fun deleteGoal(id: String)
52
+
53
+ // Category operations
54
+ fun getCategories(): List<Category>
55
+ fun getCategory(id: String): Category?
56
+
57
+ // Query operations
58
+ fun getTransactionsByCategory(categoryId: String): List<Transaction>
59
+ fun getTransactionsByDateRange(start: Date, end: Date): List<Transaction>
60
+
61
+ // Analytics operations
62
+ fun getBalance(): Double
63
+ fun getTotalIncome(): Double
64
+ fun getTotalExpenses(): Double
65
+ fun getBudgetProgress(budgetId: String): Double
66
+ fun getGoalProgress(goalId: String): Double
67
+
68
+ // Reset
69
+ fun reset()
70
+ }
@@ -0,0 +1,23 @@
1
+ package com.appship.finance.data
2
+
3
+ import com.appship.finance.AppConfig
4
+ import com.appship.finance.mock.MockDataProvider
5
+
6
+ /**
7
+ * Resolves the active [DataRepository] implementation based on [AppConfig.DataSource.active].
8
+ * Screens use this to obtain the correct data source without knowing the concrete type.
9
+ *
10
+ * Usage:
11
+ * val repository = DataSourceResolver.repository
12
+ * val accounts by repository.accounts.collectAsState()
13
+ */
14
+ object DataSourceResolver {
15
+ val repository: DataRepository
16
+ get() = when (AppConfig.DataSource.active) {
17
+ AppConfig.DataSource.SourceType.LOCAL_STORAGE -> {
18
+ // TODO: Return LocalStorageProvider once implemented
19
+ MockDataProvider
20
+ }
21
+ AppConfig.DataSource.SourceType.MOCK -> MockDataProvider
22
+ }
23
+ }
@@ -23,12 +23,13 @@ import androidx.compose.ui.unit.sp
23
23
  import com.appship.finance.AppConfig
24
24
  import com.appship.finance.data.Budget
25
25
  import com.appship.finance.features.home.formatCurrency
26
- import com.appship.finance.mock.MockDataProvider
26
+ import com.appship.finance.data.DataSourceResolver
27
27
 
28
28
  @OptIn(ExperimentalMaterial3Api::class)
29
29
  @Composable
30
30
  fun BudgetsScreen() {
31
- val budgets by MockDataProvider.budgets.collectAsState()
31
+ val repository = remember { DataSourceResolver.repository }
32
+ val budgets by repository.budgets.collectAsState()
32
33
  val totalBudget = budgets.sumOf { it.limit }
33
34
  val totalSpent = budgets.sumOf { it.spent }
34
35
 
@@ -25,12 +25,13 @@ import androidx.compose.ui.unit.sp
25
25
  import com.appship.finance.AppConfig
26
26
  import com.appship.finance.data.Goal
27
27
  import com.appship.finance.features.home.formatCurrency
28
- import com.appship.finance.mock.MockDataProvider
28
+ import com.appship.finance.data.DataSourceResolver
29
29
 
30
30
  @OptIn(ExperimentalMaterial3Api::class)
31
31
  @Composable
32
32
  fun GoalsScreen(onGoalClick: (String) -> Unit) {
33
- val goals by MockDataProvider.goals.collectAsState()
33
+ val repository = remember { DataSourceResolver.repository }
34
+ val goals by repository.goals.collectAsState()
34
35
  val totalSaved = goals.sumOf { it.currentAmount }
35
36
  val totalTarget = goals.sumOf { it.targetAmount }
36
37
 
@@ -220,7 +221,8 @@ fun GoalCard(goal: Goal, onClick: () -> Unit, testTag: String) {
220
221
  @OptIn(ExperimentalMaterial3Api::class)
221
222
  @Composable
222
223
  fun GoalDetailScreen(goalId: String, onBack: () -> Unit) {
223
- val goals by MockDataProvider.goals.collectAsState()
224
+ val repository = remember { DataSourceResolver.repository }
225
+ val goals by repository.goals.collectAsState()
224
226
  val goal = goals.find { it.id == goalId } ?: return
225
227
  var addAmount by remember { mutableStateOf("") }
226
228
 
@@ -30,7 +30,7 @@ import com.appship.finance.data.Account
30
30
  import com.appship.finance.data.SpendingInsight
31
31
  import com.appship.finance.data.Transaction
32
32
  import com.appship.finance.data.TransactionType
33
- import com.appship.finance.mock.MockDataProvider
33
+ import com.appship.finance.data.DataSourceResolver
34
34
  import java.text.NumberFormat
35
35
  import java.util.Locale
36
36
 
@@ -40,9 +40,10 @@ fun HomeScreen(
40
40
  onAddTransaction: () -> Unit,
41
41
  onViewAllTransactions: () -> Unit
42
42
  ) {
43
- val accounts = remember { MockDataProvider.accounts }
44
- val transactions by MockDataProvider.transactions.collectAsState()
45
- val insights = remember { MockDataProvider.insights }
43
+ val repository = remember { DataSourceResolver.repository }
44
+ val accounts by repository.accounts.collectAsState()
45
+ val transactions by repository.transactions.collectAsState()
46
+ val insights by repository.insights.collectAsState()
46
47
 
47
48
  Scaffold(
48
49
  topBar = {
@@ -69,9 +70,9 @@ fun HomeScreen(
69
70
  // Balance Card
70
71
  item {
71
72
  TotalBalanceCard(
72
- balance = MockDataProvider.totalBalance,
73
- income = MockDataProvider.monthlyIncome,
74
- expenses = MockDataProvider.monthlyExpenses
73
+ balance = repository.totalBalance,
74
+ income = repository.monthlyIncome,
75
+ expenses = repository.monthlyExpenses
75
76
  )
76
77
  }
77
78
 
@@ -23,7 +23,6 @@ import com.appship.finance.AppConfig
23
23
  import com.appship.finance.core.theme.*
24
24
  import com.appship.finance.data.Bill
25
25
  import com.appship.finance.features.home.formatCurrency
26
- import com.appship.finance.mock.MockDataProvider
27
26
  import java.time.format.DateTimeFormatter
28
27
 
29
28
  @OptIn(ExperimentalMaterial3Api::class)
@@ -27,13 +27,14 @@ import com.appship.finance.data.Transaction
27
27
  import com.appship.finance.data.TransactionType
28
28
  import com.appship.finance.features.home.TransactionRow
29
29
  import com.appship.finance.features.home.formatCurrency
30
- import com.appship.finance.mock.MockDataProvider
30
+ import com.appship.finance.data.DataSourceResolver
31
31
  import java.time.format.DateTimeFormatter
32
32
 
33
33
  @OptIn(ExperimentalMaterial3Api::class)
34
34
  @Composable
35
35
  fun TransactionsScreen(onAddTransaction: () -> Unit) {
36
- val transactionsFlow by MockDataProvider.transactions.collectAsState()
36
+ val repository = remember { DataSourceResolver.repository }
37
+ val transactionsFlow by repository.transactions.collectAsState()
37
38
  val transactions = remember(transactionsFlow) { transactionsFlow.toMutableStateList() }
38
39
  var searchText by remember { mutableStateOf("") }
39
40
  var selectedFilter by remember { mutableStateOf<TransactionType?>(null) }
@@ -141,7 +142,11 @@ fun AddTransactionScreen(
141
142
  var amount by remember { mutableStateOf("") }
142
143
  var type by remember { mutableStateOf(TransactionType.EXPENSE) }
143
144
  var selectedCategory by remember { mutableStateOf(AppConfig.expenseCategories[0]) }
144
- var selectedAccount by remember { mutableStateOf(MockDataProvider.accounts[0]) }
145
+ val repository = remember { DataSourceResolver.repository }
146
+ val accounts by repository.accounts.collectAsState()
147
+ var selectedAccount by remember(accounts) {
148
+ mutableStateOf(accounts.firstOrNull() ?: throw IllegalStateException("No accounts available"))
149
+ }
145
150
  var notes by remember { mutableStateOf("") }
146
151
 
147
152
  val categories = if (type == TransactionType.EXPENSE) AppConfig.expenseCategories else AppConfig.incomeCategories
@@ -242,7 +247,7 @@ fun AddTransactionScreen(
242
247
  // Account
243
248
  Text("Account", fontWeight = FontWeight.Medium)
244
249
  LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
245
- itemsIndexed(MockDataProvider.accounts) { index, account ->
250
+ itemsIndexed(accounts) { index, account ->
246
251
  FilterChip(
247
252
  selected = selectedAccount.id == account.id,
248
253
  onClick = { selectedAccount = account },