@opengis/cms 0.0.21 → 0.0.22
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/package.json +2 -2
- package/src/App.css +52 -0
- package/src/App.vue +62 -0
- package/src/assets/image.png +0 -0
- package/src/assets/main.css +3 -0
- package/src/assets/tailwind-3.4.17.js +113 -0
- package/src/components/LanguageSwitcher.vue +73 -0
- package/src/components/SettingsCard.vue +40 -0
- package/src/components/builder/CreateForm.vue +128 -0
- package/src/components/builder/formTypeSchema.js +145 -0
- package/src/components/builder/tabs/index.ts +9 -0
- package/src/components/builder/tabs/vs-builder-edit.vue +133 -0
- package/src/components/builder/tabs/vs-builder-monaco.vue +29 -0
- package/src/components/builder/tabs/vs-builder-preview.vue +39 -0
- package/src/components/builder/vs-builder-datatable-controls.vue +138 -0
- package/src/components/builder/vs-builder-datatable-form.vue +80 -0
- package/src/components/builder/vs-builder-datatable.vue +191 -0
- package/src/components/builder/vs-builder-list-item.vue +110 -0
- package/src/components/collections/CollectionsBreadcrumb.vue +52 -0
- package/src/components/collections/CollectionsGrid.vue +176 -0
- package/src/components/collections/ContentBlock.vue +75 -0
- package/src/components/collections/formWrapper.vue +156 -0
- package/src/components/dashboard/ContentItem.vue +82 -0
- package/src/components/dashboard/DashboardHeader.vue +33 -0
- package/src/components/dashboard/QuickActions.vue +84 -0
- package/src/components/dashboard/RecentContent.vue +54 -0
- package/src/components/dashboard/StatCard.vue +63 -0
- package/src/components/dashboard/StatsGrid.vue +28 -0
- package/src/components/form-components/MonacoEditor.vue +104 -0
- package/src/components/form-components/VsFormMeta.vue +40 -0
- package/src/components/form-components/VsFormTags.vue +150 -0
- package/src/components/form-components/custom-datatable/vs-form-custom-datatable-add.vue +84 -0
- package/src/components/form-components/custom-datatable/vs-form-custom-datatable-controls.vue +106 -0
- package/src/components/form-components/index.js +23 -0
- package/src/components/form-components/reference/vs-form-reference-add.vue +92 -0
- package/src/components/form-components/reference/vs-form-reference-controls.vue +101 -0
- package/src/components/form-components/reference-list/referenceOptionList.js +78 -0
- package/src/components/form-components/reference-list/vs-form-reference-add.vue +145 -0
- package/src/components/form-components/reference-list/vs-form-reference-choce.vue +39 -0
- package/src/components/form-components/reference-list/vs-form-reference-controls.vue +110 -0
- package/src/components/form-components/reference-skeleton/about-skeleton.vue +37 -0
- package/src/components/form-components/reference-skeleton/banner-skeleton.vue +29 -0
- package/src/components/form-components/reference-skeleton/body-skeleton.vue +56 -0
- package/src/components/form-components/reference-skeleton/cards-skeleton.vue +47 -0
- package/src/components/form-components/reference-skeleton/documents-skeleton.vue +64 -0
- package/src/components/form-components/reference-skeleton/faq-skeleton.vue +64 -0
- package/src/components/form-components/reference-skeleton/form-skeleton.vue +41 -0
- package/src/components/form-components/reference-skeleton/index.js +36 -0
- package/src/components/form-components/reference-skeleton/infoLine-skeleton.vue +37 -0
- package/src/components/form-components/reference-skeleton/news-skeleton.vue +54 -0
- package/src/components/form-components/reference-skeleton/slider-skeleton.vue +41 -0
- package/src/components/form-components/reference-skeleton/tabs-skeleton.vue +40 -0
- package/src/components/form-components/reference-skeleton/team-skeleton.vue +103 -0
- package/src/components/form-components/reference-skeleton/usefulLinks-skeleton.vue +52 -0
- package/src/components/form-components/reference-skeleton/video-skeleton.vue +36 -0
- package/src/components/form-components/testReferenceTypes.js +773 -0
- package/src/components/form-components/vs-form-color-picker.vue +29 -0
- package/src/components/form-components/vs-form-custom-datatable.vue +214 -0
- package/src/components/form-components/vs-form-integer.vue +86 -0
- package/src/components/form-components/vs-form-key-value.vue +201 -0
- package/src/components/form-components/vs-form-marcdown-md.vue +3 -0
- package/src/components/form-components/vs-form-media-select.vue +780 -0
- package/src/components/form-components/vs-form-reference-list.vue +97 -0
- package/src/components/form-components/vs-form-reference.vue +59 -0
- package/src/components/form-components/vs-form-relation.vue +30 -0
- package/src/components/form-components/vs-form-reletion-link.vue +34 -0
- package/src/components/form-components/vs-form-select-collection.vue +0 -0
- package/src/components/form-components/vs-form-slug.vue +72 -0
- package/src/components/form-components/vs-form-tiptap.vue +7 -0
- package/src/components/form-components/vs-richtext-md.vue +3 -0
- package/src/components/icons/BellIcon.vue +17 -0
- package/src/components/icons/GlobeIcon.vue +18 -0
- package/src/components/icons/KeyIcon.vue +20 -0
- package/src/components/icons/PaletteIcon.vue +22 -0
- package/src/components/icons/SettingsIcon.vue +19 -0
- package/src/components/icons/ShieldIcon.vue +18 -0
- package/src/components/icons/UsersIcon.vue +19 -0
- package/src/components/icons/icon-chevron-right.vue +16 -0
- package/src/components/icons/icon-drag.vue +20 -0
- package/src/components/icons/icon-file-text.vue +21 -0
- package/src/components/icons/icon-folder.vue +18 -0
- package/src/components/icons/icon-grid.vue +17 -0
- package/src/components/icons/icon-group.vue +19 -0
- package/src/components/icons/icon-home.vue +16 -0
- package/src/components/icons/icon-image.vue +18 -0
- package/src/components/icons/icon-list.vue +20 -0
- package/src/components/icons/icon-more.vue +17 -0
- package/src/components/icons/icon-plus.vue +17 -0
- package/src/components/icons-types/icon-array.vue +22 -0
- package/src/components/icons-types/icon-boolean.vue +18 -0
- package/src/components/icons-types/icon-datalist.vue +22 -0
- package/src/components/icons-types/icon-date.vue +20 -0
- package/src/components/icons-types/icon-datetime.vue +20 -0
- package/src/components/icons-types/icon-file.vue +21 -0
- package/src/components/icons-types/icon-gallery.vue +18 -0
- package/src/components/icons-types/icon-image.vue +19 -0
- package/src/components/icons-types/icon-integer.vue +20 -0
- package/src/components/icons-types/icon-merkdown.vue +18 -0
- package/src/components/icons-types/icon-multiselect.vue +22 -0
- package/src/components/icons-types/icon-number.vue +20 -0
- package/src/components/icons-types/icon-radio.vue +22 -0
- package/src/components/icons-types/icon-reference-list.vue +22 -0
- package/src/components/icons-types/icon-reference.vue +20 -0
- package/src/components/icons-types/icon-relation.vue +22 -0
- package/src/components/icons-types/icon-richtext.vue +18 -0
- package/src/components/icons-types/icon-select.vue +22 -0
- package/src/components/icons-types/icon-slug.vue +19 -0
- package/src/components/icons-types/icon-text.vue +19 -0
- package/src/components/icons-types/index.js +43 -0
- package/src/components/layout/Layout.vue +67 -0
- package/src/components/layout/Sidebar.vue +128 -0
- package/src/components/media/FileUploadProgress.vue +29 -0
- package/src/components/media/MediaBreadcrumb.vue +42 -0
- package/src/components/media/MediaCreateFolder.vue +59 -0
- package/src/components/media/MediaFileInfo.vue +148 -0
- package/src/components/media/MediaGrid.vue +148 -0
- package/src/components/media/MediaList.vue +148 -0
- package/src/components/media/MediaViewControls.vue +38 -0
- package/src/components/media/TypeTag.vue +23 -0
- package/src/components/menu/AddNewItemInTree.vue +75 -0
- package/src/components/menu/MenuBody.vue +149 -0
- package/src/components/menu/MenuItem.vue +73 -0
- package/src/components/menu/MenuList.vue +101 -0
- package/src/components/referencec/index.ts +7 -0
- package/src/components/referencec/vs-reference-faq.vue +61 -0
- package/src/components/referencec/vs-reference-user-card.vue +40 -0
- package/src/components/settings/NotificationSettings.vue +32 -0
- package/src/components/settings/SettingsTable.vue +50 -0
- package/src/components/settings/SettingsTitle.vue +33 -0
- package/src/components/settings/SettingsToggleItem.vue +25 -0
- package/src/components/sidebar/DropdownMenu.vue +34 -0
- package/src/components/sidebar/SettingsSidebar.vue +121 -0
- package/src/components/sidebar/SidebarFooter.vue +52 -0
- package/src/components/sidebar/SidebarHeader.vue +57 -0
- package/src/components/sidebar/SidebarMenu.vue +78 -0
- package/src/components/ui/EmptyData.vue +76 -0
- package/src/components/ui/UniversalTable.vue +310 -0
- package/src/components/ui/UniversalTableFilters.vue +0 -0
- package/src/components/ui/UniversalTablePagination.vue +118 -0
- package/src/components/ui/VsPreview.vue +75 -0
- package/src/composables/useCollectionView.ts +21 -0
- package/src/composables/useDebounce.ts +26 -0
- package/src/composables/useMonaco.ts +28 -0
- package/src/composables/useTheme.ts +40 -0
- package/src/content/test-slug/metadata.json +1 -0
- package/src/i18n.ts +75 -0
- package/src/index.css +3 -0
- package/src/locales/en.json +778 -0
- package/src/locales/uk.json +797 -0
- package/src/main.ts +41 -0
- package/src/pages/Dashboard.vue +168 -0
- package/src/pages/EmailPage.vue +183 -0
- package/src/pages/FeedbackPage.vue +232 -0
- package/src/pages/MediaPage.vue +372 -0
- package/src/pages/TagsPage.vue +207 -0
- package/src/pages/builder/BuilderPage.vue +195 -0
- package/src/pages/builder/EditCollectionPage.vue +163 -0
- package/src/pages/collections/ArticlesPage.vue +385 -0
- package/src/pages/collections/CollectionsPage.vue +146 -0
- package/src/pages/collections/SingletonsPage.vue +119 -0
- package/src/pages/collections/contentForm.vue +484 -0
- package/src/pages/collections/schema/seo.ts +27 -0
- package/src/pages/menu/MenuAddPage.vue +123 -0
- package/src/pages/menu/MenuItemPage.vue +183 -0
- package/src/pages/menu/MenuPage.vue +133 -0
- package/src/pages/settings/ApiKeys.vue +75 -0
- package/src/pages/settings/Appearance.vue +80 -0
- package/src/pages/settings/Logs.vue +260 -0
- package/src/pages/settings/PermissionsPage.vue +237 -0
- package/src/pages/settings/Settings.vue +186 -0
- package/src/pages/settings/Users.vue +109 -0
- package/src/pages/settings/general.vue +154 -0
- package/src/pages/settings/generalScheme.js +132 -0
- package/src/pages/users/AddUser.vue +106 -0
- package/src/pages/users/UsersPage.vue +98 -0
- package/src/props/builder.ts +67 -0
- package/src/props/content.ts +56 -0
- package/src/props/media.ts +63 -0
- package/src/router/index.ts +181 -0
- package/src/types/fastify-auth.d.ts +4 -0
- package/src/utils/getField.js +270 -0
- package/src/utils/translit.js +19 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="ml-2 border-l border-slate-200 dark:border-slate-600">
|
|
3
|
+
<div v-for="item in navigationItems" :key="item.id">
|
|
4
|
+
<router-link
|
|
5
|
+
class="w-full flex items-center space-x-3 px-6 py-2.5 mb-1 rounded-lg text-left transition-all duration-200 group border-0 text-sm"
|
|
6
|
+
:class="[
|
|
7
|
+
currentPath === item.path
|
|
8
|
+
? 'bg-blue-50 text-blue-700 border border-blue-200 dark:bg-slate-700 dark:text-slate-100'
|
|
9
|
+
: 'bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-800 dark:bg-transparent dark:text-slate-300 dark:hover:bg-slate-700 dark:hover:text-slate-100'
|
|
10
|
+
]"
|
|
11
|
+
:to="item.path"
|
|
12
|
+
>
|
|
13
|
+
<component :is="item.icon" class="w-5 h-5 shrink-0" />
|
|
14
|
+
<span class="font-medium flex-1" v-if="item.label">{{ $t(item.label) }}</span>
|
|
15
|
+
<span class="font-medium flex-1" v-else>{{ item.title }}</span>
|
|
16
|
+
<div class="rounded-md border font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 hover:bg-secondary/80 text-xs px-2 py-0.5 min-w-[20px] h-5 flex items-center justify-center bg-blue-100 text-blue-700 border-blue-200 dark:bg-slate-600 dark:text-slate-200 dark:border-slate-500">
|
|
17
|
+
{{ item.entries }}
|
|
18
|
+
</div>
|
|
19
|
+
</router-link>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { defineProps, ref, computed } from 'vue';
|
|
26
|
+
import { useRoute } from 'vue-router';
|
|
27
|
+
|
|
28
|
+
defineProps<{
|
|
29
|
+
navigationItems: any[];
|
|
30
|
+
}>();
|
|
31
|
+
|
|
32
|
+
const route = useRoute();
|
|
33
|
+
const currentPath = computed(() => route.path);
|
|
34
|
+
</script>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<aside
|
|
3
|
+
class="flex-col hidden w-64 transition-colors duration-200 bg-white border-r border-gray-200 md:flex dark:border-gray-700 dark:bg-gray-800"
|
|
4
|
+
>
|
|
5
|
+
<div class="p-3 border-b border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 flex-shrink-0">
|
|
6
|
+
<div class="flex items-center justify-between">
|
|
7
|
+
<div class="w-full flex items-center justify-between">
|
|
8
|
+
<router-link
|
|
9
|
+
to="/"
|
|
10
|
+
class="inline-flex items-center justify-center whitespace-nowrap transition-colors rounded-md h-8 px-2 bg-white text-slate-600 border-0 text-sm font-medium
|
|
11
|
+
hover:bg-slate-50 hover:text-slate-800 dark:bg-slate-800 dark:hover:bg-slate-700 dark:text-slate-300 dark:hover:text-slate-100"
|
|
12
|
+
>
|
|
13
|
+
<ArrowLeft class="w-4 h-4 mr-2" />
|
|
14
|
+
<span>{{ $t("navigation.back") }}</span>
|
|
15
|
+
</router-link>
|
|
16
|
+
<div class="ml-auto">
|
|
17
|
+
<LanguageSwitcher />
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<router-link to="/settings" class="p-3 border-b border-slate-100 dark:border-slate-700 bg-white dark:bg-slate-800 flex-shrink-0">
|
|
24
|
+
<h2 class="text-lg font-semibold text-slate-800 dark:text-slate-100">
|
|
25
|
+
{{ $t("navigation.settings") }}
|
|
26
|
+
</h2>
|
|
27
|
+
<p class="text-sm text-slate-500 dark:text-slate-400">
|
|
28
|
+
{{ $t("navigation.settingsDescription") }}
|
|
29
|
+
</p>
|
|
30
|
+
</router-link>
|
|
31
|
+
|
|
32
|
+
<nav class="flex-1 p-2 space-y-1 overflow-y-auto">
|
|
33
|
+
<template v-for="item in navigationItems" :key="item.id">
|
|
34
|
+
<router-link
|
|
35
|
+
v-if="!item.children"
|
|
36
|
+
:to="item.path"
|
|
37
|
+
class="w-full flex items-center space-x-3 px-3 py-2.5 mb-1 rounded-lg text-left transition-all duration-200 group border-0"
|
|
38
|
+
:class="[
|
|
39
|
+
currentPath === item.path
|
|
40
|
+
? 'bg-blue-50 text-blue-700 border border-blue-200 dark:bg-slate-700 dark:text-slate-100'
|
|
41
|
+
: 'bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-800 dark:bg-transparent dark:text-slate-300 dark:hover:bg-slate-700 dark:hover:text-slate-100'
|
|
42
|
+
]"
|
|
43
|
+
>
|
|
44
|
+
<component :is="item.icon" class="w-5 h-5 mr-3" />
|
|
45
|
+
<span class="font-medium flex-1">{{ $t(item.label) }}</span>
|
|
46
|
+
</router-link>
|
|
47
|
+
</template>
|
|
48
|
+
</nav>
|
|
49
|
+
<SidebarFooter />
|
|
50
|
+
</aside>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<script setup lang="ts">
|
|
54
|
+
import { computed } from "vue";
|
|
55
|
+
import {
|
|
56
|
+
User as UserIcon,
|
|
57
|
+
LogOut,
|
|
58
|
+
Settings,
|
|
59
|
+
Users,
|
|
60
|
+
ArrowLeft,
|
|
61
|
+
Key,
|
|
62
|
+
Palette,
|
|
63
|
+
TableCellsMerge,
|
|
64
|
+
Shield,
|
|
65
|
+
Layers
|
|
66
|
+
} from "lucide-vue-next";
|
|
67
|
+
import LanguageSwitcher from "../LanguageSwitcher.vue";
|
|
68
|
+
import { useRoute } from "vue-router";
|
|
69
|
+
import SidebarFooter from "./SidebarFooter.vue";
|
|
70
|
+
|
|
71
|
+
const route = useRoute();
|
|
72
|
+
|
|
73
|
+
const currentPath = computed(() => route.path);
|
|
74
|
+
|
|
75
|
+
const navigationItems = [
|
|
76
|
+
{
|
|
77
|
+
id: "settings",
|
|
78
|
+
path: "/settings/general",
|
|
79
|
+
label: "navigation.general",
|
|
80
|
+
icon: Settings,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "collections",
|
|
84
|
+
path: "/settings/collections",
|
|
85
|
+
label: "navigation.builder",
|
|
86
|
+
icon: Layers,
|
|
87
|
+
},
|
|
88
|
+
// {
|
|
89
|
+
// id: "settings",
|
|
90
|
+
// path: "/settings/users",
|
|
91
|
+
// label: "navigation.users",
|
|
92
|
+
// icon: Users,
|
|
93
|
+
// },
|
|
94
|
+
// {
|
|
95
|
+
// id: "settings",
|
|
96
|
+
// path: "/settings/api-keys",
|
|
97
|
+
// label: "navigation.apiKeys",
|
|
98
|
+
// icon: Key,
|
|
99
|
+
// },
|
|
100
|
+
// {
|
|
101
|
+
// id: "appearance",
|
|
102
|
+
// path: "/settings/appearance",
|
|
103
|
+
// label: "navigation.appearance",
|
|
104
|
+
// icon: Palette,
|
|
105
|
+
// },
|
|
106
|
+
{
|
|
107
|
+
id: "logs",
|
|
108
|
+
path: "/settings/logs",
|
|
109
|
+
label: "navigation.logs",
|
|
110
|
+
icon: TableCellsMerge
|
|
111
|
+
},
|
|
112
|
+
// {
|
|
113
|
+
// id: "permissions",
|
|
114
|
+
// path: "/settings/permissions",
|
|
115
|
+
// label: "navigation.permissions",
|
|
116
|
+
// icon: Shield,
|
|
117
|
+
// },
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
</script>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="border-t border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 flex-shrink-0">
|
|
3
|
+
<div class="p-3 bg-white dark:bg-slate-800">
|
|
4
|
+
<div class="w-full flex items-center space-x-3 p-2 rounded-lg bg-white hover:bg-slate-50 transition-colors group text-slate-700 hover:text-slate-900 border-0 dark:bg-slate-800 dark:hover:bg-slate-700 dark:text-slate-200 dark:hover:text-slate-100" type="button" id="radix-:r0:" aria-haspopup="menu" aria-expanded="false" data-state="closed">
|
|
5
|
+
<div class="relative">
|
|
6
|
+
<span class="relative flex shrink-0 overflow-hidden rounded-full w-9 h-9 border-2 border-white dark:border-slate-600 shadow-sm">
|
|
7
|
+
<div
|
|
8
|
+
class="flex items-center justify-center w-8 h-8 text-white rounded-full bg-sky-500"
|
|
9
|
+
>
|
|
10
|
+
<UserIcon class="w-4 h-4" />
|
|
11
|
+
</div>
|
|
12
|
+
</span>
|
|
13
|
+
|
|
14
|
+
<div class="absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 border-white dark:border-slate-600 bg-green-500">
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="flex-1 text-left">
|
|
18
|
+
<p class="font-semibold whitespace-nowrap text-slate-800 dark:text-slate-100 text-sm group-hover:text-slate-900 dark:group-hover:text-slate-50">
|
|
19
|
+
Softpro User
|
|
20
|
+
</p>
|
|
21
|
+
<p class="text-xs text-slate-500 dark:text-slate-400 group-hover:text-slate-600 dark:group-hover:text-slate-300">
|
|
22
|
+
Admin
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
<button
|
|
26
|
+
class="inline-flex items-center justify-center whitespace-nowrap font-medium rounded-md text-xs h-8 w-8 p-0 ml-auto bg-transparent text-slate-600 transition-all duration-200 border-0
|
|
27
|
+
hover:bg-slate-100 hover:text-slate-800
|
|
28
|
+
dark:hover:bg-slate-600 dark:text-slate-300 dark:hover:text-white"
|
|
29
|
+
@click="toggleTheme"
|
|
30
|
+
>
|
|
31
|
+
<Moon v-if="theme !== 'dark'" class="w-5 h-5" />
|
|
32
|
+
<Sun v-else class="w-5 h-5" />
|
|
33
|
+
</button>
|
|
34
|
+
<div class="flex items-center space-x-2 ml-auto">
|
|
35
|
+
<a
|
|
36
|
+
href="/logout"
|
|
37
|
+
class="relative p-2 text-gray-500 transition-colors rounded-full hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700"
|
|
38
|
+
>
|
|
39
|
+
<LogOut class="w-5 h-5" />
|
|
40
|
+
</a>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import { Moon, Sun, LogOut, User as UserIcon } from "lucide-vue-next";
|
|
49
|
+
import { useTheme } from "../../composables/useTheme";
|
|
50
|
+
|
|
51
|
+
const { toggleTheme, theme } = useTheme();
|
|
52
|
+
</script>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="flex items-center h-16 px-6 border-b gap-2 border-gray-200 dark:border-gray-700"
|
|
4
|
+
>
|
|
5
|
+
<router-link
|
|
6
|
+
to="/"
|
|
7
|
+
class="flex items-center text-xl font-semibold text-gray-900 dark:text-white"
|
|
8
|
+
>
|
|
9
|
+
<img
|
|
10
|
+
v-if="logoCms"
|
|
11
|
+
:src="logoCms"
|
|
12
|
+
:alt="siteName || 'CMS'"
|
|
13
|
+
class="w-auto h-10 mr-2 text-sky-500"
|
|
14
|
+
/>
|
|
15
|
+
<img
|
|
16
|
+
v-else
|
|
17
|
+
src="/public/images/logo.png"
|
|
18
|
+
alt="Keystatic"
|
|
19
|
+
class="w-auto h-10 mr-2 text-sky-500"
|
|
20
|
+
/>
|
|
21
|
+
<!-- <span v-if="siteName">{{ siteName }}</span> -->
|
|
22
|
+
</router-link>
|
|
23
|
+
<a :href="`https://cms.opengis.info/${locale}/`" target="_blank" :title="$t('guide.help')">
|
|
24
|
+
<HelpCircle class="w-5 h-5" />
|
|
25
|
+
</a>
|
|
26
|
+
<div class="flex items-center space-x-2 ml-auto">
|
|
27
|
+
<LanguageSwitcher />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script setup lang="ts">
|
|
33
|
+
import { ref, onMounted } from "vue";
|
|
34
|
+
import LanguageSwitcher from "../LanguageSwitcher.vue";
|
|
35
|
+
import { HelpCircle } from "lucide-vue-next";
|
|
36
|
+
import { useI18n } from "vue-i18n";
|
|
37
|
+
|
|
38
|
+
const { locale } = useI18n();
|
|
39
|
+
const siteName = ref<string>("");
|
|
40
|
+
const logoCms = ref<string>("");
|
|
41
|
+
|
|
42
|
+
const fetchSettings = async () => {
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetch("/api/settings");
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
const siteInfo = data?.settings?.site_info || {};
|
|
47
|
+
siteName.value = siteInfo.name || "";
|
|
48
|
+
logoCms.value = siteInfo["logo-cms"] || "";
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error("Error fetching settings:", error);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
onMounted(() => {
|
|
55
|
+
fetchSettings();
|
|
56
|
+
});
|
|
57
|
+
</script>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template v-for="item in navigationItems" :key="item.id">
|
|
3
|
+
<router-link
|
|
4
|
+
v-if="!item.children"
|
|
5
|
+
:to="item.path"
|
|
6
|
+
class="w-full flex items-center space-x-3 px-3 py-2.5 mb-1 rounded-lg text-left transition-all duration-200 group border-0"
|
|
7
|
+
:class="[
|
|
8
|
+
isActive(item)
|
|
9
|
+
? 'bg-blue-50 text-blue-700 border border-blue-200 dark:bg-slate-700 dark:text-slate-100'
|
|
10
|
+
: 'bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-800 dark:bg-transparent dark:text-slate-300 dark:hover:bg-slate-700 dark:hover:text-slate-100'
|
|
11
|
+
]"
|
|
12
|
+
>
|
|
13
|
+
<component :is="item.icon" class="w-5 h-5" />
|
|
14
|
+
<span class="font-medium flex-1" v-if="item.label">{{ $t(item.label) }}</span>
|
|
15
|
+
<span class="font-medium flex-1" v-else>{{ item.title }}</span>
|
|
16
|
+
</router-link>
|
|
17
|
+
|
|
18
|
+
<button
|
|
19
|
+
v-else
|
|
20
|
+
@click="toggleDropdown(item.id)"
|
|
21
|
+
class="w-full flex items-center space-x-3 px-3 py-2.5 mb-1 rounded-lg text-left transition-all duration-200 group border-0"
|
|
22
|
+
:class="[
|
|
23
|
+
isActive(item)
|
|
24
|
+
? 'bg-blue-50 text-blue-700 border border-blue-200 dark:bg-slate-700 dark:text-slate-100'
|
|
25
|
+
: 'bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-800 dark:bg-transparent dark:text-slate-300 dark:hover:bg-slate-700 dark:hover:text-slate-100'
|
|
26
|
+
]"
|
|
27
|
+
>
|
|
28
|
+
<component :is="item.icon" class="w-5 h-5" />
|
|
29
|
+
<span class="font-medium flex-1" v-if="item.label">{{ $t(item.label) }}</span>
|
|
30
|
+
<span class="font-medium flex-1" v-else>{{ item.title }}</span>
|
|
31
|
+
<component
|
|
32
|
+
:is="openDropdowns.includes(item.id) ? ChevronUp : ChevronDown"
|
|
33
|
+
class="w-4 h-4 text-slate-400 dark:text-slate-500"
|
|
34
|
+
/>
|
|
35
|
+
</button>
|
|
36
|
+
|
|
37
|
+
<transition name="slide-fade">
|
|
38
|
+
<DropdownMenu
|
|
39
|
+
v-if="item.children"
|
|
40
|
+
v-show="openDropdowns.includes(item.id)"
|
|
41
|
+
:navigation-items="item.children || []"
|
|
42
|
+
/>
|
|
43
|
+
</transition>
|
|
44
|
+
</template>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import { ChevronDown, ChevronUp } from 'lucide-vue-next';
|
|
49
|
+
import { defineProps, ref, computed } from 'vue';
|
|
50
|
+
import { useRoute } from 'vue-router';
|
|
51
|
+
import DropdownMenu from './DropdownMenu.vue';
|
|
52
|
+
|
|
53
|
+
defineProps<{
|
|
54
|
+
navigationItems: any[];
|
|
55
|
+
}>();
|
|
56
|
+
|
|
57
|
+
const route = useRoute();
|
|
58
|
+
const openDropdowns = ref<string[]>([]);
|
|
59
|
+
const currentPath = computed(() => route.path);
|
|
60
|
+
|
|
61
|
+
const isActive = (item: any) => {
|
|
62
|
+
if (item.path === '/collections') {
|
|
63
|
+
// Підсвічуємо "Керування" для всіх сторінок колекцій
|
|
64
|
+
return route.path === '/collections' || route.path.startsWith('/collections/');
|
|
65
|
+
}
|
|
66
|
+
return currentPath.value === item.path;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const toggleDropdown = (id: string) => {
|
|
70
|
+
const index = openDropdowns.value.indexOf(id);
|
|
71
|
+
|
|
72
|
+
if (index > -1) {
|
|
73
|
+
openDropdowns.value.splice(index, 1);
|
|
74
|
+
} else {
|
|
75
|
+
openDropdowns.value.push(id);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
</script>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="rounded-xl text-card-foreground shadow-lg bg-white dark:bg-slate-800 backdrop-blur-sm border-2 border-dashed border-slate-300 dark:border-slate-600">
|
|
3
|
+
<div class="p-12 text-center">
|
|
4
|
+
<svg
|
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6
|
+
width="24"
|
|
7
|
+
height="24"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
fill="none"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
stroke-width="2"
|
|
12
|
+
stroke-linecap="round"
|
|
13
|
+
stroke-linejoin="round"
|
|
14
|
+
class="lucide lucide-layers w-12 h-12 mx-auto mb-4 text-slate-400 dark:text-slate-500"
|
|
15
|
+
>
|
|
16
|
+
<path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"></path>
|
|
17
|
+
<path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"></path>
|
|
18
|
+
<path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"></path>
|
|
19
|
+
</svg>
|
|
20
|
+
|
|
21
|
+
<h3 class="text-lg font-medium text-slate-600 dark:text-slate-300 mb-2">
|
|
22
|
+
{{ $t(title) }}
|
|
23
|
+
</h3>
|
|
24
|
+
|
|
25
|
+
<p class="text-slate-500 dark:text-slate-400 mb-6">
|
|
26
|
+
{{ $t(description) }}
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
<button
|
|
30
|
+
v-if="showButton"
|
|
31
|
+
@click="$emit('action')"
|
|
32
|
+
class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 shadow h-9 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white"
|
|
33
|
+
>
|
|
34
|
+
<svg
|
|
35
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
36
|
+
width="24"
|
|
37
|
+
height="24"
|
|
38
|
+
viewBox="0 0 24 24"
|
|
39
|
+
fill="none"
|
|
40
|
+
stroke="currentColor"
|
|
41
|
+
stroke-width="2"
|
|
42
|
+
stroke-linecap="round"
|
|
43
|
+
stroke-linejoin="round"
|
|
44
|
+
class="lucide lucide-plus w-4 h-4 mr-2"
|
|
45
|
+
>
|
|
46
|
+
<path d="M5 12h14"></path>
|
|
47
|
+
<path d="M12 5v14"></path>
|
|
48
|
+
</svg>
|
|
49
|
+
{{ $t(buttonText) }}
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script setup>
|
|
56
|
+
defineProps({
|
|
57
|
+
title: {
|
|
58
|
+
type: String,
|
|
59
|
+
default: () => 'emptyData.title'
|
|
60
|
+
},
|
|
61
|
+
description: {
|
|
62
|
+
type: String,
|
|
63
|
+
default: () => 'emptyData.description'
|
|
64
|
+
},
|
|
65
|
+
buttonText: {
|
|
66
|
+
type: String,
|
|
67
|
+
default: () => 'emptyData.buttonText'
|
|
68
|
+
},
|
|
69
|
+
showButton: {
|
|
70
|
+
type: Boolean,
|
|
71
|
+
default: true
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
defineEmits(['action'])
|
|
76
|
+
</script>
|