@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.
Files changed (32) hide show
  1. package/app/components/categories/adultstore.vue +123 -0
  2. package/app/components/categories/chart/[id].vue +254 -0
  3. package/app/components/categories/chart/add-chart.vue +142 -0
  4. package/app/components/categories/chart/chart.vue +82 -0
  5. package/app/components/categories/chart/monthlyChart.vue +46 -0
  6. package/app/components/categories/chart/musicchart.vue +35 -0
  7. package/app/components/categories/chart/update-chart.vue +278 -0
  8. package/app/components/categories/chart/weeklyChart.vue +46 -0
  9. package/app/components/categories/chart/yearlyChart.vue +46 -0
  10. package/app/components/categories/charts.vue +118 -0
  11. package/app/components/categories/deals.vue +101 -0
  12. package/app/components/categories/eats.vue +49 -0
  13. package/app/components/categories/restaurants.vue +77 -0
  14. package/app/components/categories/station/[id].vue +72 -0
  15. package/app/components/categories/stations.vue +124 -0
  16. package/app/components/categories/time/features/alarms.vue +276 -0
  17. package/app/components/categories/time/features/bedtime.vue +12 -0
  18. package/app/components/categories/time/features/stopwatch.vue +109 -0
  19. package/app/components/categories/time/features/timer.vue +191 -0
  20. package/app/components/categories/time/time.vue +63 -0
  21. package/app/components/categories/travel.vue +75 -0
  22. package/app/components/categories/weather/weather.vue +44 -0
  23. package/app/components/related/relatedcharts.vue +39 -0
  24. package/app/components/related/short.vue +207 -0
  25. package/app/components/related/space.vue +79 -0
  26. package/app/pages/departments/[...slug].vue +385 -0
  27. package/app/pages/departments/category/[...slug].vue +135 -0
  28. package/dist/nuxt.config.d.ts +2 -0
  29. package/dist/nuxt.config.js +7 -0
  30. package/nuxt.config.ts +11 -0
  31. package/package.json +35 -0
  32. package/tsconfig.json +14 -0
@@ -0,0 +1,191 @@
1
+ <template>
2
+ <div>
3
+ <v-container>
4
+ <v-row justify="center">
5
+ <v-col cols="12" md="6">
6
+ <v-card class="text-center pa-4">
7
+ <v-card-title class="text-h4 justify-center">
8
+ Timer
9
+ </v-card-title>
10
+
11
+ <v-card-text>
12
+ <!-- Timer Display -->
13
+ <div class="text-h2 my-4">
14
+ {{ displayTime }}
15
+ </div>
16
+
17
+ <!-- Time Input Fields (shown when timer is not running) -->
18
+ <v-row justify="center" v-if="!isRunning && !isPaused">
19
+ <v-col cols="auto">
20
+ <v-text-field v-model="hours" label="Hours" type="number" min="0" max="99"
21
+ style="width: 100px" @input="validateInput('hours')"></v-text-field>
22
+ </v-col>
23
+ <v-col cols="auto">
24
+ <v-text-field v-model="minutes" label="Minutes" type="number" min="0" max="59"
25
+ style="width: 100px" @input="validateInput('minutes')"></v-text-field>
26
+ </v-col>
27
+ <v-col cols="auto">
28
+ <v-text-field v-model="seconds" label="Seconds" type="number" min="0" max="59"
29
+ style="width: 100px" @input="validateInput('seconds')"></v-text-field>
30
+ </v-col>
31
+ </v-row>
32
+
33
+ <!-- Control Buttons -->
34
+ <v-row justify="center" class="mt-4">
35
+ <v-col cols="auto">
36
+ <v-btn color="primary" @click="startTimer" :disabled="isRunning || !isValidInput">
37
+ Start
38
+ </v-btn>
39
+ </v-col>
40
+ <v-col cols="auto">
41
+ <v-btn color="warning" @click="pauseTimer" :disabled="!isRunning">
42
+ Pause
43
+ </v-btn>
44
+ </v-col>
45
+ <v-col cols="auto">
46
+ <v-btn color="error" @click="resetTimer"
47
+ :disabled="!isRunning && !isPaused && totalSeconds === 0">
48
+ Reset
49
+ </v-btn>
50
+ </v-col>
51
+ </v-row>
52
+ </v-card-text>
53
+ </v-card>
54
+ </v-col>
55
+ </v-row>
56
+ </v-container>
57
+ </div>
58
+ </template>
59
+
60
+ <script setup>
61
+ import {
62
+ ref,
63
+ computed,
64
+ onBeforeUnmount
65
+ } from 'vue'
66
+
67
+ useHead({
68
+ title: 'Meeovi Time - Timer'
69
+ })
70
+
71
+ // State variables
72
+ const hours = ref('0')
73
+ const minutes = ref('0')
74
+ const seconds = ref('0')
75
+ const remainingTime = ref(0)
76
+ const isRunning = ref(false)
77
+ const isPaused = ref(false)
78
+ const timer = ref(null)
79
+
80
+ // Computed properties
81
+ const isValidInput = computed(() => {
82
+ const h = parseInt(hours.value) || 0
83
+ const m = parseInt(minutes.value) || 0
84
+ const s = parseInt(seconds.value) || 0
85
+ return h > 0 || m > 0 || s > 0
86
+ })
87
+
88
+ const totalSeconds = computed(() => {
89
+ return remainingTime.value
90
+ })
91
+
92
+ const displayTime = computed(() => {
93
+ const h = Math.floor(totalSeconds.value / 3600)
94
+ const m = Math.floor((totalSeconds.value % 3600) / 60)
95
+ const s = totalSeconds.value % 60
96
+ return `${padZero(h)}:${padZero(m)}:${padZero(s)}`
97
+ })
98
+
99
+ // Methods
100
+ const validateInput = (field) => {
101
+ switch (field) {
102
+ case 'hours':
103
+ hours.value = Math.min(Math.max(parseInt(hours.value) || 0, 0), 99).toString()
104
+ break
105
+ case 'minutes':
106
+ minutes.value = Math.min(Math.max(parseInt(minutes.value) || 0, 0), 59).toString()
107
+ break
108
+ case 'seconds':
109
+ seconds.value = Math.min(Math.max(parseInt(seconds.value) || 0, 0), 59).toString()
110
+ break
111
+ }
112
+ }
113
+
114
+ const startTimer = () => {
115
+ if (!isRunning.value) {
116
+ if (!isPaused.value) {
117
+ // Calculate total seconds from input
118
+ remainingTime.value = (
119
+ parseInt(hours.value) * 3600 +
120
+ parseInt(minutes.value) * 60 +
121
+ parseInt(seconds.value)
122
+ )
123
+ }
124
+ isRunning.value = true
125
+ isPaused.value = false
126
+
127
+ timer.value = setInterval(() => {
128
+ if (remainingTime.value > 0) {
129
+ remainingTime.value--
130
+ } else {
131
+ playAlarm()
132
+ resetTimer()
133
+ }
134
+ }, 1000)
135
+ }
136
+ }
137
+
138
+ const pauseTimer = () => {
139
+ if (isRunning.value) {
140
+ clearInterval(timer.value)
141
+ isRunning.value = false
142
+ isPaused.value = true
143
+ }
144
+ }
145
+
146
+ const resetTimer = () => {
147
+ clearInterval(timer.value)
148
+ isRunning.value = false
149
+ isPaused.value = false
150
+ remainingTime.value = 0
151
+ hours.value = '0'
152
+ minutes.value = '0'
153
+ seconds.value = '0'
154
+ }
155
+
156
+ const playAlarm = () => {
157
+ // Play sound when timer ends
158
+ const audio = new Audio()
159
+ audio.src = '/path/to/alarm-sound.mp3' // Add your alarm sound file
160
+ audio.play().catch(error => console.log('Error playing alarm:', error))
161
+ }
162
+
163
+ const padZero = (num) => {
164
+ return String(num).padStart(2, '0')
165
+ }
166
+
167
+ // Cleanup on component unmount
168
+ onBeforeUnmount(() => {
169
+ if (timer.value) {
170
+ clearInterval(timer.value)
171
+ }
172
+ })
173
+ </script>
174
+
175
+ <style scoped>
176
+ .v-card {
177
+ max-width: 600px;
178
+ margin: auto;
179
+ }
180
+
181
+ /* Remove spinner buttons from number inputs */
182
+ input::-webkit-outer-spin-button,
183
+ input::-webkit-inner-spin-button {
184
+ -webkit-appearance: none;
185
+ margin: 0;
186
+ }
187
+
188
+ input[type=number] {
189
+ -moz-appearance: textfield;
190
+ }
191
+ </style>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <div>
3
+ <h1 class="mbr-section-title mbr-fonts-style mbr-white mb-3 display-1" id="dateTime">
4
+ </h1>
5
+ </div>
6
+ </template>
7
+
8
+ <script setup>
9
+ import {
10
+ onMounted,
11
+ onUnmounted
12
+ } from 'vue';
13
+ const {
14
+ $directus,
15
+ $readItem
16
+ } = useNuxtApp()
17
+
18
+ const {
19
+ data: department
20
+ } = await useAsyncData('department', () => {
21
+ return $directus.request($readItem('departments', '68', {
22
+ fields: ['*', '*', '*']
23
+ }))
24
+ })
25
+
26
+ let intervalId;
27
+
28
+ const updateDateTime = () => {
29
+ const months = [
30
+ "January", "February", "March", "April", "May", "June", "July",
31
+ "August", "September", "October", "November", "December"
32
+ ];
33
+ const date = new Date();
34
+ const monthName = months[date.getMonth()];
35
+ const formattedDate = date.toLocaleString();
36
+
37
+ const dateTimeElement = document.getElementById("dateTime");
38
+ if (dateTimeElement) {
39
+ dateTimeElement.innerHTML = `${monthName} ${formattedDate}`;
40
+ }
41
+ };
42
+
43
+ onMounted(() => {
44
+ updateDateTime(); // Initial update
45
+ intervalId = setInterval(updateDateTime, 1000);
46
+ });
47
+
48
+ onUnmounted(() => {
49
+ if (intervalId) {
50
+ clearInterval(intervalId);
51
+ }
52
+ });
53
+
54
+ const props = defineProps({
55
+ category: {
56
+ type: String,
57
+ required: true,
58
+ },
59
+ });
60
+ const {
61
+ category
62
+ } = props;
63
+ </script>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <div>
3
+ <div id="map" style="height: 400px;"></div>
4
+ <p v-if="locationError">{{ locationError }}</p>
5
+ </div>
6
+ </template>
7
+
8
+ <script setup>
9
+ import {
10
+ onMounted,
11
+ ref
12
+ } from 'vue';
13
+
14
+ const props = defineProps({
15
+ category: {
16
+ type: String,
17
+ required: true,
18
+ },
19
+ });
20
+
21
+ const mapInstance = ref(null);
22
+ const locationError = ref(null);
23
+
24
+ onMounted(() => {
25
+ import('leaflet').then((L) => {
26
+ // Create the map with a default view
27
+ mapInstance.value = L.map('map').setView([0, 0], 2);
28
+
29
+ // Add the tile layer
30
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
31
+ attribution: '&copy; <NuxtLink to="https://www.openstreetmap.org/">OpenStreetMap</NuxtLink> contributors',
32
+ maxZoom: 18,
33
+ }).addTo(mapInstance.value);
34
+
35
+ // Try to get the user's location
36
+ if ("geolocation" in navigator) {
37
+ navigator.geolocation.getCurrentPosition(
38
+ (position) => {
39
+ const {
40
+ latitude,
41
+ longitude
42
+ } = position.coords;
43
+ mapInstance.value.setView([latitude, longitude], 13);
44
+
45
+ // Add a marker at the user's location
46
+ L.marker([latitude, longitude])
47
+ .addTo(mapInstance.value)
48
+ .bindPopup('You are here!')
49
+ .openPopup();
50
+ },
51
+ (error) => {
52
+ console.error("Error getting location:", error.message);
53
+ locationError.value =
54
+ `Unable to get your location: ${error.message}. Using default view.`;
55
+ // Fallback to a default location (e.g., New York City)
56
+ mapInstance.value.setView([40.7128, -74.0060], 13);
57
+ }, {
58
+ enableHighAccuracy: true,
59
+ timeout: 10000, // Increased timeout to 10 seconds
60
+ maximumAge: 0
61
+ }
62
+ );
63
+ } else {
64
+ locationError.value =
65
+ "Geolocation is not supported by this browser. Using default view.";
66
+ // Fallback to a default location
67
+ mapInstance.value.setView([40.7128, -74.0060], 13);
68
+ }
69
+ });
70
+ });
71
+ </script>
72
+
73
+ <style>
74
+ @import 'leaflet/dist/leaflet.css';
75
+ </style>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <div>
3
+ <div id="ww_5134399f73857" v='1.3' loc='auto' a='{"t":"responsive","lang":"en","sl_lpl":1,"ids":[],"font":"Arial","sl_ics":"one_a","sl_sot":"celsius","cl_bkg":"image","cl_font":"#FFFFFF","cl_cloud":"#FFFFFF","cl_persp":"#81D4FA","cl_sun":"#FFC107","cl_moon":"#FFC107","cl_thund":"#FF5722","cl_odd":"#0000000a"}'>Weather Data Source: <NuxtLink href="https://wetterlang.de" id="ww_5134399f73857_u" target="_blank">Wetter vorhersage 30 tage</NuxtLink></div>
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import {
9
+ onMounted,
10
+ ref
11
+ } from 'vue'
12
+
13
+ const widgetId = 'ww_5134399f73857'
14
+
15
+ const loadExternalScript = (src) => {
16
+ return new Promise((resolve, reject) => {
17
+ const script = document.createElement('script')
18
+ script.type = 'text/javascript';
19
+ script.src = src
20
+ script.async = true
21
+ script.onload = resolve
22
+ document.head.appendChild(script)
23
+ })
24
+ }
25
+
26
+ onMounted(async () => {
27
+ try {
28
+ await loadExternalScript(`https://app3.weatherwidget.org/js/?id=${widgetId}`)
29
+ // You may need a delay to allow the widget script to initialize
30
+ setTimeout(() => {
31
+ // Check if additional initialization is needed here
32
+ }, 100)
33
+ } catch (error) {
34
+ console.error('Failed to load weather widget script:', error)
35
+ }
36
+ })
37
+
38
+ const props = defineProps({
39
+ category: {
40
+ type: String,
41
+ required: true,
42
+ },
43
+ });
44
+ </script>
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <div>
3
+ <v-sheet class="mx-auto row align-items-stretch items-row">
4
+ <v-toolbar title="Charts within the community" color="transparent">
5
+ <div><NuxtLink to="/departments/categories/charts/">All Charts</NuxtLink></div>
6
+ </v-toolbar>
7
+ <v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
8
+ <v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }" v-for="(result, index) in chart" :key="index">
9
+ <charts style="margin: 10px;" :chart="result" />
10
+ </v-slide-group-item>
11
+ </v-slide-group>
12
+ </v-sheet>
13
+ </div>
14
+ </template>
15
+
16
+ <script setup>
17
+ import {
18
+ ref,
19
+ } from 'vue';
20
+ import charts from '../categories/chart/musicchart.vue'
21
+ import createchart from '../categories/chart/add-chart.vue'
22
+ //import {groups} from '~/graphql/cms/queries/groups'
23
+ //import { getGroups } from '~/composables/social/getGroups.js'; // Import the composable function
24
+
25
+ const tab = ref(null);
26
+ const model = ref(null);
27
+ const {
28
+ $directus,
29
+ $readItems
30
+ } = useNuxtApp()
31
+
32
+ const {
33
+ data: chart
34
+ } = await useAsyncData('chart', () => {
35
+ return $directus.request($readItems('musicchart', {
36
+ fields: ['*', { '*': ['*'] }]
37
+ }))
38
+ })
39
+ </script>
@@ -0,0 +1,207 @@
1
+ <template>
2
+ <v-card class="mb-4" elevation="2">
3
+ <v-card-title class="d-flex align-center">
4
+ <v-avatar size="40" class="mr-3">
5
+ <v-img :src="short?.creator_avatar || '/images/display-2.png'" :alt="short?.creator" />
6
+ </v-avatar>
7
+ <div>
8
+ <div class="font-weight-bold">{{ short?.creator || 'Anonymous' }}</div>
9
+ <div class="text-caption text-grey">{{ formatDate(short?.date_created) }}</div>
10
+ </div>
11
+ <v-spacer />
12
+ <v-menu>
13
+ <template v-slot:activator="{ props }">
14
+ <v-btn icon="mdi-dots-vertical" variant="text" v-bind="props" />
15
+ </template>
16
+ <v-list>
17
+ <v-list-item @click="shareVibe">
18
+ <v-list-item-title>Share Vibe</v-list-item-title>
19
+ </v-list-item>
20
+ <v-list-item @click="reportVibe">
21
+ <v-list-item-title>Report</v-list-item-title>
22
+ </v-list-item>
23
+ </v-list>
24
+ </v-menu>
25
+ </v-card-title>
26
+
27
+ <video
28
+ ref="videoRef"
29
+ class="vibe-video"
30
+ :src="`${$directus.url}/assets/${short?.video?.filename_disk}`"
31
+ controls
32
+ preload="metadata"
33
+ @click="togglePlay"
34
+ />
35
+
36
+ <v-card-text>
37
+ <h4 v-if="short?.name" class="mb-2">{{ short.name }}</h4>
38
+ <p v-if="short?.description">{{ short.description }}</p>
39
+
40
+ <div v-if="hashtags.length" class="mt-2">
41
+ <v-chip
42
+ v-for="tag in hashtags"
43
+ :key="tag"
44
+ size="small"
45
+ color="primary"
46
+ variant="outlined"
47
+ class="mr-1 mb-1"
48
+ @click="$emit('hashtag-click', tag)"
49
+ >
50
+ #{{ tag }}
51
+ </v-chip>
52
+ </div>
53
+ </v-card-text>
54
+
55
+ <v-card-actions>
56
+ <v-btn icon="mdi-heart" variant="text" @click="toggleLike" :color="isLiked ? 'red' : 'grey'" />
57
+ <span class="text-caption">{{ short?.likes_count || 0 }}</span>
58
+
59
+ <v-btn icon="mdi-comment" variant="text" @click="toggleComments" />
60
+ <span class="text-caption">{{ short?.comments_count || 0 }}</span>
61
+
62
+ <v-btn icon="fas:fa:fa share-nodes" variant="text" @click="shareVibe" />
63
+ <span class="text-caption">{{ short?.shares_count || 0 }}</span>
64
+
65
+ <v-spacer />
66
+ <v-btn :to="`/social/vibe/${short?.id}`" variant="text" size="small">View Vibe</v-btn>
67
+ </v-card-actions>
68
+
69
+ <!-- Comments Section -->
70
+ <v-expand-transition>
71
+ <div v-show="showComments">
72
+ <v-divider />
73
+ <v-card-text>
74
+ <v-text-field
75
+ v-model="newComment"
76
+ label="Add a comment..."
77
+ variant="outlined"
78
+ density="compact"
79
+ append-inner-icon="mdi-send"
80
+ @click:append-inner="addComment"
81
+ @keyup.enter="addComment"
82
+ />
83
+
84
+ <div v-for="comment in vibeComments" :key="comment.id" class="mb-2">
85
+ <div class="d-flex align-start">
86
+ <v-avatar size="32" class="mr-2">
87
+ <v-img :src="comment.user_avatar || '/default-avatar.png'" />
88
+ </v-avatar>
89
+ <div>
90
+ <div class="font-weight-bold text-caption">{{ comment.username }}</div>
91
+ <div class="text-body-2">{{ comment.content }}</div>
92
+ <div class="text-caption text-grey">{{ formatDate(comment.date_created) }}</div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </v-card-text>
97
+ </div>
98
+ </v-expand-transition>
99
+ </v-card>
100
+ </template>
101
+
102
+ <script setup>
103
+ import { ref, computed, onMounted, onUnmounted } from 'vue'
104
+
105
+ const props = defineProps({
106
+ short: {
107
+ type: Object,
108
+ required: true,
109
+ },
110
+ })
111
+
112
+ const emit = defineEmits(['hashtag-click', 'comment-click', 'share'])
113
+
114
+ const { short } = props
115
+ const videoRef = ref(null)
116
+ const isLiked = ref(false)
117
+ const showComments = ref(false)
118
+ const newComment = ref('')
119
+ const vibeComments = ref([])
120
+
121
+ const hashtags = computed(() => {
122
+ if (!short?.description) return []
123
+ const matches = short.description.match(/#(\w+)/g)
124
+ return matches ? matches.map(tag => tag.slice(1)) : []
125
+ })
126
+
127
+ const formatDate = (dateString) => {
128
+ if (!dateString) return 'Unknown date'
129
+ return new Date(dateString).toLocaleDateString('en-US', {
130
+ year: 'numeric',
131
+ month: 'short',
132
+ day: 'numeric',
133
+ hour: '2-digit',
134
+ minute: '2-digit'
135
+ })
136
+ }
137
+
138
+ const togglePlay = () => {
139
+ if (videoRef.value) {
140
+ if (videoRef.value.paused) {
141
+ videoRef.value.play()
142
+ } else {
143
+ videoRef.value.pause()
144
+ }
145
+ }
146
+ }
147
+
148
+ const toggleLike = async () => {
149
+ isLiked.value = !isLiked.value
150
+ // TODO: Implement like functionality with Directus
151
+ }
152
+
153
+ const toggleComments = () => {
154
+ showComments.value = !showComments.value
155
+ if (showComments.value && vibeComments.value.length === 0) {
156
+ loadComments()
157
+ }
158
+ }
159
+
160
+ const addComment = async () => {
161
+ if (!newComment.value.trim()) return
162
+
163
+ // TODO: Implement comment creation with Directus
164
+ const comment = {
165
+ id: Date.now(),
166
+ username: 'Current User',
167
+ content: newComment.value,
168
+ date_created: new Date().toISOString()
169
+ }
170
+
171
+ vibeComments.value.unshift(comment)
172
+ newComment.value = ''
173
+ }
174
+
175
+ const loadComments = async () => {
176
+ // TODO: Load comments from Directus
177
+ vibeComments.value = [
178
+ {
179
+ id: 1,
180
+ username: 'User1',
181
+ content: 'Great vibe!',
182
+ date_created: new Date().toISOString()
183
+ }
184
+ ]
185
+ }
186
+
187
+ const shareVibe = () => {
188
+ emit('share', short)
189
+ }
190
+
191
+ const reportVibe = () => {
192
+ console.log('Reporting vibe:', short.id)
193
+ }
194
+ </script>
195
+
196
+ <style scoped>
197
+ .vibe-video {
198
+ width: 100%;
199
+ max-height: 400px;
200
+ object-fit: cover;
201
+ cursor: pointer;
202
+ }
203
+
204
+ .vibe-video:hover {
205
+ opacity: 0.9;
206
+ }
207
+ </style>