rothzerg 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/dist/cli.js +949 -0
  2. package/package.json +27 -0
  3. package/readme.md +0 -0
  4. package/templates/_base-website/App.tsx +59 -0
  5. package/templates/_base-website/InitialDataContext.tsx +21 -0
  6. package/templates/_base-website/_gitignore +5 -0
  7. package/templates/_base-website/client.tsx +26 -0
  8. package/templates/_base-website/components/DeepLinkLayout.css +41 -0
  9. package/templates/_base-website/components/DeepLinkLayout.tsx +39 -0
  10. package/templates/_base-website/components/DownloadSection.css +60 -0
  11. package/templates/_base-website/components/DownloadSection.tsx +61 -0
  12. package/templates/_base-website/components/SiteFeaturesSection.tsx +68 -0
  13. package/templates/_base-website/components/SiteFooter.tsx +29 -0
  14. package/templates/_base-website/components/SiteHeroSection.tsx +30 -0
  15. package/templates/_base-website/components/SiteHighlightsSection.tsx +38 -0
  16. package/templates/_base-website/components/SiteLanguageSwitcher.tsx +9 -0
  17. package/templates/_base-website/components/SiteNavigation.tsx +24 -0
  18. package/templates/_base-website/config.ts +78 -0
  19. package/templates/_base-website/data/blog/authors.json +18 -0
  20. package/templates/_base-website/data/blog/categories.json +53 -0
  21. package/templates/_base-website/data/blog/posts.json +29 -0
  22. package/templates/_base-website/data/blog/tags.json +86 -0
  23. package/templates/_base-website/i18n/config.ts +60 -0
  24. package/templates/_base-website/i18n/sections/blog/_index.ts +41 -0
  25. package/templates/_base-website/i18n/sections/blog/ar.ts +24 -0
  26. package/templates/_base-website/i18n/sections/blog/de.ts +24 -0
  27. package/templates/_base-website/i18n/sections/blog/en.ts +24 -0
  28. package/templates/_base-website/i18n/sections/blog/es.ts +24 -0
  29. package/templates/_base-website/i18n/sections/blog/fr.ts +24 -0
  30. package/templates/_base-website/i18n/sections/blog/hi.ts +24 -0
  31. package/templates/_base-website/i18n/sections/blog/id.ts +24 -0
  32. package/templates/_base-website/i18n/sections/blog/it.ts +24 -0
  33. package/templates/_base-website/i18n/sections/blog/ja.ts +24 -0
  34. package/templates/_base-website/i18n/sections/blog/ko.ts +24 -0
  35. package/templates/_base-website/i18n/sections/blog/nl.ts +24 -0
  36. package/templates/_base-website/i18n/sections/blog/pl.ts +24 -0
  37. package/templates/_base-website/i18n/sections/blog/pt.ts +24 -0
  38. package/templates/_base-website/i18n/sections/blog/ru.ts +24 -0
  39. package/templates/_base-website/i18n/sections/blog/sv.ts +24 -0
  40. package/templates/_base-website/i18n/sections/blog/th.ts +24 -0
  41. package/templates/_base-website/i18n/sections/blog/tr.ts +24 -0
  42. package/templates/_base-website/i18n/sections/blog/vi.ts +24 -0
  43. package/templates/_base-website/i18n/sections/blog/zh.ts +24 -0
  44. package/templates/_base-website/i18n/sections/common/_index.ts +41 -0
  45. package/templates/_base-website/i18n/sections/common/ar.ts +29 -0
  46. package/templates/_base-website/i18n/sections/common/de.ts +29 -0
  47. package/templates/_base-website/i18n/sections/common/en.ts +29 -0
  48. package/templates/_base-website/i18n/sections/common/es.ts +29 -0
  49. package/templates/_base-website/i18n/sections/common/fr.ts +29 -0
  50. package/templates/_base-website/i18n/sections/common/hi.ts +29 -0
  51. package/templates/_base-website/i18n/sections/common/id.ts +29 -0
  52. package/templates/_base-website/i18n/sections/common/it.ts +29 -0
  53. package/templates/_base-website/i18n/sections/common/ja.ts +29 -0
  54. package/templates/_base-website/i18n/sections/common/ko.ts +29 -0
  55. package/templates/_base-website/i18n/sections/common/nl.ts +29 -0
  56. package/templates/_base-website/i18n/sections/common/pl.ts +29 -0
  57. package/templates/_base-website/i18n/sections/common/pt.ts +29 -0
  58. package/templates/_base-website/i18n/sections/common/ru.ts +29 -0
  59. package/templates/_base-website/i18n/sections/common/sv.ts +29 -0
  60. package/templates/_base-website/i18n/sections/common/th.ts +29 -0
  61. package/templates/_base-website/i18n/sections/common/tr.ts +29 -0
  62. package/templates/_base-website/i18n/sections/common/vi.ts +29 -0
  63. package/templates/_base-website/i18n/sections/common/zh.ts +29 -0
  64. package/templates/_base-website/i18n/sections/deepLink/_index.ts +41 -0
  65. package/templates/_base-website/i18n/sections/deepLink/ar.ts +14 -0
  66. package/templates/_base-website/i18n/sections/deepLink/de.ts +14 -0
  67. package/templates/_base-website/i18n/sections/deepLink/en.ts +14 -0
  68. package/templates/_base-website/i18n/sections/deepLink/es.ts +14 -0
  69. package/templates/_base-website/i18n/sections/deepLink/fr.ts +14 -0
  70. package/templates/_base-website/i18n/sections/deepLink/hi.ts +14 -0
  71. package/templates/_base-website/i18n/sections/deepLink/id.ts +14 -0
  72. package/templates/_base-website/i18n/sections/deepLink/it.ts +14 -0
  73. package/templates/_base-website/i18n/sections/deepLink/ja.ts +14 -0
  74. package/templates/_base-website/i18n/sections/deepLink/ko.ts +14 -0
  75. package/templates/_base-website/i18n/sections/deepLink/nl.ts +14 -0
  76. package/templates/_base-website/i18n/sections/deepLink/pl.ts +14 -0
  77. package/templates/_base-website/i18n/sections/deepLink/pt.ts +14 -0
  78. package/templates/_base-website/i18n/sections/deepLink/ru.ts +14 -0
  79. package/templates/_base-website/i18n/sections/deepLink/sv.ts +14 -0
  80. package/templates/_base-website/i18n/sections/deepLink/th.ts +14 -0
  81. package/templates/_base-website/i18n/sections/deepLink/tr.ts +14 -0
  82. package/templates/_base-website/i18n/sections/deepLink/vi.ts +14 -0
  83. package/templates/_base-website/i18n/sections/deepLink/zh.ts +14 -0
  84. package/templates/_base-website/i18n/sections/home/_index.ts +41 -0
  85. package/templates/_base-website/i18n/sections/home/ar.ts +85 -0
  86. package/templates/_base-website/i18n/sections/home/de.ts +85 -0
  87. package/templates/_base-website/i18n/sections/home/en.ts +85 -0
  88. package/templates/_base-website/i18n/sections/home/es.ts +85 -0
  89. package/templates/_base-website/i18n/sections/home/fr.ts +85 -0
  90. package/templates/_base-website/i18n/sections/home/hi.ts +85 -0
  91. package/templates/_base-website/i18n/sections/home/id.ts +85 -0
  92. package/templates/_base-website/i18n/sections/home/it.ts +85 -0
  93. package/templates/_base-website/i18n/sections/home/ja.ts +85 -0
  94. package/templates/_base-website/i18n/sections/home/ko.ts +85 -0
  95. package/templates/_base-website/i18n/sections/home/nl.ts +85 -0
  96. package/templates/_base-website/i18n/sections/home/pl.ts +85 -0
  97. package/templates/_base-website/i18n/sections/home/pt.ts +85 -0
  98. package/templates/_base-website/i18n/sections/home/ru.ts +85 -0
  99. package/templates/_base-website/i18n/sections/home/sv.ts +85 -0
  100. package/templates/_base-website/i18n/sections/home/th.ts +85 -0
  101. package/templates/_base-website/i18n/sections/home/tr.ts +85 -0
  102. package/templates/_base-website/i18n/sections/home/vi.ts +85 -0
  103. package/templates/_base-website/i18n/sections/home/zh.ts +85 -0
  104. package/templates/_base-website/i18n/sections/support/_index.ts +41 -0
  105. package/templates/_base-website/i18n/sections/support/ar.ts +37 -0
  106. package/templates/_base-website/i18n/sections/support/de.ts +37 -0
  107. package/templates/_base-website/i18n/sections/support/en.ts +37 -0
  108. package/templates/_base-website/i18n/sections/support/es.ts +37 -0
  109. package/templates/_base-website/i18n/sections/support/fr.ts +37 -0
  110. package/templates/_base-website/i18n/sections/support/hi.ts +37 -0
  111. package/templates/_base-website/i18n/sections/support/id.ts +37 -0
  112. package/templates/_base-website/i18n/sections/support/it.ts +37 -0
  113. package/templates/_base-website/i18n/sections/support/ja.ts +37 -0
  114. package/templates/_base-website/i18n/sections/support/ko.ts +37 -0
  115. package/templates/_base-website/i18n/sections/support/nl.ts +37 -0
  116. package/templates/_base-website/i18n/sections/support/pl.ts +37 -0
  117. package/templates/_base-website/i18n/sections/support/pt.ts +37 -0
  118. package/templates/_base-website/i18n/sections/support/ru.ts +37 -0
  119. package/templates/_base-website/i18n/sections/support/sv.ts +37 -0
  120. package/templates/_base-website/i18n/sections/support/th.ts +37 -0
  121. package/templates/_base-website/i18n/sections/support/tr.ts +37 -0
  122. package/templates/_base-website/i18n/sections/support/vi.ts +37 -0
  123. package/templates/_base-website/i18n/sections/support/zh.ts +37 -0
  124. package/templates/_base-website/i18n/translations.ts +25 -0
  125. package/templates/_base-website/index.ts +460 -0
  126. package/templates/_base-website/pages/404.tsx +35 -0
  127. package/templates/_base-website/pages/blog/author.tsx +97 -0
  128. package/templates/_base-website/pages/blog/category.tsx +89 -0
  129. package/templates/_base-website/pages/blog/index.tsx +81 -0
  130. package/templates/_base-website/pages/blog/post.tsx +110 -0
  131. package/templates/_base-website/pages/blog/tag.tsx +86 -0
  132. package/templates/_base-website/pages/custom-pages/example.tsx +54 -0
  133. package/templates/_base-website/pages/index.tsx +29 -0
  134. package/templates/_base-website/pages/privacy.tsx +45 -0
  135. package/templates/_base-website/pages/support.tsx +154 -0
  136. package/templates/_base-website/pages/terms.tsx +68 -0
  137. package/templates/_base-website/public/images/16.png +0 -0
  138. package/templates/_base-website/public/images/apple-touch-icon.png +0 -0
  139. package/templates/_base-website/public/images/favicon-32x32.png +0 -0
  140. package/templates/_base-website/public/images/favicon.png +0 -0
  141. package/templates/_base-website/public/images/logo_dark.svg +6 -0
  142. package/templates/_base-website/public/images/logo_light.svg +6 -0
  143. package/templates/_base-website/public/images/og-image.png +0 -0
  144. package/templates/_base-website/public/images/screenshots/ar_dashboard.jpg +0 -0
  145. package/templates/_base-website/public/images/screenshots/de_dashboard.jpg +0 -0
  146. package/templates/_base-website/public/images/screenshots/en_dashboard.jpg +0 -0
  147. package/templates/_base-website/public/images/screenshots/es_dashboard.jpg +0 -0
  148. package/templates/_base-website/public/images/screenshots/fr_dashboard.jpg +0 -0
  149. package/templates/_base-website/public/images/screenshots/hi_dashboard.jpg +0 -0
  150. package/templates/_base-website/public/images/screenshots/id_dashboard.jpg +0 -0
  151. package/templates/_base-website/public/images/screenshots/it_dashboard.jpg +0 -0
  152. package/templates/_base-website/public/images/screenshots/ja_dashboard.jpg +0 -0
  153. package/templates/_base-website/public/images/screenshots/ko_dashboard.jpg +0 -0
  154. package/templates/_base-website/public/images/screenshots/nl_dashboard.jpg +0 -0
  155. package/templates/_base-website/public/images/screenshots/pl_dashboard.jpg +0 -0
  156. package/templates/_base-website/public/images/screenshots/pt_dashboard.jpg +0 -0
  157. package/templates/_base-website/public/images/screenshots/ru_dashboard.jpg +0 -0
  158. package/templates/_base-website/public/images/screenshots/sv_dashboard.jpg +0 -0
  159. package/templates/_base-website/public/images/screenshots/th_dashboard.jpg +0 -0
  160. package/templates/_base-website/public/images/screenshots/tr_dashboard.jpg +0 -0
  161. package/templates/_base-website/public/images/screenshots/vi_dashboard.jpg +0 -0
  162. package/templates/_base-website/public/images/screenshots/zh_dashboard.jpg +0 -0
  163. package/templates/_base-website/rothzerg.template.json +63 -0
  164. package/templates/_base-website/styles/404.css +32 -0
  165. package/templates/_base-website/styles/_app.css +131 -0
  166. package/templates/_base-website/styles/_shared.css +194 -0
  167. package/templates/_base-website/styles/index.css +1 -0
  168. package/templates/_base-website/styles/privacy.css +1 -0
  169. package/templates/_base-website/styles/support.css +148 -0
  170. package/templates/_base-website/styles/terms.css +22 -0
  171. package/templates/mobile-app/_gitignore +46 -0
  172. package/templates/mobile-app/analysis_options.yaml +28 -0
  173. package/templates/mobile-app/android/app/build.gradle.kts +56 -0
  174. package/templates/mobile-app/android/app/google-services.json +29 -0
  175. package/templates/mobile-app/android/app/src/debug/AndroidManifest.xml +7 -0
  176. package/templates/mobile-app/android/app/src/main/AndroidManifest.xml +54 -0
  177. package/templates/mobile-app/android/app/src/main/kotlin/com/example/{{projectNameSnake}}/MainActivity.kt +5 -0
  178. package/templates/mobile-app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  179. package/templates/mobile-app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  180. package/templates/mobile-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  181. package/templates/mobile-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  182. package/templates/mobile-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  183. package/templates/mobile-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  184. package/templates/mobile-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  185. package/templates/mobile-app/android/app/src/main/res/values/styles.xml +18 -0
  186. package/templates/mobile-app/android/app/src/main/res/values-night/styles.xml +18 -0
  187. package/templates/mobile-app/android/app/src/profile/AndroidManifest.xml +7 -0
  188. package/templates/mobile-app/android/build.gradle.kts +24 -0
  189. package/templates/mobile-app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  190. package/templates/mobile-app/android/gradle.properties +2 -0
  191. package/templates/mobile-app/android/settings.gradle.kts +31 -0
  192. package/templates/mobile-app/assets/icons/logo.png +0 -0
  193. package/templates/mobile-app/assets/icons/logo_dark.svg +5 -0
  194. package/templates/mobile-app/assets/icons/logo_light.svg +5 -0
  195. package/templates/mobile-app/assets/lottie/tick.json +1 -0
  196. package/templates/mobile-app/devtools_options.yaml +3 -0
  197. package/templates/mobile-app/ios/Flutter/AppFrameworkInfo.plist +26 -0
  198. package/templates/mobile-app/ios/Flutter/Debug.xcconfig +2 -0
  199. package/templates/mobile-app/ios/Flutter/Release.xcconfig +2 -0
  200. package/templates/mobile-app/ios/Podfile +46 -0
  201. package/templates/mobile-app/ios/Podfile.lock +1807 -0
  202. package/templates/mobile-app/ios/Runner/AppDelegate.swift +16 -0
  203. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +122 -0
  204. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  205. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  206. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  207. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  208. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  209. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  210. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  211. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  212. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  213. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  214. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  215. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  216. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  217. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  218. package/templates/mobile-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  219. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +23 -0
  220. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
  221. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
  222. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
  223. package/templates/mobile-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +5 -0
  224. package/templates/mobile-app/ios/Runner/Base.lproj/LaunchScreen.storyboard +37 -0
  225. package/templates/mobile-app/ios/Runner/Base.lproj/Main.storyboard +26 -0
  226. package/templates/mobile-app/ios/Runner/GoogleService-Info.plist +30 -0
  227. package/templates/mobile-app/ios/Runner/Info.plist +70 -0
  228. package/templates/mobile-app/ios/Runner/Runner-Bridging-Header.h +1 -0
  229. package/templates/mobile-app/ios/Runner.xcodeproj/project.pbxproj +772 -0
  230. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  231. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  232. package/templates/mobile-app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  233. package/templates/mobile-app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +101 -0
  234. package/templates/mobile-app/ios/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  235. package/templates/mobile-app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  236. package/templates/mobile-app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  237. package/templates/mobile-app/ios/RunnerTests/RunnerTests.swift +12 -0
  238. package/templates/mobile-app/l10n.yaml +5 -0
  239. package/templates/mobile-app/lib/core/config/app_config.dart +76 -0
  240. package/templates/mobile-app/lib/core/constants/app_colors.dart +13 -0
  241. package/templates/mobile-app/lib/core/constants/app_constants.dart +123 -0
  242. package/templates/mobile-app/lib/core/constants/countries.dart +199 -0
  243. package/templates/mobile-app/lib/core/constants/keys.dart +46 -0
  244. package/templates/mobile-app/lib/core/constants/languages.dart +51 -0
  245. package/templates/mobile-app/lib/core/services/admin_service.dart +271 -0
  246. package/templates/mobile-app/lib/core/services/analytics_service.dart +296 -0
  247. package/templates/mobile-app/lib/core/services/auth_service.dart +185 -0
  248. package/templates/mobile-app/lib/core/services/device_service.dart +228 -0
  249. package/templates/mobile-app/lib/core/services/firebase_service.dart +490 -0
  250. package/templates/mobile-app/lib/core/services/in_app_messaging_service.dart +166 -0
  251. package/templates/mobile-app/lib/core/services/logging_service.dart +451 -0
  252. package/templates/mobile-app/lib/core/services/messaging_service.dart +460 -0
  253. package/templates/mobile-app/lib/core/services/remote_config_service.dart +167 -0
  254. package/templates/mobile-app/lib/core/services/revenue_cat_service.dart +364 -0
  255. package/templates/mobile-app/lib/core/services/storage_service.dart +313 -0
  256. package/templates/mobile-app/lib/core/theme/dark_theme.dart +295 -0
  257. package/templates/mobile-app/lib/core/theme/light_theme.dart +296 -0
  258. package/templates/mobile-app/lib/core/utils/formatters.dart +236 -0
  259. package/templates/mobile-app/lib/core/utils/image_picker_helper.dart +78 -0
  260. package/templates/mobile-app/lib/core/utils/validators.dart +292 -0
  261. package/templates/mobile-app/lib/firebase_options.dart +19 -0
  262. package/templates/mobile-app/lib/l10n/app_en.arb +1371 -0
  263. package/templates/mobile-app/lib/l10n/app_localizations.dart +2042 -0
  264. package/templates/mobile-app/lib/l10n/app_localizations_en.dart +1033 -0
  265. package/templates/mobile-app/lib/main.dart +152 -0
  266. package/templates/mobile-app/lib/models/device_model.dart +90 -0
  267. package/templates/mobile-app/lib/models/news_banner.dart +53 -0
  268. package/templates/mobile-app/lib/models/user_model.dart +120 -0
  269. package/templates/mobile-app/lib/providers/admin_provider.dart +67 -0
  270. package/templates/mobile-app/lib/providers/auth_provider.dart +328 -0
  271. package/templates/mobile-app/lib/providers/remote_config_provider.dart +81 -0
  272. package/templates/mobile-app/lib/providers/subscription_provider.dart +67 -0
  273. package/templates/mobile-app/lib/providers/theme_provider.dart +51 -0
  274. package/templates/mobile-app/lib/screens/admin/admin_panel_screen.dart +436 -0
  275. package/templates/mobile-app/lib/screens/admin/admin_panel_user_details_screen.dart +942 -0
  276. package/templates/mobile-app/lib/screens/admin/admin_panel_users_screen.dart +344 -0
  277. package/templates/mobile-app/lib/screens/auth/auth_home_screen.dart +266 -0
  278. package/templates/mobile-app/lib/screens/auth/forgot_password_screen.dart +214 -0
  279. package/templates/mobile-app/lib/screens/auth/login_screen.dart +312 -0
  280. package/templates/mobile-app/lib/screens/auth/profile_setup_screen.dart +157 -0
  281. package/templates/mobile-app/lib/screens/auth/register_screen.dart +288 -0
  282. package/templates/mobile-app/lib/screens/developer/developer_panel_screen.dart +436 -0
  283. package/templates/mobile-app/lib/screens/developer/remote_config_screen.dart +315 -0
  284. package/templates/mobile-app/lib/screens/developer/remote_config_test_screen.dart +461 -0
  285. package/templates/mobile-app/lib/screens/home/home_screen.dart +270 -0
  286. package/templates/mobile-app/lib/screens/legal/privacy_policy_screen.dart +63 -0
  287. package/templates/mobile-app/lib/screens/legal/terms_screen.dart +82 -0
  288. package/templates/mobile-app/lib/screens/main_navigation_screen.dart +85 -0
  289. package/templates/mobile-app/lib/screens/premium/subscription_screen.dart +516 -0
  290. package/templates/mobile-app/lib/screens/profile/edit_profile_screen.dart +246 -0
  291. package/templates/mobile-app/lib/screens/settings/about_app_screen.dart +200 -0
  292. package/templates/mobile-app/lib/screens/settings/cache_manager_screen.dart +400 -0
  293. package/templates/mobile-app/lib/screens/settings/change_password_screen.dart +269 -0
  294. package/templates/mobile-app/lib/screens/settings/delete_account_screen.dart +261 -0
  295. package/templates/mobile-app/lib/screens/settings/settings_screen.dart +400 -0
  296. package/templates/mobile-app/lib/screens/splash/splash_screen.dart +125 -0
  297. package/templates/mobile-app/lib/widgets/app_button.dart +255 -0
  298. package/templates/mobile-app/lib/widgets/app_drawer.dart +207 -0
  299. package/templates/mobile-app/lib/widgets/bottom_sheet_selector.dart +79 -0
  300. package/templates/mobile-app/lib/widgets/button_banner.dart +177 -0
  301. package/templates/mobile-app/lib/widgets/country_picker.dart +169 -0
  302. package/templates/mobile-app/lib/widgets/custom_app_bar.dart +30 -0
  303. package/templates/mobile-app/lib/widgets/email_verification_banner.dart +163 -0
  304. package/templates/mobile-app/lib/widgets/empty_screen.dart +42 -0
  305. package/templates/mobile-app/lib/widgets/language_picker.dart +145 -0
  306. package/templates/mobile-app/lib/widgets/news_banner_widget.dart +264 -0
  307. package/templates/mobile-app/lib/widgets/remote_config/remote_config_widgets.dart +294 -0
  308. package/templates/mobile-app/lib/widgets/section_header.dart +23 -0
  309. package/templates/mobile-app/lib/widgets/success_overlay.dart +117 -0
  310. package/templates/mobile-app/lib/widgets/top_notification.dart +263 -0
  311. package/templates/mobile-app/lib/widgets/user_avatar_picker.dart +58 -0
  312. package/templates/mobile-app/pubspec.yaml +97 -0
  313. package/templates/mobile-app/rothzerg.template.json +43 -0
  314. package/templates/web-app/_gitignore +4 -0
  315. package/templates/web-app/index.html +12 -0
  316. package/templates/web-app/package.json +23 -0
  317. package/templates/web-app/rothzerg.template.json +41 -0
  318. package/templates/web-app/src/App.css +6 -0
  319. package/templates/web-app/src/App.tsx +12 -0
  320. package/templates/web-app/src/index.css +14 -0
  321. package/templates/web-app/src/main.tsx +10 -0
  322. package/templates/web-app/tsconfig.app.json +20 -0
  323. package/templates/web-app/tsconfig.json +7 -0
  324. package/templates/web-app/tsconfig.node.json +18 -0
  325. package/templates/web-app/vite.config.ts +6 -0
  326. package/templates/website-blog/overrides/config.ts +76 -0
  327. package/templates/website-blog/overrides/pages/index.tsx +63 -0
  328. package/templates/website-blog/rothzerg.template.json +29 -0
  329. package/templates/website-business/overrides/App.tsx +49 -0
  330. package/templates/website-business/overrides/components/SiteHeroSection.tsx +33 -0
  331. package/templates/website-business/overrides/config.ts +76 -0
  332. package/templates/website-business/overrides/pages/about.tsx +50 -0
  333. package/templates/website-business/overrides/pages/contact.tsx +61 -0
  334. package/templates/website-business/overrides/pages/index.tsx +37 -0
  335. package/templates/website-business/overrides/pages/services.tsx +70 -0
  336. package/templates/website-business/rothzerg.template.json +18 -0
  337. package/templates/website-mobile/overrides/App.tsx +62 -0
  338. package/templates/website-mobile/overrides/components/SiteHeroSection.tsx +18 -0
  339. package/templates/website-mobile/overrides/config.ts +76 -0
  340. package/templates/website-mobile/overrides/pages/index.tsx +34 -0
  341. package/templates/website-mobile/rothzerg.template.json +64 -0
@@ -0,0 +1,236 @@
1
+ import 'package:intl/intl.dart';
2
+
3
+ /// Text and date formatters
4
+ class Formatters {
5
+ Formatters._();
6
+
7
+ // ============= Date Formatting =============
8
+
9
+ static String formatDate(DateTime date, {String pattern = 'MMM d, yyyy'}) {
10
+ return DateFormat(pattern).format(date);
11
+ }
12
+
13
+ static String formatDateShort(DateTime date) {
14
+ return DateFormat('MMM d, yyyy').format(date);
15
+ }
16
+
17
+ static String formatDateLong(DateTime date) {
18
+ return DateFormat('MMMM d, yyyy').format(date);
19
+ }
20
+
21
+ static String formatDateFull(DateTime date) {
22
+ return DateFormat('EEEE, MMMM d, yyyy').format(date);
23
+ }
24
+
25
+ static String formatTime(DateTime date) {
26
+ return DateFormat('h:mm a').format(date);
27
+ }
28
+
29
+ static String formatDateTime(DateTime date) {
30
+ return DateFormat('MMM d, yyyy h:mm a').format(date);
31
+ }
32
+
33
+ static String formatDateTimeShort(DateTime date) {
34
+ return DateFormat('M/d/yy h:mm a').format(date);
35
+ }
36
+
37
+ // ============= Relative Time Formatting =============
38
+
39
+ static String formatRelativeTime(DateTime date) {
40
+ final now = DateTime.now();
41
+ final difference = now.difference(date);
42
+
43
+ if (difference.inSeconds < 60) {
44
+ return 'Just now';
45
+ } else if (difference.inMinutes < 60) {
46
+ final minutes = difference.inMinutes;
47
+ return '$minutes ${minutes == 1 ? 'minute' : 'minutes'} ago';
48
+ } else if (difference.inHours < 24) {
49
+ final hours = difference.inHours;
50
+ return '$hours ${hours == 1 ? 'hour' : 'hours'} ago';
51
+ } else if (difference.inDays < 7) {
52
+ final days = difference.inDays;
53
+ return '$days ${days == 1 ? 'day' : 'days'} ago';
54
+ } else if (difference.inDays < 30) {
55
+ final weeks = (difference.inDays / 7).floor();
56
+ return '$weeks ${weeks == 1 ? 'week' : 'weeks'} ago';
57
+ } else if (difference.inDays < 365) {
58
+ final months = (difference.inDays / 30).floor();
59
+ return '$months ${months == 1 ? 'month' : 'months'} ago';
60
+ } else {
61
+ final years = (difference.inDays / 365).floor();
62
+ return '$years ${years == 1 ? 'year' : 'years'} ago';
63
+ }
64
+ }
65
+
66
+ // ============= Number Formatting =============
67
+
68
+ static String formatNumber(num number, {int decimalDigits = 0}) {
69
+ final formatter = NumberFormat(
70
+ '#,##0${decimalDigits > 0 ? '.${'0' * decimalDigits}' : ''}',
71
+ );
72
+ return formatter.format(number);
73
+ }
74
+
75
+ static String formatCurrency(
76
+ num amount, {
77
+ String symbol = '\$',
78
+ int decimalDigits = 2,
79
+ }) {
80
+ final formatter = NumberFormat.currency(
81
+ symbol: symbol,
82
+ decimalDigits: decimalDigits,
83
+ );
84
+ return formatter.format(amount);
85
+ }
86
+
87
+ static String formatPercentage(num value, {int decimalDigits = 0}) {
88
+ final formatter = NumberFormat.percentPattern();
89
+ formatter.minimumFractionDigits = decimalDigits;
90
+ formatter.maximumFractionDigits = decimalDigits;
91
+ return formatter.format(value / 100);
92
+ }
93
+
94
+ static String formatCompactNumber(num number) {
95
+ final formatter = NumberFormat.compact();
96
+ return formatter.format(number);
97
+ }
98
+
99
+ // e.g., 1000 -> "1K", 1500000 -> "1.5M"
100
+ static String formatShortNumber(num number) {
101
+ if (number < 1000) {
102
+ return number.toString();
103
+ } else if (number < 1000000) {
104
+ return '${(number / 1000).toStringAsFixed(1)}K';
105
+ } else if (number < 1000000000) {
106
+ return '${(number / 1000000).toStringAsFixed(1)}M';
107
+ } else {
108
+ return '${(number / 1000000000).toStringAsFixed(1)}B';
109
+ }
110
+ }
111
+
112
+ // ============= File Size Formatting =============
113
+
114
+ static String formatFileSize(int bytes) {
115
+ if (bytes < 1024) {
116
+ return '$bytes B';
117
+ } else if (bytes < 1024 * 1024) {
118
+ return '${(bytes / 1024).toStringAsFixed(2)} KB';
119
+ } else if (bytes < 1024 * 1024 * 1024) {
120
+ return '${(bytes / (1024 * 1024)).toStringAsFixed(2)} MB';
121
+ } else {
122
+ return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
123
+ }
124
+ }
125
+
126
+ // ============= Duration Formatting =============
127
+
128
+ static String formatDuration(Duration duration) {
129
+ final hours = duration.inHours;
130
+ final minutes = duration.inMinutes % 60;
131
+ final seconds = duration.inSeconds % 60;
132
+
133
+ if (hours > 0) {
134
+ return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
135
+ } else {
136
+ return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
137
+ }
138
+ }
139
+
140
+ static String formatDurationWords(Duration duration) {
141
+ final days = duration.inDays;
142
+ final hours = duration.inHours % 24;
143
+ final minutes = duration.inMinutes % 60;
144
+
145
+ final parts = <String>[];
146
+ if (days > 0) parts.add('$days ${days == 1 ? 'day' : 'days'}');
147
+ if (hours > 0) parts.add('$hours ${hours == 1 ? 'hour' : 'hours'}');
148
+ if (minutes > 0) {
149
+ parts.add('$minutes ${minutes == 1 ? 'minute' : 'minutes'}');
150
+ }
151
+
152
+ if (parts.isEmpty) return 'Less than a minute';
153
+ return parts.join(', ');
154
+ }
155
+
156
+ // ============= Text Formatting =============
157
+
158
+ static String capitalize(String text) {
159
+ if (text.isEmpty) return text;
160
+ return text[0].toUpperCase() + text.substring(1);
161
+ }
162
+
163
+ static String capitalizeWords(String text) {
164
+ return text
165
+ .split(' ')
166
+ .map((word) {
167
+ if (word.isEmpty) return word;
168
+ return word[0].toUpperCase() + word.substring(1).toLowerCase();
169
+ })
170
+ .join(' ');
171
+ }
172
+
173
+ static String truncate(String text, int maxLength, {String suffix = '...'}) {
174
+ if (text.length <= maxLength) return text;
175
+ return text.substring(0, maxLength - suffix.length) + suffix;
176
+ }
177
+
178
+ static String formatPhoneNumber(String phone) {
179
+ // Remove all non-digit characters
180
+ final digitsOnly = phone.replaceAll(RegExp(r'\D'), '');
181
+
182
+ if (digitsOnly.length == 10) {
183
+ // US format: (555) 123-4567
184
+ return '(${digitsOnly.substring(0, 3)}) ${digitsOnly.substring(3, 6)}-${digitsOnly.substring(6)}';
185
+ } else if (digitsOnly.length == 11) {
186
+ // US format with country code: +1 (555) 123-4567
187
+ return '+${digitsOnly.substring(0, 1)} (${digitsOnly.substring(1, 4)}) ${digitsOnly.substring(4, 7)}-${digitsOnly.substring(7)}';
188
+ }
189
+
190
+ return phone; // Return original if format doesn't match
191
+ }
192
+
193
+ static String formatCreditCard(String cardNumber) {
194
+ // Remove all non-digit characters
195
+ final digitsOnly = cardNumber.replaceAll(RegExp(r'\D'), '');
196
+
197
+ // Format as: 1234 5678 9012 3456
198
+ final formatted = StringBuffer();
199
+ for (int i = 0; i < digitsOnly.length; i++) {
200
+ if (i > 0 && i % 4 == 0) {
201
+ formatted.write(' ');
202
+ }
203
+ formatted.write(digitsOnly[i]);
204
+ }
205
+
206
+ return formatted.toString();
207
+ }
208
+
209
+ // ============= Placeholder/Mask Formatting =============
210
+
211
+ static String maskEmail(String email) {
212
+ if (!email.contains('@')) return email;
213
+
214
+ final parts = email.split('@');
215
+ final username = parts[0];
216
+ final domain = parts[1];
217
+
218
+ if (username.length <= 2) {
219
+ return '${'*' * username.length}@$domain';
220
+ }
221
+
222
+ return '${username[0]}${'*' * (username.length - 2)}${username[username.length - 1]}@$domain';
223
+ }
224
+
225
+ static String maskPhoneNumber(String phone) {
226
+ if (phone.length <= 4) return phone;
227
+ return '*' * (phone.length - 4) + phone.substring(phone.length - 4);
228
+ }
229
+
230
+ static String maskCreditCard(String cardNumber) {
231
+ final digitsOnly = cardNumber.replaceAll(RegExp(r'\D'), '');
232
+ if (digitsOnly.length <= 4) return cardNumber;
233
+ return '*' * (digitsOnly.length - 4) +
234
+ digitsOnly.substring(digitsOnly.length - 4);
235
+ }
236
+ }
@@ -0,0 +1,78 @@
1
+ import 'dart:io';
2
+ import 'package:flutter/material.dart';
3
+ import 'package:image_picker/image_picker.dart';
4
+ import '../../l10n/app_localizations.dart';
5
+ import '../../widgets/top_notification.dart';
6
+
7
+ class ImagePickerHelper {
8
+ static final ImagePicker _imagePicker = ImagePicker();
9
+
10
+ /// Pick an image from the specified source
11
+ static Future<File?> pickImage(
12
+ BuildContext context,
13
+ ImageSource source,
14
+ ) async {
15
+ try {
16
+ final pickedFile = await _imagePicker.pickImage(
17
+ source: source,
18
+ maxWidth: 400,
19
+ maxHeight: 400,
20
+ imageQuality: 90,
21
+ );
22
+
23
+ if (pickedFile != null) {
24
+ return File(pickedFile.path);
25
+ }
26
+ return null;
27
+ } catch (e) {
28
+ if (context.mounted) {
29
+ TopNotification.showError(context, message: 'Failed to pick image: $e');
30
+ }
31
+ return null;
32
+ }
33
+ }
34
+
35
+ /// Show a bottom sheet to select image source (camera or gallery)
36
+ static Future<File?> showImageSourceDialog(BuildContext context) async {
37
+ final l10n = AppLocalizations.of(context)!;
38
+
39
+ final source = await showModalBottomSheet<ImageSource>(
40
+ context: context,
41
+ shape: const RoundedRectangleBorder(
42
+ borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
43
+ ),
44
+ builder: (context) => SafeArea(
45
+ child: Column(
46
+ mainAxisSize: MainAxisSize.min,
47
+ children: [
48
+ const SizedBox(height: 16),
49
+ Text(
50
+ l10n.selectPhoto,
51
+ style: Theme.of(
52
+ context,
53
+ ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
54
+ ),
55
+ const SizedBox(height: 16),
56
+ ListTile(
57
+ leading: const Icon(Icons.camera_alt),
58
+ title: Text(l10n.camera),
59
+ onTap: () => Navigator.pop(context, ImageSource.camera),
60
+ ),
61
+ ListTile(
62
+ leading: const Icon(Icons.photo_library),
63
+ title: Text(l10n.gallery),
64
+ onTap: () => Navigator.pop(context, ImageSource.gallery),
65
+ ),
66
+ const SizedBox(height: 16),
67
+ ],
68
+ ),
69
+ ),
70
+ );
71
+
72
+ if (source != null && context.mounted) {
73
+ return await pickImage(context, source);
74
+ }
75
+
76
+ return null;
77
+ }
78
+ }
@@ -0,0 +1,292 @@
1
+ /// Input validators for forms
2
+ class Validators {
3
+ Validators._();
4
+
5
+ // ============= Email Validation =============
6
+
7
+ static String? email(String? value) {
8
+ if (value == null || value.isEmpty) {
9
+ return 'Email is required';
10
+ }
11
+
12
+ final emailRegex = RegExp(
13
+ r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
14
+ );
15
+
16
+ if (!emailRegex.hasMatch(value)) {
17
+ return 'Please enter a valid email';
18
+ }
19
+
20
+ return null;
21
+ }
22
+
23
+ // ============= Password Validation =============
24
+
25
+ static String? password(String? value, {int minLength = 8}) {
26
+ if (value == null || value.isEmpty) {
27
+ return 'Password is required';
28
+ }
29
+
30
+ if (value.length < minLength) {
31
+ return 'Password must be at least $minLength characters';
32
+ }
33
+
34
+ return null;
35
+ }
36
+
37
+ static String? strongPassword(String? value) {
38
+ if (value == null || value.isEmpty) {
39
+ return 'Password is required';
40
+ }
41
+
42
+ if (value.length < 8) {
43
+ return 'Password must be at least 8 characters';
44
+ }
45
+
46
+ if (!value.contains(RegExp(r'[A-Z]'))) {
47
+ return 'Password must contain at least one uppercase letter';
48
+ }
49
+
50
+ if (!value.contains(RegExp(r'[a-z]'))) {
51
+ return 'Password must contain at least one lowercase letter';
52
+ }
53
+
54
+ if (!value.contains(RegExp(r'[0-9]'))) {
55
+ return 'Password must contain at least one number';
56
+ }
57
+
58
+ if (!value.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'))) {
59
+ return 'Password must contain at least one special character';
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ static String? confirmPassword(String? value, String? password) {
66
+ if (value == null || value.isEmpty) {
67
+ return 'Please confirm your password';
68
+ }
69
+
70
+ if (value != password) {
71
+ return 'Passwords do not match';
72
+ }
73
+
74
+ return null;
75
+ }
76
+
77
+ // ============= Name Validation =============
78
+
79
+ static String? name(String? value, {int minLength = 2}) {
80
+ if (value == null || value.isEmpty) {
81
+ return 'Name is required';
82
+ }
83
+
84
+ if (value.length < minLength) {
85
+ return 'Name must be at least $minLength characters';
86
+ }
87
+
88
+ if (!RegExp(r'^[a-zA-Z\s]+$').hasMatch(value)) {
89
+ return 'Name can only contain letters and spaces';
90
+ }
91
+
92
+ return null;
93
+ }
94
+
95
+ static String? required(String? value, {String? fieldName}) {
96
+ if (value == null || value.trim().isEmpty) {
97
+ return '${fieldName ?? 'This field'} is required';
98
+ }
99
+ return null;
100
+ }
101
+
102
+ // ============= Phone Validation =============
103
+
104
+ static String? phone(String? value) {
105
+ if (value == null || value.isEmpty) {
106
+ return 'Phone number is required';
107
+ }
108
+
109
+ final phoneRegex = RegExp(r'^\+?[\d\s-()]{10,}$');
110
+ if (!phoneRegex.hasMatch(value)) {
111
+ return 'Please enter a valid phone number';
112
+ }
113
+
114
+ return null;
115
+ }
116
+
117
+ // ============= URL Validation =============
118
+
119
+ static String? url(String? value) {
120
+ if (value == null || value.isEmpty) {
121
+ return 'URL is required';
122
+ }
123
+
124
+ final urlRegex = RegExp(
125
+ r'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)',
126
+ );
127
+
128
+ if (!urlRegex.hasMatch(value)) {
129
+ return 'Please enter a valid URL';
130
+ }
131
+
132
+ return null;
133
+ }
134
+
135
+ // ============= Number Validation =============
136
+
137
+ static String? number(String? value) {
138
+ if (value == null || value.isEmpty) {
139
+ return 'Please enter a number';
140
+ }
141
+
142
+ if (double.tryParse(value) == null) {
143
+ return 'Please enter a valid number';
144
+ }
145
+
146
+ return null;
147
+ }
148
+
149
+ static String? positiveNumber(String? value) {
150
+ final error = number(value);
151
+ if (error != null) return error;
152
+
153
+ final num = double.parse(value!);
154
+ if (num <= 0) {
155
+ return 'Please enter a positive number';
156
+ }
157
+
158
+ return null;
159
+ }
160
+
161
+ static String? integerNumber(String? value) {
162
+ if (value == null || value.isEmpty) {
163
+ return 'Please enter a number';
164
+ }
165
+
166
+ if (int.tryParse(value) == null) {
167
+ return 'Please enter a valid whole number';
168
+ }
169
+
170
+ return null;
171
+ }
172
+
173
+ // ============= Length Validation =============
174
+
175
+ static String? minLength(String? value, int min, {String? fieldName}) {
176
+ if (value == null || value.isEmpty) {
177
+ return '${fieldName ?? 'This field'} is required';
178
+ }
179
+
180
+ if (value.length < min) {
181
+ return '${fieldName ?? 'This field'} must be at least $min characters';
182
+ }
183
+
184
+ return null;
185
+ }
186
+
187
+ static String? maxLength(String? value, int max, {String? fieldName}) {
188
+ if (value != null && value.length > max) {
189
+ return '${fieldName ?? 'This field'} must not exceed $max characters';
190
+ }
191
+
192
+ return null;
193
+ }
194
+
195
+ static String? exactLength(String? value, int length, {String? fieldName}) {
196
+ if (value == null || value.isEmpty) {
197
+ return '${fieldName ?? 'This field'} is required';
198
+ }
199
+
200
+ if (value.length != length) {
201
+ return '${fieldName ?? 'This field'} must be exactly $length characters';
202
+ }
203
+
204
+ return null;
205
+ }
206
+
207
+ // ============= Range Validation =============
208
+
209
+ static String? numberRange(
210
+ String? value,
211
+ double min,
212
+ double max, {
213
+ String? fieldName,
214
+ }) {
215
+ final error = number(value);
216
+ if (error != null) return error;
217
+
218
+ final num = double.parse(value!);
219
+ if (num < min || num > max) {
220
+ return '${fieldName ?? 'Value'} must be between $min and $max';
221
+ }
222
+
223
+ return null;
224
+ }
225
+
226
+ // ============= Custom Pattern Validation =============
227
+
228
+ static String? pattern(String? value, RegExp pattern, String errorMessage) {
229
+ if (value == null || value.isEmpty) {
230
+ return 'This field is required';
231
+ }
232
+
233
+ if (!pattern.hasMatch(value)) {
234
+ return errorMessage;
235
+ }
236
+
237
+ return null;
238
+ }
239
+
240
+ // ============= Date Validation =============
241
+
242
+ static String? date(String? value) {
243
+ if (value == null || value.isEmpty) {
244
+ return 'Date is required';
245
+ }
246
+
247
+ try {
248
+ DateTime.parse(value);
249
+ return null;
250
+ } catch (e) {
251
+ return 'Please enter a valid date';
252
+ }
253
+ }
254
+
255
+ static String? futureDate(String? value) {
256
+ final error = date(value);
257
+ if (error != null) return error;
258
+
259
+ final dateValue = DateTime.parse(value!);
260
+ if (dateValue.isBefore(DateTime.now())) {
261
+ return 'Date must be in the future';
262
+ }
263
+
264
+ return null;
265
+ }
266
+
267
+ static String? pastDate(String? value) {
268
+ final error = date(value);
269
+ if (error != null) return error;
270
+
271
+ final dateValue = DateTime.parse(value!);
272
+ if (dateValue.isAfter(DateTime.now())) {
273
+ return 'Date must be in the past';
274
+ }
275
+
276
+ return null;
277
+ }
278
+
279
+ // ============= Multiple Validators =============
280
+
281
+ static String? Function(String?) combine(
282
+ List<String? Function(String?)> validators,
283
+ ) {
284
+ return (String? value) {
285
+ for (final validator in validators) {
286
+ final error = validator(value);
287
+ if (error != null) return error;
288
+ }
289
+ return null;
290
+ };
291
+ }
292
+ }
@@ -0,0 +1,19 @@
1
+ // This file is generated by FlutterFire CLI.
2
+ // Run `flutterfire configure` to set up Firebase for this project.
3
+ // See: https://firebase.flutter.dev/docs/cli/
4
+ //
5
+ // TODO: Run `flutterfire configure` to generate this file with your Firebase project settings.
6
+
7
+ import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
8
+ import 'package:flutter/foundation.dart'
9
+ show defaultTargetPlatform, kIsWeb, TargetPlatform;
10
+
11
+ class DefaultFirebaseOptions {
12
+ static FirebaseOptions get currentPlatform {
13
+ // TODO: Replace with your actual Firebase options from `flutterfire configure`
14
+ throw UnsupportedError(
15
+ 'No Firebase options configured. '
16
+ 'Run `flutterfire configure` to set up Firebase for this project.',
17
+ );
18
+ }
19
+ }