create-du-app 0.1.3 → 0.1.4

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 (414) hide show
  1. package/README.md +10 -7
  2. package/package.json +6 -5
  3. package/src/index.js +8 -8
  4. package/src/prompts.js +1 -1
  5. package/templates/mobile/expo/.env.example +5 -0
  6. package/templates/mobile/expo/.eslintrc.js +7 -0
  7. package/templates/mobile/expo/.prettierrc.js +7 -0
  8. package/templates/mobile/expo/.svgrrc.js +9 -0
  9. package/templates/mobile/expo/README.md +42 -7
  10. package/templates/mobile/expo/_gitignore +20 -0
  11. package/templates/mobile/expo/_package.json +62 -1
  12. package/templates/mobile/expo/app.json +18 -0
  13. package/templates/mobile/expo/babel.config.js +21 -0
  14. package/templates/mobile/expo/index.js +5 -0
  15. package/templates/mobile/expo/metro.config.js +31 -0
  16. package/templates/mobile/expo/src/app/App.tsx +24 -0
  17. package/templates/mobile/expo/src/app/app-provider.tsx +36 -0
  18. package/templates/mobile/expo/src/app/config/translation.ts +26 -0
  19. package/templates/mobile/expo/src/app/index.ts +1 -0
  20. package/templates/mobile/expo/src/app/navigation/app-route-type.ts +27 -0
  21. package/templates/mobile/expo/src/app/navigation/bottom-tabs.tsx +34 -0
  22. package/templates/mobile/expo/src/app/navigation/index.tsx +14 -0
  23. package/templates/mobile/expo/src/app/stores/auth.store.ts +33 -0
  24. package/templates/mobile/expo/src/app/stores/common.store.ts +19 -0
  25. package/templates/mobile/expo/src/app/stores/index.ts +3 -0
  26. package/templates/mobile/expo/src/app/stores/loading.store.ts +22 -0
  27. package/templates/mobile/expo/src/assets/Images/empty-list.png +0 -0
  28. package/templates/mobile/expo/src/assets/Images/index.ts +5 -0
  29. package/templates/mobile/expo/src/assets/Images/screen-bg-gradian.png +0 -0
  30. package/templates/mobile/expo/src/assets/i18n/en.json +12 -0
  31. package/templates/mobile/expo/src/assets/i18n/fr.json +12 -0
  32. package/templates/mobile/expo/src/assets/lotties/index.ts +3 -0
  33. package/templates/mobile/expo/src/assets/lotties/loading.json +1 -0
  34. package/templates/mobile/expo/src/assets/svgs/arrow-left.svg +3 -0
  35. package/templates/mobile/expo/src/assets/svgs/arrow-right.svg +3 -0
  36. package/templates/mobile/expo/src/assets/svgs/calendar.svg +12 -0
  37. package/templates/mobile/expo/src/assets/svgs/check.svg +3 -0
  38. package/templates/mobile/expo/src/assets/svgs/close.svg +3 -0
  39. package/templates/mobile/expo/src/assets/svgs/eye-hide.svg +3 -0
  40. package/templates/mobile/expo/src/assets/svgs/eye.svg +4 -0
  41. package/templates/mobile/expo/src/assets/svgs/index.ts +29 -0
  42. package/templates/mobile/expo/src/assets/svgs/minus.svg +3 -0
  43. package/templates/mobile/expo/src/assets/svgs/plus.svg +3 -0
  44. package/templates/mobile/expo/src/assets/svgs/search.svg +3 -0
  45. package/templates/mobile/expo/src/assets/svgs/toast-error.svg +5 -0
  46. package/templates/mobile/expo/src/assets/svgs/toast-success.svg +4 -0
  47. package/templates/mobile/expo/src/core/api/endpoints.ts +8 -0
  48. package/templates/mobile/expo/src/core/api/example.api.ts +53 -0
  49. package/templates/mobile/expo/src/core/api/index.ts +4 -0
  50. package/templates/mobile/expo/src/core/components/common/index.ts +1 -0
  51. package/templates/mobile/expo/src/core/components/common/list-empty/index.ts +2 -0
  52. package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.tsx +30 -0
  53. package/templates/mobile/expo/src/core/components/common/list-empty/list-empty.type.ts +5 -0
  54. package/templates/mobile/expo/src/core/components/forms/hf-date-time.tsx +301 -0
  55. package/templates/mobile/expo/src/core/components/forms/hf-password-input.tsx +153 -0
  56. package/templates/mobile/expo/src/core/components/forms/hf-text-input.tsx +59 -0
  57. package/templates/mobile/expo/src/core/components/forms/hf-time-picker.tsx +117 -0
  58. package/templates/mobile/expo/src/core/components/forms/index.ts +4 -0
  59. package/templates/mobile/expo/src/core/components/index.ts +5 -0
  60. package/templates/mobile/expo/src/core/components/loading/index.ts +1 -0
  61. package/templates/mobile/expo/src/core/components/loading/loading-app/index.ts +1 -0
  62. package/templates/mobile/expo/src/core/components/loading/loading-app/loading-app.tsx +50 -0
  63. package/templates/mobile/expo/src/core/components/offline/index.ts +1 -0
  64. package/templates/mobile/expo/src/core/components/offline/offline-banner.tsx +186 -0
  65. package/templates/mobile/expo/src/core/components/screen/index.ts +1 -0
  66. package/templates/mobile/expo/src/core/components/screen/screen-container/index.ts +1 -0
  67. package/templates/mobile/expo/src/core/components/screen/screen-container/screen-container.tsx +252 -0
  68. package/templates/mobile/expo/src/core/components/splash/index.ts +1 -0
  69. package/templates/mobile/expo/src/core/components/splash/splash-overlay/index.ts +1 -0
  70. package/templates/mobile/expo/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
  71. package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
  72. package/templates/mobile/expo/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
  73. package/templates/mobile/expo/src/core/components/ui/animated-list-item/index.ts +2 -0
  74. package/templates/mobile/expo/src/core/components/ui/app-image/app-image.tsx +104 -0
  75. package/templates/mobile/expo/src/core/components/ui/app-image/app-image.type.ts +19 -0
  76. package/templates/mobile/expo/src/core/components/ui/app-image/index.ts +2 -0
  77. package/templates/mobile/expo/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
  78. package/templates/mobile/expo/src/core/components/ui/asset-placeholder/index.ts +1 -0
  79. package/templates/mobile/expo/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
  80. package/templates/mobile/expo/src/core/components/ui/avatar-image/index.ts +1 -0
  81. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
  82. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
  83. package/templates/mobile/expo/src/core/components/ui/bottom-sheet/index.ts +2 -0
  84. package/templates/mobile/expo/src/core/components/ui/button/button.style.ts +146 -0
  85. package/templates/mobile/expo/src/core/components/ui/button/button.tsx +97 -0
  86. package/templates/mobile/expo/src/core/components/ui/button/button.type.ts +47 -0
  87. package/templates/mobile/expo/src/core/components/ui/button/index.ts +4 -0
  88. package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.tsx +127 -0
  89. package/templates/mobile/expo/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
  90. package/templates/mobile/expo/src/core/components/ui/checkbox/index.ts +2 -0
  91. package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
  92. package/templates/mobile/expo/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
  93. package/templates/mobile/expo/src/core/components/ui/collapsible-section/index.ts +2 -0
  94. package/templates/mobile/expo/src/core/components/ui/components.registry.json +313 -0
  95. package/templates/mobile/expo/src/core/components/ui/field/field-frame.tsx +62 -0
  96. package/templates/mobile/expo/src/core/components/ui/field/field.shared.ts +31 -0
  97. package/templates/mobile/expo/src/core/components/ui/header/header.tsx +196 -0
  98. package/templates/mobile/expo/src/core/components/ui/header/header.type.ts +30 -0
  99. package/templates/mobile/expo/src/core/components/ui/header/index.ts +2 -0
  100. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
  101. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.tsx +66 -0
  102. package/templates/mobile/expo/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
  103. package/templates/mobile/expo/src/core/components/ui/icon-button/index.ts +2 -0
  104. package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.tsx +107 -0
  105. package/templates/mobile/expo/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
  106. package/templates/mobile/expo/src/core/components/ui/image-slider/index.ts +2 -0
  107. package/templates/mobile/expo/src/core/components/ui/index.ts +25 -0
  108. package/templates/mobile/expo/src/core/components/ui/label/index.ts +2 -0
  109. package/templates/mobile/expo/src/core/components/ui/label/label.tsx +36 -0
  110. package/templates/mobile/expo/src/core/components/ui/label/label.type.ts +12 -0
  111. package/templates/mobile/expo/src/core/components/ui/modal/index.ts +2 -0
  112. package/templates/mobile/expo/src/core/components/ui/modal/modal.tsx +62 -0
  113. package/templates/mobile/expo/src/core/components/ui/modal/modal.type.ts +11 -0
  114. package/templates/mobile/expo/src/core/components/ui/otp-input/index.ts +2 -0
  115. package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.tsx +129 -0
  116. package/templates/mobile/expo/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
  117. package/templates/mobile/expo/src/core/components/ui/page-dots/index.ts +1 -0
  118. package/templates/mobile/expo/src/core/components/ui/page-dots/page-dots.tsx +60 -0
  119. package/templates/mobile/expo/src/core/components/ui/radio/index.ts +2 -0
  120. package/templates/mobile/expo/src/core/components/ui/radio/radio.tsx +121 -0
  121. package/templates/mobile/expo/src/core/components/ui/radio/radio.type.ts +20 -0
  122. package/templates/mobile/expo/src/core/components/ui/screen/index.ts +1 -0
  123. package/templates/mobile/expo/src/core/components/ui/screen/screen-gradient.tsx +33 -0
  124. package/templates/mobile/expo/src/core/components/ui/search-box/index.ts +2 -0
  125. package/templates/mobile/expo/src/core/components/ui/search-box/search-box.tsx +162 -0
  126. package/templates/mobile/expo/src/core/components/ui/search-box/search-box.type.ts +26 -0
  127. package/templates/mobile/expo/src/core/components/ui/segmented-control/index.ts +2 -0
  128. package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
  129. package/templates/mobile/expo/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
  130. package/templates/mobile/expo/src/core/components/ui/skeleton/index.ts +2 -0
  131. package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.tsx +106 -0
  132. package/templates/mobile/expo/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
  133. package/templates/mobile/expo/src/core/components/ui/success-state/index.ts +1 -0
  134. package/templates/mobile/expo/src/core/components/ui/success-state/success-state.tsx +68 -0
  135. package/templates/mobile/expo/src/core/components/ui/tabs/index.ts +2 -0
  136. package/templates/mobile/expo/src/core/components/ui/tabs/tabs.tsx +273 -0
  137. package/templates/mobile/expo/src/core/components/ui/tabs/tabs.type.ts +21 -0
  138. package/templates/mobile/expo/src/core/components/ui/tag-input/index.ts +2 -0
  139. package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.tsx +146 -0
  140. package/templates/mobile/expo/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
  141. package/templates/mobile/expo/src/core/components/ui/text-area/index.ts +2 -0
  142. package/templates/mobile/expo/src/core/components/ui/text-area/text-area.tsx +90 -0
  143. package/templates/mobile/expo/src/core/components/ui/text-area/text-area.type.ts +20 -0
  144. package/templates/mobile/expo/src/core/components/ui/text-field/index.ts +2 -0
  145. package/templates/mobile/expo/src/core/components/ui/text-field/text-field.tsx +116 -0
  146. package/templates/mobile/expo/src/core/components/ui/text-field/text-field.type.ts +21 -0
  147. package/templates/mobile/expo/src/core/components/ui/toggle/index.ts +2 -0
  148. package/templates/mobile/expo/src/core/components/ui/toggle/toggle.tsx +110 -0
  149. package/templates/mobile/expo/src/core/components/ui/toggle/toggle.type.ts +19 -0
  150. package/templates/mobile/expo/src/core/constants/external-urls.constant.ts +5 -0
  151. package/templates/mobile/expo/src/core/constants/hard-data.constant.ts +0 -0
  152. package/templates/mobile/expo/src/core/constants/index.ts +2 -0
  153. package/templates/mobile/expo/src/core/constants/type.constant.ts +3 -0
  154. package/templates/mobile/expo/src/core/context/index.ts +1 -0
  155. package/templates/mobile/expo/src/core/context/shared-transition-context.tsx +35 -0
  156. package/templates/mobile/expo/src/core/hook/index.ts +8 -0
  157. package/templates/mobile/expo/src/core/hook/useActiveRouteName.ts +63 -0
  158. package/templates/mobile/expo/src/core/hook/useAppNavigation.tsx +7 -0
  159. package/templates/mobile/expo/src/core/hook/useBottomInset.tsx +26 -0
  160. package/templates/mobile/expo/src/core/hook/useDebounce.tsx +16 -0
  161. package/templates/mobile/expo/src/core/hook/useEndReached.tsx +46 -0
  162. package/templates/mobile/expo/src/core/hook/useManualRefetch.ts +56 -0
  163. package/templates/mobile/expo/src/core/hook/useNetworkStatus.ts +68 -0
  164. package/templates/mobile/expo/src/core/hook/useTimeout.tsx +30 -0
  165. package/templates/mobile/expo/src/core/index.ts +7 -0
  166. package/templates/mobile/expo/src/core/services/api.service.ts +230 -0
  167. package/templates/mobile/expo/src/core/services/device-id.service.ts +23 -0
  168. package/templates/mobile/expo/src/core/services/index.ts +3 -0
  169. package/templates/mobile/expo/src/core/services/session-end.bridge.ts +26 -0
  170. package/templates/mobile/expo/src/core/theme/dark.theme.ts +10 -0
  171. package/templates/mobile/expo/src/core/theme/index.ts +5 -0
  172. package/templates/mobile/expo/src/core/theme/light.theme.ts +44 -0
  173. package/templates/mobile/expo/src/core/theme/theme-context.tsx +82 -0
  174. package/templates/mobile/expo/src/core/theme/theme.types.ts +26 -0
  175. package/templates/mobile/expo/src/core/theme/use-themed-styles.ts +25 -0
  176. package/templates/mobile/expo/src/core/utils/color.util.tsx +198 -0
  177. package/templates/mobile/expo/src/core/utils/date.util.ts +97 -0
  178. package/templates/mobile/expo/src/core/utils/device-locale.util.ts +22 -0
  179. package/templates/mobile/expo/src/core/utils/emitter/index.ts +161 -0
  180. package/templates/mobile/expo/src/core/utils/emitter/type.ts +40 -0
  181. package/templates/mobile/expo/src/core/utils/enum.util.tsx +15 -0
  182. package/templates/mobile/expo/src/core/utils/font.util.tsx +42 -0
  183. package/templates/mobile/expo/src/core/utils/func.util.ts +48 -0
  184. package/templates/mobile/expo/src/core/utils/greeting.util.ts +20 -0
  185. package/templates/mobile/expo/src/core/utils/image-format.util.ts +117 -0
  186. package/templates/mobile/expo/src/core/utils/image-picker.util.ts +84 -0
  187. package/templates/mobile/expo/src/core/utils/index.ts +18 -0
  188. package/templates/mobile/expo/src/core/utils/linking.util.ts +16 -0
  189. package/templates/mobile/expo/src/core/utils/navigation.util.tsx +100 -0
  190. package/templates/mobile/expo/src/core/utils/number-format.ts +28 -0
  191. package/templates/mobile/expo/src/core/utils/query-client.util.ts +35 -0
  192. package/templates/mobile/expo/src/core/utils/query-persister.util.ts +36 -0
  193. package/templates/mobile/expo/src/core/utils/schema.util.tsx +2713 -0
  194. package/templates/mobile/expo/src/core/utils/size.util.tsx +74 -0
  195. package/templates/mobile/expo/src/core/utils/storage.util.tsx +53 -0
  196. package/templates/mobile/expo/src/core/utils/toast.util.tsx +151 -0
  197. package/templates/mobile/expo/src/core/utils/translator.util.tsx +23 -0
  198. package/templates/mobile/expo/src/core/utils/typography.util.tsx +69 -0
  199. package/templates/mobile/expo/src/core/utils/validate.util.tsx +13 -0
  200. package/templates/mobile/expo/src/declarations.d.ts +54 -0
  201. package/templates/mobile/expo/src/modules/home/home.screen.tsx +33 -0
  202. package/templates/mobile/expo/src/modules/profile/profile.screen.tsx +29 -0
  203. package/templates/mobile/expo/src/scripts/link-fonts.js +60 -0
  204. package/templates/mobile/expo/src/scripts/sync-images.js +56 -0
  205. package/templates/mobile/expo/src/scripts/sync-svgs.js +50 -0
  206. package/templates/mobile/expo/src/types/models.d.ts +24 -0
  207. package/templates/mobile/expo/tsconfig.json +19 -0
  208. package/templates/mobile/rn/.env.example +5 -0
  209. package/templates/mobile/rn/.eslintrc.js +7 -0
  210. package/templates/mobile/rn/.prettierrc.js +7 -0
  211. package/templates/mobile/rn/.svgrrc.js +9 -0
  212. package/templates/mobile/rn/README.md +40 -7
  213. package/templates/mobile/rn/_gitignore +24 -0
  214. package/templates/mobile/rn/_package.json +67 -1
  215. package/templates/mobile/rn/app.json +4 -0
  216. package/templates/mobile/rn/babel.config.js +18 -0
  217. package/templates/mobile/rn/index.js +8 -0
  218. package/templates/mobile/rn/metro.config.js +33 -0
  219. package/templates/mobile/rn/src/app/App.tsx +24 -0
  220. package/templates/mobile/rn/src/app/app-provider.tsx +36 -0
  221. package/templates/mobile/rn/src/app/config/translation.ts +26 -0
  222. package/templates/mobile/rn/src/app/index.ts +1 -0
  223. package/templates/mobile/rn/src/app/navigation/app-route-type.ts +27 -0
  224. package/templates/mobile/rn/src/app/navigation/bottom-tabs.tsx +34 -0
  225. package/templates/mobile/rn/src/app/navigation/index.tsx +14 -0
  226. package/templates/mobile/rn/src/app/stores/auth.store.ts +33 -0
  227. package/templates/mobile/rn/src/app/stores/common.store.ts +19 -0
  228. package/templates/mobile/rn/src/app/stores/index.ts +3 -0
  229. package/templates/mobile/rn/src/app/stores/loading.store.ts +22 -0
  230. package/templates/mobile/rn/src/assets/Images/empty-list.png +0 -0
  231. package/templates/mobile/rn/src/assets/Images/index.ts +5 -0
  232. package/templates/mobile/rn/src/assets/Images/screen-bg-gradian.png +0 -0
  233. package/templates/mobile/rn/src/assets/i18n/en.json +12 -0
  234. package/templates/mobile/rn/src/assets/i18n/fr.json +12 -0
  235. package/templates/mobile/rn/src/assets/lotties/index.ts +3 -0
  236. package/templates/mobile/rn/src/assets/lotties/loading.json +1 -0
  237. package/templates/mobile/rn/src/assets/svgs/arrow-left.svg +3 -0
  238. package/templates/mobile/rn/src/assets/svgs/arrow-right.svg +3 -0
  239. package/templates/mobile/rn/src/assets/svgs/calendar.svg +12 -0
  240. package/templates/mobile/rn/src/assets/svgs/check.svg +3 -0
  241. package/templates/mobile/rn/src/assets/svgs/close.svg +3 -0
  242. package/templates/mobile/rn/src/assets/svgs/eye-hide.svg +3 -0
  243. package/templates/mobile/rn/src/assets/svgs/eye.svg +4 -0
  244. package/templates/mobile/rn/src/assets/svgs/index.ts +29 -0
  245. package/templates/mobile/rn/src/assets/svgs/minus.svg +3 -0
  246. package/templates/mobile/rn/src/assets/svgs/plus.svg +3 -0
  247. package/templates/mobile/rn/src/assets/svgs/search.svg +3 -0
  248. package/templates/mobile/rn/src/assets/svgs/toast-error.svg +5 -0
  249. package/templates/mobile/rn/src/assets/svgs/toast-success.svg +4 -0
  250. package/templates/mobile/rn/src/core/api/endpoints.ts +8 -0
  251. package/templates/mobile/rn/src/core/api/example.api.ts +53 -0
  252. package/templates/mobile/rn/src/core/api/index.ts +4 -0
  253. package/templates/mobile/rn/src/core/components/common/index.ts +1 -0
  254. package/templates/mobile/rn/src/core/components/common/list-empty/index.ts +2 -0
  255. package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.tsx +30 -0
  256. package/templates/mobile/rn/src/core/components/common/list-empty/list-empty.type.ts +5 -0
  257. package/templates/mobile/rn/src/core/components/forms/hf-date-time.tsx +301 -0
  258. package/templates/mobile/rn/src/core/components/forms/hf-password-input.tsx +153 -0
  259. package/templates/mobile/rn/src/core/components/forms/hf-text-input.tsx +59 -0
  260. package/templates/mobile/rn/src/core/components/forms/hf-time-picker.tsx +117 -0
  261. package/templates/mobile/rn/src/core/components/forms/index.ts +4 -0
  262. package/templates/mobile/rn/src/core/components/index.ts +5 -0
  263. package/templates/mobile/rn/src/core/components/loading/index.ts +1 -0
  264. package/templates/mobile/rn/src/core/components/loading/loading-app/index.ts +1 -0
  265. package/templates/mobile/rn/src/core/components/loading/loading-app/loading-app.tsx +50 -0
  266. package/templates/mobile/rn/src/core/components/offline/index.ts +1 -0
  267. package/templates/mobile/rn/src/core/components/offline/offline-banner.tsx +186 -0
  268. package/templates/mobile/rn/src/core/components/screen/index.ts +1 -0
  269. package/templates/mobile/rn/src/core/components/screen/screen-container/index.ts +1 -0
  270. package/templates/mobile/rn/src/core/components/screen/screen-container/screen-container.tsx +252 -0
  271. package/templates/mobile/rn/src/core/components/splash/index.ts +1 -0
  272. package/templates/mobile/rn/src/core/components/splash/splash-overlay/index.ts +1 -0
  273. package/templates/mobile/rn/src/core/components/splash/splash-overlay/splash-overlay.tsx +93 -0
  274. package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.tsx +48 -0
  275. package/templates/mobile/rn/src/core/components/ui/animated-list-item/animated-list-item.type.ts +10 -0
  276. package/templates/mobile/rn/src/core/components/ui/animated-list-item/index.ts +2 -0
  277. package/templates/mobile/rn/src/core/components/ui/app-image/app-image.tsx +104 -0
  278. package/templates/mobile/rn/src/core/components/ui/app-image/app-image.type.ts +19 -0
  279. package/templates/mobile/rn/src/core/components/ui/app-image/index.ts +2 -0
  280. package/templates/mobile/rn/src/core/components/ui/asset-placeholder/asset-placeholder.tsx +76 -0
  281. package/templates/mobile/rn/src/core/components/ui/asset-placeholder/index.ts +1 -0
  282. package/templates/mobile/rn/src/core/components/ui/avatar-image/avatar-image.tsx +90 -0
  283. package/templates/mobile/rn/src/core/components/ui/avatar-image/index.ts +1 -0
  284. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.tsx +145 -0
  285. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/bottom-sheet.type.ts +10 -0
  286. package/templates/mobile/rn/src/core/components/ui/bottom-sheet/index.ts +2 -0
  287. package/templates/mobile/rn/src/core/components/ui/button/button.style.ts +146 -0
  288. package/templates/mobile/rn/src/core/components/ui/button/button.tsx +97 -0
  289. package/templates/mobile/rn/src/core/components/ui/button/button.type.ts +47 -0
  290. package/templates/mobile/rn/src/core/components/ui/button/index.ts +4 -0
  291. package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.tsx +127 -0
  292. package/templates/mobile/rn/src/core/components/ui/checkbox/checkbox.type.ts +24 -0
  293. package/templates/mobile/rn/src/core/components/ui/checkbox/index.ts +2 -0
  294. package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.tsx +141 -0
  295. package/templates/mobile/rn/src/core/components/ui/collapsible-section/collapsible-section.type.ts +10 -0
  296. package/templates/mobile/rn/src/core/components/ui/collapsible-section/index.ts +2 -0
  297. package/templates/mobile/rn/src/core/components/ui/components.registry.json +313 -0
  298. package/templates/mobile/rn/src/core/components/ui/field/field-frame.tsx +62 -0
  299. package/templates/mobile/rn/src/core/components/ui/field/field.shared.ts +31 -0
  300. package/templates/mobile/rn/src/core/components/ui/header/header.tsx +196 -0
  301. package/templates/mobile/rn/src/core/components/ui/header/header.type.ts +30 -0
  302. package/templates/mobile/rn/src/core/components/ui/header/index.ts +2 -0
  303. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.style.ts +23 -0
  304. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.tsx +66 -0
  305. package/templates/mobile/rn/src/core/components/ui/icon-button/icon-button.type.ts +25 -0
  306. package/templates/mobile/rn/src/core/components/ui/icon-button/index.ts +2 -0
  307. package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.tsx +107 -0
  308. package/templates/mobile/rn/src/core/components/ui/image-slider/image-slider.type.ts +10 -0
  309. package/templates/mobile/rn/src/core/components/ui/image-slider/index.ts +2 -0
  310. package/templates/mobile/rn/src/core/components/ui/index.ts +25 -0
  311. package/templates/mobile/rn/src/core/components/ui/label/index.ts +2 -0
  312. package/templates/mobile/rn/src/core/components/ui/label/label.tsx +36 -0
  313. package/templates/mobile/rn/src/core/components/ui/label/label.type.ts +12 -0
  314. package/templates/mobile/rn/src/core/components/ui/modal/index.ts +2 -0
  315. package/templates/mobile/rn/src/core/components/ui/modal/modal.tsx +62 -0
  316. package/templates/mobile/rn/src/core/components/ui/modal/modal.type.ts +11 -0
  317. package/templates/mobile/rn/src/core/components/ui/otp-input/index.ts +2 -0
  318. package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.tsx +129 -0
  319. package/templates/mobile/rn/src/core/components/ui/otp-input/otp-input.type.ts +20 -0
  320. package/templates/mobile/rn/src/core/components/ui/page-dots/index.ts +1 -0
  321. package/templates/mobile/rn/src/core/components/ui/page-dots/page-dots.tsx +60 -0
  322. package/templates/mobile/rn/src/core/components/ui/radio/index.ts +2 -0
  323. package/templates/mobile/rn/src/core/components/ui/radio/radio.tsx +121 -0
  324. package/templates/mobile/rn/src/core/components/ui/radio/radio.type.ts +20 -0
  325. package/templates/mobile/rn/src/core/components/ui/screen/index.ts +1 -0
  326. package/templates/mobile/rn/src/core/components/ui/screen/screen-gradient.tsx +33 -0
  327. package/templates/mobile/rn/src/core/components/ui/search-box/index.ts +2 -0
  328. package/templates/mobile/rn/src/core/components/ui/search-box/search-box.tsx +162 -0
  329. package/templates/mobile/rn/src/core/components/ui/search-box/search-box.type.ts +26 -0
  330. package/templates/mobile/rn/src/core/components/ui/segmented-control/index.ts +2 -0
  331. package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.tsx +86 -0
  332. package/templates/mobile/rn/src/core/components/ui/segmented-control/segmented-control.type.ts +22 -0
  333. package/templates/mobile/rn/src/core/components/ui/skeleton/index.ts +2 -0
  334. package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.tsx +106 -0
  335. package/templates/mobile/rn/src/core/components/ui/skeleton/skeleton.type.ts +8 -0
  336. package/templates/mobile/rn/src/core/components/ui/success-state/index.ts +1 -0
  337. package/templates/mobile/rn/src/core/components/ui/success-state/success-state.tsx +68 -0
  338. package/templates/mobile/rn/src/core/components/ui/tabs/index.ts +2 -0
  339. package/templates/mobile/rn/src/core/components/ui/tabs/tabs.tsx +273 -0
  340. package/templates/mobile/rn/src/core/components/ui/tabs/tabs.type.ts +21 -0
  341. package/templates/mobile/rn/src/core/components/ui/tag-input/index.ts +2 -0
  342. package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.tsx +146 -0
  343. package/templates/mobile/rn/src/core/components/ui/tag-input/tag-input.type.ts +22 -0
  344. package/templates/mobile/rn/src/core/components/ui/text-area/index.ts +2 -0
  345. package/templates/mobile/rn/src/core/components/ui/text-area/text-area.tsx +90 -0
  346. package/templates/mobile/rn/src/core/components/ui/text-area/text-area.type.ts +20 -0
  347. package/templates/mobile/rn/src/core/components/ui/text-field/index.ts +2 -0
  348. package/templates/mobile/rn/src/core/components/ui/text-field/text-field.tsx +116 -0
  349. package/templates/mobile/rn/src/core/components/ui/text-field/text-field.type.ts +21 -0
  350. package/templates/mobile/rn/src/core/components/ui/toggle/index.ts +2 -0
  351. package/templates/mobile/rn/src/core/components/ui/toggle/toggle.tsx +110 -0
  352. package/templates/mobile/rn/src/core/components/ui/toggle/toggle.type.ts +19 -0
  353. package/templates/mobile/rn/src/core/constants/external-urls.constant.ts +5 -0
  354. package/templates/mobile/rn/src/core/constants/hard-data.constant.ts +0 -0
  355. package/templates/mobile/rn/src/core/constants/index.ts +2 -0
  356. package/templates/mobile/rn/src/core/constants/type.constant.ts +3 -0
  357. package/templates/mobile/rn/src/core/context/index.ts +1 -0
  358. package/templates/mobile/rn/src/core/context/shared-transition-context.tsx +35 -0
  359. package/templates/mobile/rn/src/core/hook/index.ts +8 -0
  360. package/templates/mobile/rn/src/core/hook/useActiveRouteName.ts +63 -0
  361. package/templates/mobile/rn/src/core/hook/useAppNavigation.tsx +7 -0
  362. package/templates/mobile/rn/src/core/hook/useBottomInset.tsx +26 -0
  363. package/templates/mobile/rn/src/core/hook/useDebounce.tsx +16 -0
  364. package/templates/mobile/rn/src/core/hook/useEndReached.tsx +46 -0
  365. package/templates/mobile/rn/src/core/hook/useManualRefetch.ts +56 -0
  366. package/templates/mobile/rn/src/core/hook/useNetworkStatus.ts +68 -0
  367. package/templates/mobile/rn/src/core/hook/useTimeout.tsx +30 -0
  368. package/templates/mobile/rn/src/core/index.ts +7 -0
  369. package/templates/mobile/rn/src/core/services/api.service.ts +230 -0
  370. package/templates/mobile/rn/src/core/services/device-id.service.ts +23 -0
  371. package/templates/mobile/rn/src/core/services/index.ts +3 -0
  372. package/templates/mobile/rn/src/core/services/session-end.bridge.ts +26 -0
  373. package/templates/mobile/rn/src/core/theme/dark.theme.ts +10 -0
  374. package/templates/mobile/rn/src/core/theme/index.ts +5 -0
  375. package/templates/mobile/rn/src/core/theme/light.theme.ts +44 -0
  376. package/templates/mobile/rn/src/core/theme/theme-context.tsx +82 -0
  377. package/templates/mobile/rn/src/core/theme/theme.types.ts +26 -0
  378. package/templates/mobile/rn/src/core/theme/use-themed-styles.ts +25 -0
  379. package/templates/mobile/rn/src/core/utils/color.util.tsx +198 -0
  380. package/templates/mobile/rn/src/core/utils/date.util.ts +97 -0
  381. package/templates/mobile/rn/src/core/utils/device-locale.util.ts +22 -0
  382. package/templates/mobile/rn/src/core/utils/emitter/index.ts +161 -0
  383. package/templates/mobile/rn/src/core/utils/emitter/type.ts +40 -0
  384. package/templates/mobile/rn/src/core/utils/enum.util.tsx +15 -0
  385. package/templates/mobile/rn/src/core/utils/font.util.tsx +42 -0
  386. package/templates/mobile/rn/src/core/utils/func.util.ts +48 -0
  387. package/templates/mobile/rn/src/core/utils/greeting.util.ts +20 -0
  388. package/templates/mobile/rn/src/core/utils/image-format.util.ts +117 -0
  389. package/templates/mobile/rn/src/core/utils/image-picker.util.ts +84 -0
  390. package/templates/mobile/rn/src/core/utils/index.ts +18 -0
  391. package/templates/mobile/rn/src/core/utils/linking.util.ts +16 -0
  392. package/templates/mobile/rn/src/core/utils/navigation.util.tsx +100 -0
  393. package/templates/mobile/rn/src/core/utils/number-format.ts +28 -0
  394. package/templates/mobile/rn/src/core/utils/query-client.util.ts +35 -0
  395. package/templates/mobile/rn/src/core/utils/query-persister.util.ts +36 -0
  396. package/templates/mobile/rn/src/core/utils/schema.util.tsx +2713 -0
  397. package/templates/mobile/rn/src/core/utils/size.util.tsx +74 -0
  398. package/templates/mobile/rn/src/core/utils/storage.util.tsx +53 -0
  399. package/templates/mobile/rn/src/core/utils/toast.util.tsx +151 -0
  400. package/templates/mobile/rn/src/core/utils/translator.util.tsx +23 -0
  401. package/templates/mobile/rn/src/core/utils/typography.util.tsx +69 -0
  402. package/templates/mobile/rn/src/core/utils/validate.util.tsx +13 -0
  403. package/templates/mobile/rn/src/declarations.d.ts +54 -0
  404. package/templates/mobile/rn/src/modules/home/home.screen.tsx +33 -0
  405. package/templates/mobile/rn/src/modules/profile/profile.screen.tsx +29 -0
  406. package/templates/mobile/rn/src/scripts/link-fonts.js +60 -0
  407. package/templates/mobile/rn/src/scripts/sync-images.js +56 -0
  408. package/templates/mobile/rn/src/scripts/sync-svgs.js +50 -0
  409. package/templates/mobile/rn/src/types/models.d.ts +24 -0
  410. package/templates/mobile/rn/tsconfig.json +21 -0
  411. package/templates/shared/src/api-endpoints.ts +8 -0
  412. package/templates/shared/src/enums.ts +34 -0
  413. package/templates/shared/src/external-urls.ts +5 -0
  414. package/templates/shared/src/index.ts +6 -3
@@ -0,0 +1,24 @@
1
+ // App-wide ambient types that used to live in the project's `model/` layer.
2
+ // Kept minimal here; move real contracts into `@repo/shared` as you build out.
3
+
4
+ /** Standard API envelope returned by ApiService. */
5
+ declare interface IResponse<T = any> {
6
+ data: T;
7
+ message?: string;
8
+ success?: boolean;
9
+ statusCode?: number;
10
+ }
11
+
12
+ /** A file ready for multipart/form-data upload (image picker, etc.). */
13
+ declare interface IMediaFormData {
14
+ uri: string;
15
+ name: string;
16
+ type: string;
17
+ }
18
+
19
+ /** Result of a place/address selection (geo features). */
20
+ declare interface AddressSelectedResult {
21
+ address: string;
22
+ latitude: number;
23
+ longitude: number;
24
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "extends": "expo/tsconfig.base",
3
+ "compilerOptions": {
4
+ "jsx": "react",
5
+ "strict": true,
6
+ "noUnusedLocals": true,
7
+ "noUnusedParameters": true,
8
+ "noImplicitReturns": true,
9
+ "resolveJsonModule": true,
10
+ "skipLibCheck": true,
11
+ "paths": {
12
+ "@src/*": ["./src/*"],
13
+ "@modules/*": ["./src/modules/*"],
14
+ "@repo/shared": ["../../packages/shared/dist/index.d.ts"]
15
+ }
16
+ },
17
+ "include": ["src"],
18
+ "exclude": ["node_modules"]
19
+ }
@@ -0,0 +1,5 @@
1
+ # Copy to .env and fill in. Read at build time by react-native-config.
2
+ # NEVER commit the real .env — only this example.
3
+
4
+ # Base URL of your API (include trailing slash).
5
+ BASE_URL=https://api.example.com/
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: '@react-native',
4
+ rules: {
5
+ semi: 0,
6
+ },
7
+ };
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ arrowParens: 'avoid',
3
+ bracketSameLine: true,
4
+ bracketSpacing: true,
5
+ singleQuote: true,
6
+ trailingComma: 'all',
7
+ };
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ replaceAttrValues: {
3
+ '{currentColor}': '{props.color || "#0D1F2D"}',
4
+ '{292929}': '{props.color || "#292929"}',
5
+ '{D4D4FF}': '{props.color || "#D4D4FF"}',
6
+ '{7C7C7C}': '{props.color || "#7C7C7C"}',
7
+ '{D97706}': '{props.color || "#D97706"}',
8
+ },
9
+ }
@@ -1,11 +1,44 @@
1
- # Placeholder: React Native (mobile/rn)
1
+ # {{PROJECT_NAME}} — Mobile (React Native CLI)
2
2
 
3
- This is a **placeholder** for the **React Native** template.
3
+ A bare **React Native CLI** starter (no Expo) built from the company's production
4
+ core. Same `src/` architecture and reusable `src/core` kit as the Expo variant —
5
+ only the build tooling differs.
4
6
 
5
- Drop the standard React Native source here. Conventions:
7
+ ## Architecture
6
8
 
7
- - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
- - Name `.gitignore` as **`_gitignore`**, `.npmrc` as **`_npmrc`** if present.
9
- - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
9
+ ```
10
+ src/
11
+ ├── app/ composition: providers, navigation, global stores (auth/loading/common), i18n
12
+ ├── core/ reusable infrastructure (API/services, theme, UI kit, forms, hooks, utils)
13
+ ├── modules/ your features (home/profile examples included)
14
+ └── assets/ i18n, svgs (sync-generated barrel), images, lotties
15
+ ```
10
16
 
11
- `lang: js` this app counts toward the condition for creating `packages/shared`.
17
+ See the Expo template's README for the full `src/core` breakdown — it is identical.
18
+
19
+ ## Expo vs this (bare RN CLI)
20
+
21
+ | | This (RN CLI) | Expo |
22
+ |--|---------------|------|
23
+ | Entry | `AppRegistry.registerComponent` + `app.json` name | `registerRootComponent` |
24
+ | Babel | `module:@react-native/babel-preset` | `babel-preset-expo` |
25
+ | Metro | `@react-native/metro-config` | `expo/metro-config` |
26
+ | TS base | `@react-native/typescript-config` | `expo/tsconfig.base` |
27
+ | Native build | `pod install` + Xcode / Gradle | `expo prebuild` then run |
28
+
29
+ ## Shared contracts
30
+ The core imports `ApiEndpoints`, `ExternalUrls`, enums from **`@repo/shared`** —
31
+ generate this app alongside another JS/TS stack; the CLI wires `workspace:*` automatically.
32
+
33
+ ## Getting started
34
+
35
+ ```bash
36
+ pnpm bootstrap # install + build @repo/shared
37
+ pnpm --filter {{PROJECT_NAME}}-mobile typecheck # verify
38
+ pnpm --filter {{PROJECT_NAME}}-mobile start # metro
39
+ ```
40
+
41
+ Add an icon: drop `name.svg` in `src/assets/svgs/` then `pnpm --filter {{PROJECT_NAME}}-mobile sync-svgs`.
42
+
43
+ This template ships **without** `ios/`/`android/` — generate native projects (RN CLI
44
+ `init` conventions) or copy yours in, then `cd ios && pod install`.
@@ -0,0 +1,24 @@
1
+ # Native build artifacts
2
+ /ios/Pods/
3
+ /ios/build/
4
+ /android/build/
5
+ /android/app/build/
6
+ /android/.gradle/
7
+ /android/local.properties
8
+
9
+ # Dependencies
10
+ node_modules/
11
+
12
+ # Metro
13
+ .metro-health-check*
14
+
15
+ # Env (keep .env.example)
16
+ .env
17
+ .env.*
18
+ !.env.example
19
+
20
+ # Logs / OS
21
+ *.log
22
+ .DS_Store
23
+ *.keystore
24
+ !debug.keystore
@@ -2,5 +2,71 @@
2
2
  "name": "{{PROJECT_NAME}}-mobile",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Placeholder React Native app for {{PROJECT_NAME}}"
5
+ "scripts": {
6
+ "start": "react-native start",
7
+ "android": "react-native run-android",
8
+ "ios": "react-native run-ios",
9
+ "lint": "eslint . --ext .ts,.tsx",
10
+ "typecheck": "tsc --noEmit",
11
+ "sync-svgs": "node src/scripts/sync-svgs.js",
12
+ "sync-images": "node src/scripts/sync-images.js"
13
+ },
14
+ "dependencies": {
15
+ "@hookform/error-message": "2.0.0",
16
+ "@react-native-async-storage/async-storage": "^2.1.2",
17
+ "@react-native-community/netinfo": "^12.0.1",
18
+ "@react-navigation/bottom-tabs": "^7.12.0",
19
+ "@react-navigation/native": "^7.0.13",
20
+ "@react-navigation/native-stack": "^7.1.14",
21
+ "@tanstack/react-query": "^5.101.0",
22
+ "@tanstack/react-query-persist-client": "^5.101.0",
23
+ "axios": "^1.7.9",
24
+ "i18next": "^21.5.4",
25
+ "lodash": "^4.17.21",
26
+ "lottie-react-native": "^7.3.4",
27
+ "qs": "^6.10.1",
28
+ "react": "19.2.3",
29
+ "react-hook-form": "^7.53.2",
30
+ "react-i18next": "^11.14.3",
31
+ "react-native": "0.85.3",
32
+ "react-native-config": "^1.6.1",
33
+ "react-native-date-picker": "5.0.12",
34
+ "react-native-device-info": "^15.0.2",
35
+ "react-native-fast-image": "^8.6.3",
36
+ "react-native-gesture-handler": "~2.31.1",
37
+ "react-native-image-picker": "^8.2.1",
38
+ "react-native-keyboard-controller": "^1.21.6",
39
+ "react-native-linear-gradient": "^2.8.3",
40
+ "react-native-localize": "^3.7.0",
41
+ "react-native-mmkv": "^4.3.1",
42
+ "react-native-nitro-modules": "^0.35.9",
43
+ "react-native-reanimated": "4.3.1",
44
+ "react-native-safe-area-context": "^5.7.0",
45
+ "react-native-screens": "4.25.2",
46
+ "react-native-svg": "^15.15.4",
47
+ "react-native-toast-message": "^2.2.1",
48
+ "react-native-worklets": "0.8.3",
49
+ "yup": "^0.32.11",
50
+ "zustand": "^5.0.7"
51
+ },
52
+ "devDependencies": {
53
+ "@babel/core": "^7.25.2",
54
+ "@babel/preset-env": "^7.25.3",
55
+ "@babel/runtime": "^7.25.0",
56
+ "@react-native-community/cli": "20.1.3",
57
+ "@react-native-community/cli-platform-android": "20.1.3",
58
+ "@react-native-community/cli-platform-ios": "20.1.3",
59
+ "@react-native/babel-preset": "0.85.3",
60
+ "@react-native/eslint-config": "0.85.3",
61
+ "@react-native/metro-config": "0.85.3",
62
+ "@react-native/typescript-config": "0.85.3",
63
+ "@types/lodash": "^4.14.176",
64
+ "@types/qs": "^6.9.7",
65
+ "@types/react": "^19.1.1",
66
+ "babel-plugin-module-resolver": "^4.1.0",
67
+ "eslint": "^8.19.0",
68
+ "prettier": "2.8.8",
69
+ "react-native-svg-transformer": "^1.5.0",
70
+ "typescript": "~5.8.3"
71
+ }
6
72
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "displayName": "{{PROJECT_NAME}}"
4
+ }
@@ -0,0 +1,18 @@
1
+ module.exports = {
2
+ presets: ['module:@react-native/babel-preset'],
3
+ plugins: [
4
+ [
5
+ 'module-resolver',
6
+ {
7
+ root: ['.'],
8
+ extensions: ['.ios.js', '.android.js', '.js', '.ts', '.tsx', '.json', '.svg'],
9
+ alias: {
10
+ '@src': './src',
11
+ '@modules': './src/modules',
12
+ },
13
+ },
14
+ ],
15
+ // Must be last.
16
+ 'react-native-reanimated/plugin',
17
+ ],
18
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @format
3
+ */
4
+ import { AppRegistry } from 'react-native';
5
+ import AppProvider from './src/app/app-provider';
6
+ import { name as appName } from './app.json';
7
+
8
+ AppRegistry.registerComponent(appName, () => AppProvider);
@@ -0,0 +1,33 @@
1
+ const path = require('path');
2
+ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
3
+ const { wrapWithReanimatedMetroConfig } = require('react-native-reanimated/metro-config');
4
+
5
+ // --- Monorepo paths ---
6
+ const projectRoot = __dirname;
7
+ const monorepoRoot = path.resolve(projectRoot, '../..');
8
+
9
+ const defaultConfig = getDefaultConfig(projectRoot);
10
+ const { assetExts, sourceExts } = defaultConfig.resolver;
11
+
12
+ /**
13
+ * Metro configuration (monorepo-aware, with SVG support)
14
+ * https://reactnative.dev/docs/metro
15
+ *
16
+ * @type {import('@react-native/metro-config').MetroConfig}
17
+ */
18
+ const config = {
19
+ watchFolders: [monorepoRoot],
20
+ transformer: {
21
+ babelTransformerPath: require.resolve('react-native-svg-transformer/react-native'),
22
+ },
23
+ resolver: {
24
+ nodeModulesPaths: [
25
+ path.resolve(projectRoot, 'node_modules'),
26
+ path.resolve(monorepoRoot, 'node_modules'),
27
+ ],
28
+ assetExts: assetExts.filter(ext => ext !== 'svg'),
29
+ sourceExts: [...sourceExts, 'svg'],
30
+ },
31
+ };
32
+
33
+ module.exports = wrapWithReanimatedMetroConfig(mergeConfig(defaultConfig, config));
@@ -0,0 +1,24 @@
1
+ import React, { useEffect } from 'react';
2
+ import { StatusBar } from 'react-native';
3
+ import { useTheme } from '@src/core/theme';
4
+ import { RootNavigator } from './navigation';
5
+ import { useAuthStore } from './stores';
6
+ import { bootstrapLanguage } from './config/translation';
7
+
8
+ // Root UI component. Restores the persisted session + language, then mounts navigation.
9
+ export function App() {
10
+ const { resolved } = useTheme();
11
+ const hydrate = useAuthStore(s => s.hydrate);
12
+
13
+ useEffect(() => {
14
+ void bootstrapLanguage();
15
+ void hydrate();
16
+ }, [hydrate]);
17
+
18
+ return (
19
+ <>
20
+ <StatusBar barStyle={resolved === 'dark' ? 'light-content' : 'dark-content'} />
21
+ <RootNavigator />
22
+ </>
23
+ );
24
+ }
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { GestureHandlerRootView } from 'react-native-gesture-handler';
3
+ import { SafeAreaProvider } from 'react-native-safe-area-context';
4
+ import { I18nextProvider } from 'react-i18next';
5
+ import { onlineManager } from '@tanstack/react-query';
6
+ import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
7
+ import NetInfo from '@react-native-community/netinfo';
8
+ import { queryClient, mmkvQueryPersister } from '@src/core/utils';
9
+ import { ThemeProvider } from '@src/core/theme';
10
+ import { i18n } from './config/translation';
11
+ import { App } from './App';
12
+
13
+ // Pause/resume React Query based on connectivity (offline-first).
14
+ onlineManager.setEventListener(setOnline =>
15
+ NetInfo.addEventListener(state => setOnline(Boolean(state.isConnected))),
16
+ );
17
+
18
+ // The provider tree. Order: gesture root → persisted query cache → i18n →
19
+ // safe-area → theme → app UI.
20
+ export default function AppProvider() {
21
+ return (
22
+ <GestureHandlerRootView style={{ flex: 1 }}>
23
+ <PersistQueryClientProvider
24
+ client={queryClient}
25
+ persistOptions={{ persister: mmkvQueryPersister }}>
26
+ <I18nextProvider i18n={i18n}>
27
+ <SafeAreaProvider>
28
+ <ThemeProvider>
29
+ <App />
30
+ </ThemeProvider>
31
+ </SafeAreaProvider>
32
+ </I18nextProvider>
33
+ </PersistQueryClientProvider>
34
+ </GestureHandlerRootView>
35
+ );
36
+ }
@@ -0,0 +1,26 @@
1
+ import { findBestLanguageTag } from 'react-native-localize';
2
+ import { Translator } from '@src/core/utils';
3
+ import en from '@src/assets/i18n/en.json';
4
+ import fr from '@src/assets/i18n/fr.json';
5
+
6
+ const resources = {
7
+ en: { translation: en },
8
+ fr: { translation: fr },
9
+ };
10
+
11
+ const fallbackLng = 'en';
12
+ const best = findBestLanguageTag(Object.keys(resources));
13
+
14
+ // Initialize i18next once and expose the instance. The provider mounts it in
15
+ // app-provider.tsx; the active language can be changed via Translator.changeLanguages.
16
+ export const i18n = Translator.setup({
17
+ resources,
18
+ lng: best?.languageTag?.split('-')[0] ?? fallbackLng,
19
+ fallbackLng,
20
+ interpolation: { escapeValue: false },
21
+ });
22
+
23
+ // Hook persisted-language restore here if you store a user preference.
24
+ export async function bootstrapLanguage(): Promise<void> {
25
+ // no-op for the starter; wire AppStorageUtil + Translator.changeLanguages here.
26
+ }
@@ -0,0 +1 @@
1
+ export * from './stores';
@@ -0,0 +1,27 @@
1
+ // Central screen registry + typed param list. Add a screen here and every
2
+ // navigation call to it (via NavigationUtil / useNavigation) is type-checked.
3
+ export enum Screen {
4
+ Home = 'Home',
5
+ Profile = 'Profile',
6
+ // Auth / feature screens the core references (e.g. session-expired reset).
7
+ // Wire real screens for these as you build the app.
8
+ SignIn = 'SignIn',
9
+ OtpVerification = 'OtpVerification',
10
+ PlayerFull = 'PlayerFull',
11
+ LibraryMain = 'LibraryMain',
12
+ }
13
+
14
+ export type NavigatorParamList = {
15
+ [Screen.Home]: undefined;
16
+ [Screen.Profile]: undefined;
17
+ [Screen.SignIn]: undefined;
18
+ [Screen.OtpVerification]: undefined;
19
+ [Screen.PlayerFull]: undefined;
20
+ [Screen.LibraryMain]: undefined;
21
+ };
22
+
23
+ declare global {
24
+ namespace ReactNavigation {
25
+ interface RootParamList extends NavigatorParamList {}
26
+ }
27
+ }
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { useTheme } from '@src/core/theme';
5
+ import { Screen } from './app-route-type';
6
+ import { HomeScreen } from '@src/modules/home/home.screen';
7
+ import { ProfileScreen } from '@src/modules/profile/profile.screen';
8
+
9
+ const Tab = createBottomTabNavigator();
10
+
11
+ export function BottomTabs() {
12
+ const { t } = useTranslation();
13
+ const { colors } = useTheme();
14
+ return (
15
+ <Tab.Navigator
16
+ screenOptions={{
17
+ headerStyle: { backgroundColor: colors.bg_elevation_level_1_normal },
18
+ headerTintColor: colors.fg_neutral_normal,
19
+ tabBarStyle: {
20
+ backgroundColor: colors.bg_elevation_level_1_normal,
21
+ borderTopColor: colors.bd_neutral_faded,
22
+ },
23
+ tabBarActiveTintColor: colors.fg_primary_normal,
24
+ tabBarInactiveTintColor: colors.fg_neutral_faded,
25
+ }}>
26
+ <Tab.Screen name={Screen.Home} component={HomeScreen} options={{ title: t('home.title') }} />
27
+ <Tab.Screen
28
+ name={Screen.Profile}
29
+ component={ProfileScreen}
30
+ options={{ title: t('profile.title') }}
31
+ />
32
+ </Tab.Navigator>
33
+ );
34
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { NavigationContainer } from '@react-navigation/native';
3
+ import { navigationRef } from '@src/core/utils';
4
+ import { BottomTabs } from './bottom-tabs';
5
+
6
+ // Mounts the navigation tree and wires the global navigationRef so the API
7
+ // layer (and any non-component code) can navigate / reset on session expiry.
8
+ export function RootNavigator() {
9
+ return (
10
+ <NavigationContainer ref={navigationRef}>
11
+ <BottomTabs />
12
+ </NavigationContainer>
13
+ );
14
+ }
@@ -0,0 +1,33 @@
1
+ import { create } from 'zustand';
2
+ import { AppStorageUtil, StorageKeys } from '@src/core/utils';
3
+
4
+ interface AuthState {
5
+ token: string | null;
6
+ isAuthenticated: boolean;
7
+ setToken: (token: string | null) => void;
8
+ clearTokens: () => void;
9
+ hydrate: () => Promise<void>;
10
+ }
11
+
12
+ // Global auth store (Zustand). The API layer reads `token` for the auth header
13
+ // and calls `clearTokens()` on a 401 (see core/services/api.service.ts).
14
+ export const useAuthStore = create<AuthState>()(set => ({
15
+ token: null,
16
+ isAuthenticated: false,
17
+ setToken: token => {
18
+ if (token) {
19
+ AppStorageUtil.setItem(StorageKeys.APP_ACCESS_TOKEN, token);
20
+ } else {
21
+ AppStorageUtil.removeItem(StorageKeys.APP_ACCESS_TOKEN);
22
+ }
23
+ set({ token, isAuthenticated: Boolean(token) });
24
+ },
25
+ clearTokens: () => {
26
+ AppStorageUtil.removeItem(StorageKeys.APP_ACCESS_TOKEN);
27
+ set({ token: null, isAuthenticated: false });
28
+ },
29
+ hydrate: async () => {
30
+ const token = (await AppStorageUtil.getItem(StorageKeys.APP_ACCESS_TOKEN)) as string | null;
31
+ set({ token: token ?? null, isAuthenticated: Boolean(token) });
32
+ },
33
+ }));
@@ -0,0 +1,19 @@
1
+ import { create } from 'zustand';
2
+
3
+ export interface EnumValue {
4
+ key: string;
5
+ value: string;
6
+ label?: string;
7
+ }
8
+
9
+ interface CommonState {
10
+ /** Server-provided enum/lookup values (seeded after login/bootstrap). */
11
+ enumValues: EnumValue[];
12
+ setEnumValues: (values: EnumValue[]) => void;
13
+ }
14
+
15
+ // App-wide misc state (enum lookups, etc.). Used by core/utils/func.util.
16
+ export const useCommonStore = create<CommonState>()(set => ({
17
+ enumValues: [],
18
+ setEnumValues: enumValues => set({ enumValues }),
19
+ }));
@@ -0,0 +1,3 @@
1
+ export * from './auth.store';
2
+ export * from './loading.store';
3
+ export * from './common.store';
@@ -0,0 +1,22 @@
1
+ import { create } from 'zustand';
2
+
3
+ interface LoadingState {
4
+ /** True while at least one non-silent request is in flight. */
5
+ isLoading: boolean;
6
+ count: number;
7
+ showLoading: () => void;
8
+ hideLoading: () => void;
9
+ }
10
+
11
+ // Ref-counted global loading flag. The API layer toggles it around non-silent
12
+ // requests; <LoadingApp> renders the overlay when `isLoading` is true.
13
+ export const useLoadingStore = create<LoadingState>()(set => ({
14
+ isLoading: false,
15
+ count: 0,
16
+ showLoading: () => set(s => ({ count: s.count + 1, isLoading: true })),
17
+ hideLoading: () =>
18
+ set(s => {
19
+ const count = Math.max(0, s.count - 1);
20
+ return { count, isLoading: count > 0 };
21
+ }),
22
+ }));
@@ -0,0 +1,5 @@
1
+ // AUTO-GENERATED by `pnpm sync-images` (kebab-case.png -> ImgKebabCase).
2
+ import ImgEmptyList from './empty-list.png';
3
+ import ImgScreenBgGradian from './screen-bg-gradian.png';
4
+
5
+ export { ImgEmptyList, ImgScreenBgGradian };
@@ -0,0 +1,12 @@
1
+ {
2
+ "home": {
3
+ "title": "Home",
4
+ "greeting": "Hello, {{name}}",
5
+ "loadError": "Failed to load.",
6
+ "count": "{{n}} item(s)"
7
+ },
8
+ "profile": {
9
+ "title": "Profile",
10
+ "signOut": "Sign out"
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "home": {
3
+ "title": "Accueil",
4
+ "greeting": "Bonjour, {{name}}",
5
+ "loadError": "Échec du chargement.",
6
+ "count": "{{n}} élément(s)"
7
+ },
8
+ "profile": {
9
+ "title": "Profil",
10
+ "signOut": "Se déconnecter"
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ import LottieLoading from './loading.json';
2
+
3
+ export { LottieLoading };
@@ -0,0 +1 @@
1
+ {"nm":"Main Scene","ddd":0,"h":500,"w":500,"meta":{"g":"@lottiefiles/creator 1.74.0"},"layers":[{"ty":4,"nm":"Rectangle_1","sr":1,"st":0,"op":90.0000036657751,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-125,-107,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,156.235],"ix":2},"r":{"a":0,"k":135,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[57,-57],[57,57],[-57,57],[-57,-57]]}],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[-31.437,0],[0,-31.437],[31.437,0],[0,31.437]],"o":[[31.437,0],[0,31.437],[-31.437,0],[0,-31.437]],"v":[[0.718,-187.388],[57.64,-130.466],[0.718,-73.544],[-56.204,-130.466]]}],"t":15},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[188.6,-188.5],[188.6,-74.5],[74.6,-74.5],[74.6,-188.5]]}],"t":30.0000012219251}],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4078,0.3059,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-125,-107],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Rectangle_2","sr":1,"st":0,"op":90.0000036657751,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-125,-107,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[343,250],"ix":2},"r":{"a":0,"k":585,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[57,-57],[57,57],[-57,57],[-57,-57]]}],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[-31.437,0],[0,-31.437],[31.437,0],[0,31.437]],"o":[[31.437,0],[0,31.437],[-31.437,0],[0,-31.437]],"v":[[0.718,-187.388],[57.64,-130.466],[0.718,-73.544],[-56.204,-130.466]]}],"t":15},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[188.6,-188.5],[188.6,-74.5],[74.6,-74.5],[74.6,-188.5]]}],"t":30.0000012219251}],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.2118,0.0863,0.6118],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-125,-107],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2},{"ty":4,"nm":"Rectangle_3","sr":1,"st":0,"op":90.0000036657751,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-125,-107,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,342.86],"ix":2},"r":{"a":0,"k":315,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[57,-57],[57,57],[-57,57],[-57,-57]]}],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[-31.437,0],[0,-31.437],[31.437,0],[0,31.437]],"o":[[31.437,0],[0,31.437],[-31.437,0],[0,-31.437]],"v":[[0.718,-187.388],[57.64,-130.466],[0.718,-73.544],[-56.204,-130.466]]}],"t":15},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[188.6,-188.5],[188.6,-74.5],[74.6,-74.5],[74.6,-188.5]]}],"t":30.0000012219251}],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4078,0.3059,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-125,-107],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3},{"ty":4,"nm":"Rectangle_4","sr":1,"st":0,"op":90.0000036657751,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-125,-107,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[157.156,250],"ix":2},"r":{"a":0,"k":45,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[57,-57],[57,57],[-57,57],[-57,-57]]}],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[-31.437,0],[0,-31.437],[31.437,0],[0,31.437]],"o":[[31.437,0],[0,31.437],[-31.437,0],[0,-31.437]],"v":[[0.718,-187.388],[57.64,-130.466],[0.718,-73.544],[-56.204,-130.466]]}],"t":15},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[188.6,-188.5],[188.6,-74.5],[74.6,-74.5],[74.6,-188.5]]}],"t":30.0000012219251}],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.2118,0.0863,0.6118],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-125,-107],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":4}],"v":"5.7.0","fr":29.9700012207031,"op":31.0000012626559,"ip":0,"assets":[]}
@@ -0,0 +1,3 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M10.0001 4L6.47152 7.5286C6.24929 7.75082 6.13818 7.86193 6.13818 8C6.13818 8.13807 6.24929 8.24918 6.47152 8.4714L10.0001 12" stroke="#F5F5F5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M9 18L14.2929 12.7071C14.6262 12.3738 14.7929 12.2071 14.7929 12C14.7929 11.7929 14.6262 11.6262 14.2929 11.2929L9 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>