@meeovi/layer-departments 1.0.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/app/components/categories/adultstore.vue +123 -0
- package/app/components/categories/chart/[id].vue +254 -0
- package/app/components/categories/chart/add-chart.vue +142 -0
- package/app/components/categories/chart/chart.vue +82 -0
- package/app/components/categories/chart/monthlyChart.vue +46 -0
- package/app/components/categories/chart/musicchart.vue +35 -0
- package/app/components/categories/chart/update-chart.vue +278 -0
- package/app/components/categories/chart/weeklyChart.vue +46 -0
- package/app/components/categories/chart/yearlyChart.vue +46 -0
- package/app/components/categories/charts.vue +118 -0
- package/app/components/categories/deals.vue +101 -0
- package/app/components/categories/eats.vue +49 -0
- package/app/components/categories/restaurants.vue +77 -0
- package/app/components/categories/station/[id].vue +72 -0
- package/app/components/categories/stations.vue +124 -0
- package/app/components/categories/time/features/alarms.vue +276 -0
- package/app/components/categories/time/features/bedtime.vue +12 -0
- package/app/components/categories/time/features/stopwatch.vue +109 -0
- package/app/components/categories/time/features/timer.vue +191 -0
- package/app/components/categories/time/time.vue +63 -0
- package/app/components/categories/travel.vue +75 -0
- package/app/components/categories/weather/weather.vue +44 -0
- package/app/components/related/relatedcharts.vue +39 -0
- package/app/components/related/short.vue +207 -0
- package/app/components/related/space.vue +79 -0
- package/app/pages/departments/[...slug].vue +385 -0
- package/app/pages/departments/category/[...slug].vue +135 -0
- package/dist/nuxt.config.d.ts +2 -0
- package/dist/nuxt.config.js +7 -0
- package/nuxt.config.ts +11 -0
- package/package.json +35 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div class="card card-1 col-12 col-lg-6" :style="`backgroundImage: url(${$directus.url}assets/${space?.image?.filename_disk})`">
|
|
4
|
+
<div class="card-wrap">
|
|
5
|
+
<div class="item-content">
|
|
6
|
+
<h5 class="card-title-1 mbr-fonts-style align-center display-7">
|
|
7
|
+
<strong>{{ space?.type }}</strong>
|
|
8
|
+
</h5>
|
|
9
|
+
<h6 class="card-subtitle-1 mb-0 mbr-fonts-style align-center display-2">
|
|
10
|
+
<strong>
|
|
11
|
+
{{ space?.name }}
|
|
12
|
+
</strong>
|
|
13
|
+
</h6>
|
|
14
|
+
<p class="card-text-1 mbr-fonts-style align-center mb-0 display-7">Created: {{ new Date(space?.date_created).toLocaleDateString() }}</p>
|
|
15
|
+
<p class="card-text-1 mbr-fonts-style align-center mb-0 display-7">
|
|
16
|
+
{{ space?.description }}
|
|
17
|
+
</p>
|
|
18
|
+
<div class="mbr-section-btn mbr-section-btn-1 align-center">
|
|
19
|
+
<NuxtLink :href="`/social/group/${space?.id}`" class="btn btn-danger display-7" target="_blank">
|
|
20
|
+
<span class="mobi-mbri mobi-mbri-arrow-next mbr-iconfont mbr-iconfont-btn"></span>
|
|
21
|
+
Learn More
|
|
22
|
+
</NuxtLink>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script setup>
|
|
31
|
+
import {
|
|
32
|
+
ref
|
|
33
|
+
} from 'vue'
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
$directus
|
|
37
|
+
} = useNuxtApp()
|
|
38
|
+
|
|
39
|
+
const props = defineProps({
|
|
40
|
+
space: {
|
|
41
|
+
type: Object,
|
|
42
|
+
required: true,
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
space
|
|
48
|
+
} = props
|
|
49
|
+
|
|
50
|
+
const getTypeColor = (type) => {
|
|
51
|
+
const colors = {
|
|
52
|
+
'Audio': 'purple',
|
|
53
|
+
'Video': 'red',
|
|
54
|
+
'Text': 'blue',
|
|
55
|
+
'Mixed': 'green'
|
|
56
|
+
}
|
|
57
|
+
return colors[type] || 'grey'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const formatDate = (dateString) => {
|
|
61
|
+
if (!dateString) return 'Unknown date'
|
|
62
|
+
return new Date(dateString).toLocaleDateString('en-US', {
|
|
63
|
+
year: 'numeric',
|
|
64
|
+
month: 'short',
|
|
65
|
+
day: 'numeric'
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const joinSpace = async () => {
|
|
70
|
+
// TODO: Implement join space functionality
|
|
71
|
+
console.log('Joining space:', space.id)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const shareSpace = () => {
|
|
75
|
+
const url = `${window.location.origin}/social/group/${space.id}`
|
|
76
|
+
navigator.clipboard.writeText(url)
|
|
77
|
+
console.log('Space link copied to clipboard')
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="department?.name === 'Deals'">
|
|
4
|
+
<v-toolbar :style="`background-color: ${department?.color}; color: ${department?.colortext}`"
|
|
5
|
+
:title="department?.name"></v-toolbar>
|
|
6
|
+
<deals :category="department?.id" />
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div v-else>
|
|
10
|
+
<v-card variant="text">
|
|
11
|
+
<v-toolbar :style="`background-color: ${department?.color}; color: ${department?.colortext}`"
|
|
12
|
+
:title="department?.name">
|
|
13
|
+
<v-slide-group show-arrows v-if="department?.categories?.length">
|
|
14
|
+
<v-slide-group-item v-slot="{ isSelected, toggle }">
|
|
15
|
+
<v-menu>
|
|
16
|
+
<template v-slot:activator="{ props }">
|
|
17
|
+
<v-btn :color="isSelected ? 'primary' : undefined" class="ma-2" @click="toggle"
|
|
18
|
+
v-bind="props" append-icon="fas:fa fa-caret-down" variant="text">
|
|
19
|
+
Categories
|
|
20
|
+
</v-btn>
|
|
21
|
+
</template>
|
|
22
|
+
<v-list class="departmentMenu">
|
|
23
|
+
<v-row>
|
|
24
|
+
<v-col cols="3" v-for="categories in department?.categories"
|
|
25
|
+
:key="categories?.id">
|
|
26
|
+
<v-list-item>
|
|
27
|
+
<v-chip><NuxtLink
|
|
28
|
+
:to="`/departments/category/${categories?.categories_id?.slug}`">
|
|
29
|
+
{{ categories?.categories_id?.name }}</NuxtLink></v-chip>
|
|
30
|
+
</v-list-item>
|
|
31
|
+
</v-col>
|
|
32
|
+
</v-row>
|
|
33
|
+
</v-list>
|
|
34
|
+
</v-menu>
|
|
35
|
+
</v-slide-group-item>
|
|
36
|
+
|
|
37
|
+
<v-slide-group-item v-if="department?.menus?.length" v-for="menu in department?.menus"
|
|
38
|
+
:key="menu" v-slot="{ isSelected, toggle }">
|
|
39
|
+
<v-btn :color="isSelected ? 'primary' : undefined" class="ma-2" @click="toggle"
|
|
40
|
+
:href="`${menu?.url}`">
|
|
41
|
+
{{ menu?.name }}
|
|
42
|
+
</v-btn>
|
|
43
|
+
</v-slide-group-item>
|
|
44
|
+
</v-slide-group>
|
|
45
|
+
</v-toolbar>
|
|
46
|
+
|
|
47
|
+
<!--Department Top Banner Section-->
|
|
48
|
+
<section data-bs-version="5.1" class="pricing6 shopm5 cid-tZY31Y2JxZ" id="apricing6-6g">
|
|
49
|
+
|
|
50
|
+
<div class="mbr-overlay"></div>
|
|
51
|
+
<div class="container-fluid">
|
|
52
|
+
<div class="row align-items-stretch items-row justify-content-center">
|
|
53
|
+
|
|
54
|
+
<div class="col-lg-6">
|
|
55
|
+
<div v-if="department?.name === 'Travel'">
|
|
56
|
+
<travel :category="department?.name" />
|
|
57
|
+
</div>
|
|
58
|
+
<!--<div v-else-if="department?.name === 'Appstore'">
|
|
59
|
+
<appstore :category="department?.name" />
|
|
60
|
+
</div>-->
|
|
61
|
+
<div v-else-if="department?.name === 'Weather'">
|
|
62
|
+
<weather :category="department?.name" />
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div v-else-if="department?.name === 'Time'">
|
|
66
|
+
<timeComponent :category="department?.name" />
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div v-else class="mbr-section-head" :style="`background-color: ${department?.color}`">
|
|
70
|
+
<h4 class="mbr-section-title mbr-fonts-style mb-0 display-7"
|
|
71
|
+
:style="`color: ${department?.colortext}`">
|
|
72
|
+
<strong>Meeovi</strong>
|
|
73
|
+
</h4>
|
|
74
|
+
<h5 class="mbr-section-subtitle mbr-fonts-style mb-0 display-2"
|
|
75
|
+
:style="`color: ${department?.colortext}`">
|
|
76
|
+
<strong>{{ department?.name }}</strong>
|
|
77
|
+
</h5>
|
|
78
|
+
<h5 class="main-text mbr-fonts-style mb-0 display-7"
|
|
79
|
+
:style="`color: ${department?.colortext}`">
|
|
80
|
+
{{ department?.description }}
|
|
81
|
+
</h5>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<v-sheet
|
|
86
|
+
class="mx-auto col-lg-6" style="background-color: transparent; box-shadow: none;">
|
|
87
|
+
<h4 style="left: 15px; position: relative;">{{ callouts?.menus?.[1]?.name }}</h4>
|
|
88
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
89
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
90
|
+
v-for="products in department?.products" :key="products">
|
|
91
|
+
<productCard :product="products?.products_id" :class="['ma-4', selectedClass]"
|
|
92
|
+
@click="toggle" />
|
|
93
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
94
|
+
<v-scale-transition>
|
|
95
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline"
|
|
96
|
+
size="48"></v-icon>
|
|
97
|
+
</v-scale-transition>
|
|
98
|
+
</div>
|
|
99
|
+
</v-slide-group-item>
|
|
100
|
+
</v-slide-group>
|
|
101
|
+
</v-sheet>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</section>
|
|
105
|
+
|
|
106
|
+
<!--Department Content Section-->
|
|
107
|
+
<section data-bs-version="5.1" class="gallery2 shopm5 cid-uW1BojE78S" id="agallery2-0" v-if="department?.shorts?.length && department?.products?.products_id?.type === 'department'"
|
|
108
|
+
:style="`background-image: url(${$directus?.url}assets/${department?.image?.filename_disk})`">
|
|
109
|
+
<div class="mbr-overlay" style="opacity: 0.8; background-color: rgb(255, 255, 255);">
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<div class="container-fluid">
|
|
113
|
+
<div class="row">
|
|
114
|
+
<!--Vibez Slider-->
|
|
115
|
+
<v-sheet
|
|
116
|
+
class="mx-auto sliderProducts row align-items-stretch items-row justify-content-center">
|
|
117
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
118
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
119
|
+
v-for="shorts in department?.shorts" :key="shorts">
|
|
120
|
+
<shorts :short="shorts?.shorts_id" :class="['ma-4', selectedClass]"
|
|
121
|
+
@click="toggle" />
|
|
122
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
123
|
+
<v-scale-transition>
|
|
124
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline"
|
|
125
|
+
size="48"></v-icon>
|
|
126
|
+
</v-scale-transition>
|
|
127
|
+
</div>
|
|
128
|
+
</v-slide-group-item>
|
|
129
|
+
</v-slide-group>
|
|
130
|
+
</v-sheet>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</section>
|
|
134
|
+
|
|
135
|
+
<v-row class="departmentRow">
|
|
136
|
+
<!--Best Seller Product Slider-->
|
|
137
|
+
<v-sheet class="mx-auto sliderProducts row align-items-stretch items-row justify-content-center">
|
|
138
|
+
<h4 style="left: 15px; position: relative;">{{ callouts?.menus?.[1]?.name }}</h4>
|
|
139
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
140
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
141
|
+
v-for="products in best?.products" :key="products">
|
|
142
|
+
<productCard :product="products?.products_id" :class="['ma-4', selectedClass]"
|
|
143
|
+
@click="toggle" />
|
|
144
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
145
|
+
<v-scale-transition>
|
|
146
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline"
|
|
147
|
+
size="48"></v-icon>
|
|
148
|
+
</v-scale-transition>
|
|
149
|
+
</div>
|
|
150
|
+
</v-slide-group-item>
|
|
151
|
+
</v-slide-group>
|
|
152
|
+
</v-sheet>
|
|
153
|
+
|
|
154
|
+
<!--List of latest products in the department-->
|
|
155
|
+
<v-sheet class="mx-auto sliderProducts row align-items-stretch items-row justify-content-center">
|
|
156
|
+
<h4 style="left: 15px; position: relative;">{{ callouts?.menus?.[2]?.name }}</h4>
|
|
157
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
158
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
159
|
+
v-for="products in latestProducts?.products" :key="products">
|
|
160
|
+
<productCard :product="products?.products_id" :class="['ma-4', selectedClass]"
|
|
161
|
+
@click="toggle" />
|
|
162
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
163
|
+
<v-scale-transition>
|
|
164
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline"
|
|
165
|
+
size="48"></v-icon>
|
|
166
|
+
</v-scale-transition>
|
|
167
|
+
</div>
|
|
168
|
+
</v-slide-group-item>
|
|
169
|
+
</v-slide-group>
|
|
170
|
+
</v-sheet>
|
|
171
|
+
|
|
172
|
+
<!--List of products in the department-->
|
|
173
|
+
<v-sheet class="mx-auto sliderProducts row align-items-stretch items-row justify-content-center">
|
|
174
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
175
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
176
|
+
v-for="products in department?.products" :key="products">
|
|
177
|
+
<productCard :product="products?.products_id" :class="['ma-4', selectedClass]"
|
|
178
|
+
@click="toggle" />
|
|
179
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
180
|
+
<v-scale-transition>
|
|
181
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline"
|
|
182
|
+
size="48"></v-icon>
|
|
183
|
+
</v-scale-transition>
|
|
184
|
+
</div>
|
|
185
|
+
</v-slide-group-item>
|
|
186
|
+
</v-slide-group>
|
|
187
|
+
</v-sheet>
|
|
188
|
+
|
|
189
|
+
<!--List of events in this department-->
|
|
190
|
+
<v-sheet class="mx-auto sliderProducts row align-items-stretch items-row justify-content-center"
|
|
191
|
+
v-if="department?.products?.products_id?.type === 'department' && events?.length">
|
|
192
|
+
<h4 style="left: 15px; position: relative;">{{ callouts?.menus?.[3]?.name }}
|
|
193
|
+
{{ department?.name }}</h4>
|
|
194
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
195
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
196
|
+
v-for="products in events" :key="products">
|
|
197
|
+
<productCard :product="products?.events_id" :class="['ma-4', selectedClass]"
|
|
198
|
+
@click="toggle" />
|
|
199
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
200
|
+
<v-scale-transition>
|
|
201
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline"
|
|
202
|
+
size="48"></v-icon>
|
|
203
|
+
</v-scale-transition>
|
|
204
|
+
</div>
|
|
205
|
+
</v-slide-group-item>
|
|
206
|
+
</v-slide-group>
|
|
207
|
+
</v-sheet>
|
|
208
|
+
<!---->
|
|
209
|
+
|
|
210
|
+
<!--List of spaces in the department-->
|
|
211
|
+
<v-sheet class="mx-auto sliderProducts row align-items-stretch items-row justify-content-center"
|
|
212
|
+
v-if="department?.spaces?.length">
|
|
213
|
+
<h4 style="left: 15px; position: relative;">{{ callouts?.menus?.[4]?.name }}
|
|
214
|
+
{{ department?.name }}</h4>
|
|
215
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
216
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
217
|
+
v-for="spaces in department?.spaces" :key="spaces">
|
|
218
|
+
<spaces :space="spaces?.spaces_id" :class="['ma-4', selectedClass]" @click="toggle" />
|
|
219
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
220
|
+
<v-scale-transition>
|
|
221
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline"
|
|
222
|
+
size="48"></v-icon>
|
|
223
|
+
</v-scale-transition>
|
|
224
|
+
</div>
|
|
225
|
+
</v-slide-group-item>
|
|
226
|
+
</v-slide-group>
|
|
227
|
+
</v-sheet>
|
|
228
|
+
</v-row>
|
|
229
|
+
</v-card>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
</template>
|
|
233
|
+
|
|
234
|
+
<script setup>
|
|
235
|
+
import shorts from '#social/app/components/related/short.vue'
|
|
236
|
+
import spaces from '#social/app/components/related/space.vue'
|
|
237
|
+
import productCard from '#commerce/app/components/catalog/product/productCard.vue'
|
|
238
|
+
import travel from '@/components/categories/travel.vue'
|
|
239
|
+
import deals from '@/components/categories/deals.vue'
|
|
240
|
+
import timeComponent from '@/components/categories/time/time.vue'
|
|
241
|
+
import weather from '@/components/categories/weather/weather.vue'
|
|
242
|
+
|
|
243
|
+
const route = useRoute()
|
|
244
|
+
const model = ref(null)
|
|
245
|
+
const {
|
|
246
|
+
$directus,
|
|
247
|
+
$readItem,
|
|
248
|
+
$readItems
|
|
249
|
+
} = useNuxtApp()
|
|
250
|
+
|
|
251
|
+
const slug = computed(() => {
|
|
252
|
+
const s = route.params.slug
|
|
253
|
+
return Array.isArray(s) ? s[0] : s
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
const {
|
|
257
|
+
data: departmentRaw
|
|
258
|
+
} = await useAsyncData('department', () => {
|
|
259
|
+
return $directus.request(
|
|
260
|
+
$readItems('departments', {
|
|
261
|
+
fields: [
|
|
262
|
+
'*',
|
|
263
|
+
'categories.categories_id.*',
|
|
264
|
+
'spaces.spaces_id.*',
|
|
265
|
+
'products.products_id.*',
|
|
266
|
+
'products.products_id.image.*',
|
|
267
|
+
'shorts.shorts_id.*',
|
|
268
|
+
'shops.shops_id.*',
|
|
269
|
+
'image.*'
|
|
270
|
+
],
|
|
271
|
+
filter: {
|
|
272
|
+
slug: {
|
|
273
|
+
_eq: slug.value
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
limit: 1
|
|
277
|
+
})
|
|
278
|
+
)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
const department = computed(() => departmentRaw.value?.[0] || null)
|
|
282
|
+
|
|
283
|
+
const {
|
|
284
|
+
data: introProducts
|
|
285
|
+
} = await useAsyncData('introProducts', () => {
|
|
286
|
+
return $directus.request($readItems('departments', {
|
|
287
|
+
fields: ['*', { '*': ['*'] }],
|
|
288
|
+
limit: 2,
|
|
289
|
+
}))
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
const {
|
|
293
|
+
data: best
|
|
294
|
+
} = await useAsyncData('best', () => {
|
|
295
|
+
return $directus.request($readItems('departments', {
|
|
296
|
+
fields: ['*',
|
|
297
|
+
'products.products_id.*',
|
|
298
|
+
'images.*'
|
|
299
|
+
],
|
|
300
|
+
limit: 10,
|
|
301
|
+
filter: {
|
|
302
|
+
showcases: {
|
|
303
|
+
showcases_id: {
|
|
304
|
+
name: {
|
|
305
|
+
_eq: "Best Sellers"
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
}))
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
const {
|
|
314
|
+
data: latestProducts
|
|
315
|
+
} = await useAsyncData('latestProducts', () => {
|
|
316
|
+
return $directus.request($readItems('departments', {
|
|
317
|
+
fields: ['*',
|
|
318
|
+
'products.products_id.*',
|
|
319
|
+
'images.*'
|
|
320
|
+
],
|
|
321
|
+
limit: 10,
|
|
322
|
+
filter: {
|
|
323
|
+
products: {
|
|
324
|
+
products_id: {
|
|
325
|
+
status: {
|
|
326
|
+
_eq: "published"
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
}
|
|
331
|
+
}))
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
const {
|
|
335
|
+
data: limitProducts
|
|
336
|
+
} = await useAsyncData('limitProducts', () => {
|
|
337
|
+
return $directus.request($readItems('departments', {
|
|
338
|
+
fields: ['*',
|
|
339
|
+
'products.products_id.*',
|
|
340
|
+
'images.*'
|
|
341
|
+
],
|
|
342
|
+
limit: 2,
|
|
343
|
+
filter: {
|
|
344
|
+
products: {
|
|
345
|
+
products_id: {
|
|
346
|
+
status: {
|
|
347
|
+
_eq: "published"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
}
|
|
352
|
+
}))
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
const {
|
|
356
|
+
data: events
|
|
357
|
+
} = await useAsyncData('events', () => {
|
|
358
|
+
return $directus.request($readItems('departments', {
|
|
359
|
+
fields: ['*',
|
|
360
|
+
'products.products_id.*',
|
|
361
|
+
'images.*'
|
|
362
|
+
],
|
|
363
|
+
limit: 10,
|
|
364
|
+
filter: {
|
|
365
|
+
products: {
|
|
366
|
+
products_id: {
|
|
367
|
+
type: {
|
|
368
|
+
_eq: "event"
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
}
|
|
373
|
+
}))
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
const {
|
|
377
|
+
data: callouts
|
|
378
|
+
} = await useAsyncData('callouts', () => {
|
|
379
|
+
return $directus.request($readItem('callouts', '2'))
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
useHead({
|
|
383
|
+
title: computed(() => department?.value?.name || 'Department Page')
|
|
384
|
+
});
|
|
385
|
+
</script>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="category">
|
|
3
|
+
|
|
4
|
+
<!-- Special Categories -->
|
|
5
|
+
<div v-if="category.slug === 'charts'">
|
|
6
|
+
<charts />
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div v-else-if="category.slug === 'radio-stations'">
|
|
10
|
+
<stations />
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div v-else-if="category.slug === 'eats'">
|
|
14
|
+
<eats />
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div v-else-if="category.slug === 'restaurants'">
|
|
18
|
+
<div v-for="(shop, i) in restaurantsList" :key="i">
|
|
19
|
+
<restaurants :restaurant="shop" />
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<!-- Default Category Layout -->
|
|
24
|
+
<v-card variant="text" v-else>
|
|
25
|
+
<v-toolbar :style="`background-color: ${category?.color}; color: ${category?.colortext}`">
|
|
26
|
+
<v-toolbar-title><NuxtLink :to="`/departments/${category?.departments?.[0]?.departments_id?.name}`">Meeovi {{ category?.departments?.[0]?.departments_id?.name }}</NuxtLink> - {{ category?.name }}</v-toolbar-title>
|
|
27
|
+
<v-slide-group show-arrows v-if="category?.categories?.length">
|
|
28
|
+
<v-slide-group-item v-slot="{ isSelected, toggle }">
|
|
29
|
+
<v-menu>
|
|
30
|
+
<template #activator="{ props }">
|
|
31
|
+
<v-btn :color="isSelected ? 'primary' : undefined" class="ma-2" v-bind="props"
|
|
32
|
+
variant="text">
|
|
33
|
+
Categories
|
|
34
|
+
</v-btn>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<v-list class="departmentMenu">
|
|
38
|
+
<v-row>
|
|
39
|
+
<v-col cols="3" v-for="sub in category.categories" :key="sub?.categories_id?.id">
|
|
40
|
+
<v-list-item>
|
|
41
|
+
<NuxtLink :to="`/departments/categories/${sub.categories_id.id}`">
|
|
42
|
+
{{ sub.categories_id.name }}
|
|
43
|
+
</NuxtLink>
|
|
44
|
+
</v-list-item>
|
|
45
|
+
</v-col>
|
|
46
|
+
</v-row>
|
|
47
|
+
</v-list>
|
|
48
|
+
</v-menu>
|
|
49
|
+
</v-slide-group-item>
|
|
50
|
+
|
|
51
|
+
<v-slide-group-item v-for="menu in category.menus" :key="menu.id" v-slot="{ isSelected, toggle }">
|
|
52
|
+
<v-btn :color="isSelected ? 'primary' : undefined" class="ma-2" :href="menu.url">
|
|
53
|
+
{{ menu.name }}
|
|
54
|
+
</v-btn>
|
|
55
|
+
</v-slide-group-item>
|
|
56
|
+
</v-slide-group>
|
|
57
|
+
</v-toolbar>
|
|
58
|
+
</v-card>
|
|
59
|
+
|
|
60
|
+
<!-- Product List -->
|
|
61
|
+
<v-row>
|
|
62
|
+
<v-col cols="3" v-for="productRel in category.products" :key="productRel.products_id.id">
|
|
63
|
+
<productCard :product="productRel.products_id" />
|
|
64
|
+
</v-col>
|
|
65
|
+
</v-row>
|
|
66
|
+
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- Loading / Not Found -->
|
|
70
|
+
<div v-else class="p-10 text-center text-xl text-neutral-700">
|
|
71
|
+
Loading category...
|
|
72
|
+
</div>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<script setup>
|
|
76
|
+
import charts from '~/components/categories/charts.vue'
|
|
77
|
+
import stations from '~/components/categories/stations.vue'
|
|
78
|
+
import eats from '~/components/categories/eats.vue'
|
|
79
|
+
import restaurants from '~/components/categories/restaurants.vue'
|
|
80
|
+
import productCard from '#commerce/app/components/catalog/product/productCard.vue'
|
|
81
|
+
|
|
82
|
+
const route = useRoute()
|
|
83
|
+
const {
|
|
84
|
+
$directus,
|
|
85
|
+
$readItem,
|
|
86
|
+
$readItems
|
|
87
|
+
} = useNuxtApp()
|
|
88
|
+
|
|
89
|
+
const slug = computed(() => {
|
|
90
|
+
const s = route.params.slug
|
|
91
|
+
return Array.isArray(s) ? s[0] : s
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const {
|
|
95
|
+
data: categoryRaw
|
|
96
|
+
} = await useAsyncData('categoryRaw', () => {
|
|
97
|
+
return $directus.request(
|
|
98
|
+
$readItems('categories', {
|
|
99
|
+
fields: [
|
|
100
|
+
'*',
|
|
101
|
+
'tags.tags_id.*',
|
|
102
|
+
'departments.departments_id.*',
|
|
103
|
+
'products.products_id.*',
|
|
104
|
+
'products.products_id.image.*',
|
|
105
|
+
'menus.*',
|
|
106
|
+
'image.*'
|
|
107
|
+
],
|
|
108
|
+
filter: {
|
|
109
|
+
slug: {
|
|
110
|
+
_eq: slug.value
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
limit: 1
|
|
114
|
+
})
|
|
115
|
+
)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Unwrap array cleanly
|
|
119
|
+
const category = computed(() => categoryRaw.value?.[0] || null)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
// FETCH SHOPS FOR RESTAURANTS CATEGORY
|
|
123
|
+
const {
|
|
124
|
+
data: restaurantsList
|
|
125
|
+
} = await useAsyncData('restaurantsList', () => {
|
|
126
|
+
return $directus.request($readItems('shops', {
|
|
127
|
+
fields: ['*']
|
|
128
|
+
}))
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// SEO
|
|
132
|
+
useHead({
|
|
133
|
+
title: computed(() => category.value?.name || 'Category Page')
|
|
134
|
+
})
|
|
135
|
+
</script>
|
package/nuxt.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meeovi/layer-departments",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Departments Layer for the Alternate Framework",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"departments",
|
|
7
|
+
"alternate-framework",
|
|
8
|
+
"nuxt",
|
|
9
|
+
"layer"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "Meeovi",
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"module": "dist/index.js",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "vitest",
|
|
25
|
+
"build": "tsc -p tsconfig.json",
|
|
26
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
27
|
+
"lint": "eslint 'src/**/*.{ts,tsx}' || true",
|
|
28
|
+
"clean": "rimraf dist",
|
|
29
|
+
"prepare": "npm run build",
|
|
30
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"nuxt": "^4.3.0"
|
|
34
|
+
}
|
|
35
|
+
}
|