refactor: i18n, store detail

refactor/issue-1/first-setup
Frédérik Benoist 2023-12-13 00:22:29 +01:00
parent 3f72b1737e
commit 60f141deba
19 changed files with 1164 additions and 505 deletions

View File

@ -1 +0,0 @@
VITE_API_BASE_URL=

View File

@ -1,6 +1,4 @@
<script setup lang="ts">
import avatar1 from '@images/avatars/avatar-1.png'
import { userStore } from '@stores/user.store'
const useUserStore = userStore()
@ -10,6 +8,17 @@ const logoutAndRedirect = () => {
useUserStore.logout()
router.push('/login')
}
const avatarColor = computed(() => {
switch (useUserStore.role) {
// eslint-disable-next-line @stylistic/ts/indent
case 'Admin': return 'error'
// eslint-disable-next-line @stylistic/ts/indent
case 'Support': return 'secondary'
// eslint-disable-next-line @stylistic/ts/indent
default: return 'info'
}
})
</script>
<template>
@ -23,10 +32,18 @@ const logoutAndRedirect = () => {
>
<VAvatar
class="cursor-pointer"
color="primary"
:color="avatarColor"
variant="tonal"
>
<VImg :src="avatar1" />
<template v-if="useUserStore.role === 'Admin'">
AD
</template>
<template v-else-if="useUserStore.role === 'Support'">
SU
</template>
<template v-else>
IN
</template>
<!-- SECTION Menu -->
<VMenu
@ -48,10 +65,18 @@ const logoutAndRedirect = () => {
color="success"
>
<VAvatar
color="primary"
:color="avatarColor"
variant="tonal"
>
<VImg :src="avatar1" />
<template v-if="useUserStore.role === 'Admin'">
AD
</template>
<template v-else-if="useUserStore.role === 'Support'">
SU
</template>
<template v-else>
IN
</template>
</VAvatar>
</VBadge>
</VListItemAction>
@ -65,61 +90,6 @@ const logoutAndRedirect = () => {
<VDivider class="my-2" />
<!-- 👉 Profile -->
<VListItem link>
<template #prepend>
<VIcon
class="me-2"
icon="tabler-user"
size="22"
/>
</template>
<VListItemTitle>Profile</VListItemTitle>
</VListItem>
<!-- 👉 Settings -->
<VListItem link>
<template #prepend>
<VIcon
class="me-2"
icon="tabler-settings"
size="22"
/>
</template>
<VListItemTitle>Settings</VListItemTitle>
</VListItem>
<!-- 👉 Pricing -->
<VListItem link>
<template #prepend>
<VIcon
class="me-2"
icon="tabler-currency-dollar"
size="22"
/>
</template>
<VListItemTitle>Pricing</VListItemTitle>
</VListItem>
<!-- 👉 FAQ -->
<VListItem link>
<template #prepend>
<VIcon
class="me-2"
icon="tabler-help"
size="22"
/>
</template>
<VListItemTitle>FAQ</VListItemTitle>
</VListItem>
<!-- Divider -->
<VDivider class="my-2" />
<!-- 👉 Logout -->
<VListItem @click="logoutAndRedirect">
<template #prepend>

View File

@ -4,14 +4,9 @@ export default [
to: { name: 'root' },
icon: { icon: 'tabler-smart-home' },
},
{
title: 'Second page',
to: { name: 'second-page' },
icon: { icon: 'tabler-file' },
},
{
title: 'Store',
to: { name: 'store' },
to: { name: 'store-list' },
icon: { icon: 'tabler-file' },
},
]

View File

@ -4,14 +4,9 @@ export default [
to: { name: 'root' },
icon: { icon: 'tabler-smart-home' },
},
{
title: 'Second page',
to: { name: 'second-page' },
icon: { icon: 'tabler-file' },
},
{
title: 'Store',
to: { name: 'store' },
to: { name: 'store-list' },
icon: { icon: 'tabler-file' },
},
]

View File

@ -1,418 +0,0 @@
<script setup lang="ts">
import { VDataTableServer } from 'vuetify/labs/VDataTable'
import { paginationMeta } from '@api-utils/paginationMeta'
const widgetData = ref([
{ title: 'In-Store Sales', value: '$5,345.43', icon: 'tabler-home', desc: '5k orders', change: 5.7 },
{ title: 'Website Sales', value: '$674,347.12', icon: 'tabler-device-laptop', desc: '21k orders', change: 12.4 },
{ title: 'Discount', value: '$14,235.12', icon: 'tabler-gift', desc: '6k orders' },
{ title: 'Affiliate', value: '$8,345.23', icon: 'tabler-wallet', desc: '150 orders', change: -3.5 },
])
const headers = [
{ title: 'Product', key: 'product' },
{ title: 'Category', key: 'category' },
{ title: 'Stock', key: 'stock', sortable: false },
{ title: 'SKU', key: 'sku' },
{ title: 'Price', key: 'price' },
{ title: 'QTY', key: 'qty' },
{ title: 'Status', key: 'status' },
{ title: 'Actions', key: 'actions', sortable: false },
]
const selectedStatus = ref()
const selectedCategory = ref()
const selectedStock = ref<boolean | undefined>()
const searchQuery = ref('')
const status = ref([
{ title: 'Scheduled', value: 'Scheduled' },
{ title: 'Publish', value: 'Published' },
{ title: 'Inactive', value: 'Inactive' },
])
const categories = ref([
{ title: 'Accessories', value: 'Accessories' },
{ title: 'Home Decor', value: 'Home Decor' },
{ title: 'Electronics', value: 'Electronics' },
{ title: 'Shoes', value: 'Shoes' },
{ title: 'Office', value: 'Office' },
{ title: 'Games', value: 'Games' },
])
const stockStatus = ref([
{ title: 'In Stock', value: true },
{ title: 'Out of Stock', value: false },
])
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
// Update data table options
const updateOptions = (options: any) => {
page.value = options.page
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const resolveCategory = (category: string) => {
if (category === 'Accessories')
return { color: 'error', icon: 'tabler-device-watch' }
if (category === 'Home Decor')
return { color: 'info', icon: 'tabler-home' }
if (category === 'Electronics')
return { color: 'primary', icon: 'tabler-device-imac' }
if (category === 'Shoes')
return { color: 'success', icon: 'tabler-shoe' }
if (category === 'Office')
return { color: 'warning', icon: 'tabler-briefcase' }
if (category === 'Games')
return { color: 'primary', icon: 'tabler-device-gamepad-2' }
}
const resolveStatus = (statusMsg: string) => {
if (statusMsg === 'Scheduled')
return { text: 'Scheduled', color: 'warning' }
if (statusMsg === 'Published')
return { text: 'Publish', color: 'success' }
if (statusMsg === 'Inactive')
return { text: 'Inactive', color: 'error' }
}
const { data: productsData, execute: fetchProducts } = await useApi<any>(createUrl('/apps/ecommerce/products',
{
query: {
q: searchQuery,
stock: selectedStock,
category: selectedCategory,
status: selectedStatus,
page,
itemsPerPage,
sortBy,
orderBy,
},
},
))
const products = computed(() => productsData.value.products)
const totalProduct = computed(() => productsData.value.total)
const deleteProduct = async (id: number) => {
await $api(`apps/ecommerce/products/${id}`, {
method: 'DELETE',
})
fetchProducts()
}
</script>
<template>
<div>
<!-- 👉 widgets -->
<VCard class="mb-6">
<VCardText>
<VRow>
<template
v-for="(data, id) in widgetData"
:key="id"
>
<VCol
cols="12"
sm="6"
md="3"
class="px-6"
>
<div
class="d-flex justify-space-between"
:class="$vuetify.display.xs
? 'product-widget'
: $vuetify.display.sm
? id < 2 ? 'product-widget' : ''
: ''"
>
<div class="d-flex flex-column gap-y-1">
<div class="text-body-1 font-weight-medium text-capitalize">
{{ data.title }}
</div>
<h4 class="text-h4 my-1">
{{ data.value }}
</h4>
<div class="d-flex">
<div class="me-2 text-disabled text-no-wrap">
{{ data.desc }}
</div>
<VChip
v-if="data.change"
label
:color="data.change > 0 ? 'success' : 'error'"
>
{{ prefixWithPlus(data.change) }}%
</VChip>
</div>
</div>
<VAvatar
variant="tonal"
rounded
size="38"
>
<VIcon
:icon="data.icon"
size="28"
/>
</VAvatar>
</div>
</VCol>
<VDivider
v-if="$vuetify.display.mdAndUp ? id !== widgetData.length - 1
: $vuetify.display.smAndUp ? id % 2 === 0
: false"
vertical
inset
length="95"
/>
</template>
</VRow>
</VCardText>
</VCard>
<!-- 👉 products -->
<VCard
title="Filters"
class="mb-6"
>
<VCardText>
<VRow>
<!-- 👉 Select Status -->
<VCol
cols="12"
sm="4"
>
<AppSelect
v-model="selectedStatus"
placeholder="Status"
:items="status"
clearable
clear-icon="tabler-x"
/>
</VCol>
<!-- 👉 Select Category -->
<VCol
cols="12"
sm="4"
>
<AppSelect
v-model="selectedCategory"
placeholder="Category"
:items="categories"
clearable
clear-icon="tabler-x"
/>
</VCol>
<!-- 👉 Select Stock Status -->
<VCol
cols="12"
sm="4"
>
<AppSelect
v-model="selectedStock"
placeholder="Stock"
:items="stockStatus"
clearable
clear-icon="tabler-x"
/>
</VCol>
</VRow>
</VCardText>
<VDivider class="my-4" />
<div class="d-flex flex-wrap gap-4 mx-5">
<div class="d-flex align-center">
<!-- 👉 Search -->
<AppTextField
v-model="searchQuery"
placeholder="Search Product"
density="compact"
style="inline-size: 200px;"
class="me-3"
/>
</div>
<VSpacer />
<div class="d-flex gap-4 flex-wrap align-center">
<AppSelect
v-model="itemsPerPage"
:items="[5, 10, 20, 25, 50]"
/>
<!-- 👉 Export button -->
<VBtn
variant="tonal"
color="secondary"
prepend-icon="tabler-upload"
>
Export
</VBtn>
<VBtn
color="primary"
prepend-icon="tabler-plus"
@click="$router.push('/apps/ecommerce/product/add')"
>
Add Product
</VBtn>
</div>
</div>
<VDivider class="mt-4" />
<!-- 👉 Datatable -->
<VDataTableServer
v-model:items-per-page="itemsPerPage"
v-model:page="page"
:headers="headers"
show-select
:items="products"
:items-length="totalProduct"
class="text-no-wrap"
@update:options="updateOptions"
>
<!-- product -->
<template #item.product="{ item }">
<div class="d-flex align-center gap-x-2">
<VAvatar
v-if="item.image"
size="38"
variant="tonal"
rounded
:image="item.image"
/>
<div class="d-flex flex-column">
<span class="text-body-1 font-weight-medium">{{ item.productName }}</span>
<span class="text-sm text-disabled">{{ item.productBrand }}</span>
</div>
</div>
</template>
<!-- category -->
<template #item.category="{ item }">
<VAvatar
size="30"
variant="tonal"
:color="resolveCategory(item.category)?.color"
class="me-2"
>
<VIcon
:icon="resolveCategory(item.category)?.icon"
size="18"
/>
</VAvatar>
<span class="text-body-1 font-weight-medium">{{ item.category }}</span>
</template>
<!-- stock -->
<template #item.stock="{ item }">
<VSwitch :model-value="item.stock" />
</template>
<!-- status -->
<template #item.status="{ item }">
<VChip
v-bind="resolveStatus(item.status)"
density="default"
label
/>
</template>
<!-- Actions -->
<template #item.actions="{ item }">
<IconBtn>
<VIcon icon="tabler-edit" />
</IconBtn>
<IconBtn>
<VIcon icon="tabler-dots-vertical" />
<VMenu activator="parent">
<VList>
<VListItem
value="download"
prepend-icon="tabler-download"
>
Download
</VListItem>
<VListItem
value="delete"
prepend-icon="tabler-trash"
@click="deleteProduct(item.id)"
>
Delete
</VListItem>
<VListItem
value="duplicate"
prepend-icon="tabler-copy"
>
Duplicate
</VListItem>
</VList>
</VMenu>
</IconBtn>
</template>
<template #bottom>
<VDivider />
<div class="d-flex align-center justify-space-between flex-wrap gap-3 pa-5 pt-3">
<p class="text-sm text-medium-emphasis mb-0">
{{ paginationMeta({ page, itemsPerPage }, totalProduct) }}
</p>
<VPagination
v-model="page"
:length="Math.min(Math.ceil(totalProduct / itemsPerPage), 5)"
:total-visible="$vuetify.display.xs ? 1 : Math.min(Math.ceil(totalProduct / itemsPerPage), 5)"
>
<template #prev="slotProps">
<VBtn
variant="tonal"
color="default"
v-bind="slotProps"
:icon="false"
>
Previous
</VBtn>
</template>
<template #next="slotProps">
<VBtn
variant="tonal"
color="default"
v-bind="slotProps"
:icon="false"
>
Next
</VBtn>
</template>
</VPagination>
</div>
</template>
</VDataTableServer>
</VCard>
</div>
</template>
<style lang="scss" scoped>
.product-widget{
border-block-end: 1px solid rgba(var(--v-theme-on-surface), var(--v-border-opacity));
padding-block-end: 1rem;
}
</style>

View File

@ -1,10 +0,0 @@
<template>
<div>
<VCard title="Stores 🙌">
<VCardText>This is the Store Page.</VCardText>
<VCardText>
Liste des boutiques
</VCardText>
</VCard>
</div>
</template>

View File

@ -0,0 +1,68 @@
<script lang="ts" setup>
import StoreHeader from '@/views/pages/store/view/StoreHeader.vue'
import StoreTabAdmin from '@/views/pages/store/view/StoreTabAdmin.vue'
import StoreTabGeneral from '@/views/pages/store/view/StoreTabGeneral.vue'
import StoreTabItem from '@/views/pages/store/view/StoreTabItem.vue'
const route = useRoute('store-details')
const storeTab = ref(route.query.tab || 'general')
console.log(route.query.dbHost)
// tabs
const tabs = [
{ title: 'General', icon: 'tabler-users', tab: 'general' },
{ title: 'Item', icon: 'tabler-lock', tab: 'item' },
{ title: 'Admin', icon: 'tabler-file-text', tab: 'admin' },
]
const { data: storeData } = await useApi<any>(`/stores/${route.query.storeId}/details?dbHost=${route.query.dbHost}`)
</script>
<template>
<div>
<StoreHeader
class="mb-5"
:store-data="storeData"
/>
<VTabs
v-model="storeTab"
class="v-tabs-pill"
>
<VTab
v-for="item in tabs"
:key="item.icon"
>
<VIcon
size="20"
start
:icon="item.icon"
/>
{{ item.title }}
</VTab>
</VTabs>
<VWindow
v-model="storeTab"
class="mt-6 disable-tab-transition"
:touch="false"
>
<!-- General -->
<VWindowItem>
<StoreTabGeneral :store-data="storeData" />
</VWindowItem>
<!-- item -->
<VWindowItem>
<StoreTabItem />
</VWindowItem>
<!-- admin -->
<VWindowItem>
<StoreTabAdmin />
</VWindowItem>
</VWindow>
</div>
</template>

View File

@ -0,0 +1,184 @@
<script setup lang="ts">
import { VDataTable } from 'vuetify/labs/VDataTable'
const { t } = useI18n()
const headers = computed(() => [
{ title: 'ID', key: 'id_structure' },
{ title: t('Name'), key: 'nom' },
{ title: 'Pos', key: 'nbcaisses' },
{ title: 'IP', key: 'ip_master', sortable: false },
{ title: t('Phone'), key: 'telephone', sortable: false },
{ title: t('Brand'), key: 'enseigne' },
{ title: t('Country'), key: 'pays' },
])
const selectedCountry = ref()
const selectedBrand = ref()
const selectedNbPos = ref()
const searchQuery = ref('')
// Data table options
const { data: storesList } = await useApi<any>(createUrl('/stores'))
const options = ref({ page: 1, itemsPerPage: 10, sortBy: [''], sortDesc: [false] })
const brand = computed(() => {
const allBrands = storesList.value.map((store: { enseigne: any }) => store.enseigne)
// eslint-disable-next-line @typescript-eslint/no-shadow
const uniqueBrands = allBrands.filter((brand: any, index: any, self: string | any[]) => self.indexOf(brand) === index)
const sortedBrands = uniqueBrands.sort()
// eslint-disable-next-line @typescript-eslint/no-shadow
return sortedBrands.map((brand: any) => ({ title: brand, value: brand }))
})
const country = computed(() => {
const allCountries = storesList.value.map((store: { pays: any }) => store.pays)
// eslint-disable-next-line @typescript-eslint/no-shadow
const uniqueCountries = allCountries.filter((country: any, index: any, self: string | any[]) => self.indexOf(country) === index)
const sortedCountries = uniqueCountries.sort()
// eslint-disable-next-line @typescript-eslint/no-shadow
return sortedCountries.map((country: any) => ({ title: country, value: country }))
})
const nbPos = computed(() => {
const allNbPos = storesList.value.map((store: { nbcaisses: number }) => store.nbcaisses)
const uniqueNbPos = Array.from(new Set(allNbPos)) as number[] // Utilisez une assertion de type
const sortedNbPos = uniqueNbPos.sort((a, b) => a - b) // Triez les nombres en ordre croissant
// eslint-disable-next-line @typescript-eslint/no-shadow
return sortedNbPos.map((nbPos: number) => ({ title: nbPos, value: nbPos }))
})
const filteredStoresList = computed(() => {
let filtered = storesList.value
// If a brand is selected, filter the records for this brand
if (selectedBrand.value)
filtered = filtered.filter((store: { enseigne: any }) => store.enseigne === selectedBrand.value)
if (selectedCountry.value)
filtered = filtered.filter((store: { pays: any }) => store.pays === selectedCountry.value)
if (selectedNbPos.value)
filtered = filtered.filter((store: { nbcaisses: any }) => store.nbcaisses === selectedNbPos.value)
// If a search query is provided, filter the records for this query
if (searchQuery.value) {
filtered = filtered.filter((store: { [s: string]: unknown } | ArrayLike<unknown>) =>
Object.values(store).some(value =>
String(value).toLowerCase().includes(searchQuery.value.toLowerCase()),
),
)
}
return filtered
})
</script>
<template>
<div>
<!-- 👉 stores -->
<VCard
:title="$t('List of stores')"
class="mb-6"
>
<VCardText>
<VRow>
<!-- 👉 Select country -->
<VCol
cols="12"
sm="4"
>
<AppSelect
v-model="selectedCountry"
:placeholder="$t('Country')"
:items="country"
clearable
clear-icon="tabler-x"
/>
</VCol>
<!-- 👉 Select Brand -->
<VCol
cols="12"
sm="4"
>
<AppSelect
v-model="selectedBrand"
:placeholder="$t('Brand')"
:items="brand"
clearable
clear-icon="tabler-x"
/>
</VCol>
<!-- 👉 Select Multi POS -->
<VCol
cols="12"
sm="4"
>
<AppSelect
v-model="selectedNbPos"
:placeholder="$t('Multi POS')"
:items="nbPos"
clearable
clear-icon="tabler-x"
/>
</VCol>
</VRow>
</VCardText>
<VDivider class="my-4" />
<div class="d-flex flex-wrap gap-4 mx-5">
<div class="d-flex align-center">
<!-- 👉 Search -->
<AppTextField
v-model="searchQuery"
:placeholder="$t('Search')"
density="compact"
style="inline-size: 200px;"
class="me-3"
/>
</div>
<VSpacer />
<div class="d-flex gap-4 flex-wrap align-center">
<VBtn
color="primary"
prepend-icon="tabler-reload"
@click="$router.push('/apps/ecommerce/product/add')"
>
Reload
</VBtn>
</div>
</div>
<VDivider class="mt-4" />
<VCol cols="12">
<!-- 👉 Datatable -->
<VDataTable
:headers="headers"
:items="filteredStoresList"
:items-per-page="options.itemsPerPage"
:page="options.page"
:options="options"
>
<template #item.nom="{ item }">
<RouterLink :to="`/store/details?dbHost=${item.ip_master}&storeId=${item.id_structure}`">
{{ item.nom }}
</RouterLink>
</template>
<template #item.nbcaisses="{ item }">
<VIcon v-if="item.nbcaisses > 1 && item.nbcaisses <= 9">
{{ `tabler-square-rounded-number-${item.nbcaisses}` }}
</VIcon>
<span v-else-if="item.nbcaisses > 9">{{ item.nbcaisses }}</span>
</template>
</VDataTable>
</VCol>
</vcard>
</div>
</template>

29
src/plugins/i18n/index.ts Executable file
View File

@ -0,0 +1,29 @@
import type { App } from 'vue'
import { createI18n } from 'vue-i18n'
import { cookieRef } from '@layouts/stores/config'
import { themeConfig } from '@themeConfig'
const messages = Object.fromEntries(
Object.entries(
import.meta.glob<{ default: any }>('./locales/*.json', { eager: true }))
.map(([key, value]) => [key.slice(10, -5), value.default]),
)
let _i18n: any = null
export const getI18n = () => {
if (_i18n === null) {
_i18n = createI18n({
legacy: false,
locale: cookieRef('language', themeConfig.app.i18n.defaultLocale).value,
fallbackLocale: 'en',
messages,
})
}
return _i18n
}
export default function (app: App) {
app.use(getI18n())
}

219
src/plugins/i18n/locales/ar.json Executable file
View File

@ -0,0 +1,219 @@
{
"Home": "الصفحة الرئيسية",
"Store": "متجر",
"List of stores": "قائمة المتاجر",
"Country": "الدولة",
"Brand": "العلامة التجارية",
"Multi POS": "Multi POS",
"Search": "بحث",
"Name": "الاسم",
"Phone": "هاتف",
"---------------------------": "---------------------------",
"UI Elements": "عناصر واجهة المستخدم",
"Forms & Tables": "النماذج والجداول",
"Pages": "الصفحات",
"Charts & Maps": "الرسوم البيانية والخرائط",
"Others": "آحرون",
"Typography": "الطباعة",
"Cards": "البطاقات",
"Basic": "أساسي",
"Advance": "يتقدم",
"Widgets": "الحاجيات",
"Actions": "أجراءات",
"Components": "عناصر",
"Alert": "انذار",
"Close Alert": "أغلق التنبيه",
"Avatar": "الصورة الرمزية",
"Badge": "شارة",
"Button": "زر",
"Calendar": "تقويم",
"Image": "صورة",
"Pagination": "ترقيم الصفحات",
"Progress Circular": "تقدم التعميم",
"Progress Linear": "تقدم خطي",
"Autocomplete": "الإكمال التلقائي",
"Tooltip": "تلميح",
"Slider": "المنزلق",
"Date Time Picker": "منتقي التاريخ والوقت",
"Select": "يختار",
"Switch": "يُحوّل",
"Checkbox": "خانة اختيار",
"Radio": "مذياع",
"Textarea": "تيكستاريا",
"Rating": "تقييم",
"File Input": "إدخال الملف",
"Otp Input": "إدخال أوتب",
"Form Layout": "تخطيط النموذج",
"Form Validation": "التحقق من صحة النموذج",
"Charts": "الرسوم البيانية",
"Apex Chart": "مخطط أبيكس",
"Chartjs": "تشارتجس",
"Account Settings": "إعدادت الحساب",
"User Profile": "ملف تعريفي للمستخدم",
"FAQ": "التعليمات",
"Dialog Examples": "أمثلة على الحوار",
"Pricing": "التسعير",
"List": "قائمة",
"Edit": "يحرر",
"Nav Levels": "مستويات التنقل",
"Level 2.1": "المستوى 2.1",
"Level 2.2": "مستوى 2.2",
"Level 3.1": "المستوى 3.1",
"Level 3.2": "المستوى 3.2",
"Raise Support": "رفع الدعم",
"Documentation": "توثيق",
"Dashboards": "لوحات القيادة",
"Apps & Pages": "التطبيقات والصفحات",
"Email": "البريد الإلكتروني",
"Chat": "دردشة",
"Invoice": "فاتورة",
"Preview": "معاينة",
"Add": "يضيف",
"User": "المستعمل",
"View": "رأي",
"Login v1": "تسجيل الدخول v1",
"Login v2": "تسجيل الدخول v2",
"Login": "تسجيل الدخول",
"Register v1": "تسجيل v1",
"Register v2": "تسجيل v2",
"Register": "تسجيل",
"Forget Password v1": "نسيت كلمة المرور v1",
"Forget Password v2": "نسيت كلمة المرور v2",
"Forgot Password v1": "نسيت كلمة المرور v1",
"Forgot Password v2": "نسيت كلمة المرور v2",
"Forgot Password": "نسيت كلمة المرور",
"Reset Password v1": "إعادة تعيين كلمة المرور v1",
"Reset Password v2": "إعادة تعيين كلمة المرور v2",
"Reset Password": "إعادة تعيين كلمة المرور",
"Miscellaneous": "متفرقات",
"Coming Soon": "قريبا",
"Not Authorized": "غير مخول",
"Under Maintenance": "تحت الصيانة",
"Error": "خطأ",
"Statistics": "إحصائيات",
"Analytics": "تحليلات",
"Access Control": "صلاحية التحكم صلاحية الدخول",
"User Interface": "واجهة المستخدم",
"CRM": "سي آر إم",
"Icons": "أيقونات",
"Chip": "رقاقة",
"Dialog": "حوار",
"Expansion Panel": "لوحة التوسع",
"Combobox": "صندوق التحرير",
"Textfield": "مجال التحرير مكان كتابة النص",
"Range Slider": "نطاق المنزلق",
"Menu": "قائمة الطعام",
"Snackbar": "مطعم الوجبات الخفيفة",
"Tabs": "نوافذ التبويب",
"Form Elements": "عناصر النموذج",
"Form Layouts": "تخطيطات النموذج",
"Authentication": "المصادقة",
"Page Not Found - 404": "الصفحة غير موجودة - 404",
"Not Authorized - 401": "غير مصرح - 401",
"Server Error - 500": "خطأ في الخادم - 500",
"2": "2",
"Forms": "نماذج",
"Timeline": "الجدول الزمني",
"Disabled Menu": "قائمة المعوقين",
"Help Center": "مركز المساعدة",
"Verify Email": "التحقق من البريد الإلكتروني",
"Verify Email v1": "تحقق من البريد الإلكتروني v1",
"Verify Email v2": "تحقق من البريد الإلكتروني v2",
"Two Steps": "خطوتين",
"Two Steps v1": "خطوتين v1.0",
"Two Steps v2": "خطوتين v2.0",
"Custom Input": "إدخال مخصص",
"Extensions": "ملحقات",
"Tour": "رحلة",
"Register Multi-Steps": "تسجيل خطوات متعددة",
"Wizard Examples": "أمثلة المعالج",
"Checkout": "الدفع",
"Create Deal": "إنشاء صفقة",
"Property Listing": "قائمة الممتلكات ",
"Roles & Permissions": "الأدوار والأذونات",
"Roles": "الأدوار",
"Permissions": "الأذونات",
"Simple Table": "جدول بسيط",
"Tables": "الجداول",
"DataTable": "جدول البيانات",
"Data Table": "جدول البيانات",
"Apps": "التطبيقات",
"Misc": "متفرقات",
"Wizard Pages": "صفحات المعالج",
"eCommerce": "التجارة الإلكترونية",
"Form Wizard": "معالج النموذج",
"Numbered": "مرقم",
"ecommerce": "التجارة الإلكترونية",
"Ecommerce": "التجارة الإلكترونية",
"Product": "المنتج",
"Category": "الفئة",
"Order": "طلب",
"Details": "تفاصيل",
"Customer": "الزبون",
"Manage Review": "إدارة المراجعة",
"Referrals": "الإحالات",
"Settings": "الإعدادات",
"Course Details": "تفاصيل الدورة التدريبية",
"My Course": "دورتي",
"Overview": "نظرة عامة",
"Academy": "أكاديمية",
"Logistics": "الخدمات اللوجستية",
"Dashboard": "لوحة القيادة",
"Fleet": "الأسطول",
"Editors": "المحررين",
"Front Pages": "الصفحات الأمامية",
"Landing": "المقصودة",
"checkout": "الدفع",
"Payment": "دفع",
"Swiper": "المنزلق",
"3": "3",
"5": "5",
"10": "10",
"20": "20",
"25": "25",
"50": "50",
"100": "100",
"$vuetify": {
"badge": "شارة",
"noDataText": "لا تتوافر بيانات",
"close": "قريب",
"open": "افتح",
"carousel": {
"ariaLabel": {
"delimiter": "تحديد"
}
},
"dataFooter": {
"itemsPerPageText": "مواد لكل صفحة:",
"itemsPerPageAll": "الجميع",
"pageText": "{0} - {1} من {2}",
"firstPage": "الصفحة الأولى",
"prevPage": "الصفحة السابقة",
"nextPage": "الصفحة التالية",
"lastPage": "آخر صفحة"
},
"pagination": {
"ariaLabel": {
"root": "جذر",
"previous": "السابق",
"next": "التالي",
"currentPage": "الصفحه الحاليه",
"page": "صفحة"
}
},
"input": {
"clear": "صافي",
"appendAction": "إلحاق الإجراء",
"prependAction": "قبل العمل",
"otp": "أوتب"
},
"fileInput": {
"counterSize": "حجم العداد"
},
"rating": {
"ariaLabel": {
"item": "العنصر"
}
}
}
}

219
src/plugins/i18n/locales/en.json Executable file
View File

@ -0,0 +1,219 @@
{
"Home": "Home",
"Store": "Store",
"List of stores": "List of stores",
"Country": "Country",
"Brand": "Brand",
"Multi POS": "Multi POS",
"Search": "Search",
"Name": "Name",
"Phone": "Phone",
"---------------------------": "---------------------------",
"UI Elements": "UI Elements",
"Forms & Tables": "Forms & Tables",
"Pages": "Pages",
"Charts & Maps": "Charts & Maps",
"Others": "Others",
"Typography": "Typography",
"Cards": "Cards",
"Basic": "Basic",
"Advance": "Advance",
"Widgets": "Widgets",
"Components": "Components",
"Alert": "Alert",
"Close Alert": "Close Alert",
"Avatar": "Avatar",
"Badge": "Badge",
"Button": "Button",
"Calendar": "Calendar",
"Image": "Image",
"Pagination": "Pagination",
"Progress Circular": "Progress Circular",
"Progress Linear": "Progress Linear",
"Autocomplete": "Autocomplete",
"Tooltip": "Tooltip",
"Slider": "Slider",
"Date Time Picker": "Date Time Picker",
"Select": "Select",
"Switch": "Switch",
"Checkbox": "Checkbox",
"Radio": "Radio",
"Textarea": "Textarea",
"Rating": "Rating",
"File Input": "File Input",
"Otp Input": "Otp Input",
"Form Layout": "Form Layout",
"Form Validation": "Form Validation",
"Charts": "Charts",
"Apex Chart": "Apex Chart",
"Chartjs": "Chartjs",
"Account Settings": "Account Settings",
"User Profile": "User Profile",
"FAQ": "FAQ",
"Dialog Examples": "Dialog Examples",
"Pricing": "Pricing",
"List": "List",
"Edit": "Edit",
"Nav Levels": "Nav Levels",
"Level 2.1": "Level 2.1",
"Level 2.2": "Level 2.2",
"Level 3.1": "Level 3.1",
"Level 3.2": "Level 3.2",
"Raise Support": "Raise Support",
"Documentation": "Documentation",
"Dashboards": "Dashboards",
"Analytics": "Analytics",
"Apps & Pages": "Apps & Pages",
"Email": "Email",
"Chat": "Chat",
"Invoice": "Invoice",
"Preview": "Preview",
"Add": "Add",
"User": "User",
"View": "View",
"Login v1": "Login v1",
"Login v2": "Login v2",
"Login": "Login",
"Register v1": "Register v1",
"Register v2": "Register v2",
"Register": "Register",
"Forget Password v1": "Forget Password v1",
"Forget Password v2": "Forget Password v2",
"Forgot Password v1": "Forgot Password v1",
"Forgot Password v2": "Forgot Password v2",
"Forgot Password": "Forgot Password",
"Reset Password v1": "Reset Password v1",
"Reset Password v2": "Reset Password v2",
"Reset Password": "Reset Password",
"Miscellaneous": "Miscellaneous",
"Coming Soon": "Coming Soon",
"Not Authorized": "Not Authorized",
"Under Maintenance": "Under Maintenance",
"Error": "Error",
"Statistics": "Statistics",
"Actions": "Actions",
"Access Control": "Access Control",
"User Interface": "User Interface",
"CRM": "CRM",
"eCommerce": "eCommerce",
"Icons": "Icons",
"Chip": "Chip",
"Dialog": "Dialog",
"Expansion Panel": "Expansion Panel",
"Combobox": "Combobox",
"Textfield": "Textfield",
"Range Slider": "Range Slider",
"Menu": "Menu",
"Snackbar": "Snackbar",
"Tabs": "Tabs",
"Form Elements": "Form Elements",
"Form Layouts": "Form Layouts",
"Authentication": "Authentication",
"Page Not Found - 404": "Page Not Found - 404",
"Not Authorized - 401": "Not Authorized - 401",
"Server Error - 500": "Server Error - 500",
"2": "2",
"Forms": "Forms",
"Timeline": "Timeline",
"Disabled Menu": "Disabled Menu",
"Help Center": "Help Center",
"Verify Email": "Verify Email",
"Verify Email v1": "Verify Email v1",
"Verify Email v2": "Verify Email v2",
"Two Steps": "Two Steps",
"Two Steps v1": "Two Steps v1",
"Two Steps v2": "Two Steps v2",
"Custom Input": "Custom Input",
"Extensions": "Extensions",
"Tour": "Tour",
"Register Multi-Steps": "Register Multi-Steps",
"Wizard Examples": "Wizard Examples",
"Checkout": "Checkout",
"Create Deal": "Create Deal",
"Property Listing": "Property Listing",
"Roles & Permissions": "Roles & Permissions",
"Roles": "Roles",
"Simple Table": "Simple Table",
"Tables": "Tables",
"Data Table": "Data Table",
"Permissions": "Permissions",
"Apps": "Apps",
"Misc": "Misc",
"Wizard Pages": "Wizard Pages",
"Form Wizard": "Form Wizard",
"Numbered": "Numbered",
"3": "3",
"ecommerce": "ecommerce",
"Ecommerce": "Ecommerce",
"Editors": "Editors",
"Front Pages": "Front Pages",
"Landing": "Landing",
"checkout": "checkout",
"Payment": "Payment",
"Swiper": "Swiper",
"Product": "Product",
"Category": "Category",
"Order": "Order",
"Details": "Details",
"Customer": "Customer",
"Manage Review": "Manage Review",
"Referrals": "Referrals",
"Settings": "Settings",
"Overview": "Overview",
"My Course": "My Course",
"Course Details": "Course Details",
"Academy": "Academy",
"Logistics": "Logistics",
"Dashboard": "Dashboard",
"Fleet": "Fleet",
"5": "5",
"10": "10",
"20": "20",
"25": "25",
"50": "50",
"100": "100",
"$vuetify": {
"badge": "Badge",
"noDataText": "No data available",
"close": "Close",
"open": "open",
"carousel": {
"ariaLabel": {
"delimiter": "delimiter"
}
},
"dataFooter": {
"itemsPerPageText": "Items per page:",
"itemsPerPageAll": "All",
"pageText": "{0}-{1} of {2}",
"firstPage": "First Page",
"prevPage": "Previous Page",
"nextPage": "Next Page",
"lastPage": "Last Page"
},
"pagination": {
"ariaLabel": {
"root": "root",
"previous": "previous",
"next": "next",
"currentPage": "currentPage",
"page": "page"
}
},
"input": {
"clear": "clear",
"appendAction": "appendAction",
"prependAction": "prependAction",
"counterSize": "counterSize",
"otp": "otp"
},
"fileInput": {
"counterSize": "counterSize"
},
"rating": {
"ariaLabel": {
"item": "item"
}
}
}
}

220
src/plugins/i18n/locales/fr.json Executable file
View File

@ -0,0 +1,220 @@
{
"Home": "Acceuil",
"Store": "Boutique",
"List of stores": "Liste des boutiques",
"Country": "Pays",
"Brand": "Enseigne",
"Multi POS": "Multi POS",
"Search": "Chercher",
"Name": "Nom",
"Phone": "Téléphone",
"---------------------------": "---------------------------",
"UI Elements": "ÉLÉMENTS DE L'UI",
"Forms & Tables": "Formulaires et tableaux",
"Pages": "Des pages",
"Charts & Maps": "Graphiques et cartes",
"Others": "Autres",
"Typography": "Typographie",
"Cards": "Cartes",
"Basic": "De base",
"Advance": "Avance",
"Widgets": "Widget",
"Card Action": "Action de la carte",
"Components": "Composants",
"Alert": "Alerte",
"Close Alert": "Fermer l'alerte",
"Avatar": "Avatar",
"Badge": "Badge",
"Button": "Bouton",
"Calendar": "Calendrier",
"Image": "Image",
"Pagination": "Pagination",
"Progress Circular": "Progrès circulaire",
"Progress Linear": "Progrès Linéaire",
"Autocomplete": "Saisie automatique",
"Tooltip": "Info-bulle",
"Slider": "Glissière",
"Date Time Picker": "Sélecteur de date et d'heure",
"Select": "Sélectionner",
"Switch": "Commutateur",
"Checkbox": "Case à cocher",
"Radio": "Radio",
"Textarea": "Textarea",
"Rating": "Évaluation",
"File Input": "Entrée de fichier",
"Otp Input": "Entrée Otp",
"Form Layout": "Disposition du formulaire",
"Form Validation": "Validation de formulaire",
"Charts": "Graphiques",
"Apex Chart": "Graphique Apex",
"Chartjs": "Chartjs",
"Account Settings": "Paramètres du compte",
"User Profile": "Profil de l'utilisateur",
"FAQ": "FAQ",
"Dialog Examples": "Exemples de dialogue",
"Pricing": "Tarification",
"List": "liste",
"Edit": "Éditer",
"Nav Levels": "Niveaux de navigation",
"Level 2.1": "Niveau 2.1",
"Level 2.2": "Niveau 2.2",
"Level 3.1": "Niveau 3.1",
"Level 3.2": "Niveau 3.2",
"Raise Support": "Augmenter le soutien",
"Documentation": "Documentation",
"Dashboards": "Tableaux de bord",
"Analytics": "Analytique",
"Apps & Pages": "Applications et pages",
"Email": "Email",
"Chat": "Bavarder",
"Invoice": "Facture d'achat",
"Preview": "Aperçu",
"Add": "Ajouter",
"User": "Utilisateur",
"View": "Vue",
"Login v1": "Connexion v1",
"Login v2": "Connexion v2",
"Login": "Connexion",
"Register v1": "S'inscrire v1",
"Register v2": "S'inscrire v2",
"Register": "S'inscrire",
"Forget Password v1": "Oubliez le mot de passe v1",
"Forget Password v2": "Oubliez le mot de passe v2",
"Forgot Password v1": "Oubliez le mot de passe v1",
"Forgot Password v2": "Oubliez le mot de passe v2",
"Forgot Password": "Oubliez le mot de passe",
"Reset Password v1": "Réinitialiser le mot de passe v1",
"Reset Password v2": "Réinitialiser le mot de passe v2",
"Reset Password": "Réinitialiser le mot de passe",
"Miscellaneous": "Divers",
"Coming Soon": "Bientôt disponible",
"Not Authorized": "Pas autorisé",
"Under Maintenance": "En maintenance",
"Error": "Erreur",
"Statistics": "Statistiques",
"Card Actions": "Actions de la carte",
"Actions": "Actions",
"Access Control": "Contrôle d'accès",
"User Interface": "Interface utilisateur",
"CRM": "CRM",
"eCommerce": "commerce électronique",
"Icons": "Icône",
"Chip": "Ébrécher",
"Dialog": "Dialogue",
"Expansion Panel": "Panneau d'extension",
"Combobox": "Boîte combo",
"Textfield": "Champ de texte",
"Range Slider": "Curseur Gamme",
"Menu": "Menu",
"Snackbar": "Casse-croûte",
"Tabs": "Onglets",
"Form Elements": "Éléments de formulaire",
"Form Layouts": "Dispositions de formulaire",
"Authentication": "Authentification",
"Page Not Found - 404": "Page introuvable - 404",
"Not Authorized - 401": "Non autorisé - 401",
"Server Error - 500": "Erreur de serveur - 500",
"2": "2",
"Forms": "Formes",
"Timeline": "Chronologie",
"Disabled Menu": "Menu désactivé",
"Help Center": "Centre d'aide",
"Verify Email": "Vérifier les courriels",
"Verify Email v1": "Vérifier l'e-mail v1",
"Verify Email v2": "Vérifier l'e-mail v2",
"Two Steps": "Deux étapes",
"Two Steps v1": "Deux étapes v1",
"Two Steps v2": "Deux étapes v2",
"Custom Input": "Entrée personnalisée",
"Extensions": "Rallonges",
"Tour": "Tour",
"Register Multi-Steps": "Enregistrer plusieurs étapes",
"Wizard Examples": "Exemples de guide",
"Checkout": "Check-out",
"Create Deal": "Créer une offre",
"Property Listing": "Liste des propriétés",
"Roles & Permissions": "Rôles et autorisations",
"Roles": "Rôles",
"Permissions": "Autorisations",
"Simple Table": "Table simple",
"Tables": "Tables",
"Data Table": "Table de données",
"Apps": "Applications",
"Misc": "Divers",
"Wizard Pages": "Pages de l'assistant",
"Form Wizard": "Assistant de formulaire",
"Numbered": "Numéroté",
"3": "3",
"ecommerce": "commerce électronique",
"Ecommerce": "Commerce électronique",
"Product": "Produit",
"Category": "Catégorie",
"Order": "Ordre",
"Details": "Détails",
"Customer": "Client",
"Manage Review": "Gérer la revue",
"Referrals": "Références",
"Settings": "Paramètres",
"Course Details": "Détails du cours",
"My Course": "Mon cours",
"Overview": "Aperçu",
"Academy": "Académie",
"Logistics": "Logistique",
"Dashboard": "Tableau de bord",
"Fleet": "Flotte",
"Editors": "Éditeurs",
"Front Pages": "Pages frontales",
"Landing": "d'atterrissage",
"checkout": "Check-out",
"Payment": "Paiement",
"Swiper": "Swiper",
"5": "5",
"10": "10",
"20": "20",
"25": "25",
"50": "50",
"100": "100",
"$vuetify": {
"badge": "Badge",
"noDataText": "Pas de données disponibles",
"close": "Fermer",
"open": "Ouvert",
"carousel": {
"ariaLabel": {
"delimiter": "délimiteur"
}
},
"dataFooter": {
"itemsPerPageText": "Objets par page:",
"itemsPerPageAll": "Tout",
"pageText": "{0}-{1} of {2}",
"firstPage": "Première page",
"prevPage": "Page précédente",
"nextPage": "Page suivante",
"lastPage": "Dernière page"
},
"pagination": {
"ariaLabel": {
"root": "racine",
"previous": "précédente",
"next": "suivante",
"currentPage": "page actuelle",
"page": "page"
}
},
"input": {
"clear": "dégager",
"appendAction": "ajouter une action",
"prependAction": "préfixer l'action",
"otp": "otp"
},
"fileInput": {
"counterSize": "Taille du compteur"
},
"rating": {
"ariaLabel": {
"item": "Objet"
}
}
}
}

17
src/plugins/i18n/vue-i18n.d.ts vendored Executable file
View File

@ -0,0 +1,17 @@
/**
* global type definitions
* using the typescript interface, you can define the i18n resources that is type-safed!
*/
/**
* you need to import the some interfaces
*/
import en from '@/plugins/i18n/locales/en.json';
import 'vue-i18n';
type LocaleMessage = typeof en
declare module 'vue-i18n' {
export interface DefineLocaleMessage extends LocaleMessage {
}
}

View File

@ -0,0 +1,106 @@
<script setup lang="ts">
import UserProfileHeaderBg from '@images/pages/user-profile-header-bg.png'
interface Store {
id_structure: number
nom: string
ip: string
telephone: string
photoLink: string
enseigne: string
adresse: string
}
interface StoreHeaderData {
store: Store
}
interface Props {
storeData: StoreHeaderData
}
const props = defineProps<Props>()
</script>
<template>
<VCard v-if="props">
<VImg
:src="UserProfileHeaderBg"
min-height="80"
max-height="120"
cover
/>
<VCardText class="d-flex align-bottom flex-sm-row flex-column justify-center gap-x-5">
<div class="d-flex h-0">
<VAvatar
rounded
size="120"
:image="props.storeData.store.photoLink"
class="user-profile-avatar mx-auto"
/>
</div>
<div class="user-profile-info w-100 mt-16 pt-6 pt-sm-0 mt-sm-0">
<h5 class="text-h5 text-center text-sm-start font-weight-medium mb-3">
{{ props?.storeData.store.id_structure }} - {{ props?.storeData.store.nom }}
</h5>
<div class="d-flex align-center justify-center justify-sm-space-between flex-wrap gap-4">
<div class="d-flex flex-wrap justify-center justify-sm-start flex-grow-1 gap-4">
<span class="d-flex">
<VIcon
size="20"
icon="tabler-color-swatch"
class="me-1"
/>
<span class="text-body-1">
{{ props?.storeData.store.enseigne }}
</span>
</span>
<span class="d-flex">
<VIcon
size="20"
icon="tabler-phone-call"
class="me-1"
/>
<span class="text-body-1">
{{ props?.storeData.store.telephone }}
</span>
</span>
<span class="d-flex">
<VIcon
size="20"
icon="tabler-map-pin"
class="me-1"
/>
<span class="text-body-1">
{{ props?.storeData.store.adresse }}
</span>
</span>
</div>
<VBtn
prepend-icon="tabler-check"
color="success"
>
Connected
</VBtn>
</div>
</div>
</VCardText>
</VCard>
</template>
<style lang="scss">
.user-profile-avatar {
border: 5px solid rgb(var(--v-theme-surface));
background-color: rgb(var(--v-theme-surface)) !important;
inset-block-start: -3rem;
.v-img__img {
border-radius: 0.125rem;
}
}
</style>

View File

@ -0,0 +1,8 @@
<template>
<div>
<VCard title="STORE TAB ADMIN">
<VCardText>We carefully crafted JWT flow so you can implement JWT with ease and with minimum efforts.</VCardText>
<VCardText>Please read our JWT Documentation to get more out of JWT authentication.</VCardText>
</VCard>
</div>
</template>

View File

@ -0,0 +1,50 @@
<script setup lang="ts">
interface Store {
id_structure: number
nom: string
ip: string
telephone: string
photoLink: string
enseigne: string
}
interface Replication {
pendingReplicationOk: boolean
pendingReplications: number
minPendingReplicationDate: string
maxPendingReplicationDate: string
}
interface Transaction {
backOfficeTransactionOk: boolean
backOfficeTransactions: number
minBackOfficeTransactionDate: string
maxBackOfficeTransactionDate: string
backOfficeBusinessDate: string
}
interface StoreData {
store: Store
replication: Replication
transaction: Transaction
}
interface Props {
storeData: StoreData
}
const props = defineProps<Props>()
</script>
<template>
<!-- 👉 User fullName -->
<h6 class="text-h4 mt-4">
{{ props.storeData.store.id_structure }}
</h6>
<div>
<VCard title="STORE TAB GENERAL">
<VCardText>We carefully crafted JWT flow so you can implement JWT with ease and with minimum efforts.</VCardText>
<VCardText>Please read our JWT Documentation to get more out of JWT authentication.</VCardText>
</VCard>
</div>
</template>

View File

@ -0,0 +1,8 @@
<template>
<div>
<VCard title="STORE TAB ITEM">
<VCardText>We carefully crafted JWT flow so you can implement JWT with ease and with minimum efforts.</VCardText>
<VCardText>Please read our JWT Documentation to get more out of JWT authentication.</VCardText>
</VCard>
</div>
</template>

View File

@ -16,7 +16,7 @@ export const { themeConfig, layoutConfig } = defineThemeConfig({
contentLayoutNav: AppContentLayoutNav.Horizontal,
overlayNavFromBreakpoint: breakpointsVuetify.md + 16, // 16 for scrollbar. Docs: https://next.vuetifyjs.com/en/features/display-and-platform/
i18n: {
enable: false,
enable: true,
defaultLocale: 'en',
langConfig: [
{

4
typed-router.d.ts vendored
View File

@ -42,8 +42,8 @@ declare module 'vue-router/auto/routes' {
'root': RouteRecordInfo<'root', '/', Record<never, never>, Record<never, never>>,
'$error': RouteRecordInfo<'$error', '/:error(.*)', { error: ParamValue<true> }, { error: ParamValue<false> }>,
'login': RouteRecordInfo<'login', '/login', Record<never, never>, Record<never, never>>,
'second-page': RouteRecordInfo<'second-page', '/second-page', Record<never, never>, Record<never, never>>,
'store': RouteRecordInfo<'store', '/store', Record<never, never>, Record<never, never>>,
'store-details': RouteRecordInfo<'store-details', '/store/details', Record<never, never>, Record<never, never>>,
'store-list': RouteRecordInfo<'store-list', '/store/list', Record<never, never>, Record<never, never>>,
}
}