@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.
- package/README.md +12 -1
- package/bin/create.mjs +18 -25
- package/lib/templates.mjs +56 -0
- package/package.json +13 -5
- package/templates/f7-app/CLAUDE.md +3 -0
- package/templates/f7-app/auto-imports.d.ts +15 -0
- package/templates/f7-app/package.json +6 -3
- package/templates/f7-app/src/App.vue +1 -0
- package/templates/f7-app/src/env.d.ts +1 -0
- package/templates/f7-app/src/main.ts +3 -0
- package/templates/f7-app/src/modules/demo/router/routes/demo.routes.ts +4 -16
- package/templates/f7-app/src/modules/home/router/routes/home.routes.ts +3 -11
- package/templates/f7-app/src/modules/settings/router/routes/settings.routes.ts +2 -6
- package/templates/f7-app/src/plugins/bootstrapError.ts +2 -0
- package/templates/f7-app/src/plugins/recorder.plugin.ts +155 -0
- package/templates/f7-app/src/router/global/global.routes.ts +2 -6
- package/templates/f7-app/src/shared/composables/useNavigationGuard.ts +48 -0
- package/templates/f7-app/src/shared/recorder/capacitorSink.ts +124 -0
- package/templates/f7-app/src/shared/utils/lazyRoute.ts +25 -0
- package/templates/f7-app/tests/lazyRoute.test.ts +30 -0
- package/templates/f7-app/tests/navigationGuard.test.ts +82 -0
- package/templates/f7-app/vite.config.ts +1 -0
- package/templates/m3e-app/.claude/rules/data-fetching.md +68 -0
- package/templates/m3e-app/.claude/rules/database.md +106 -0
- package/templates/m3e-app/.claude/rules/m3e-ui.md +48 -0
- package/templates/m3e-app/.claude/rules/modules.md +43 -0
- package/templates/m3e-app/.claude/rules/native.md +60 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/SKILL.md +68 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/color.md +44 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/components.md +325 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/layout.md +42 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/motion.md +51 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/shapes-type.md +48 -0
- package/templates/m3e-app/.claude/skills/m3-expressive/sources.md +53 -0
- package/templates/m3e-app/.claude/skills/module-architecture/SKILL.md +65 -0
- package/templates/m3e-app/.claude/skills/module-architecture/file-templates.md +178 -0
- package/templates/m3e-app/.claude/skills/reactive-data/SKILL.md +90 -0
- package/templates/m3e-app/.claude/skills/reactive-data/testing.md +55 -0
- package/templates/m3e-app/.env.example +32 -0
- package/templates/m3e-app/CLAUDE.md +93 -0
- package/templates/m3e-app/auto-imports.d.ts +851 -0
- package/templates/m3e-app/capacitor.config.ts +44 -0
- package/templates/m3e-app/components.d.ts +252 -0
- package/templates/m3e-app/index.html +16 -0
- package/templates/m3e-app/package.json +107 -0
- package/templates/m3e-app/src/App.vue +129 -0
- package/templates/m3e-app/src/app/pragmas.config.ts +35 -0
- package/templates/m3e-app/src/app/scroll.config.ts +20 -0
- package/templates/m3e-app/src/app/storage.config.ts +65 -0
- package/templates/m3e-app/src/app/tabs.ts +33 -0
- package/templates/m3e-app/src/app/theme.config.ts +51 -0
- package/templates/m3e-app/src/assets/css/app.css +18 -0
- package/templates/m3e-app/src/assets/css/base.css +53 -0
- package/templates/m3e-app/src/assets/css/layout/container-transform.css +113 -0
- package/templates/m3e-app/src/assets/css/layout/shell.css +69 -0
- package/templates/m3e-app/src/assets/css/layout/transitions.css +121 -0
- package/templates/m3e-app/src/assets/css/m3e.css +6 -0
- package/templates/m3e-app/src/assets/css/theme/framework7.css +18 -0
- package/templates/m3e-app/src/assets/css/theme/tailwind.css +365 -0
- package/templates/m3e-app/src/domains/benchmark/benchmark.dataset.ts +209 -0
- package/templates/m3e-app/src/domains/benchmark/benchmark.suite.ts +662 -0
- package/templates/m3e-app/src/domains/sales/sales.repository.ts +458 -0
- package/templates/m3e-app/src/env.d.ts +40 -0
- package/templates/m3e-app/src/locales/ar.json +1260 -0
- package/templates/m3e-app/src/locales/en.json +1260 -0
- package/templates/m3e-app/src/locales/fr.json +1260 -0
- package/templates/m3e-app/src/main.ts +41 -0
- package/templates/m3e-app/src/modules/demo/components/DemoBenchmark.vue +119 -0
- package/templates/m3e-app/src/modules/demo/components/DemoBusLog.vue +63 -0
- package/templates/m3e-app/src/modules/demo/components/DemoCreateOrderSheet.vue +203 -0
- package/templates/m3e-app/src/modules/demo/components/DemoMetricsSheet.vue +75 -0
- package/templates/m3e-app/src/modules/demo/components/DemoOrderList.vue +75 -0
- package/templates/m3e-app/src/modules/demo/components/DemoPipelineBenchmark.vue +65 -0
- package/templates/m3e-app/src/modules/demo/components/DemoStatCards.vue +71 -0
- package/templates/m3e-app/src/modules/demo/composables/useBenchmark.ts +139 -0
- package/templates/m3e-app/src/modules/demo/composables/useOrderStatus.ts +64 -0
- package/templates/m3e-app/src/modules/demo/composables/useReactiveDemo.ts +171 -0
- package/templates/m3e-app/src/modules/demo/router/routes/demo.routes.ts +22 -0
- package/templates/m3e-app/src/modules/demo/views/DemoView.vue +210 -0
- package/templates/m3e-app/src/modules/demo/views/OrderDetailView.vue +136 -0
- package/templates/m3e-app/src/modules/demo/views/OrderSearchView.vue +74 -0
- package/templates/m3e-app/src/modules/gallery/components/GalleryBlock.vue +16 -0
- package/templates/m3e-app/src/modules/gallery/components/GalleryCarouselTile.vue +71 -0
- package/templates/m3e-app/src/modules/gallery/components/GalleryCustomerForm.vue +76 -0
- package/templates/m3e-app/src/modules/gallery/components/GalleryProofOfDelivery.vue +69 -0
- package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaDay.vue +106 -0
- package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaMonth.vue +40 -0
- package/templates/m3e-app/src/modules/gallery/components/agenda/AgendaVisitList.vue +45 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatAttachSheet.vue +106 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatContactPicker.vue +85 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatInviteComposer.vue +131 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatPollComposer.vue +148 -0
- package/templates/m3e-app/src/modules/gallery/components/chat/ChatRecentPhotos.vue +62 -0
- package/templates/m3e-app/src/modules/gallery/components/inputs/InputsAccount.vue +79 -0
- package/templates/m3e-app/src/modules/gallery/components/inputs/InputsTags.vue +53 -0
- package/templates/m3e-app/src/modules/gallery/components/inputs/InputsVerification.vue +73 -0
- package/templates/m3e-app/src/modules/gallery/components/inputs/PasswordStrength.vue +37 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryButtons.vue +128 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryCarousels.vue +236 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryCharts.vue +84 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryFabs.vue +70 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryInputs.vue +219 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryNavigation.vue +141 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryOverlays.vue +368 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryPickers.vue +175 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryProgress.vue +118 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryScale.vue +103 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GallerySelection.vue +219 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryShapes.vue +48 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GallerySurfaces.vue +213 -0
- package/templates/m3e-app/src/modules/gallery/components/sections/GalleryTables.vue +185 -0
- package/templates/m3e-app/src/modules/gallery/components/surfaces/SurfacesContainerTransform.vue +46 -0
- package/templates/m3e-app/src/modules/gallery/composables/chatTeam.ts +18 -0
- package/templates/m3e-app/src/modules/gallery/composables/composeKind.ts +2 -0
- package/templates/m3e-app/src/modules/gallery/composables/currentPosition.ts +35 -0
- package/templates/m3e-app/src/modules/gallery/composables/demoOrders.ts +29 -0
- package/templates/m3e-app/src/modules/gallery/composables/featuredStories.ts +73 -0
- package/templates/m3e-app/src/modules/gallery/composables/fieldFormats.ts +36 -0
- package/templates/m3e-app/src/modules/gallery/composables/openExternal.ts +11 -0
- package/templates/m3e-app/src/modules/gallery/composables/routePlan.ts +90 -0
- package/templates/m3e-app/src/modules/gallery/composables/useAgenda.ts +61 -0
- package/templates/m3e-app/src/modules/gallery/composables/useChatCustomers.ts +31 -0
- package/templates/m3e-app/src/modules/gallery/composables/useChatDemo.ts +299 -0
- package/templates/m3e-app/src/modules/gallery/composables/useGallerySections.ts +230 -0
- package/templates/m3e-app/src/modules/gallery/composables/useLocalAttachments.ts +71 -0
- package/templates/m3e-app/src/modules/gallery/composables/useLocationShare.ts +38 -0
- package/templates/m3e-app/src/modules/gallery/composables/usePhotoScenes.ts +82 -0
- package/templates/m3e-app/src/modules/gallery/composables/useTreeDemo.ts +67 -0
- package/templates/m3e-app/src/modules/gallery/composables/wilayas.ts +67 -0
- package/templates/m3e-app/src/modules/gallery/router/routes/gallery.routes.ts +52 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryAgendaView.vue +117 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryChatView.vue +292 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryContactsView.vue +77 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryFeaturedView.vue +60 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryLoginView.vue +135 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryOnboardingView.vue +116 -0
- package/templates/m3e-app/src/modules/gallery/views/GallerySectionView.vue +27 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryTabsView.vue +135 -0
- package/templates/m3e-app/src/modules/gallery/views/GalleryView.vue +50 -0
- package/templates/m3e-app/src/modules/home/components/HomeHero.vue +57 -0
- package/templates/m3e-app/src/modules/home/composables/useHomeFeatures.ts +128 -0
- package/templates/m3e-app/src/modules/home/composables/useOpenFeature.ts +22 -0
- package/templates/m3e-app/src/modules/home/router/routes/home.routes.ts +21 -0
- package/templates/m3e-app/src/modules/home/views/FeatureDetailView.vue +66 -0
- package/templates/m3e-app/src/modules/home/views/HomeView.vue +47 -0
- package/templates/m3e-app/src/modules/settings/components/RolePalette.vue +38 -0
- package/templates/m3e-app/src/modules/settings/components/SeedSwatches.vue +41 -0
- package/templates/m3e-app/src/modules/settings/components/SettingsChoice.vue +43 -0
- package/templates/m3e-app/src/modules/settings/components/StudioPreview.vue +52 -0
- package/templates/m3e-app/src/modules/settings/router/routes/settings.routes.ts +17 -0
- package/templates/m3e-app/src/modules/settings/types.ts +7 -0
- package/templates/m3e-app/src/modules/settings/views/ColorStudioView.vue +166 -0
- package/templates/m3e-app/src/modules/settings/views/SettingsView.vue +157 -0
- package/templates/m3e-app/src/plugins/bootstrapError.ts +62 -0
- package/templates/m3e-app/src/plugins/capacitor/index.ts +14 -0
- package/templates/m3e-app/src/plugins/capacitor/useAndroidBackButton.ts +36 -0
- package/templates/m3e-app/src/plugins/capacitor/useKeyboard.ts +48 -0
- package/templates/m3e-app/src/plugins/capacitor/useSplashScreen.ts +43 -0
- package/templates/m3e-app/src/plugins/capacitor/useStatusBar.ts +16 -0
- package/templates/m3e-app/src/plugins/framework7.plugin.ts +36 -0
- package/templates/m3e-app/src/plugins/i18n.plugin.ts +87 -0
- package/templates/m3e-app/src/plugins/m3e.plugin.ts +22 -0
- package/templates/m3e-app/src/plugins/seed.plugin.ts +7 -0
- package/templates/m3e-app/src/plugins/sqlite.plugin.ts +32 -0
- package/templates/m3e-app/src/router/global/global.routes.ts +12 -0
- package/templates/m3e-app/src/router/index.ts +20 -0
- package/templates/m3e-app/src/shared/components/error/404.vue +13 -0
- package/templates/m3e-app/src/shared/components/layout/EmptyState.vue +30 -0
- package/templates/m3e-app/src/shared/components/layout/SectionHeader.vue +10 -0
- package/templates/m3e-app/src/shared/components/page/AppPage.vue +104 -0
- package/templates/m3e-app/src/shared/composables/navigation/useActiveTab.ts +39 -0
- package/templates/m3e-app/src/shared/composables/navigation/useContainerTransform.ts +117 -0
- package/templates/m3e-app/src/shared/composables/navigation/useNavigationGuard.ts +48 -0
- package/templates/m3e-app/src/shared/composables/navigation/useNavigationVisibility.ts +64 -0
- package/templates/m3e-app/src/shared/composables/navigation/useViewRouter.ts +25 -0
- package/templates/m3e-app/src/shared/composables/navigation/useWindowClass.ts +22 -0
- package/templates/m3e-app/src/shared/composables/theme/useThemeSettings.ts +155 -0
- package/templates/m3e-app/src/shared/database/candidates/capacitorSqlite.ts +36 -0
- package/templates/m3e-app/src/shared/database/candidates/index.ts +4 -0
- package/templates/m3e-app/src/shared/database/candidates/opfsSahPool.ts +25 -0
- package/templates/m3e-app/src/shared/database/candidates/types.ts +56 -0
- package/templates/m3e-app/src/shared/database/candidates/waSqlite.ts +56 -0
- package/templates/m3e-app/src/shared/database/database.ts +219 -0
- package/templates/m3e-app/src/shared/database/index.ts +3 -0
- package/templates/m3e-app/src/shared/database/migrations.ts +223 -0
- package/templates/m3e-app/src/shared/database/opfs.worker.ts +5 -0
- package/templates/m3e-app/src/shared/database/queries.ts +26 -0
- package/templates/m3e-app/src/shared/database/schema.ts +153 -0
- package/templates/m3e-app/src/shared/database/storage.ts +64 -0
- package/templates/m3e-app/src/shared/database/wa.worker.ts +5 -0
- package/templates/m3e-app/src/shared/utils/lazyRoute.ts +41 -0
- package/templates/m3e-app/src/shared/utils/resolvers/resolvers.ts +42 -0
- package/templates/m3e-app/src/shared/utils/textDirection.ts +22 -0
- package/templates/m3e-app/src/shared/utils/theme/themeSettings.ts +59 -0
- package/templates/m3e-app/src/shared/utils/tone.ts +8 -0
- package/templates/m3e-app/tests/benchmark.suite.test.ts +104 -0
- package/templates/m3e-app/tests/containerTransform.test.ts +58 -0
- package/templates/m3e-app/tests/fieldFormats.test.ts +25 -0
- package/templates/m3e-app/tests/lazyRoute.test.ts +30 -0
- package/templates/m3e-app/tests/locales.test.ts +73 -0
- package/templates/m3e-app/tests/migrations.test.ts +74 -0
- package/templates/m3e-app/tests/navigationGuard.test.ts +82 -0
- package/templates/m3e-app/tests/openDatabase.test.ts +54 -0
- package/templates/m3e-app/tests/sales.repository.test.ts +329 -0
- package/templates/m3e-app/tests/storage.test.ts +117 -0
- package/templates/m3e-app/tests/textDirection.test.ts +11 -0
- package/templates/m3e-app/tooling/linkedPackages.ts +112 -0
- package/templates/m3e-app/tsconfig.json +21 -0
- package/templates/m3e-app/vite.config.ts +144 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AppPage :title="t('gallery.contacts.title')" back variant="small">
|
|
3
|
+
<div class="pe-6">
|
|
4
|
+
<M3ListGroup v-for="group in groups" :key="group.key" :title="group.key">
|
|
5
|
+
<M3ListItem
|
|
6
|
+
v-for="customer in group.customers"
|
|
7
|
+
:key="customer.name"
|
|
8
|
+
clickable
|
|
9
|
+
:headline="customer.name"
|
|
10
|
+
:supporting="customer.city"
|
|
11
|
+
>
|
|
12
|
+
<template #leading>
|
|
13
|
+
<span
|
|
14
|
+
class="type-title-medium grid size-10 place-items-center rounded-full bg-secondary-container text-on-secondary-container"
|
|
15
|
+
>{{ customer.name.charAt(0) }}</span
|
|
16
|
+
>
|
|
17
|
+
</template>
|
|
18
|
+
</M3ListItem>
|
|
19
|
+
</M3ListGroup>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<template #fixed>
|
|
23
|
+
<M3ListIndex :keys="keys" :label="t('gallery.contacts.jump')" />
|
|
24
|
+
</template>
|
|
25
|
+
</AppPage>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
import { groupKey } from "@cavulsqa/m3e-vue";
|
|
30
|
+
import { WILAYAS } from "@/modules/gallery/composables/wilayas";
|
|
31
|
+
|
|
32
|
+
const NAMES = [
|
|
33
|
+
"Amine",
|
|
34
|
+
"Bachir",
|
|
35
|
+
"Chérif",
|
|
36
|
+
"Djamel",
|
|
37
|
+
"Elias",
|
|
38
|
+
"Farid",
|
|
39
|
+
"Ghani",
|
|
40
|
+
"Hakim",
|
|
41
|
+
"Idir",
|
|
42
|
+
"Jamel",
|
|
43
|
+
"Karim",
|
|
44
|
+
"Lotfi",
|
|
45
|
+
"Mourad",
|
|
46
|
+
"Nabil",
|
|
47
|
+
"Omar",
|
|
48
|
+
"Rachid",
|
|
49
|
+
"Samir",
|
|
50
|
+
"Tarek",
|
|
51
|
+
"Walid",
|
|
52
|
+
"Yacine",
|
|
53
|
+
"Zinedine",
|
|
54
|
+
];
|
|
55
|
+
const TRADES = ["Alimentation", "Supérette"];
|
|
56
|
+
|
|
57
|
+
const { t } = useI18n();
|
|
58
|
+
|
|
59
|
+
const customers = NAMES.flatMap((name, index) =>
|
|
60
|
+
TRADES.map((trade, offset) => ({
|
|
61
|
+
name: `${name} ${trade}`,
|
|
62
|
+
city: WILAYAS[(index * 7 + offset * 13) % WILAYAS.length]!,
|
|
63
|
+
})),
|
|
64
|
+
).sort((a, b) => a.name.localeCompare(b.name, "fr"));
|
|
65
|
+
|
|
66
|
+
const groups = [
|
|
67
|
+
...customers
|
|
68
|
+
.reduce((byKey, customer) => {
|
|
69
|
+
const key = groupKey(customer.name);
|
|
70
|
+
byKey.set(key, [...(byKey.get(key) ?? []), customer]);
|
|
71
|
+
return byKey;
|
|
72
|
+
}, new Map<string, typeof customers>())
|
|
73
|
+
.entries(),
|
|
74
|
+
].map(([key, members]) => ({ key, customers: members }));
|
|
75
|
+
|
|
76
|
+
const keys = groups.map((group) => group.key);
|
|
77
|
+
</script>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AppPage
|
|
3
|
+
:title="story ? t(`gallery.featured.${story.id}.title`) : t('errors.notFoundTitle')"
|
|
4
|
+
variant="small"
|
|
5
|
+
back
|
|
6
|
+
>
|
|
7
|
+
<template v-if="story">
|
|
8
|
+
<section
|
|
9
|
+
class="mx-4 flex flex-col gap-4 rounded-xl p-6"
|
|
10
|
+
:class="TONE_CLASSES[story.tone]"
|
|
11
|
+
:aria-label="t(`gallery.featured.${story.id}.title`)"
|
|
12
|
+
>
|
|
13
|
+
<M3Shape :shape="story.shape" class="size-20 bg-surface/60">
|
|
14
|
+
<component :is="story.icon" class="size-10" />
|
|
15
|
+
</M3Shape>
|
|
16
|
+
<h2 class="type-headline-small m-0">{{ t(`gallery.featured.${story.id}.title`) }}</h2>
|
|
17
|
+
<p class="type-body-large m-0 opacity-80">{{ t(`gallery.featured.${story.id}.text`) }}</p>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<p class="type-body-large m-0 px-6 pt-6 text-on-surface">
|
|
21
|
+
{{ t(`gallery.featured.${story.id}.body`) }}
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<SectionHeader :title="t('gallery.featured.glance')" />
|
|
25
|
+
<M3List variant="segmented" inset :label="t('gallery.featured.glance')">
|
|
26
|
+
<M3ListItem
|
|
27
|
+
v-for="fact in story.facts"
|
|
28
|
+
:key="fact.id"
|
|
29
|
+
:headline="t(`gallery.featured.${story.id}.facts.${fact.id}`)"
|
|
30
|
+
:trailing-text="number.format(fact.value)"
|
|
31
|
+
>
|
|
32
|
+
<template #leading><component :is="fact.icon" /></template>
|
|
33
|
+
</M3ListItem>
|
|
34
|
+
</M3List>
|
|
35
|
+
|
|
36
|
+
<p class="type-body-small m-0 px-8 pt-4 text-on-surface-variant">
|
|
37
|
+
{{ t("gallery.featured.note") }}
|
|
38
|
+
</p>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<EmptyState
|
|
42
|
+
v-else
|
|
43
|
+
shape="ghostish"
|
|
44
|
+
:headline="t('errors.notFoundTitle')"
|
|
45
|
+
:text="t('errors.notFound')"
|
|
46
|
+
/>
|
|
47
|
+
</AppPage>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<script setup lang="ts">
|
|
51
|
+
import type { Router } from "framework7/types";
|
|
52
|
+
import { findStory } from "@/modules/gallery/composables/featuredStories";
|
|
53
|
+
import { TONE_CLASSES } from "@/shared/utils/tone";
|
|
54
|
+
|
|
55
|
+
const props = defineProps<{ f7route: Router.Route; f7router: Router.Router }>();
|
|
56
|
+
const { t, locale } = useI18n();
|
|
57
|
+
|
|
58
|
+
const story = computed(() => findStory(String(props.f7route.params.id ?? "")));
|
|
59
|
+
const number = computed(() => new Intl.NumberFormat(locale.value));
|
|
60
|
+
</script>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AppPage :title="t('gallery.login.title')" back variant="small">
|
|
3
|
+
<form
|
|
4
|
+
class="mx-auto flex w-full max-w-md flex-col gap-5 px-6 pt-6"
|
|
5
|
+
novalidate
|
|
6
|
+
@submit.prevent="signIn"
|
|
7
|
+
>
|
|
8
|
+
<div class="flex flex-col items-center gap-4 pb-2 text-center">
|
|
9
|
+
<M3Shape
|
|
10
|
+
shape="cookie12Sided"
|
|
11
|
+
class="size-24 bg-primary-container text-on-primary-container"
|
|
12
|
+
>
|
|
13
|
+
<i-ms-storefront-outline-rounded class="size-10" />
|
|
14
|
+
</M3Shape>
|
|
15
|
+
<h2 class="type-headline-small-emphasized m-0 text-on-surface">
|
|
16
|
+
{{ t("gallery.login.heading") }}
|
|
17
|
+
</h2>
|
|
18
|
+
<p class="type-body-medium m-0 text-on-surface-variant">
|
|
19
|
+
{{ t("gallery.login.subtitle") }}
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<M3TextField
|
|
24
|
+
v-model="account.email"
|
|
25
|
+
:label="t('gallery.login.email')"
|
|
26
|
+
:error="emailError"
|
|
27
|
+
:supporting="restored ? t('gallery.login.restored') : undefined"
|
|
28
|
+
type="email"
|
|
29
|
+
inputmode="email"
|
|
30
|
+
autocomplete="username"
|
|
31
|
+
autocapitalize="off"
|
|
32
|
+
spellcheck="false"
|
|
33
|
+
enterkeyhint="next"
|
|
34
|
+
:disabled="busy"
|
|
35
|
+
@blur="touched.email = true"
|
|
36
|
+
>
|
|
37
|
+
<template #leading><i-ms-mail-outline-rounded /></template>
|
|
38
|
+
</M3TextField>
|
|
39
|
+
|
|
40
|
+
<M3TextField
|
|
41
|
+
v-model="password"
|
|
42
|
+
:label="t('gallery.login.password')"
|
|
43
|
+
:error="passwordError"
|
|
44
|
+
:type="reveal ? 'text' : 'password'"
|
|
45
|
+
autocomplete="current-password"
|
|
46
|
+
enterkeyhint="go"
|
|
47
|
+
:disabled="busy"
|
|
48
|
+
@blur="touched.password = true"
|
|
49
|
+
>
|
|
50
|
+
<template #leading><i-ms-lock-outline-rounded /></template>
|
|
51
|
+
<template #trailing>
|
|
52
|
+
<M3IconButton
|
|
53
|
+
toggle
|
|
54
|
+
:selected="reveal"
|
|
55
|
+
:label="reveal ? t('gallery.login.hide') : t('gallery.login.show')"
|
|
56
|
+
@update:selected="reveal = $event"
|
|
57
|
+
>
|
|
58
|
+
<i-ms-visibility-off-outline-rounded v-if="reveal" />
|
|
59
|
+
<i-ms-visibility-outline-rounded v-else />
|
|
60
|
+
</M3IconButton>
|
|
61
|
+
</template>
|
|
62
|
+
</M3TextField>
|
|
63
|
+
|
|
64
|
+
<div class="flex justify-end">
|
|
65
|
+
<M3Button variant="text" type="button" @click="forgot">{{
|
|
66
|
+
t("gallery.login.forgot")
|
|
67
|
+
}}</M3Button>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<M3Button type="submit" size="m" :disabled="busy">
|
|
71
|
+
<template v-if="busy" #leading
|
|
72
|
+
><M3LoadingIndicator :size="24" :label="t('gallery.login.signingIn')"
|
|
73
|
+
/></template>
|
|
74
|
+
{{ busy ? t("gallery.login.signingIn") : t("gallery.login.signIn") }}
|
|
75
|
+
</M3Button>
|
|
76
|
+
|
|
77
|
+
<p class="type-body-small m-0 text-center text-on-surface-variant">
|
|
78
|
+
{{ t("gallery.login.hint") }}
|
|
79
|
+
</p>
|
|
80
|
+
</form>
|
|
81
|
+
</AppPage>
|
|
82
|
+
</template>
|
|
83
|
+
|
|
84
|
+
<script setup lang="ts">
|
|
85
|
+
import type { Router } from "framework7/types";
|
|
86
|
+
import { useFormDraft } from "@cavulsqa/m3e-vue";
|
|
87
|
+
|
|
88
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
89
|
+
|
|
90
|
+
const props = defineProps<{ f7router: Router.Router }>();
|
|
91
|
+
const { t } = useI18n();
|
|
92
|
+
const snackbar = useSnackbar();
|
|
93
|
+
|
|
94
|
+
const account = reactive({ email: "" });
|
|
95
|
+
const password = ref("");
|
|
96
|
+
const reveal = ref(false);
|
|
97
|
+
const busy = ref(false);
|
|
98
|
+
const rejected = ref(false);
|
|
99
|
+
const touched = reactive({ email: false, password: false });
|
|
100
|
+
const { restored, clear } = useFormDraft("gallery.login", account);
|
|
101
|
+
|
|
102
|
+
const emailError = computed(() =>
|
|
103
|
+
touched.email && !EMAIL.test(account.email.trim()) ? t("gallery.login.emailInvalid") : undefined,
|
|
104
|
+
);
|
|
105
|
+
const passwordError = computed(() => {
|
|
106
|
+
if (rejected.value) return t("gallery.login.rejected");
|
|
107
|
+
return touched.password && !password.value ? t("gallery.login.passwordMissing") : undefined;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
watch(password, () => (rejected.value = false));
|
|
111
|
+
|
|
112
|
+
async function signIn() {
|
|
113
|
+
touched.email = true;
|
|
114
|
+
touched.password = true;
|
|
115
|
+
if (emailError.value || !password.value || busy.value) return;
|
|
116
|
+
busy.value = true;
|
|
117
|
+
try {
|
|
118
|
+
await new Promise((resolve) => setTimeout(resolve, 900));
|
|
119
|
+
if (password.value !== "demo") {
|
|
120
|
+
rejected.value = true;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
await clear();
|
|
124
|
+
password.value = "";
|
|
125
|
+
void snackbar.show(t("gallery.login.welcome", { email: account.email.trim() }));
|
|
126
|
+
props.f7router.back();
|
|
127
|
+
} finally {
|
|
128
|
+
busy.value = false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function forgot() {
|
|
133
|
+
void snackbar.show(t("gallery.login.forgotSent"));
|
|
134
|
+
}
|
|
135
|
+
</script>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AppPage :title="t('gallery.onboarding.title')" back variant="small" class="gallery-onboarding">
|
|
3
|
+
<M3Pager
|
|
4
|
+
v-model:page="page"
|
|
5
|
+
:label="t('gallery.onboarding.title')"
|
|
6
|
+
class="gallery-onboarding__pager"
|
|
7
|
+
>
|
|
8
|
+
<M3PagerPage
|
|
9
|
+
v-for="slide in SLIDES"
|
|
10
|
+
:key="slide.id"
|
|
11
|
+
:label="t(`gallery.onboarding.pages.${slide.id}.title`)"
|
|
12
|
+
>
|
|
13
|
+
<div class="flex h-full flex-col items-center justify-center gap-6 px-8 text-center">
|
|
14
|
+
<M3Shape :shape="slide.shape" class="size-48" :class="slide.tone">
|
|
15
|
+
<component :is="slide.icon" class="size-20" />
|
|
16
|
+
</M3Shape>
|
|
17
|
+
<h2 class="type-headline-medium-emphasized m-0 text-on-surface">
|
|
18
|
+
{{ t(`gallery.onboarding.pages.${slide.id}.title`) }}
|
|
19
|
+
</h2>
|
|
20
|
+
<p class="type-body-large m-0 max-w-80 text-on-surface-variant">
|
|
21
|
+
{{ t(`gallery.onboarding.pages.${slide.id}.text`) }}
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
</M3PagerPage>
|
|
25
|
+
|
|
26
|
+
<template #footer="{ page: current, count, progress, go, next }">
|
|
27
|
+
<div class="flex items-center justify-between gap-2 px-4 pb-6 pt-2">
|
|
28
|
+
<M3Button
|
|
29
|
+
variant="text"
|
|
30
|
+
:class="{ invisible: current === count - 1 }"
|
|
31
|
+
@click="go(count - 1)"
|
|
32
|
+
>
|
|
33
|
+
{{ t("gallery.onboarding.skip") }}
|
|
34
|
+
</M3Button>
|
|
35
|
+
<M3PageIndicator
|
|
36
|
+
:count="count"
|
|
37
|
+
:progress="progress"
|
|
38
|
+
:label="t('gallery.onboarding.title')"
|
|
39
|
+
:page-label="pageLabel"
|
|
40
|
+
@select="go"
|
|
41
|
+
/>
|
|
42
|
+
<M3Button v-if="current < count - 1" @click="next">
|
|
43
|
+
{{ t("gallery.onboarding.next") }}
|
|
44
|
+
</M3Button>
|
|
45
|
+
<M3Button v-else @click="finish">{{ t("gallery.onboarding.start") }}</M3Button>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
</M3Pager>
|
|
49
|
+
</AppPage>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<script setup lang="ts">
|
|
53
|
+
import type { MaterialShapeName } from "@cavulsqa/m3e";
|
|
54
|
+
import type { Router } from "framework7/types";
|
|
55
|
+
import { markRaw, type Component } from "vue";
|
|
56
|
+
import OfflineIcon from "~icons/material-symbols/cloud-off-outline-rounded";
|
|
57
|
+
import RouteIcon from "~icons/material-symbols/route-outline-rounded";
|
|
58
|
+
import ReceiptIcon from "~icons/material-symbols/receipt-long-outline-rounded";
|
|
59
|
+
|
|
60
|
+
interface Slide {
|
|
61
|
+
id: string;
|
|
62
|
+
shape: MaterialShapeName;
|
|
63
|
+
tone: string;
|
|
64
|
+
icon: Component;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const SLIDES: readonly Slide[] = [
|
|
68
|
+
{
|
|
69
|
+
id: "route",
|
|
70
|
+
shape: "cookie9Sided",
|
|
71
|
+
tone: "bg-primary-container text-on-primary-container",
|
|
72
|
+
icon: markRaw(RouteIcon),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "orders",
|
|
76
|
+
shape: "sunny",
|
|
77
|
+
tone: "bg-tertiary-container text-on-tertiary-container",
|
|
78
|
+
icon: markRaw(ReceiptIcon),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "offline",
|
|
82
|
+
shape: "clover4Leaf",
|
|
83
|
+
tone: "bg-secondary-container text-on-secondary-container",
|
|
84
|
+
icon: markRaw(OfflineIcon),
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const props = defineProps<{ f7router: Router.Router }>();
|
|
89
|
+
const { t } = useI18n();
|
|
90
|
+
const page = ref(0);
|
|
91
|
+
|
|
92
|
+
function pageLabel(n: number) {
|
|
93
|
+
return t("gallery.onboarding.page", { n });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function finish() {
|
|
97
|
+
props.f7router.back();
|
|
98
|
+
}
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<style scoped>
|
|
102
|
+
:global(.gallery-onboarding .page-content) {
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.gallery-onboarding__pager {
|
|
109
|
+
flex: 1;
|
|
110
|
+
min-height: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.gallery-onboarding__pager :deep(.m3-pager__track) {
|
|
114
|
+
flex: 1;
|
|
115
|
+
}
|
|
116
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AppPage :title="entry ? t(entry.titleKey) : t('errors.notFoundTitle')" variant="medium" back>
|
|
3
|
+
<Suspense v-if="entry">
|
|
4
|
+
<component :is="entry.component" />
|
|
5
|
+
<template #fallback>
|
|
6
|
+
<div class="grid place-items-center py-16">
|
|
7
|
+
<M3LoadingIndicator :label="t('gallery.loading')" />
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
</Suspense>
|
|
11
|
+
<EmptyState
|
|
12
|
+
v-else
|
|
13
|
+
shape="ghostish"
|
|
14
|
+
:headline="t('errors.notFoundTitle')"
|
|
15
|
+
:text="t('errors.notFound')"
|
|
16
|
+
/>
|
|
17
|
+
</AppPage>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
import type { Router } from "framework7/types";
|
|
22
|
+
import { findSection } from "@/modules/gallery/composables/useGallerySections";
|
|
23
|
+
|
|
24
|
+
const props = defineProps<{ f7route: Router.Route }>();
|
|
25
|
+
const { t } = useI18n();
|
|
26
|
+
const entry = computed(() => findSection(String(props.f7route.params.section ?? "")));
|
|
27
|
+
</script>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AppPage :title="t('gallery.tabs.title')" back variant="small">
|
|
3
|
+
<template #bottom>
|
|
4
|
+
<M3Tabs v-model="tab" :pager="pager" :label="t('gallery.tabs.title')">
|
|
5
|
+
<M3Tab v-for="item in TABS" :key="item" :value="item" :label="t(`gallery.tabs.${item}`)" />
|
|
6
|
+
</M3Tabs>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<M3TabPanels v-model="tab" :pager="pager" class="gallery-tabs__panels">
|
|
10
|
+
<M3TabPanel value="orders" :label="t('gallery.tabs.orders')">
|
|
11
|
+
<p class="type-body-medium m-0 px-6 py-3 text-on-surface-variant">
|
|
12
|
+
{{ t("gallery.tabs.ordersNote") }}
|
|
13
|
+
</p>
|
|
14
|
+
<M3List variant="segmented" inset>
|
|
15
|
+
<M3ListItem
|
|
16
|
+
v-for="row in 18"
|
|
17
|
+
:key="row"
|
|
18
|
+
:headline="t('gallery.tabs.order', { n: 1000 + row })"
|
|
19
|
+
:supporting="t('gallery.tabs.orderLines', { count: (row % 5) + 1 }, (row % 5) + 1)"
|
|
20
|
+
>
|
|
21
|
+
<template #leading><i-ms-receipt-long-outline-rounded /></template>
|
|
22
|
+
<template #swipe-end>
|
|
23
|
+
<M3SwipeAction :label="t('gallery.tabs.archive')" tone="secondary">
|
|
24
|
+
<i-ms-archive-outline-rounded />
|
|
25
|
+
</M3SwipeAction>
|
|
26
|
+
</template>
|
|
27
|
+
</M3ListItem>
|
|
28
|
+
</M3List>
|
|
29
|
+
</M3TabPanel>
|
|
30
|
+
|
|
31
|
+
<M3TabPanel value="invoices" :label="t('gallery.tabs.invoices')">
|
|
32
|
+
<M3List variant="segmented" inset class="py-3">
|
|
33
|
+
<M3ListItem
|
|
34
|
+
v-for="row in 24"
|
|
35
|
+
:key="row"
|
|
36
|
+
:headline="t('gallery.tabs.invoice', { n: 2000 + row })"
|
|
37
|
+
:trailing-text="t('gallery.tabs.amount', { value: (row * 3150).toLocaleString() })"
|
|
38
|
+
>
|
|
39
|
+
<template #leading><i-ms-request-quote-outline-rounded /></template>
|
|
40
|
+
</M3ListItem>
|
|
41
|
+
</M3List>
|
|
42
|
+
</M3TabPanel>
|
|
43
|
+
|
|
44
|
+
<M3TabPanel value="payments" :label="t('gallery.tabs.payments')">
|
|
45
|
+
<p class="type-body-medium m-0 px-6 py-3 text-on-surface-variant">
|
|
46
|
+
{{ t("gallery.tabs.paymentsNote") }}
|
|
47
|
+
</p>
|
|
48
|
+
<M3Carousel
|
|
49
|
+
:items="METHODS"
|
|
50
|
+
:item-key="methodKey"
|
|
51
|
+
:label="t('gallery.tabs.payments')"
|
|
52
|
+
:height="160"
|
|
53
|
+
>
|
|
54
|
+
<template #default="{ item }">
|
|
55
|
+
<div
|
|
56
|
+
class="type-title-medium-emphasized grid size-full place-items-center bg-secondary-container text-on-secondary-container"
|
|
57
|
+
>
|
|
58
|
+
{{ t(`gallery.tabs.methods.${item}`) }}
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
</M3Carousel>
|
|
62
|
+
</M3TabPanel>
|
|
63
|
+
|
|
64
|
+
<M3TabPanel value="returns" :label="t('gallery.tabs.returns')">
|
|
65
|
+
<EmptyState
|
|
66
|
+
shape="ghostish"
|
|
67
|
+
:headline="t('gallery.tabs.noReturns')"
|
|
68
|
+
:text="t('gallery.tabs.noReturnsText')"
|
|
69
|
+
/>
|
|
70
|
+
</M3TabPanel>
|
|
71
|
+
</M3TabPanels>
|
|
72
|
+
|
|
73
|
+
<template #fab>
|
|
74
|
+
<M3FabMorph
|
|
75
|
+
v-model:open="actionsOpen"
|
|
76
|
+
variant="toolbar"
|
|
77
|
+
:label="t('gallery.tabs.quickActions')"
|
|
78
|
+
>
|
|
79
|
+
<template #icon><i-ms-bolt-outline-rounded /></template>
|
|
80
|
+
<template #default="{ close }">
|
|
81
|
+
<M3IconButton
|
|
82
|
+
v-for="action in ACTIONS"
|
|
83
|
+
:key="action.id"
|
|
84
|
+
:label="t(`gallery.tabs.actions.${action.id}`)"
|
|
85
|
+
@click="run(action.id, close)"
|
|
86
|
+
>
|
|
87
|
+
<component :is="action.icon" />
|
|
88
|
+
</M3IconButton>
|
|
89
|
+
</template>
|
|
90
|
+
</M3FabMorph>
|
|
91
|
+
</template>
|
|
92
|
+
</AppPage>
|
|
93
|
+
</template>
|
|
94
|
+
|
|
95
|
+
<script setup lang="ts">
|
|
96
|
+
import { createTabPager } from "@cavulsqa/m3e-vue";
|
|
97
|
+
import { markRaw } from "vue";
|
|
98
|
+
import AddIcon from "~icons/material-symbols/add-shopping-cart-rounded";
|
|
99
|
+
import CallIcon from "~icons/material-symbols/call-outline-rounded";
|
|
100
|
+
import CloseIcon from "~icons/material-symbols/close-rounded";
|
|
101
|
+
import MapIcon from "~icons/material-symbols/map-outline-rounded";
|
|
102
|
+
import ScanIcon from "~icons/material-symbols/barcode-scanner-rounded";
|
|
103
|
+
|
|
104
|
+
const TABS = ["orders", "invoices", "payments", "returns"] as const;
|
|
105
|
+
const METHODS = ["cash", "cheque", "transfer", "credit"] as const;
|
|
106
|
+
|
|
107
|
+
const { t } = useI18n();
|
|
108
|
+
const tab = ref<string>("orders");
|
|
109
|
+
const pager = createTabPager();
|
|
110
|
+
const snackbar = useSnackbar();
|
|
111
|
+
const actionsOpen = ref(false);
|
|
112
|
+
|
|
113
|
+
const ACTIONS = [
|
|
114
|
+
{ id: "order", icon: markRaw(AddIcon) },
|
|
115
|
+
{ id: "scan", icon: markRaw(ScanIcon) },
|
|
116
|
+
{ id: "call", icon: markRaw(CallIcon) },
|
|
117
|
+
{ id: "map", icon: markRaw(MapIcon) },
|
|
118
|
+
{ id: "close", icon: markRaw(CloseIcon) },
|
|
119
|
+
] as const;
|
|
120
|
+
|
|
121
|
+
function run(id: (typeof ACTIONS)[number]["id"], close: () => void) {
|
|
122
|
+
close();
|
|
123
|
+
if (id !== "close") void snackbar.show(t(`gallery.tabs.actions.${id}`));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function methodKey(method: (typeof METHODS)[number]) {
|
|
127
|
+
return method;
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<style scoped>
|
|
132
|
+
.gallery-tabs__panels {
|
|
133
|
+
height: calc(100dvh - env(safe-area-inset-top) - 112px);
|
|
134
|
+
}
|
|
135
|
+
</style>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AppPage :title="t('gallery.title')" name="gallery">
|
|
3
|
+
<p class="type-body-large m-0 px-6 pb-2 text-on-surface-variant">{{ t("gallery.intro") }}</p>
|
|
4
|
+
<M3List variant="segmented" inset :label="t('gallery.title')">
|
|
5
|
+
<M3ListItem
|
|
6
|
+
v-for="entry in entries"
|
|
7
|
+
:key="entry.id"
|
|
8
|
+
clickable
|
|
9
|
+
:headline="t(entry.titleKey)"
|
|
10
|
+
:supporting="t(entry.subtitleKey)"
|
|
11
|
+
@click="f7router.navigate(entry.path ?? `/gallery/${entry.id}/`)"
|
|
12
|
+
>
|
|
13
|
+
<template #leading>
|
|
14
|
+
<M3Shape :shape="entry.shape" class="size-10" :class="TONE_CLASSES[entry.tone]">
|
|
15
|
+
<component :is="entry.icon" class="size-5" />
|
|
16
|
+
</M3Shape>
|
|
17
|
+
</template>
|
|
18
|
+
<template #trailing><i-ms-chevron-right-rounded class="rtl:-scale-x-100" /></template>
|
|
19
|
+
</M3ListItem>
|
|
20
|
+
</M3List>
|
|
21
|
+
</AppPage>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import type { Router } from "framework7/types";
|
|
26
|
+
import {
|
|
27
|
+
AGENDA_SECTION,
|
|
28
|
+
CHAT_SECTION,
|
|
29
|
+
CONTACTS_SECTION,
|
|
30
|
+
LOGIN_SECTION,
|
|
31
|
+
ONBOARDING_SECTION,
|
|
32
|
+
TABS_SECTION,
|
|
33
|
+
sections,
|
|
34
|
+
} from "@/modules/gallery/composables/useGallerySections";
|
|
35
|
+
import { TONE_CLASSES } from "@/shared/utils/tone";
|
|
36
|
+
|
|
37
|
+
defineProps<{ f7router: Router.Router }>();
|
|
38
|
+
const { t } = useI18n();
|
|
39
|
+
const entries = [
|
|
40
|
+
...sections.slice(0, 5),
|
|
41
|
+
TABS_SECTION,
|
|
42
|
+
...sections.slice(5, 9),
|
|
43
|
+
CONTACTS_SECTION,
|
|
44
|
+
CHAT_SECTION,
|
|
45
|
+
ONBOARDING_SECTION,
|
|
46
|
+
LOGIN_SECTION,
|
|
47
|
+
AGENDA_SECTION,
|
|
48
|
+
...sections.slice(9),
|
|
49
|
+
];
|
|
50
|
+
</script>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section
|
|
3
|
+
class="mx-4 mt-2 grid grid-cols-[1fr_auto] items-center gap-4 rounded-xl bg-primary-container p-6 text-on-primary-container"
|
|
4
|
+
>
|
|
5
|
+
<div class="flex min-w-0 flex-col gap-2">
|
|
6
|
+
<span class="type-label-large text-on-primary-container/80">{{ t("home.overline") }}</span>
|
|
7
|
+
<h2 class="type-headline-medium-emphasized font-rounded m-0">{{ t("home.headline") }}</h2>
|
|
8
|
+
<p class="type-body-medium m-0 text-on-primary-container/80">
|
|
9
|
+
{{ t("home.intro", { app: appName }) }}
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
<button
|
|
13
|
+
type="button"
|
|
14
|
+
class="m3-focus-ring grid size-28 place-items-center rounded-full text-primary"
|
|
15
|
+
:aria-label="t('home.morph')"
|
|
16
|
+
@click="next"
|
|
17
|
+
>
|
|
18
|
+
<M3ShapeMorph :shape="shapes[index]!" :rotate="index * 45" class="size-28">
|
|
19
|
+
<i-ms-auto-awesome-outline-rounded class="size-10 text-on-primary" />
|
|
20
|
+
</M3ShapeMorph>
|
|
21
|
+
</button>
|
|
22
|
+
<M3ButtonGroup
|
|
23
|
+
class="col-span-2 mt-2 flex-wrap max-[600px]:[&>.m3-button]:grow"
|
|
24
|
+
size="m"
|
|
25
|
+
:label="t('home.actions')"
|
|
26
|
+
>
|
|
27
|
+
<M3Button @click="emit('explore')">{{ t("home.explore") }}</M3Button>
|
|
28
|
+
<M3Button variant="tonal" @click="emit('personalise')">{{ t("home.personalise") }}</M3Button>
|
|
29
|
+
</M3ButtonGroup>
|
|
30
|
+
</section>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import type { MaterialShapeName } from "@cavulsqa/m3e";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The first thing the app shows: what it is in one line, a shape that morphs when touched - the
|
|
38
|
+
* Expressive idea in one gesture - and the two places worth going next.
|
|
39
|
+
*/
|
|
40
|
+
const emit = defineEmits<{ explore: []; personalise: [] }>();
|
|
41
|
+
const { t } = useI18n();
|
|
42
|
+
const appName = __APP_NAME__;
|
|
43
|
+
|
|
44
|
+
const shapes: readonly MaterialShapeName[] = [
|
|
45
|
+
"cookie9Sided",
|
|
46
|
+
"clover4Leaf",
|
|
47
|
+
"sunny",
|
|
48
|
+
"pentagon",
|
|
49
|
+
"softBurst",
|
|
50
|
+
"flower",
|
|
51
|
+
];
|
|
52
|
+
const index = ref(0);
|
|
53
|
+
|
|
54
|
+
function next() {
|
|
55
|
+
index.value = (index.value + 1) % shapes.length;
|
|
56
|
+
}
|
|
57
|
+
</script>
|