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,942 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter/services.dart';
3
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
4
+ import 'package:cached_network_image/cached_network_image.dart';
5
+ import 'package:intl/intl.dart';
6
+ import 'package:cloud_firestore/cloud_firestore.dart';
7
+ import 'dart:developer' as developer;
8
+ import '../../providers/admin_provider.dart';
9
+ import '../../models/user_model.dart';
10
+ import '../../widgets/custom_app_bar.dart';
11
+ import '../../widgets/app_button.dart';
12
+ import '../../widgets/top_notification.dart';
13
+ import '../../widgets/success_overlay.dart';
14
+ import '../../core/services/logging_service.dart';
15
+
16
+ class AdminPanelUserDetailsScreen extends ConsumerStatefulWidget {
17
+ final String uid;
18
+
19
+ const AdminPanelUserDetailsScreen({super.key, required this.uid});
20
+
21
+ @override
22
+ ConsumerState<AdminPanelUserDetailsScreen> createState() =>
23
+ _AdminPanelUserDetailsScreenState();
24
+ }
25
+
26
+ class _AdminPanelUserDetailsScreenState
27
+ extends ConsumerState<AdminPanelUserDetailsScreen> {
28
+ bool _isEditing = false;
29
+ bool _isInitialized = false;
30
+ final _formKey = GlobalKey<FormState>();
31
+
32
+ late TextEditingController _displayNameController;
33
+ late TextEditingController _bioController;
34
+ late TextEditingController _countryCodeController;
35
+ late TextEditingController _langController;
36
+
37
+ bool _isPremium = false;
38
+ bool _isAdmin = false;
39
+ bool _isDeveloper = false;
40
+ bool _isAnonymous = false;
41
+ bool _isProfileComplete = false;
42
+
43
+ final LoggingService _loggingService = LoggingService();
44
+ String? _selectedLogFilter;
45
+ final List<String> _logFilters = [
46
+ 'All',
47
+ 'login',
48
+ 'logout',
49
+ 'signup',
50
+ 'profile_edit',
51
+ 'password_change',
52
+ 'theme_change',
53
+ 'cache_cleared',
54
+ ];
55
+
56
+ @override
57
+ void dispose() {
58
+ _displayNameController.dispose();
59
+ _bioController.dispose();
60
+ _countryCodeController.dispose();
61
+ _langController.dispose();
62
+ super.dispose();
63
+ }
64
+
65
+ void _initializeControllers(UserModel user) {
66
+ _displayNameController = TextEditingController(
67
+ text: user.displayName ?? '',
68
+ );
69
+ _bioController = TextEditingController(text: user.bio ?? '');
70
+ _countryCodeController = TextEditingController(
71
+ text: user.countryCode ?? '',
72
+ );
73
+ _langController = TextEditingController(text: user.lang ?? '');
74
+
75
+ _isPremium = user.isPremium;
76
+ _isAdmin = user.isAdmin;
77
+ _isDeveloper = user.isDeveloper;
78
+ _isAnonymous = user.isAnonymous;
79
+ _isProfileComplete = user.isProfileComplete;
80
+
81
+ _isInitialized = true;
82
+ }
83
+
84
+ void _resetToUserData(UserModel user) {
85
+ _displayNameController.text = user.displayName ?? '';
86
+ _bioController.text = user.bio ?? '';
87
+ _countryCodeController.text = user.countryCode ?? '';
88
+ _langController.text = user.lang ?? '';
89
+
90
+ _isPremium = user.isPremium;
91
+ _isAdmin = user.isAdmin;
92
+ _isDeveloper = user.isDeveloper;
93
+ _isAnonymous = user.isAnonymous;
94
+ _isProfileComplete = user.isProfileComplete;
95
+ }
96
+
97
+ Future<void> _saveChanges() async {
98
+ if (!_formKey.currentState!.validate()) return;
99
+
100
+ try {
101
+ final data = {
102
+ 'displayName': _displayNameController.text.trim(),
103
+ 'bio': _bioController.text.trim(),
104
+ 'countryCode': _countryCodeController.text.trim(),
105
+ 'lang': _langController.text.trim(),
106
+ 'isPremium': _isPremium,
107
+ 'isAdmin': _isAdmin,
108
+ 'isDeveloper': _isDeveloper,
109
+ 'isAnonymous': _isAnonymous,
110
+ 'isProfileComplete': _isProfileComplete,
111
+ };
112
+
113
+ await ref.read(adminControllerProvider).updateUser(widget.uid, data);
114
+
115
+ if (mounted) {
116
+ await SuccessOverlay.show(context);
117
+ setState(() {
118
+ _isEditing = false;
119
+ });
120
+ ref.invalidate(singleUserProvider(widget.uid));
121
+ }
122
+ } catch (e) {
123
+ if (mounted) {
124
+ TopNotification.showError(
125
+ context,
126
+ message: 'Failed to update user: $e',
127
+ );
128
+ }
129
+ }
130
+ }
131
+
132
+ Future<void> _deleteUser() async {
133
+ final confirmed = await showDialog<bool>(
134
+ context: context,
135
+ builder: (context) => AlertDialog(
136
+ title: const Text('Delete User'),
137
+ content: const Text(
138
+ 'Are you sure you want to delete this user? This action cannot be undone.',
139
+ ),
140
+ actions: [
141
+ TextButton(
142
+ onPressed: () => Navigator.pop(context, false),
143
+ child: const Text('Cancel'),
144
+ ),
145
+ TextButton(
146
+ onPressed: () => Navigator.pop(context, true),
147
+ style: TextButton.styleFrom(
148
+ foregroundColor: Theme.of(context).colorScheme.error,
149
+ ),
150
+ child: const Text('Delete'),
151
+ ),
152
+ ],
153
+ ),
154
+ );
155
+
156
+ if (confirmed == true && mounted) {
157
+ try {
158
+ await ref.read(adminControllerProvider).deleteUser(widget.uid);
159
+ if (mounted) {
160
+ TopNotification.showInfo(
161
+ context,
162
+ message: 'User deleted successfully',
163
+ );
164
+ Navigator.pop(context);
165
+ }
166
+ } catch (e) {
167
+ if (mounted) {
168
+ TopNotification.showError(
169
+ context,
170
+ message: 'Failed to delete user: $e',
171
+ );
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ void _copyToClipboard(String text, String label) {
178
+ Clipboard.setData(ClipboardData(text: text));
179
+ TopNotification.showInfo(
180
+ context,
181
+ message: '$label copied to clipboard',
182
+ duration: const Duration(seconds: 2),
183
+ );
184
+ }
185
+
186
+ @override
187
+ Widget build(BuildContext context) {
188
+ final theme = Theme.of(context);
189
+ final userAsync = ref.watch(singleUserProvider(widget.uid));
190
+
191
+ return Scaffold(
192
+ appBar: CustomAppBar(
193
+ title: 'User Details',
194
+ actions: [
195
+ if (!_isEditing)
196
+ IconButton(
197
+ icon: const Icon(Icons.edit),
198
+ onPressed: () {
199
+ setState(() {
200
+ _isEditing = true;
201
+ });
202
+ },
203
+ ),
204
+ ],
205
+ ),
206
+ body: userAsync.when(
207
+ data: (user) {
208
+ if (user == null) {
209
+ return const Center(child: Text('User not found'));
210
+ }
211
+
212
+ // Initialize controllers on first load
213
+ if (!_isInitialized) {
214
+ _initializeControllers(user);
215
+ }
216
+
217
+ return SingleChildScrollView(
218
+ padding: const EdgeInsets.all(16.0),
219
+ child: Form(
220
+ key: _formKey,
221
+ child: Column(
222
+ crossAxisAlignment: CrossAxisAlignment.start,
223
+ children: [
224
+ // Avatar and basic info
225
+ Center(
226
+ child: Column(
227
+ children: [
228
+ CircleAvatar(
229
+ radius: 50,
230
+ backgroundColor: theme.colorScheme.primaryContainer,
231
+ backgroundImage: user.photoUrl != null
232
+ ? CachedNetworkImageProvider(user.photoUrl!)
233
+ : null,
234
+ child: user.photoUrl == null
235
+ ? Text(
236
+ _getInitials(user.displayName ?? 'U'),
237
+ style: TextStyle(
238
+ fontSize: 32,
239
+ color: theme.colorScheme.onPrimaryContainer,
240
+ fontWeight: FontWeight.bold,
241
+ ),
242
+ )
243
+ : null,
244
+ ),
245
+ const SizedBox(height: 16),
246
+ Text(
247
+ user.displayName ?? 'No Name',
248
+ style: theme.textTheme.headlineSmall?.copyWith(
249
+ fontWeight: FontWeight.bold,
250
+ ),
251
+ ),
252
+ const SizedBox(height: 4),
253
+ SelectableText(
254
+ user.uid,
255
+ style: theme.textTheme.bodySmall?.copyWith(
256
+ color: theme.textTheme.bodySmall?.color?.withValues(
257
+ alpha: 0.7,
258
+ ),
259
+ ),
260
+ ),
261
+ ],
262
+ ),
263
+ ),
264
+ const SizedBox(height: 24),
265
+
266
+ // Editable fields
267
+ if (_isEditing) ...[
268
+ _buildSectionHeader('Basic Information', theme),
269
+ const SizedBox(height: 12),
270
+ TextFormField(
271
+ controller: _displayNameController,
272
+ decoration: const InputDecoration(
273
+ labelText: 'Display Name',
274
+ border: OutlineInputBorder(),
275
+ ),
276
+ ),
277
+ const SizedBox(height: 12),
278
+ TextFormField(
279
+ controller: _bioController,
280
+ decoration: const InputDecoration(
281
+ labelText: 'Bio',
282
+ border: OutlineInputBorder(),
283
+ ),
284
+ maxLines: 3,
285
+ ),
286
+ const SizedBox(height: 12),
287
+ TextFormField(
288
+ controller: _countryCodeController,
289
+ decoration: const InputDecoration(
290
+ labelText: 'Country Code',
291
+ border: OutlineInputBorder(),
292
+ ),
293
+ ),
294
+ const SizedBox(height: 12),
295
+ TextFormField(
296
+ controller: _langController,
297
+ decoration: const InputDecoration(
298
+ labelText: 'Language Code',
299
+ border: OutlineInputBorder(),
300
+ ),
301
+ ),
302
+ const SizedBox(height: 24),
303
+
304
+ _buildSectionHeader('Permissions & Status', theme),
305
+ const SizedBox(height: 12),
306
+ SwitchListTile(
307
+ title: const Text('Premium User'),
308
+ value: _isPremium,
309
+ onChanged: (value) {
310
+ setState(() {
311
+ _isPremium = value;
312
+ });
313
+ },
314
+ ),
315
+ SwitchListTile(
316
+ title: const Text('Admin User'),
317
+ value: _isAdmin,
318
+ onChanged: (value) {
319
+ setState(() {
320
+ _isAdmin = value;
321
+ });
322
+ },
323
+ ),
324
+ SwitchListTile(
325
+ title: const Text('Developer User'),
326
+ value: _isDeveloper,
327
+ onChanged: (value) {
328
+ setState(() {
329
+ _isDeveloper = value;
330
+ });
331
+ },
332
+ ),
333
+ SwitchListTile(
334
+ title: const Text('Anonymous User'),
335
+ value: _isAnonymous,
336
+ onChanged: (value) {
337
+ setState(() {
338
+ _isAnonymous = value;
339
+ });
340
+ },
341
+ ),
342
+ SwitchListTile(
343
+ title: const Text('Profile Complete'),
344
+ value: _isProfileComplete,
345
+ onChanged: (value) {
346
+ setState(() {
347
+ _isProfileComplete = value;
348
+ });
349
+ },
350
+ ),
351
+ const SizedBox(height: 24),
352
+
353
+ // Action buttons
354
+ Row(
355
+ children: [
356
+ Expanded(
357
+ child: AppButton(
358
+ label: 'Cancel',
359
+ onPressed: () {
360
+ setState(() {
361
+ _isEditing = false;
362
+ _resetToUserData(user);
363
+ });
364
+ },
365
+ variant: AppButtonVariant.outlined,
366
+ ),
367
+ ),
368
+ const SizedBox(width: 12),
369
+ Expanded(
370
+ child: AppButton(
371
+ label: 'Save',
372
+ onPressed: _saveChanges,
373
+ ),
374
+ ),
375
+ ],
376
+ ),
377
+ ] else ...[
378
+ // View mode - User Information
379
+ _buildSectionHeader('User Information', theme),
380
+ const SizedBox(height: 12),
381
+ _buildInfoCard([
382
+ _buildInfoRow(
383
+ 'UID',
384
+ user.uid,
385
+ theme,
386
+ onCopy: () => _copyToClipboard(user.uid, 'UID'),
387
+ ),
388
+ _buildInfoRow(
389
+ 'Display Name',
390
+ user.displayName ?? 'N/A',
391
+ theme,
392
+ ),
393
+ _buildInfoRow('Bio', user.bio ?? 'N/A', theme),
394
+ _buildInfoRow(
395
+ 'Country',
396
+ user.countryCode ?? 'N/A',
397
+ theme,
398
+ ),
399
+ _buildInfoRow('Language', user.lang ?? 'N/A', theme),
400
+ if (user.createdAt != null)
401
+ _buildInfoRow(
402
+ 'Created At',
403
+ _formatDateTime(user.createdAt!),
404
+ theme,
405
+ ),
406
+ if (user.updatedAt != null)
407
+ _buildInfoRow(
408
+ 'Updated At',
409
+ _formatDateTime(user.updatedAt!),
410
+ theme,
411
+ ),
412
+ ], theme),
413
+ const SizedBox(height: 24),
414
+
415
+ // Status & Permissions
416
+ _buildSectionHeader('Status & Permissions', theme),
417
+ const SizedBox(height: 12),
418
+ _buildInfoCard([
419
+ _buildInfoRow(
420
+ 'Premium',
421
+ user.isPremium ? 'Yes' : 'No',
422
+ theme,
423
+ color: user.isPremium ? Colors.amber : null,
424
+ ),
425
+ _buildInfoRow(
426
+ 'Admin',
427
+ user.isAdmin ? 'Yes' : 'No',
428
+ theme,
429
+ color: user.isAdmin ? Colors.purple : null,
430
+ ),
431
+ _buildInfoRow(
432
+ 'Developer',
433
+ user.isDeveloper ? 'Yes' : 'No',
434
+ theme,
435
+ color: user.isDeveloper ? Colors.blue : null,
436
+ ),
437
+ _buildInfoRow(
438
+ 'Anonymous',
439
+ user.isAnonymous ? 'Yes' : 'No',
440
+ theme,
441
+ color: user.isAnonymous ? Colors.orange : null,
442
+ ),
443
+ _buildInfoRow(
444
+ 'Profile Complete',
445
+ user.isProfileComplete ? 'Yes' : 'No',
446
+ theme,
447
+ color: user.isProfileComplete ? Colors.green : null,
448
+ ),
449
+ ], theme),
450
+ const SizedBox(height: 24),
451
+
452
+ // User Activity Logs
453
+ _buildSectionHeader('User Activity Logs', theme),
454
+ const SizedBox(height: 8),
455
+ _buildLogsSection(theme),
456
+ const SizedBox(height: 24),
457
+
458
+ // Danger Zone
459
+ _buildSectionHeader('Danger Zone', theme),
460
+ const SizedBox(height: 12),
461
+ SizedBox(
462
+ width: double.infinity,
463
+ height: 48,
464
+ child: ElevatedButton(
465
+ onPressed: _deleteUser,
466
+ style: ElevatedButton.styleFrom(
467
+ backgroundColor: theme.colorScheme.error,
468
+ foregroundColor: theme.colorScheme.onError,
469
+ shape: RoundedRectangleBorder(
470
+ borderRadius: BorderRadius.circular(12),
471
+ ),
472
+ ),
473
+ child: const Text(
474
+ 'Delete User',
475
+ style: TextStyle(
476
+ fontSize: 16,
477
+ fontWeight: FontWeight.w600,
478
+ ),
479
+ ),
480
+ ),
481
+ ),
482
+ ],
483
+ const SizedBox(height: 24),
484
+ ],
485
+ ),
486
+ ),
487
+ );
488
+ },
489
+ loading: () => const Center(child: CircularProgressIndicator()),
490
+ error: (error, stack) {
491
+ developer.log(
492
+ 'Error loading user details for uid: ${widget.uid}',
493
+ name: 'AdminPanelUserDetails',
494
+ error: error,
495
+ stackTrace: stack,
496
+ );
497
+ return Center(
498
+ child: Padding(
499
+ padding: const EdgeInsets.all(24.0),
500
+ child: Column(
501
+ mainAxisAlignment: MainAxisAlignment.center,
502
+ children: [
503
+ Icon(
504
+ Icons.error_outline,
505
+ size: 64,
506
+ color: theme.colorScheme.error,
507
+ ),
508
+ const SizedBox(height: 16),
509
+ Text(
510
+ 'Error loading user details',
511
+ style: theme.textTheme.titleMedium,
512
+ ),
513
+ const SizedBox(height: 8),
514
+ Text(
515
+ error.toString(),
516
+ style: theme.textTheme.bodySmall,
517
+ textAlign: TextAlign.center,
518
+ ),
519
+ ],
520
+ ),
521
+ ),
522
+ );
523
+ },
524
+ ),
525
+ );
526
+ }
527
+
528
+ Widget _buildSectionHeader(String title, ThemeData theme) {
529
+ return Text(
530
+ title,
531
+ style: theme.textTheme.titleMedium?.copyWith(
532
+ fontWeight: FontWeight.bold,
533
+ color: theme.colorScheme.primary,
534
+ ),
535
+ );
536
+ }
537
+
538
+ Widget _buildInfoCard(List<Widget> children, ThemeData theme) {
539
+ return Card(
540
+ child: Padding(
541
+ padding: const EdgeInsets.all(16.0),
542
+ child: Column(
543
+ crossAxisAlignment: CrossAxisAlignment.start,
544
+ children: children,
545
+ ),
546
+ ),
547
+ );
548
+ }
549
+
550
+ Widget _buildInfoRow(
551
+ String label,
552
+ String value,
553
+ ThemeData theme, {
554
+ VoidCallback? onCopy,
555
+ Color? color,
556
+ }) {
557
+ return Padding(
558
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
559
+ child: Row(
560
+ crossAxisAlignment: CrossAxisAlignment.start,
561
+ children: [
562
+ Expanded(
563
+ flex: 2,
564
+ child: Text(
565
+ label,
566
+ style: theme.textTheme.bodyMedium?.copyWith(
567
+ fontWeight: FontWeight.w600,
568
+ color: theme.textTheme.bodyMedium?.color?.withValues(
569
+ alpha: 0.7,
570
+ ),
571
+ ),
572
+ ),
573
+ ),
574
+ Expanded(
575
+ flex: 3,
576
+ child: Row(
577
+ children: [
578
+ Expanded(
579
+ child: SelectableText(
580
+ value,
581
+ style: theme.textTheme.bodyMedium?.copyWith(
582
+ color: color,
583
+ fontWeight: color != null ? FontWeight.bold : null,
584
+ ),
585
+ ),
586
+ ),
587
+ if (onCopy != null)
588
+ IconButton(
589
+ icon: const Icon(Icons.copy, size: 18),
590
+ onPressed: onCopy,
591
+ padding: EdgeInsets.zero,
592
+ constraints: const BoxConstraints(),
593
+ ),
594
+ ],
595
+ ),
596
+ ),
597
+ ],
598
+ ),
599
+ );
600
+ }
601
+
602
+ String _getInitials(String name) {
603
+ final parts = name.trim().split(' ');
604
+ if (parts.isEmpty) return 'U';
605
+ if (parts.length == 1) return parts[0][0].toUpperCase();
606
+ return '${parts[0][0]}${parts[1][0]}'.toUpperCase();
607
+ }
608
+
609
+ String _formatDateTime(DateTime dateTime) {
610
+ return DateFormat('MMM dd, yyyy HH:mm').format(dateTime);
611
+ }
612
+
613
+ Widget _buildLogsSection(ThemeData theme) {
614
+ return Card(
615
+ child: Padding(
616
+ padding: const EdgeInsets.all(16.0),
617
+ child: Column(
618
+ crossAxisAlignment: CrossAxisAlignment.start,
619
+ children: [
620
+ // Filter dropdown
621
+ Row(
622
+ children: [
623
+ Text(
624
+ 'Filter:',
625
+ style: theme.textTheme.bodyMedium?.copyWith(
626
+ fontWeight: FontWeight.w600,
627
+ ),
628
+ ),
629
+ const SizedBox(width: 12),
630
+ Expanded(
631
+ child: DropdownButton<String>(
632
+ value: _selectedLogFilter,
633
+ hint: const Text('All Logs'),
634
+ isExpanded: true,
635
+ items: _logFilters.map((filter) {
636
+ return DropdownMenuItem<String>(
637
+ value: filter == 'All' ? null : filter,
638
+ child: Text(filter),
639
+ );
640
+ }).toList(),
641
+ onChanged: (value) {
642
+ setState(() {
643
+ _selectedLogFilter = value;
644
+ });
645
+ },
646
+ ),
647
+ ),
648
+ ],
649
+ ),
650
+ const Divider(height: 24),
651
+ // Logs stream
652
+ StreamBuilder<QuerySnapshot>(
653
+ stream: _loggingService.streamUserLogs(
654
+ uid: widget.uid,
655
+ limit: 50,
656
+ action: _selectedLogFilter,
657
+ ),
658
+ builder: (context, snapshot) {
659
+ if (snapshot.connectionState == ConnectionState.waiting) {
660
+ return const Center(
661
+ child: Padding(
662
+ padding: EdgeInsets.all(24.0),
663
+ child: CircularProgressIndicator(),
664
+ ),
665
+ );
666
+ }
667
+
668
+ if (snapshot.hasError) {
669
+ developer.log(
670
+ 'Error loading logs for uid: ${widget.uid}',
671
+ name: 'AdminPanelUserDetails',
672
+ error: snapshot.error,
673
+ stackTrace: snapshot.stackTrace,
674
+ );
675
+ return Padding(
676
+ padding: const EdgeInsets.all(24.0),
677
+ child: Column(
678
+ children: [
679
+ Icon(
680
+ Icons.error_outline,
681
+ size: 48,
682
+ color: theme.colorScheme.error,
683
+ ),
684
+ const SizedBox(height: 8),
685
+ Text(
686
+ 'Error loading logs',
687
+ style: theme.textTheme.bodyMedium,
688
+ ),
689
+ const SizedBox(height: 4),
690
+ Text(
691
+ snapshot.error.toString(),
692
+ style: theme.textTheme.bodySmall?.copyWith(
693
+ color: theme.colorScheme.error,
694
+ ),
695
+ textAlign: TextAlign.center,
696
+ ),
697
+ ],
698
+ ),
699
+ );
700
+ }
701
+
702
+ final logs = snapshot.data?.docs ?? [];
703
+
704
+ if (logs.isEmpty) {
705
+ return Padding(
706
+ padding: const EdgeInsets.all(24.0),
707
+ child: Column(
708
+ children: [
709
+ Icon(
710
+ Icons.info_outline,
711
+ size: 48,
712
+ color: theme.colorScheme.primary.withValues(alpha: 0.5),
713
+ ),
714
+ const SizedBox(height: 8),
715
+ Text(
716
+ 'No logs found',
717
+ style: theme.textTheme.bodyMedium?.copyWith(
718
+ color: theme.textTheme.bodyMedium?.color
719
+ ?.withValues(alpha: 0.7),
720
+ ),
721
+ ),
722
+ ],
723
+ ),
724
+ );
725
+ }
726
+
727
+ return Column(
728
+ children: [
729
+ Text(
730
+ '${logs.length} log${logs.length == 1 ? '' : 's'}',
731
+ style: theme.textTheme.bodySmall?.copyWith(
732
+ color: theme.textTheme.bodySmall?.color
733
+ ?.withValues(alpha: 0.7),
734
+ ),
735
+ ),
736
+ const SizedBox(height: 12),
737
+ ListView.separated(
738
+ shrinkWrap: true,
739
+ physics: const NeverScrollableScrollPhysics(),
740
+ itemCount: logs.length,
741
+ separatorBuilder: (context, index) => const Divider(),
742
+ itemBuilder: (context, index) {
743
+ final log = logs[index].data() as Map<String, dynamic>;
744
+ return _buildLogItem(log, theme);
745
+ },
746
+ ),
747
+ ],
748
+ );
749
+ },
750
+ ),
751
+ ],
752
+ ),
753
+ ),
754
+ );
755
+ }
756
+
757
+ Widget _buildLogItem(Map<String, dynamic> log, ThemeData theme) {
758
+ final action = log['action'] as String? ?? 'unknown';
759
+ final timestamp = log['timestamp'] as Timestamp?;
760
+ final values = log['values'] as Map<String, dynamic>? ?? {};
761
+ final deviceId = log['deviceId'] as String?;
762
+
763
+ return ExpansionTile(
764
+ tilePadding: EdgeInsets.zero,
765
+ leading: _getActionIcon(action, theme),
766
+ title: Text(
767
+ _formatActionName(action),
768
+ style: theme.textTheme.bodyMedium?.copyWith(
769
+ fontWeight: FontWeight.w600,
770
+ ),
771
+ ),
772
+ subtitle: timestamp != null
773
+ ? Text(
774
+ _formatDateTime(timestamp.toDate()),
775
+ style: theme.textTheme.bodySmall?.copyWith(
776
+ color: theme.textTheme.bodySmall?.color?.withValues(alpha: 0.7),
777
+ ),
778
+ )
779
+ : null,
780
+ children: [
781
+ Padding(
782
+ padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
783
+ child: Column(
784
+ crossAxisAlignment: CrossAxisAlignment.start,
785
+ children: [
786
+ if (values.isNotEmpty) ...[
787
+ Text(
788
+ 'Details:',
789
+ style: theme.textTheme.bodySmall?.copyWith(
790
+ fontWeight: FontWeight.bold,
791
+ color: theme.colorScheme.primary,
792
+ ),
793
+ ),
794
+ const SizedBox(height: 8),
795
+ ...values.entries.map((entry) {
796
+ return Padding(
797
+ padding: const EdgeInsets.only(bottom: 4.0),
798
+ child: Row(
799
+ crossAxisAlignment: CrossAxisAlignment.start,
800
+ children: [
801
+ Expanded(
802
+ flex: 2,
803
+ child: Text(
804
+ _formatKey(entry.key),
805
+ style: theme.textTheme.bodySmall?.copyWith(
806
+ color: theme.textTheme.bodySmall?.color
807
+ ?.withValues(alpha: 0.7),
808
+ ),
809
+ ),
810
+ ),
811
+ Expanded(
812
+ flex: 3,
813
+ child: Text(
814
+ _formatValue(entry.value),
815
+ style: theme.textTheme.bodySmall,
816
+ ),
817
+ ),
818
+ ],
819
+ ),
820
+ );
821
+ }),
822
+ const SizedBox(height: 8),
823
+ ],
824
+ if (deviceId != null) ...[
825
+ Text(
826
+ 'Device ID:',
827
+ style: theme.textTheme.bodySmall?.copyWith(
828
+ fontWeight: FontWeight.bold,
829
+ color: theme.colorScheme.primary,
830
+ ),
831
+ ),
832
+ const SizedBox(height: 4),
833
+ SelectableText(
834
+ deviceId,
835
+ style: theme.textTheme.bodySmall?.copyWith(
836
+ fontFamily: 'monospace',
837
+ color: theme.textTheme.bodySmall?.color
838
+ ?.withValues(alpha: 0.7),
839
+ ),
840
+ ),
841
+ ],
842
+ ],
843
+ ),
844
+ ),
845
+ ],
846
+ );
847
+ }
848
+
849
+ Widget _getActionIcon(String action, ThemeData theme) {
850
+ IconData iconData;
851
+ Color color;
852
+
853
+ switch (action) {
854
+ case 'login':
855
+ iconData = Icons.login;
856
+ color = Colors.green;
857
+ break;
858
+ case 'logout':
859
+ iconData = Icons.logout;
860
+ color = Colors.orange;
861
+ break;
862
+ case 'signup':
863
+ iconData = Icons.person_add;
864
+ color = Colors.blue;
865
+ break;
866
+ case 'profile_edit':
867
+ iconData = Icons.edit;
868
+ color = Colors.purple;
869
+ break;
870
+ case 'password_change':
871
+ iconData = Icons.lock_reset;
872
+ color = Colors.red;
873
+ break;
874
+ case 'theme_change':
875
+ iconData = Icons.palette;
876
+ color = Colors.indigo;
877
+ break;
878
+ case 'language_change':
879
+ iconData = Icons.language;
880
+ color = Colors.teal;
881
+ break;
882
+ case 'cache_cleared':
883
+ iconData = Icons.delete_sweep;
884
+ color = Colors.amber;
885
+ break;
886
+ case 'account_deletion':
887
+ iconData = Icons.delete_forever;
888
+ color = Colors.red[900]!;
889
+ break;
890
+ case 'subscription_purchase':
891
+ iconData = Icons.shopping_cart;
892
+ color = Colors.green[700]!;
893
+ break;
894
+ case 'subscription_cancel':
895
+ iconData = Icons.cancel;
896
+ color = Colors.orange[700]!;
897
+ break;
898
+ case 'screen_view':
899
+ iconData = Icons.visibility;
900
+ color = Colors.blue[300]!;
901
+ break;
902
+ case 'feature_usage':
903
+ iconData = Icons.touch_app;
904
+ color = Colors.cyan;
905
+ break;
906
+ case 'error':
907
+ iconData = Icons.error;
908
+ color = Colors.red;
909
+ break;
910
+ default:
911
+ iconData = Icons.info;
912
+ color = theme.colorScheme.primary;
913
+ }
914
+
915
+ return CircleAvatar(
916
+ backgroundColor: color.withValues(alpha: 0.2),
917
+ radius: 20,
918
+ child: Icon(iconData, size: 20, color: color),
919
+ );
920
+ }
921
+
922
+ String _formatActionName(String action) {
923
+ return action
924
+ .split('_')
925
+ .map((word) => word[0].toUpperCase() + word.substring(1))
926
+ .join(' ');
927
+ }
928
+
929
+ String _formatKey(String key) {
930
+ return key
931
+ .split('_')
932
+ .map((word) => word[0].toUpperCase() + word.substring(1))
933
+ .join(' ');
934
+ }
935
+
936
+ String _formatValue(dynamic value) {
937
+ if (value is List) {
938
+ return value.join(', ');
939
+ }
940
+ return value.toString();
941
+ }
942
+ }