@cavulsqa/create 2.9.2 → 2.10.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 (209) hide show
  1. package/README.md +12 -1
  2. package/bin/create.mjs +18 -25
  3. package/lib/templates.mjs +56 -0
  4. package/package.json +13 -5
  5. package/templates/f7-app/CLAUDE.md +3 -0
  6. package/templates/f7-app/auto-imports.d.ts +15 -0
  7. package/templates/f7-app/package.json +6 -3
  8. package/templates/f7-app/src/App.vue +1 -0
  9. package/templates/f7-app/src/env.d.ts +1 -0
  10. package/templates/f7-app/src/main.ts +3 -0
  11. package/templates/f7-app/src/modules/demo/router/routes/demo.routes.ts +4 -16
  12. package/templates/f7-app/src/modules/home/router/routes/home.routes.ts +3 -11
  13. package/templates/f7-app/src/modules/settings/router/routes/settings.routes.ts +2 -6
  14. package/templates/f7-app/src/plugins/bootstrapError.ts +2 -0
  15. package/templates/f7-app/src/plugins/recorder.plugin.ts +155 -0
  16. package/templates/f7-app/src/router/global/global.routes.ts +2 -6
  17. package/templates/f7-app/src/shared/composables/useNavigationGuard.ts +48 -0
  18. package/templates/f7-app/src/shared/recorder/capacitorSink.ts +124 -0
  19. package/templates/f7-app/src/shared/utils/lazyRoute.ts +25 -0
  20. package/templates/f7-app/tests/lazyRoute.test.ts +30 -0
  21. package/templates/f7-app/tests/navigationGuard.test.ts +82 -0
  22. package/templates/f7-app/vite.config.ts +1 -0
  23. package/templates/m3e-app/.claude/rules/data-fetching.md +68 -0
  24. package/templates/m3e-app/.claude/rules/database.md +106 -0
  25. package/templates/m3e-app/.claude/rules/m3e-ui.md +48 -0
  26. package/templates/m3e-app/.claude/rules/modules.md +43 -0
  27. package/templates/m3e-app/.claude/rules/native.md +60 -0
  28. package/templates/m3e-app/.claude/skills/m3-expressive/SKILL.md +68 -0
  29. package/templates/m3e-app/.claude/skills/m3-expressive/color.md +44 -0
  30. package/templates/m3e-app/.claude/skills/m3-expressive/components.md +325 -0
  31. package/templates/m3e-app/.claude/skills/m3-expressive/layout.md +42 -0
  32. package/templates/m3e-app/.claude/skills/m3-expressive/motion.md +51 -0
  33. package/templates/m3e-app/.claude/skills/m3-expressive/shapes-type.md +48 -0
  34. package/templates/m3e-app/.claude/skills/m3-expressive/sources.md +53 -0
  35. package/templates/m3e-app/.claude/skills/module-architecture/SKILL.md +65 -0
  36. package/templates/m3e-app/.claude/skills/module-architecture/file-templates.md +178 -0
  37. package/templates/m3e-app/.claude/skills/reactive-data/SKILL.md +90 -0
  38. package/templates/m3e-app/.claude/skills/reactive-data/testing.md +55 -0
  39. package/templates/m3e-app/.env.example +32 -0
  40. package/templates/m3e-app/CLAUDE.md +93 -0
  41. package/templates/m3e-app/auto-imports.d.ts +851 -0
  42. package/templates/m3e-app/capacitor.config.ts +44 -0
  43. package/templates/m3e-app/components.d.ts +252 -0
  44. package/templates/m3e-app/index.html +16 -0
  45. package/templates/m3e-app/package.json +107 -0
  46. package/templates/m3e-app/src/App.vue +129 -0
  47. package/templates/m3e-app/src/app/pragmas.config.ts +35 -0
  48. package/templates/m3e-app/src/app/scroll.config.ts +20 -0
  49. package/templates/m3e-app/src/app/storage.config.ts +65 -0
  50. package/templates/m3e-app/src/app/tabs.ts +33 -0
  51. package/templates/m3e-app/src/app/theme.config.ts +51 -0
  52. package/templates/m3e-app/src/assets/css/app.css +18 -0
  53. package/templates/m3e-app/src/assets/css/base.css +53 -0
  54. package/templates/m3e-app/src/assets/css/layout/container-transform.css +113 -0
  55. package/templates/m3e-app/src/assets/css/layout/shell.css +69 -0
  56. package/templates/m3e-app/src/assets/css/layout/transitions.css +121 -0
  57. package/templates/m3e-app/src/assets/css/m3e.css +6 -0
  58. package/templates/m3e-app/src/assets/css/theme/framework7.css +18 -0
  59. package/templates/m3e-app/src/assets/css/theme/tailwind.css +365 -0
  60. package/templates/m3e-app/src/domains/benchmark/benchmark.dataset.ts +209 -0
  61. package/templates/m3e-app/src/domains/benchmark/benchmark.suite.ts +662 -0
  62. package/templates/m3e-app/src/domains/sales/sales.repository.ts +458 -0
  63. package/templates/m3e-app/src/env.d.ts +40 -0
  64. package/templates/m3e-app/src/locales/ar.json +1260 -0
  65. package/templates/m3e-app/src/locales/en.json +1260 -0
  66. package/templates/m3e-app/src/locales/fr.json +1260 -0
  67. package/templates/m3e-app/src/main.ts +41 -0
  68. package/templates/m3e-app/src/modules/demo/components/DemoBenchmark.vue +119 -0
  69. package/templates/m3e-app/src/modules/demo/components/DemoBusLog.vue +63 -0
  70. package/templates/m3e-app/src/modules/demo/components/DemoCreateOrderSheet.vue +203 -0
  71. package/templates/m3e-app/src/modules/demo/components/DemoMetricsSheet.vue +75 -0
  72. package/templates/m3e-app/src/modules/demo/components/DemoOrderList.vue +75 -0
  73. package/templates/m3e-app/src/modules/demo/components/DemoPipelineBenchmark.vue +65 -0
  74. package/templates/m3e-app/src/modules/demo/components/DemoStatCards.vue +71 -0
  75. package/templates/m3e-app/src/modules/demo/composables/useBenchmark.ts +139 -0
  76. package/templates/m3e-app/src/modules/demo/composables/useOrderStatus.ts +64 -0
  77. package/templates/m3e-app/src/modules/demo/composables/useReactiveDemo.ts +171 -0
  78. package/templates/m3e-app/src/modules/demo/router/routes/demo.routes.ts +22 -0
  79. package/templates/m3e-app/src/modules/demo/views/DemoView.vue +210 -0
  80. package/templates/m3e-app/src/modules/demo/views/OrderDetailView.vue +136 -0
  81. package/templates/m3e-app/src/modules/demo/views/OrderSearchView.vue +74 -0
  82. package/templates/m3e-app/src/modules/gallery/components/GalleryBlock.vue +16 -0
  83. package/templates/m3e-app/src/modules/gallery/components/GalleryCarouselTile.vue +71 -0
  84. package/templates/m3e-app/src/modules/gallery/components/GalleryCustomerForm.vue +76 -0
  85. package/templates/m3e-app/src/modules/gallery/components/GalleryProofOfDelivery.vue +69 -0
  86. package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaDay.vue +106 -0
  87. package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaMonth.vue +40 -0
  88. package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaVisitList.vue +45 -0
  89. package/templates/m3e-app/src/modules/gallery/components/chat/ChatAttachSheet.vue +106 -0
  90. package/templates/m3e-app/src/modules/gallery/components/chat/ChatContactPicker.vue +85 -0
  91. package/templates/m3e-app/src/modules/gallery/components/chat/ChatInviteComposer.vue +131 -0
  92. package/templates/m3e-app/src/modules/gallery/components/chat/ChatPollComposer.vue +148 -0
  93. package/templates/m3e-app/src/modules/gallery/components/chat/ChatRecentPhotos.vue +62 -0
  94. package/templates/m3e-app/src/modules/gallery/components/inputs/InputsAccount.vue +79 -0
  95. package/templates/m3e-app/src/modules/gallery/components/inputs/InputsTags.vue +53 -0
  96. package/templates/m3e-app/src/modules/gallery/components/inputs/InputsVerification.vue +73 -0
  97. package/templates/m3e-app/src/modules/gallery/components/inputs/PasswordStrength.vue +37 -0
  98. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryButtons.vue +128 -0
  99. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryCarousels.vue +236 -0
  100. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryCharts.vue +84 -0
  101. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryFabs.vue +70 -0
  102. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryInputs.vue +219 -0
  103. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryNavigation.vue +141 -0
  104. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryOverlays.vue +368 -0
  105. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryPickers.vue +175 -0
  106. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryProgress.vue +118 -0
  107. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryScale.vue +103 -0
  108. package/templates/m3e-app/src/modules/gallery/components/sections/GallerySelection.vue +219 -0
  109. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryShapes.vue +48 -0
  110. package/templates/m3e-app/src/modules/gallery/components/sections/GallerySurfaces.vue +213 -0
  111. package/templates/m3e-app/src/modules/gallery/components/sections/GalleryTables.vue +185 -0
  112. package/templates/m3e-app/src/modules/gallery/components/surfaces/SurfacesContainerTransform.vue +46 -0
  113. package/templates/m3e-app/src/modules/gallery/composables/chatTeam.ts +18 -0
  114. package/templates/m3e-app/src/modules/gallery/composables/composeKind.ts +2 -0
  115. package/templates/m3e-app/src/modules/gallery/composables/currentPosition.ts +35 -0
  116. package/templates/m3e-app/src/modules/gallery/composables/demoOrders.ts +29 -0
  117. package/templates/m3e-app/src/modules/gallery/composables/featuredStories.ts +73 -0
  118. package/templates/m3e-app/src/modules/gallery/composables/fieldFormats.ts +36 -0
  119. package/templates/m3e-app/src/modules/gallery/composables/openExternal.ts +11 -0
  120. package/templates/m3e-app/src/modules/gallery/composables/routePlan.ts +90 -0
  121. package/templates/m3e-app/src/modules/gallery/composables/useAgenda.ts +61 -0
  122. package/templates/m3e-app/src/modules/gallery/composables/useChatCustomers.ts +31 -0
  123. package/templates/m3e-app/src/modules/gallery/composables/useChatDemo.ts +299 -0
  124. package/templates/m3e-app/src/modules/gallery/composables/useGallerySections.ts +230 -0
  125. package/templates/m3e-app/src/modules/gallery/composables/useLocalAttachments.ts +71 -0
  126. package/templates/m3e-app/src/modules/gallery/composables/useLocationShare.ts +38 -0
  127. package/templates/m3e-app/src/modules/gallery/composables/usePhotoScenes.ts +82 -0
  128. package/templates/m3e-app/src/modules/gallery/composables/useTreeDemo.ts +67 -0
  129. package/templates/m3e-app/src/modules/gallery/composables/wilayas.ts +67 -0
  130. package/templates/m3e-app/src/modules/gallery/router/routes/gallery.routes.ts +52 -0
  131. package/templates/m3e-app/src/modules/gallery/views/GalleryAgendaView.vue +117 -0
  132. package/templates/m3e-app/src/modules/gallery/views/GalleryChatView.vue +292 -0
  133. package/templates/m3e-app/src/modules/gallery/views/GalleryContactsView.vue +77 -0
  134. package/templates/m3e-app/src/modules/gallery/views/GalleryFeaturedView.vue +60 -0
  135. package/templates/m3e-app/src/modules/gallery/views/GalleryLoginView.vue +135 -0
  136. package/templates/m3e-app/src/modules/gallery/views/GalleryOnboardingView.vue +116 -0
  137. package/templates/m3e-app/src/modules/gallery/views/GallerySectionView.vue +27 -0
  138. package/templates/m3e-app/src/modules/gallery/views/GalleryTabsView.vue +135 -0
  139. package/templates/m3e-app/src/modules/gallery/views/GalleryView.vue +50 -0
  140. package/templates/m3e-app/src/modules/home/components/HomeHero.vue +57 -0
  141. package/templates/m3e-app/src/modules/home/composables/useHomeFeatures.ts +128 -0
  142. package/templates/m3e-app/src/modules/home/composables/useOpenFeature.ts +22 -0
  143. package/templates/m3e-app/src/modules/home/router/routes/home.routes.ts +21 -0
  144. package/templates/m3e-app/src/modules/home/views/FeatureDetailView.vue +66 -0
  145. package/templates/m3e-app/src/modules/home/views/HomeView.vue +47 -0
  146. package/templates/m3e-app/src/modules/settings/components/RolePalette.vue +38 -0
  147. package/templates/m3e-app/src/modules/settings/components/SeedSwatches.vue +41 -0
  148. package/templates/m3e-app/src/modules/settings/components/SettingsChoice.vue +43 -0
  149. package/templates/m3e-app/src/modules/settings/components/StudioPreview.vue +52 -0
  150. package/templates/m3e-app/src/modules/settings/router/routes/settings.routes.ts +17 -0
  151. package/templates/m3e-app/src/modules/settings/types.ts +7 -0
  152. package/templates/m3e-app/src/modules/settings/views/ColorStudioView.vue +166 -0
  153. package/templates/m3e-app/src/modules/settings/views/SettingsView.vue +157 -0
  154. package/templates/m3e-app/src/plugins/bootstrapError.ts +62 -0
  155. package/templates/m3e-app/src/plugins/capacitor/index.ts +14 -0
  156. package/templates/m3e-app/src/plugins/capacitor/useAndroidBackButton.ts +36 -0
  157. package/templates/m3e-app/src/plugins/capacitor/useKeyboard.ts +48 -0
  158. package/templates/m3e-app/src/plugins/capacitor/useSplashScreen.ts +43 -0
  159. package/templates/m3e-app/src/plugins/capacitor/useStatusBar.ts +16 -0
  160. package/templates/m3e-app/src/plugins/framework7.plugin.ts +36 -0
  161. package/templates/m3e-app/src/plugins/i18n.plugin.ts +87 -0
  162. package/templates/m3e-app/src/plugins/m3e.plugin.ts +22 -0
  163. package/templates/m3e-app/src/plugins/seed.plugin.ts +7 -0
  164. package/templates/m3e-app/src/plugins/sqlite.plugin.ts +32 -0
  165. package/templates/m3e-app/src/router/global/global.routes.ts +12 -0
  166. package/templates/m3e-app/src/router/index.ts +20 -0
  167. package/templates/m3e-app/src/shared/components/error/404.vue +13 -0
  168. package/templates/m3e-app/src/shared/components/layout/EmptyState.vue +30 -0
  169. package/templates/m3e-app/src/shared/components/layout/SectionHeader.vue +10 -0
  170. package/templates/m3e-app/src/shared/components/page/AppPage.vue +104 -0
  171. package/templates/m3e-app/src/shared/composables/navigation/useActiveTab.ts +39 -0
  172. package/templates/m3e-app/src/shared/composables/navigation/useContainerTransform.ts +117 -0
  173. package/templates/m3e-app/src/shared/composables/navigation/useNavigationGuard.ts +48 -0
  174. package/templates/m3e-app/src/shared/composables/navigation/useNavigationVisibility.ts +64 -0
  175. package/templates/m3e-app/src/shared/composables/navigation/useViewRouter.ts +25 -0
  176. package/templates/m3e-app/src/shared/composables/navigation/useWindowClass.ts +22 -0
  177. package/templates/m3e-app/src/shared/composables/theme/useThemeSettings.ts +155 -0
  178. package/templates/m3e-app/src/shared/database/candidates/capacitorSqlite.ts +36 -0
  179. package/templates/m3e-app/src/shared/database/candidates/index.ts +4 -0
  180. package/templates/m3e-app/src/shared/database/candidates/opfsSahPool.ts +25 -0
  181. package/templates/m3e-app/src/shared/database/candidates/types.ts +56 -0
  182. package/templates/m3e-app/src/shared/database/candidates/waSqlite.ts +56 -0
  183. package/templates/m3e-app/src/shared/database/database.ts +219 -0
  184. package/templates/m3e-app/src/shared/database/index.ts +3 -0
  185. package/templates/m3e-app/src/shared/database/migrations.ts +223 -0
  186. package/templates/m3e-app/src/shared/database/opfs.worker.ts +5 -0
  187. package/templates/m3e-app/src/shared/database/queries.ts +26 -0
  188. package/templates/m3e-app/src/shared/database/schema.ts +153 -0
  189. package/templates/m3e-app/src/shared/database/storage.ts +64 -0
  190. package/templates/m3e-app/src/shared/database/wa.worker.ts +5 -0
  191. package/templates/m3e-app/src/shared/utils/lazyRoute.ts +41 -0
  192. package/templates/m3e-app/src/shared/utils/resolvers/resolvers.ts +42 -0
  193. package/templates/m3e-app/src/shared/utils/textDirection.ts +22 -0
  194. package/templates/m3e-app/src/shared/utils/theme/themeSettings.ts +59 -0
  195. package/templates/m3e-app/src/shared/utils/tone.ts +8 -0
  196. package/templates/m3e-app/tests/benchmark.suite.test.ts +104 -0
  197. package/templates/m3e-app/tests/containerTransform.test.ts +58 -0
  198. package/templates/m3e-app/tests/fieldFormats.test.ts +25 -0
  199. package/templates/m3e-app/tests/lazyRoute.test.ts +30 -0
  200. package/templates/m3e-app/tests/locales.test.ts +73 -0
  201. package/templates/m3e-app/tests/migrations.test.ts +74 -0
  202. package/templates/m3e-app/tests/navigationGuard.test.ts +82 -0
  203. package/templates/m3e-app/tests/openDatabase.test.ts +54 -0
  204. package/templates/m3e-app/tests/sales.repository.test.ts +329 -0
  205. package/templates/m3e-app/tests/storage.test.ts +117 -0
  206. package/templates/m3e-app/tests/textDirection.test.ts +11 -0
  207. package/templates/m3e-app/tooling/linkedPackages.ts +112 -0
  208. package/templates/m3e-app/tsconfig.json +21 -0
  209. package/templates/m3e-app/vite.config.ts +144 -0
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <GalleryBlock
3
+ :title="t('gallery.inputs.verification')"
4
+ :note="t('gallery.inputs.verificationNote', { code: DEMO_CODE })"
5
+ stack
6
+ >
7
+ <M3CodeField
8
+ ref="field"
9
+ v-model="code"
10
+ :label="t('gallery.inputs.codeLabel')"
11
+ :error="error"
12
+ :supporting="verified ? t('gallery.inputs.codeVerified') : t('gallery.inputs.codeSent')"
13
+ :success="verified"
14
+ @complete="check"
15
+ />
16
+ <div class="flex flex-wrap items-center gap-2">
17
+ <M3Button v-if="verified" variant="tonal" size="s" @click="reset">
18
+ {{ t("gallery.inputs.codeAgain") }}
19
+ </M3Button>
20
+ <M3Button v-else variant="text" size="s" :disabled="wait > 0" @click="resend">
21
+ <template #icon><i-ms-sms-outline-rounded /></template>
22
+ {{ wait > 0 ? t("gallery.inputs.resendIn", { s: wait }) : t("gallery.inputs.resend") }}
23
+ </M3Button>
24
+ </div>
25
+ </GalleryBlock>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ import type { M3CodeField } from "@cavulsqa/m3e-vue";
30
+ import GalleryBlock from "@/modules/gallery/components/GalleryBlock.vue";
31
+
32
+ const DEMO_CODE = "246810";
33
+ const RESEND_AFTER = 30;
34
+
35
+ const { t } = useI18n();
36
+ const snackbar = useSnackbar();
37
+ const field = useTemplateRef<InstanceType<typeof M3CodeField>>("field");
38
+ const code = ref("");
39
+ const error = ref<string>();
40
+ const verified = ref(false);
41
+ const wait = ref(RESEND_AFTER);
42
+
43
+ const { pause, resume } = useIntervalFn(() => {
44
+ wait.value = Math.max(0, wait.value - 1);
45
+ if (wait.value === 0) pause();
46
+ }, 1000);
47
+
48
+ watch(code, (value) => {
49
+ if (value.length < DEMO_CODE.length) error.value = undefined;
50
+ });
51
+
52
+ function check(value: string) {
53
+ if (value === DEMO_CODE) {
54
+ verified.value = true;
55
+ pause();
56
+ return;
57
+ }
58
+ error.value = t("gallery.inputs.codeWrong");
59
+ }
60
+
61
+ function resend() {
62
+ code.value = "";
63
+ wait.value = RESEND_AFTER;
64
+ resume();
65
+ void snackbar.show(t("gallery.inputs.codeResent"));
66
+ field.value?.focus();
67
+ }
68
+
69
+ function reset() {
70
+ verified.value = false;
71
+ resend();
72
+ }
73
+ </script>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <div class="flex items-center gap-3" :class="TONE[score]">
3
+ <div class="flex flex-1 gap-1" aria-hidden="true">
4
+ <span
5
+ v-for="step in 4"
6
+ :key="step"
7
+ class="password-strength__step h-1.5 flex-1 rounded-full"
8
+ :class="{ 'password-strength__step--on': step <= score }"
9
+ />
10
+ </div>
11
+ <span class="type-label-medium min-w-14 text-end" role="status">{{ label }}</span>
12
+ </div>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ defineProps<{ score: 0 | 1 | 2 | 3 | 4; label: string }>();
17
+
18
+ const TONE = [
19
+ "text-on-surface-variant",
20
+ "text-error",
21
+ "text-tertiary",
22
+ "text-primary",
23
+ "text-primary",
24
+ ] as const;
25
+ </script>
26
+
27
+ <style scoped>
28
+ .password-strength__step {
29
+ background: var(--md-sys-color-surface-container-highest);
30
+ transition: background-color var(--md-sys-motion-spring-default-effects-duration)
31
+ var(--md-sys-motion-spring-default-effects);
32
+ }
33
+
34
+ .password-strength__step--on {
35
+ background: currentColor;
36
+ }
37
+ </style>
@@ -0,0 +1,128 @@
1
+ <template>
2
+ <GalleryBlock :title="t('gallery.buttons.variants')" :note="t('gallery.buttons.variantsNote')">
3
+ <M3Button v-for="variant in VARIANTS" :key="variant" :variant="variant">{{ variant }}</M3Button>
4
+ </GalleryBlock>
5
+
6
+ <GalleryBlock :title="t('gallery.buttons.sizes')" :note="t('gallery.buttons.sizesNote')" stack>
7
+ <div v-for="size in SIZES" :key="size" class="flex flex-wrap items-center gap-3">
8
+ <M3Button :size="size">
9
+ <template #icon><i-ms-add-rounded /></template>
10
+ {{ size.toUpperCase() }}
11
+ </M3Button>
12
+ <M3Button :size="size" shape="square" variant="tonal">{{ size.toUpperCase() }}</M3Button>
13
+ </div>
14
+ </GalleryBlock>
15
+
16
+ <GalleryBlock :title="t('gallery.buttons.toggle')" :note="t('gallery.buttons.toggleNote')">
17
+ <M3Button v-model:selected="saved" toggle size="m">
18
+ <template #icon="{ selected }">
19
+ <i-ms-bookmark-rounded v-if="selected" />
20
+ <i-ms-bookmark-outline-rounded v-else />
21
+ </template>
22
+ {{ t("gallery.buttons.save") }}
23
+ </M3Button>
24
+ <M3Button v-model:selected="starred" toggle variant="outlined" size="m" shape="square">
25
+ {{ t("gallery.buttons.star") }}
26
+ </M3Button>
27
+ </GalleryBlock>
28
+
29
+ <GalleryBlock :title="t('gallery.buttons.icon')" :note="t('gallery.buttons.iconNote')">
30
+ <M3IconButton
31
+ :label="t('gallery.buttons.favorite')"
32
+ variant="standard"
33
+ toggle
34
+ :selected="liked"
35
+ @update:selected="liked = $event"
36
+ >
37
+ <template #default="{ selected }">
38
+ <i-ms-favorite-rounded v-if="selected" />
39
+ <i-ms-favorite-outline-rounded v-else />
40
+ </template>
41
+ </M3IconButton>
42
+ <M3IconButton :label="t('gallery.buttons.share')" variant="filled" width="wide"
43
+ ><i-ms-share-outline-rounded
44
+ /></M3IconButton>
45
+ <M3IconButton :label="t('gallery.buttons.edit')" variant="tonal" size="m" shape="square"
46
+ ><i-ms-edit-outline-rounded
47
+ /></M3IconButton>
48
+ <M3IconButton :label="t('gallery.buttons.more')" variant="outlined" width="narrow"
49
+ ><i-ms-more-vert-rounded
50
+ /></M3IconButton>
51
+ </GalleryBlock>
52
+
53
+ <GalleryBlock :title="t('gallery.buttons.group')" :note="t('gallery.buttons.groupNote')">
54
+ <M3ButtonGroup size="m" :label="t('gallery.buttons.group')">
55
+ <M3IconButton :label="t('gallery.buttons.bold')" variant="tonal"
56
+ ><i-ms-format-bold-rounded
57
+ /></M3IconButton>
58
+ <M3IconButton :label="t('gallery.buttons.italic')" variant="tonal"
59
+ ><i-ms-format-italic-rounded
60
+ /></M3IconButton>
61
+ <M3IconButton :label="t('gallery.buttons.underline')" variant="tonal"
62
+ ><i-ms-format-underlined-rounded
63
+ /></M3IconButton>
64
+ </M3ButtonGroup>
65
+ </GalleryBlock>
66
+
67
+ <GalleryBlock :title="t('gallery.buttons.connected')" :note="t('gallery.buttons.connectedNote')">
68
+ <M3ButtonGroup variant="connected" size="s" :label="t('gallery.buttons.connected')">
69
+ <M3Button
70
+ v-for="period in PERIODS"
71
+ :key="period"
72
+ variant="tonal"
73
+ toggle
74
+ :selected="range === period"
75
+ @update:selected="range = period"
76
+ >
77
+ {{ t(`gallery.buttons.periods.${period}`) }}
78
+ </M3Button>
79
+ </M3ButtonGroup>
80
+ </GalleryBlock>
81
+
82
+ <GalleryBlock :title="t('gallery.buttons.split')" :note="t('gallery.buttons.splitNote')">
83
+ <M3SplitButton
84
+ v-model:open="splitOpen"
85
+ size="m"
86
+ :menu-label="t('gallery.buttons.moreOptions')"
87
+ @click="sent"
88
+ >
89
+ <template #icon><i-ms-share-outline-rounded /></template>
90
+ {{ t("gallery.buttons.send") }}
91
+ <template #menu>
92
+ <span ref="splitAnchor" class="absolute inset-0 pointer-events-none" />
93
+ </template>
94
+ </M3SplitButton>
95
+ <M3Menu
96
+ v-model:open="splitOpen"
97
+ :anchor="splitAnchor"
98
+ align="end"
99
+ :label="t('gallery.buttons.moreOptions')"
100
+ >
101
+ <M3MenuItem :label="t('gallery.buttons.schedule')" @select="sent" />
102
+ <M3MenuItem :label="t('gallery.buttons.saveDraft')" @select="sent" />
103
+ </M3Menu>
104
+ </GalleryBlock>
105
+ </template>
106
+
107
+ <script setup lang="ts">
108
+ import GalleryBlock from "@/modules/gallery/components/GalleryBlock.vue";
109
+ import type { ButtonSize } from "@cavulsqa/m3e-vue";
110
+
111
+ const { t } = useI18n();
112
+ const snackbar = useSnackbar();
113
+
114
+ const VARIANTS = ["filled", "tonal", "outlined", "elevated", "text"] as const;
115
+ const SIZES: readonly ButtonSize[] = ["xs", "s", "m", "l", "xl"];
116
+ const PERIODS = ["day", "week", "month"] as const;
117
+
118
+ const saved = ref(false);
119
+ const starred = ref(true);
120
+ const liked = ref(false);
121
+ const range = ref<(typeof PERIODS)[number]>("week");
122
+ const splitOpen = ref(false);
123
+ const splitAnchor = useTemplateRef<HTMLElement>("splitAnchor");
124
+
125
+ function sent() {
126
+ void snackbar.show(t("gallery.buttons.sent"));
127
+ }
128
+ </script>
@@ -0,0 +1,236 @@
1
+ <template>
2
+ <GalleryBlock
3
+ :title="t('gallery.carousels.multiBrowse')"
4
+ :note="t('gallery.carousels.multiBrowseNote')"
5
+ stack
6
+ >
7
+ <M3Carousel
8
+ class="-mx-4 w-[calc(100%+2rem)]"
9
+ :items="PLACES"
10
+ :item-key="placeKey"
11
+ :label="t('gallery.carousels.multiBrowse')"
12
+ :slide-label="slideLabel"
13
+ >
14
+ <template #default="{ item, scrollTo }">
15
+ <GalleryCarouselTile
16
+ :title="t(`gallery.carousels.places.${item.id}`)"
17
+ :icon="item.icon"
18
+ :shape="item.shape"
19
+ :from="item.from"
20
+ :to="item.to"
21
+ @select="scrollTo"
22
+ />
23
+ </template>
24
+ </M3Carousel>
25
+ </GalleryBlock>
26
+
27
+ <GalleryBlock :title="t('gallery.carousels.hero')" :note="t('gallery.carousels.heroNote')" stack>
28
+ <M3Carousel
29
+ v-model:item="featured"
30
+ class="-mx-4 w-[calc(100%+2rem)]"
31
+ variant="hero"
32
+ :items="PLACES.slice(0, 6)"
33
+ :item-key="placeKey"
34
+ :label="t('gallery.carousels.hero')"
35
+ :slide-label="slideLabel"
36
+ >
37
+ <template #default="{ item, scrollTo }">
38
+ <GalleryCarouselTile
39
+ :title="t(`gallery.carousels.places.${item.id}`)"
40
+ :icon="item.icon"
41
+ :shape="item.shape"
42
+ :from="item.from"
43
+ :to="item.to"
44
+ @select="scrollTo"
45
+ />
46
+ </template>
47
+ </M3Carousel>
48
+ <span class="type-body-medium text-on-surface-variant">{{
49
+ t("gallery.carousels.featured", {
50
+ place: t(`gallery.carousels.places.${PLACES[featured]!.id}`),
51
+ })
52
+ }}</span>
53
+ </GalleryBlock>
54
+
55
+ <GalleryBlock
56
+ :title="t('gallery.carousels.uncontained')"
57
+ :note="t('gallery.carousels.uncontainedNote')"
58
+ stack
59
+ >
60
+ <M3Carousel
61
+ class="-mx-4 w-[calc(100%+2rem)]"
62
+ variant="uncontained"
63
+ :items="PLACES"
64
+ :item-key="placeKey"
65
+ :item-width="160"
66
+ :height="180"
67
+ :label="t('gallery.carousels.uncontained')"
68
+ :slide-label="slideLabel"
69
+ >
70
+ <template #default="{ item }">
71
+ <GalleryCarouselTile
72
+ :title="t(`gallery.carousels.places.${item.id}`)"
73
+ :icon="item.icon"
74
+ :shape="item.shape"
75
+ :from="item.from"
76
+ :to="item.to"
77
+ />
78
+ </template>
79
+ </M3Carousel>
80
+ </GalleryBlock>
81
+
82
+ <GalleryBlock
83
+ :title="t('gallery.carousels.photos')"
84
+ :note="t('gallery.carousels.photosNote')"
85
+ stack
86
+ >
87
+ <div class="grid grid-cols-3 gap-1 overflow-hidden rounded-lg">
88
+ <button
89
+ v-for="(photo, position) in photos"
90
+ :key="position"
91
+ type="button"
92
+ class="aspect-square w-full overflow-hidden border-0 p-0"
93
+ :aria-label="photo.alt"
94
+ @click="openPhoto(position)"
95
+ >
96
+ <M3Image :src="photo.src" alt="" ratio="1 / 1" class="size-full" />
97
+ </button>
98
+ </div>
99
+ </GalleryBlock>
100
+
101
+ <GalleryBlock :title="t('gallery.carousels.lazy')" :note="t('gallery.carousels.lazyNote')" stack>
102
+ <div class="grid grid-cols-2 gap-3">
103
+ <M3Image
104
+ v-if="photos[0]"
105
+ :src="photos[0].src"
106
+ :alt="photos[0].alt"
107
+ :width="photos[0].width"
108
+ :height="photos[0].height"
109
+ placeholder="var(--md-sys-color-secondary-container)"
110
+ class="rounded-lg"
111
+ />
112
+ <M3Image
113
+ src="/missing-photo.jpg"
114
+ :alt="t('gallery.carousels.missing')"
115
+ ratio="4 / 3"
116
+ class="rounded-lg"
117
+ />
118
+ </div>
119
+ </GalleryBlock>
120
+
121
+ <M3PhotoBrowser
122
+ v-model:open="browserOpen"
123
+ v-model:index="photoIndex"
124
+ :photos="photos"
125
+ :label="t('gallery.carousels.photos')"
126
+ :close-label="t('shell.dismiss')"
127
+ :counter-text="counterText"
128
+ />
129
+ </template>
130
+
131
+ <script setup lang="ts">
132
+ import type { MaterialShapeName } from "@cavulsqa/m3e";
133
+ import { markRaw, type Component } from "vue";
134
+ import BeachIcon from "~icons/material-symbols/beach-access-outline-rounded";
135
+ import CafeIcon from "~icons/material-symbols/local-cafe-outline-rounded";
136
+ import CityIcon from "~icons/material-symbols/location-city-rounded";
137
+ import ForestIcon from "~icons/material-symbols/forest-outline-rounded";
138
+ import HikingIcon from "~icons/material-symbols/hiking-rounded";
139
+ import LandscapeIcon from "~icons/material-symbols/landscape-outline-rounded";
140
+ import ParkIcon from "~icons/material-symbols/park-outline-rounded";
141
+ import SailingIcon from "~icons/material-symbols/sailing-outline-rounded";
142
+ import GalleryBlock from "@/modules/gallery/components/GalleryBlock.vue";
143
+ import GalleryCarouselTile from "@/modules/gallery/components/GalleryCarouselTile.vue";
144
+ import { usePhotoScenes } from "@/modules/gallery/composables/usePhotoScenes";
145
+
146
+ interface Place {
147
+ id: string;
148
+ icon: Component;
149
+ shape: MaterialShapeName;
150
+ from: string;
151
+ to: string;
152
+ }
153
+
154
+ const { t } = useI18n();
155
+ const featured = ref(0);
156
+ const browserOpen = ref(false);
157
+ const photoIndex = ref(0);
158
+ const { photos } = usePhotoScenes((kind) => t(`gallery.carousels.scenes.${kind}`));
159
+ useDarkStatusBar(() => browserOpen.value);
160
+
161
+ function openPhoto(position: number) {
162
+ photoIndex.value = position;
163
+ browserOpen.value = true;
164
+ }
165
+
166
+ function counterText(position: number, count: number) {
167
+ return t("gallery.carousels.counter", { position, count });
168
+ }
169
+
170
+ const PLACES: readonly Place[] = [
171
+ {
172
+ id: "mountains",
173
+ icon: markRaw(LandscapeIcon),
174
+ shape: "cookie9Sided",
175
+ from: "primary-container",
176
+ to: "tertiary-container",
177
+ },
178
+ {
179
+ id: "forest",
180
+ icon: markRaw(ForestIcon),
181
+ shape: "clover4Leaf",
182
+ from: "secondary-container",
183
+ to: "primary-container",
184
+ },
185
+ {
186
+ id: "coast",
187
+ icon: markRaw(BeachIcon),
188
+ shape: "sunny",
189
+ from: "tertiary-container",
190
+ to: "secondary-container",
191
+ },
192
+ {
193
+ id: "city",
194
+ icon: markRaw(CityIcon),
195
+ shape: "pentagon",
196
+ from: "primary-container",
197
+ to: "secondary-container",
198
+ },
199
+ {
200
+ id: "harbour",
201
+ icon: markRaw(SailingIcon),
202
+ shape: "flower",
203
+ from: "tertiary-container",
204
+ to: "primary-container",
205
+ },
206
+ {
207
+ id: "trail",
208
+ icon: markRaw(HikingIcon),
209
+ shape: "softBurst",
210
+ from: "secondary-container",
211
+ to: "tertiary-container",
212
+ },
213
+ {
214
+ id: "park",
215
+ icon: markRaw(ParkIcon),
216
+ shape: "gem",
217
+ from: "primary-container",
218
+ to: "tertiary-container",
219
+ },
220
+ {
221
+ id: "cafe",
222
+ icon: markRaw(CafeIcon),
223
+ shape: "cookie4Sided",
224
+ from: "tertiary-container",
225
+ to: "secondary-container",
226
+ },
227
+ ];
228
+
229
+ function placeKey(place: Place) {
230
+ return place.id;
231
+ }
232
+
233
+ function slideLabel(index: number, count: number) {
234
+ return t("gallery.carousels.slide", { n: index + 1, count });
235
+ }
236
+ </script>
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <GalleryBlock :title="t('gallery.charts.line')" :note="t('gallery.charts.lineNote')" stack>
3
+ <M3LineChart
4
+ :labels="months"
5
+ :series="sales"
6
+ :label="t('gallery.charts.line')"
7
+ :format="compact"
8
+ area
9
+ />
10
+ </GalleryBlock>
11
+
12
+ <GalleryBlock :title="t('gallery.charts.bar')" :note="t('gallery.charts.barNote')" stack>
13
+ <div>
14
+ <M3Chip
15
+ kind="filter"
16
+ :label="t('gallery.charts.stacked')"
17
+ :selected="stacked"
18
+ @update:selected="stacked = $event"
19
+ />
20
+ </div>
21
+ <M3BarChart
22
+ :labels="REPS"
23
+ :series="reps"
24
+ :label="t('gallery.charts.bar')"
25
+ :format="compact"
26
+ :stacked="stacked"
27
+ />
28
+ </GalleryBlock>
29
+
30
+ <GalleryBlock :title="t('gallery.charts.donut')" :note="t('gallery.charts.donutNote')" stack>
31
+ <M3DonutChart
32
+ :segments="channels"
33
+ :label="t('gallery.charts.donut')"
34
+ :total-label="t('gallery.charts.total')"
35
+ :format="compact"
36
+ />
37
+ </GalleryBlock>
38
+ </template>
39
+
40
+ <script setup lang="ts">
41
+ import type { ChartSeries } from "@cavulsqa/m3e-vue";
42
+ import GalleryBlock from "@/modules/gallery/components/GalleryBlock.vue";
43
+
44
+ const REPS = ["Karim", "Samir", "Nabil", "Yacine", "Walid"];
45
+
46
+ const { t, locale } = useI18n();
47
+ const stacked = ref(false);
48
+
49
+ const months = computed(() =>
50
+ Array.from({ length: 12 }, (_, month) =>
51
+ new Date(2026, month, 1).toLocaleDateString(locale.value, { month: "short" }),
52
+ ),
53
+ );
54
+
55
+ const compact = (value: number) =>
56
+ new Intl.NumberFormat(locale.value, { notation: "compact", maximumFractionDigits: 1 }).format(
57
+ value,
58
+ );
59
+
60
+ const sales = computed<ChartSeries[]>(() => [
61
+ {
62
+ label: t("gallery.charts.thisYear"),
63
+ values: [420, 460, 510, 480, 560, 640, 700, 690, 760, 820, null, null].map((v) =>
64
+ v === null ? null : v * 1000,
65
+ ),
66
+ },
67
+ {
68
+ label: t("gallery.charts.lastYear"),
69
+ values: [380, 400, 430, 470, 450, 520, 580, 610, 600, 650, 700, 780].map((v) => v * 1000),
70
+ },
71
+ ]);
72
+
73
+ const reps = computed<ChartSeries[]>(() => [
74
+ { label: t("gallery.charts.august"), values: [180, 140, 210, 95, 160].map((v) => v * 1000) },
75
+ { label: t("gallery.charts.september"), values: [210, 120, 240, 130, 175].map((v) => v * 1000) },
76
+ ]);
77
+
78
+ const channels = computed(() => [
79
+ { label: t("gallery.charts.channels.shops"), value: 1_240_000 },
80
+ { label: t("gallery.charts.channels.wholesale"), value: 860_000 },
81
+ { label: t("gallery.charts.channels.online"), value: 310_000 },
82
+ { label: t("gallery.charts.channels.export"), value: 120_000 },
83
+ ]);
84
+ </script>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <GalleryBlock :title="t('gallery.fabs.sizes')" :note="t('gallery.fabs.sizesNote')">
3
+ <M3Fab v-for="size in SIZES" :key="size" :size="size" :label="t('gallery.fabs.compose')">
4
+ <i-ms-edit-outline-rounded />
5
+ </M3Fab>
6
+ </GalleryBlock>
7
+
8
+ <GalleryBlock :title="t('gallery.fabs.colors')" :note="t('gallery.fabs.colorsNote')">
9
+ <M3Fab v-for="color in COLORS" :key="color" :color="color" size="small" :label="color">
10
+ <i-ms-add-rounded />
11
+ </M3Fab>
12
+ </GalleryBlock>
13
+
14
+ <GalleryBlock :title="t('gallery.fabs.extended')" :note="t('gallery.fabs.extendedNote')">
15
+ <M3Fab
16
+ :extended="extended"
17
+ :label="t('gallery.fabs.compose')"
18
+ size="medium"
19
+ @click="extended = !extended"
20
+ >
21
+ <i-ms-edit-outline-rounded />
22
+ </M3Fab>
23
+ </GalleryBlock>
24
+
25
+ <GalleryBlock :title="t('gallery.fabs.menu')" :note="t('gallery.fabs.menuNote')">
26
+ <div class="flex w-full justify-end pt-64">
27
+ <M3FabMenu
28
+ v-model:open="menuOpen"
29
+ :label="t('gallery.fabs.create')"
30
+ :close-label="t('shell.dismiss')"
31
+ >
32
+ <template #icon><i-ms-add-rounded /></template>
33
+ <M3FabMenuItem :index="2" :label="t('gallery.fabs.order')" @click="done">
34
+ <i-ms-receipt-long-outline-rounded />
35
+ </M3FabMenuItem>
36
+ <M3FabMenuItem :index="1" :label="t('gallery.fabs.customer')" @click="done">
37
+ <i-ms-person-outline-rounded />
38
+ </M3FabMenuItem>
39
+ <M3FabMenuItem :index="0" :label="t('gallery.fabs.note')" @click="done">
40
+ <i-ms-draft-outline-rounded />
41
+ </M3FabMenuItem>
42
+ </M3FabMenu>
43
+ </div>
44
+ </GalleryBlock>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import type { FabColor, FabSize } from "@cavulsqa/m3e-vue";
49
+ import GalleryBlock from "@/modules/gallery/components/GalleryBlock.vue";
50
+
51
+ const { t } = useI18n();
52
+ const snackbar = useSnackbar();
53
+
54
+ const SIZES: readonly FabSize[] = ["small", "default", "medium", "large"];
55
+ const COLORS: readonly FabColor[] = [
56
+ "primary-container",
57
+ "secondary-container",
58
+ "tertiary-container",
59
+ "primary",
60
+ "secondary",
61
+ "tertiary",
62
+ ];
63
+
64
+ const extended = ref(true);
65
+ const menuOpen = ref(false);
66
+
67
+ function done() {
68
+ void snackbar.show(t("gallery.fabs.picked"));
69
+ }
70
+ </script>