@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.
Files changed (183) hide show
  1. package/package.json +2 -2
  2. package/src/App.css +52 -0
  3. package/src/App.vue +62 -0
  4. package/src/assets/image.png +0 -0
  5. package/src/assets/main.css +3 -0
  6. package/src/assets/tailwind-3.4.17.js +113 -0
  7. package/src/components/LanguageSwitcher.vue +73 -0
  8. package/src/components/SettingsCard.vue +40 -0
  9. package/src/components/builder/CreateForm.vue +128 -0
  10. package/src/components/builder/formTypeSchema.js +145 -0
  11. package/src/components/builder/tabs/index.ts +9 -0
  12. package/src/components/builder/tabs/vs-builder-edit.vue +133 -0
  13. package/src/components/builder/tabs/vs-builder-monaco.vue +29 -0
  14. package/src/components/builder/tabs/vs-builder-preview.vue +39 -0
  15. package/src/components/builder/vs-builder-datatable-controls.vue +138 -0
  16. package/src/components/builder/vs-builder-datatable-form.vue +80 -0
  17. package/src/components/builder/vs-builder-datatable.vue +191 -0
  18. package/src/components/builder/vs-builder-list-item.vue +110 -0
  19. package/src/components/collections/CollectionsBreadcrumb.vue +52 -0
  20. package/src/components/collections/CollectionsGrid.vue +176 -0
  21. package/src/components/collections/ContentBlock.vue +75 -0
  22. package/src/components/collections/formWrapper.vue +156 -0
  23. package/src/components/dashboard/ContentItem.vue +82 -0
  24. package/src/components/dashboard/DashboardHeader.vue +33 -0
  25. package/src/components/dashboard/QuickActions.vue +84 -0
  26. package/src/components/dashboard/RecentContent.vue +54 -0
  27. package/src/components/dashboard/StatCard.vue +63 -0
  28. package/src/components/dashboard/StatsGrid.vue +28 -0
  29. package/src/components/form-components/MonacoEditor.vue +104 -0
  30. package/src/components/form-components/VsFormMeta.vue +40 -0
  31. package/src/components/form-components/VsFormTags.vue +150 -0
  32. package/src/components/form-components/custom-datatable/vs-form-custom-datatable-add.vue +84 -0
  33. package/src/components/form-components/custom-datatable/vs-form-custom-datatable-controls.vue +106 -0
  34. package/src/components/form-components/index.js +23 -0
  35. package/src/components/form-components/reference/vs-form-reference-add.vue +92 -0
  36. package/src/components/form-components/reference/vs-form-reference-controls.vue +101 -0
  37. package/src/components/form-components/reference-list/referenceOptionList.js +78 -0
  38. package/src/components/form-components/reference-list/vs-form-reference-add.vue +145 -0
  39. package/src/components/form-components/reference-list/vs-form-reference-choce.vue +39 -0
  40. package/src/components/form-components/reference-list/vs-form-reference-controls.vue +110 -0
  41. package/src/components/form-components/reference-skeleton/about-skeleton.vue +37 -0
  42. package/src/components/form-components/reference-skeleton/banner-skeleton.vue +29 -0
  43. package/src/components/form-components/reference-skeleton/body-skeleton.vue +56 -0
  44. package/src/components/form-components/reference-skeleton/cards-skeleton.vue +47 -0
  45. package/src/components/form-components/reference-skeleton/documents-skeleton.vue +64 -0
  46. package/src/components/form-components/reference-skeleton/faq-skeleton.vue +64 -0
  47. package/src/components/form-components/reference-skeleton/form-skeleton.vue +41 -0
  48. package/src/components/form-components/reference-skeleton/index.js +36 -0
  49. package/src/components/form-components/reference-skeleton/infoLine-skeleton.vue +37 -0
  50. package/src/components/form-components/reference-skeleton/news-skeleton.vue +54 -0
  51. package/src/components/form-components/reference-skeleton/slider-skeleton.vue +41 -0
  52. package/src/components/form-components/reference-skeleton/tabs-skeleton.vue +40 -0
  53. package/src/components/form-components/reference-skeleton/team-skeleton.vue +103 -0
  54. package/src/components/form-components/reference-skeleton/usefulLinks-skeleton.vue +52 -0
  55. package/src/components/form-components/reference-skeleton/video-skeleton.vue +36 -0
  56. package/src/components/form-components/testReferenceTypes.js +773 -0
  57. package/src/components/form-components/vs-form-color-picker.vue +29 -0
  58. package/src/components/form-components/vs-form-custom-datatable.vue +214 -0
  59. package/src/components/form-components/vs-form-integer.vue +86 -0
  60. package/src/components/form-components/vs-form-key-value.vue +201 -0
  61. package/src/components/form-components/vs-form-marcdown-md.vue +3 -0
  62. package/src/components/form-components/vs-form-media-select.vue +780 -0
  63. package/src/components/form-components/vs-form-reference-list.vue +97 -0
  64. package/src/components/form-components/vs-form-reference.vue +59 -0
  65. package/src/components/form-components/vs-form-relation.vue +30 -0
  66. package/src/components/form-components/vs-form-reletion-link.vue +34 -0
  67. package/src/components/form-components/vs-form-select-collection.vue +0 -0
  68. package/src/components/form-components/vs-form-slug.vue +72 -0
  69. package/src/components/form-components/vs-form-tiptap.vue +7 -0
  70. package/src/components/form-components/vs-richtext-md.vue +3 -0
  71. package/src/components/icons/BellIcon.vue +17 -0
  72. package/src/components/icons/GlobeIcon.vue +18 -0
  73. package/src/components/icons/KeyIcon.vue +20 -0
  74. package/src/components/icons/PaletteIcon.vue +22 -0
  75. package/src/components/icons/SettingsIcon.vue +19 -0
  76. package/src/components/icons/ShieldIcon.vue +18 -0
  77. package/src/components/icons/UsersIcon.vue +19 -0
  78. package/src/components/icons/icon-chevron-right.vue +16 -0
  79. package/src/components/icons/icon-drag.vue +20 -0
  80. package/src/components/icons/icon-file-text.vue +21 -0
  81. package/src/components/icons/icon-folder.vue +18 -0
  82. package/src/components/icons/icon-grid.vue +17 -0
  83. package/src/components/icons/icon-group.vue +19 -0
  84. package/src/components/icons/icon-home.vue +16 -0
  85. package/src/components/icons/icon-image.vue +18 -0
  86. package/src/components/icons/icon-list.vue +20 -0
  87. package/src/components/icons/icon-more.vue +17 -0
  88. package/src/components/icons/icon-plus.vue +17 -0
  89. package/src/components/icons-types/icon-array.vue +22 -0
  90. package/src/components/icons-types/icon-boolean.vue +18 -0
  91. package/src/components/icons-types/icon-datalist.vue +22 -0
  92. package/src/components/icons-types/icon-date.vue +20 -0
  93. package/src/components/icons-types/icon-datetime.vue +20 -0
  94. package/src/components/icons-types/icon-file.vue +21 -0
  95. package/src/components/icons-types/icon-gallery.vue +18 -0
  96. package/src/components/icons-types/icon-image.vue +19 -0
  97. package/src/components/icons-types/icon-integer.vue +20 -0
  98. package/src/components/icons-types/icon-merkdown.vue +18 -0
  99. package/src/components/icons-types/icon-multiselect.vue +22 -0
  100. package/src/components/icons-types/icon-number.vue +20 -0
  101. package/src/components/icons-types/icon-radio.vue +22 -0
  102. package/src/components/icons-types/icon-reference-list.vue +22 -0
  103. package/src/components/icons-types/icon-reference.vue +20 -0
  104. package/src/components/icons-types/icon-relation.vue +22 -0
  105. package/src/components/icons-types/icon-richtext.vue +18 -0
  106. package/src/components/icons-types/icon-select.vue +22 -0
  107. package/src/components/icons-types/icon-slug.vue +19 -0
  108. package/src/components/icons-types/icon-text.vue +19 -0
  109. package/src/components/icons-types/index.js +43 -0
  110. package/src/components/layout/Layout.vue +67 -0
  111. package/src/components/layout/Sidebar.vue +128 -0
  112. package/src/components/media/FileUploadProgress.vue +29 -0
  113. package/src/components/media/MediaBreadcrumb.vue +42 -0
  114. package/src/components/media/MediaCreateFolder.vue +59 -0
  115. package/src/components/media/MediaFileInfo.vue +148 -0
  116. package/src/components/media/MediaGrid.vue +148 -0
  117. package/src/components/media/MediaList.vue +148 -0
  118. package/src/components/media/MediaViewControls.vue +38 -0
  119. package/src/components/media/TypeTag.vue +23 -0
  120. package/src/components/menu/AddNewItemInTree.vue +75 -0
  121. package/src/components/menu/MenuBody.vue +149 -0
  122. package/src/components/menu/MenuItem.vue +73 -0
  123. package/src/components/menu/MenuList.vue +101 -0
  124. package/src/components/referencec/index.ts +7 -0
  125. package/src/components/referencec/vs-reference-faq.vue +61 -0
  126. package/src/components/referencec/vs-reference-user-card.vue +40 -0
  127. package/src/components/settings/NotificationSettings.vue +32 -0
  128. package/src/components/settings/SettingsTable.vue +50 -0
  129. package/src/components/settings/SettingsTitle.vue +33 -0
  130. package/src/components/settings/SettingsToggleItem.vue +25 -0
  131. package/src/components/sidebar/DropdownMenu.vue +34 -0
  132. package/src/components/sidebar/SettingsSidebar.vue +121 -0
  133. package/src/components/sidebar/SidebarFooter.vue +52 -0
  134. package/src/components/sidebar/SidebarHeader.vue +57 -0
  135. package/src/components/sidebar/SidebarMenu.vue +78 -0
  136. package/src/components/ui/EmptyData.vue +76 -0
  137. package/src/components/ui/UniversalTable.vue +310 -0
  138. package/src/components/ui/UniversalTableFilters.vue +0 -0
  139. package/src/components/ui/UniversalTablePagination.vue +118 -0
  140. package/src/components/ui/VsPreview.vue +75 -0
  141. package/src/composables/useCollectionView.ts +21 -0
  142. package/src/composables/useDebounce.ts +26 -0
  143. package/src/composables/useMonaco.ts +28 -0
  144. package/src/composables/useTheme.ts +40 -0
  145. package/src/content/test-slug/metadata.json +1 -0
  146. package/src/i18n.ts +75 -0
  147. package/src/index.css +3 -0
  148. package/src/locales/en.json +778 -0
  149. package/src/locales/uk.json +797 -0
  150. package/src/main.ts +41 -0
  151. package/src/pages/Dashboard.vue +168 -0
  152. package/src/pages/EmailPage.vue +183 -0
  153. package/src/pages/FeedbackPage.vue +232 -0
  154. package/src/pages/MediaPage.vue +372 -0
  155. package/src/pages/TagsPage.vue +207 -0
  156. package/src/pages/builder/BuilderPage.vue +195 -0
  157. package/src/pages/builder/EditCollectionPage.vue +163 -0
  158. package/src/pages/collections/ArticlesPage.vue +385 -0
  159. package/src/pages/collections/CollectionsPage.vue +146 -0
  160. package/src/pages/collections/SingletonsPage.vue +119 -0
  161. package/src/pages/collections/contentForm.vue +484 -0
  162. package/src/pages/collections/schema/seo.ts +27 -0
  163. package/src/pages/menu/MenuAddPage.vue +123 -0
  164. package/src/pages/menu/MenuItemPage.vue +183 -0
  165. package/src/pages/menu/MenuPage.vue +133 -0
  166. package/src/pages/settings/ApiKeys.vue +75 -0
  167. package/src/pages/settings/Appearance.vue +80 -0
  168. package/src/pages/settings/Logs.vue +260 -0
  169. package/src/pages/settings/PermissionsPage.vue +237 -0
  170. package/src/pages/settings/Settings.vue +186 -0
  171. package/src/pages/settings/Users.vue +109 -0
  172. package/src/pages/settings/general.vue +154 -0
  173. package/src/pages/settings/generalScheme.js +132 -0
  174. package/src/pages/users/AddUser.vue +106 -0
  175. package/src/pages/users/UsersPage.vue +98 -0
  176. package/src/props/builder.ts +67 -0
  177. package/src/props/content.ts +56 -0
  178. package/src/props/media.ts +63 -0
  179. package/src/router/index.ts +181 -0
  180. package/src/types/fastify-auth.d.ts +4 -0
  181. package/src/utils/getField.js +270 -0
  182. package/src/utils/translit.js +19 -0
  183. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,186 @@
1
+ <template>
2
+ <div
3
+ class="flex-1 overflow-y-auto bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50 dark:from-slate-900 dark:via-slate-800 dark:to-slate-900"
4
+ >
5
+ <div class="w-full max-w-7xl mx-auto space-y-6">
6
+ <div class="mb-8">
7
+ <div class="flex items-center gap-2">
8
+ <h1 class="text-3xl font-bold text-slate-800 dark:text-slate-100 mb-2">
9
+ {{ $t("settings.title") }}
10
+ </h1>
11
+ <a :href="`https://cms.opengis.info/${locale}/config/general-settings/`" target="_blank" :title="$t('guide.settings')">
12
+ <HelpCircle class="w-5 h-5" />
13
+ </a>
14
+ </div>
15
+ <p class="text-slate-600 dark:text-slate-300">
16
+ {{ $t("settings.description") }}
17
+ </p>
18
+ </div>
19
+ <div class="space-y-6 max-w-7xl mx-auto">
20
+ <!-- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
21
+ <SettingsCard
22
+ v-for="card in cards"
23
+ :key="card.title"
24
+ :path="card.path"
25
+ :title="card.title"
26
+ :description="card.description"
27
+ :button-text="card.buttonText"
28
+ >
29
+ <template #icon>
30
+ <component :is="card.icon" class="w-5 h-5 mr-2" />
31
+ </template>
32
+ </SettingsCard>
33
+ </div> -->
34
+ <div
35
+ class="rounded-xl text-card-foreground shadow-lg border-0 bg-white dark:bg-slate-800 backdrop-blur-sm"
36
+ >
37
+ <div
38
+ class="flex flex-col space-y-1.5 p-6 border-b border-slate-200 dark:border-slate-700 bg-gradient-to-r from-slate-50 dark:from-slate-800 to-white dark:to-slate-800"
39
+ >
40
+ <h3
41
+ class="tracking-tight text-xl font-semibold text-slate-800 dark:text-slate-100 flex items-center"
42
+ >
43
+ <BellIcon class="w-5 h-5 mr-2" />{{ $t("settings.systemStatus") }}
44
+ </h3>
45
+ </div>
46
+ <div class="p-6">
47
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
48
+ <div class="space-y-4">
49
+ <h3 class="font-medium text-slate-800 dark:text-slate-100">
50
+ {{ $t("settings.siteInformation") }}
51
+ </h3>
52
+ <div class="space-y-2">
53
+ <div class="flex justify-between items-center">
54
+ <span class="text-sm text-slate-600 dark:text-slate-400"
55
+ >{{ $t("settings.siteName") }}</span
56
+ ><span
57
+ class="text-sm font-medium text-slate-800 dark:text-slate-100"
58
+ >CMS Pro</span
59
+ >
60
+ </div>
61
+ <div class="flex justify-between items-center">
62
+ <span class="text-sm text-slate-600 dark:text-slate-400"
63
+ >{{ $t("settings.version") }}</span
64
+ ><span
65
+ class="text-sm font-medium text-slate-800 dark:text-slate-100"
66
+ >2.1.0</span
67
+ >
68
+ </div>
69
+ <div class="flex justify-between items-center">
70
+ <span class="text-sm text-slate-600 dark:text-slate-400"
71
+ >{{ $t("settings.environment") }}</span
72
+ >
73
+ <div
74
+ class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent shadow hover:bg-primary/80 bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300"
75
+ >
76
+ {{ $t("settings.production") }}
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ <div class="space-y-4">
82
+ <h3 class="font-medium text-slate-800 dark:text-slate-100">
83
+ {{ $t("settings.systemHealth") }}
84
+ </h3>
85
+ <div class="space-y-2">
86
+ <div class="flex justify-between items-center">
87
+ <span class="text-sm text-slate-600 dark:text-slate-400"
88
+ >{{ $t("settings.database") }}</span
89
+ >
90
+ <div
91
+ class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent shadow hover:bg-primary/80 bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300"
92
+ >
93
+ {{ $t("settings.connected") }}
94
+ </div>
95
+ </div>
96
+ <div class="flex justify-between items-center">
97
+ <span class="text-sm text-slate-600 dark:text-slate-400"
98
+ >{{ $t("settings.cache") }}</span
99
+ >
100
+ <div
101
+ class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent shadow hover:bg-primary/80 bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300"
102
+ >
103
+ {{ $t("settings.active") }}
104
+ </div>
105
+ </div>
106
+ <div class="flex justify-between items-center">
107
+ <span class="text-sm text-slate-600 dark:text-slate-400"
108
+ >{{ $t("settings.storage") }}</span
109
+ >
110
+ <div
111
+ class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent shadow hover:bg-primary/80 bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"
112
+ >
113
+ {{ $t("settings.used") }}
114
+ </div>
115
+ </div>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </template>
125
+
126
+ <script setup lang="ts">
127
+ import SettingsCard from '../../components/SettingsCard.vue'
128
+ import SettingsIcon from '../../components/icons/SettingsIcon.vue'
129
+ import UsersIcon from '../../components/icons/UsersIcon.vue'
130
+ import ShieldIcon from '../../components/icons/ShieldIcon.vue'
131
+ import KeyIcon from '../../components/icons/KeyIcon.vue'
132
+ import PaletteIcon from '../../components/icons/PaletteIcon.vue'
133
+ import GlobeIcon from '../../components/icons/GlobeIcon.vue'
134
+ import BellIcon from '../../components/icons/BellIcon.vue'
135
+ import { HelpCircle } from 'lucide-vue-next'
136
+ import { useI18n } from 'vue-i18n'
137
+
138
+ const { t, locale } = useI18n()
139
+
140
+ const cards = [
141
+ {
142
+ title: t('settings.general.general'),
143
+ path: '/settings/general',
144
+ description: t('settings.general.general'),
145
+ buttonText: t('settings.configure'),
146
+ icon: SettingsIcon
147
+ },
148
+ {
149
+ title: t('settings.users.users'),
150
+ path: '/settings/users',
151
+ description: t('settings.users.users'),
152
+ buttonText: t('settings.configure'),
153
+ icon: UsersIcon
154
+ },
155
+ {
156
+ title: t('settings.security'),
157
+ path: '/settings/security',
158
+ description: t('settings.security'),
159
+ buttonText: t('settings.configure'),
160
+ icon: ShieldIcon
161
+ },
162
+ {
163
+ title: t('settings.api-keys.apiKeys'),
164
+ path: '/settings/api-keys',
165
+ description: t('settings.api-keys.apiKeys'),
166
+ buttonText: t('settings.configure'),
167
+ icon: KeyIcon
168
+ },
169
+ {
170
+ title: t('settings.appearance.appearance'),
171
+ path: '/settings/appearance',
172
+ description: t('settings.appearance.appearance'),
173
+ buttonText: t('settings.configure'),
174
+ icon: PaletteIcon
175
+ },
176
+ {
177
+ title: t('settings.localization'),
178
+ path: '/settings/localization',
179
+ description: t('settings.localization'),
180
+ buttonText: t('settings.configure'),
181
+ icon: GlobeIcon
182
+ }
183
+ ]
184
+ </script>
185
+
186
+ <style scoped></style>
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <div class="w-full max-w-7xl mx-auto space-y-6">
3
+ <SettingsTitle
4
+ :title="$t('settings.users.users')"
5
+ :description="$t('settings.users.usersDescription')"
6
+ />
7
+ <SettingsTable
8
+ v-model="userSettings"
9
+ :icon="Users"
10
+ :title="$t('settings.users.userRegistration')"
11
+ :color="'green'"
12
+ />
13
+ </div>
14
+ </template>
15
+
16
+ <script setup>
17
+ import { Users } from "lucide-vue-next";
18
+ import { ref, onMounted, getCurrentInstance } from "vue";
19
+ import SettingsTitle from "@/components/settings/SettingsTitle.vue";
20
+ import SettingsTable from "@/components/settings/SettingsTable.vue";
21
+ import { useI18n } from "vue-i18n";
22
+ import { notify } from "@opengis/core";
23
+
24
+ const { t } = useI18n();
25
+
26
+ const userSettings = ref([
27
+ {
28
+ key: "allowRegistration",
29
+ label: t("settings.users.allowRegistration"),
30
+ description: t("settings.users.allowRegistrationDescription"),
31
+ checked: true,
32
+ },
33
+ {
34
+ key: "emailVerification",
35
+ label: t("settings.users.emailVerification"),
36
+ description: t("settings.users.emailVerificationDescription"),
37
+ checked: true,
38
+ },
39
+ {
40
+ key: "autoApprove",
41
+ label: t("settings.users.autoApprove"),
42
+ description: t("settings.users.autoApproveDescription"),
43
+ checked: false,
44
+ },
45
+ {
46
+ key: "defaultRole",
47
+ label: t("settings.users.defaultRole"),
48
+ description: "",
49
+ value: "Viewer",
50
+ options: [
51
+ { text: "Viewer", id: "Viewer" },
52
+ { text: "Contributor", id: "Contributor" },
53
+ { text: "Author", id: "Author" },
54
+ ],
55
+ },
56
+ ]);
57
+
58
+ const fetchData = async () => {
59
+ const response = await fetch("/api/settings");
60
+ const data = await response.json();
61
+ setUserSettings(data?.settings?.users || {});
62
+ };
63
+
64
+ const setUserSettings = (data) => {
65
+ if (!data) return;
66
+ Object.entries(data).forEach(([key, value]) => {
67
+ const setting = userSettings.value.find((setting) => setting.key === key);
68
+ if (setting) {
69
+ setting.checked = value;
70
+ }
71
+ });
72
+ };
73
+
74
+ const saveChanges = async () => {
75
+ let data = {
76
+ users: {},
77
+ };
78
+
79
+ userSettings.value.forEach((setting) => {
80
+ data.users[setting.key] = setting.checked;
81
+ });
82
+ try {
83
+ await fetch("/api/settings", {
84
+ method: "POST",
85
+ headers: {
86
+ "Content-Type": "application/json",
87
+ },
88
+ body: JSON.stringify(data),
89
+ });
90
+
91
+ notify({
92
+ title: "Успішно",
93
+ message: "Налаштування успішно збережено",
94
+ type: "success",
95
+ });
96
+ } catch (error) {
97
+ console.error(error);
98
+ notify({
99
+ title: "Помилка",
100
+ message: "Помилка при збереженні налаштувань",
101
+ type: "error",
102
+ });
103
+ }
104
+ };
105
+
106
+ onMounted(() => {
107
+ fetchData();
108
+ });
109
+ </script>
@@ -0,0 +1,154 @@
1
+ <template>
2
+ <div class="w-full max-w-7xl mx-auto space-y-6">
3
+ <SettingsTitle
4
+ :title="$t('settings.general.general')"
5
+ :description="$t('settings.general.generalDescription')"
6
+ />
7
+
8
+ <div class="space-y-6 max-w-7xl mx-auto">
9
+ <SettingsTable
10
+ :icon="Settings"
11
+ :title="$t('settings.general.siteInformation')"
12
+ :color="'blue'"
13
+ >
14
+ <template #content>
15
+ <VsForm
16
+ v-model:form="form"
17
+ :schema="generalScheme(t)"
18
+ v-model="formValues"
19
+ :key="formKey"
20
+ />
21
+ <div class="flex justify-end space-x-2 pt-4">
22
+ <button
23
+ 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 border shadow-sm hover:text-accent-foreground h-9 px-4 py-2 bg-white dark:bg-slate-700 border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-600"
24
+ >
25
+ {{ $t("settings.general.cancel") }}
26
+ </button>
27
+ <button
28
+ @click="saveChanges"
29
+ class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-white 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 text-primary-foreground shadow h-9 px-4 py-2 bg-blue-600 hover:bg-blue-700"
30
+ >
31
+ <Save class="w-4 h-4 mr-2" />
32
+ {{ $t("settings.general.saveChanges") }}
33
+ </button>
34
+ </div>
35
+ </template>
36
+ </SettingsTable>
37
+
38
+ <!-- <SettingsTable v-model="notificationSettings" :icon="Bell" :title="$t('settings.general.notifications')" :color="'blue'" /> -->
39
+ </div>
40
+ </div>
41
+ </template>
42
+
43
+ <script setup>
44
+ import { ref, getCurrentInstance, onMounted } from "vue";
45
+ import { Save, Settings, Bell } from "lucide-vue-next";
46
+ import SettingsTitle from "@/components/settings/SettingsTitle.vue";
47
+ import SettingsTable from "@/components/settings/SettingsTable.vue";
48
+ import { useI18n } from "vue-i18n";
49
+ import generalScheme from "./generalScheme.js";
50
+ import { notify } from "@opengis/core";
51
+ const { t } = useI18n();
52
+ import VsForm from "@opengis/form";
53
+
54
+ const form = ref({});
55
+
56
+ const formValues = ref({});
57
+
58
+ const formKey = ref(0);
59
+
60
+ const fetchData = async () => {
61
+ const response = await fetch("/api/settings");
62
+ const data = await response.json();
63
+ Object.assign(formValues.value, data?.settings?.site_info || {});
64
+ formKey.value++;
65
+ setNotificationSettings(data?.settings?.notifications || {});
66
+ };
67
+
68
+ const setNotificationSettings = (data) => {
69
+ if (!data) return;
70
+ Object.entries(data).forEach(([key, value]) => {
71
+ const setting = notificationSettings.value.find(
72
+ (setting) => setting.key === key
73
+ );
74
+ if (setting) {
75
+ setting.checked = value;
76
+ }
77
+ });
78
+ };
79
+
80
+ const saveChanges = async () => {
81
+ let result = await form.value.validate();
82
+ if (result) {
83
+ notify({
84
+ type: "warning",
85
+ title: t("common.actions.warning"),
86
+ message: t("settings.general.saveChangesFailed"),
87
+ });
88
+ return;
89
+ }
90
+ let data = {
91
+ notifications: {},
92
+ };
93
+
94
+ notificationSettings.value.forEach((setting) => {
95
+ data.notifications[setting.key] = setting.checked;
96
+ });
97
+ data.site_info = formValues.value;
98
+
99
+ try {
100
+ // await fetch("/api/cms-space", { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formValues.value) });
101
+ await fetch("/api/settings", {
102
+ method: "POST",
103
+ headers: {
104
+ "Content-Type": "application/json",
105
+ },
106
+ body: JSON.stringify(data),
107
+ });
108
+
109
+ notify({
110
+ title: t("settings.successTitle"),
111
+ message: t("settings.successMessage"),
112
+ type: "success",
113
+ });
114
+ } catch (error) {
115
+ console.error(error);
116
+ notify({
117
+ title: t("settings.errorTitle"),
118
+ message: t("settings.errorMessage"),
119
+ type: "error",
120
+ });
121
+ }
122
+ };
123
+
124
+ const notificationSettings = ref([
125
+ {
126
+ key: "email",
127
+ label: t("settings.general.emailNotifications"),
128
+ description: t("settings.general.emailNotificationsDescription"),
129
+ checked: true,
130
+ },
131
+ {
132
+ key: "push",
133
+ label: t("settings.general.pushNotifications"),
134
+ description: t("settings.general.pushNotificationsDescription"),
135
+ checked: false,
136
+ },
137
+ {
138
+ key: "sms",
139
+ label: t("settings.general.smsNotifications"),
140
+ description: t("settings.general.smsNotificationsDescription"),
141
+ checked: false,
142
+ },
143
+ {
144
+ key: "desktop",
145
+ label: t("settings.general.desktopNotifications"),
146
+ description: t("settings.general.desktopNotificationsDescription"),
147
+ checked: true,
148
+ },
149
+ ]);
150
+
151
+ onMounted(() => {
152
+ fetchData();
153
+ });
154
+ </script>
@@ -0,0 +1,132 @@
1
+ const scheme = (t) => [
2
+ {
3
+ key: "name",
4
+ label: t('settings.general.siteName'),
5
+ type: "text",
6
+ value: t('settings.general.siteName'),
7
+ col: 6,
8
+ validators: ["required"],
9
+ },
10
+ {
11
+ key: "url",
12
+ label: t('settings.general.siteUrl'),
13
+ type: "text",
14
+ value: t('settings.general.siteUrl'),
15
+ col: 6,
16
+ validators: ["required"],
17
+ },
18
+ {
19
+ key: "previewUrl",
20
+ label: t('settings.general.previewUrl'),
21
+ type: "text",
22
+ value: "http://ip.local.softpro.ua",
23
+ col: 3,
24
+ validators: ["required"],
25
+ },
26
+ {
27
+ key: "logo",
28
+ label: t('settings.general.logoSite'),
29
+ type: "file",
30
+ col: 3,
31
+ validators: ["required"],
32
+ },
33
+ {
34
+ key: "logo-cms",
35
+ label: t('settings.general.logoCms'),
36
+ type: "file",
37
+ col: 3,
38
+ validators: ["required"],
39
+ },
40
+ {
41
+ key: "image-login",
42
+ label: t('settings.general.imageLogin'),
43
+ type: "file",
44
+ col: 3,
45
+ validators: ["required"],
46
+ },
47
+ {
48
+ key: "description",
49
+ label: t('settings.general.siteDescription'),
50
+ type: "textarea",
51
+ value: t('settings.general.siteDescription'),
52
+ },
53
+ {
54
+ key: "language",
55
+ label: t('settings.general.language'),
56
+ type: "select",
57
+ col: 6,
58
+ validators: ["required"],
59
+ options: [
60
+ {
61
+ text: "Ukrainian",
62
+ id: "uk",
63
+ },
64
+ { text: "English", id: "en" },
65
+ ],
66
+ },
67
+ {
68
+ key: "languages",
69
+ label: t('settings.general.languages'),
70
+ type: "select",
71
+ multiple: true,
72
+ col: 6,
73
+ options: [
74
+ {
75
+ text: "Ukrainian",
76
+ id: "uk",
77
+ },
78
+ { text: "English", id: "en" },
79
+ ],
80
+ },
81
+ {
82
+ key: "contacts",
83
+ label: t('settings.general.contacts'),
84
+ type:"DataTable",
85
+ colModel:[
86
+ {
87
+ key:"title",
88
+ ua:t('settings.general.title'),
89
+ type:"text",
90
+ },
91
+ {
92
+ key:"content",
93
+ ua:t('settings.general.content'),
94
+ type:"text",
95
+ },
96
+ {
97
+ key:"url",
98
+ ua:t('settings.general.url'),
99
+ type:"text",
100
+ },
101
+ ]
102
+ },
103
+ {
104
+ key:"showSocialsNumber",
105
+ label: t('settings.general.showSocialsNumber'),
106
+ type:"number",
107
+ },
108
+ {
109
+ key: "socials",
110
+ label: t('settings.general.socials'),
111
+ type:"DataTable",
112
+ colModel:[
113
+ {
114
+ key:"id",
115
+ ua:t('settings.general.name'),
116
+ type:"text",
117
+ },
118
+ {
119
+ key:"link",
120
+ ua:t('settings.general.url'),
121
+ type:"text",
122
+ },
123
+ ]
124
+ },
125
+ {
126
+ key:"mapCoords",
127
+ label: t('settings.general.mapCoords'),
128
+ type:"text",
129
+ }
130
+ ];
131
+
132
+ export default scheme;
@@ -0,0 +1,106 @@
1
+ <template>
2
+ <div class="space-y-6 max-w-6xl mx-auto relative bg-white">
3
+ <div class="p-4 sticky top-[-24px] z-10 bg-white dark:bg-slate-800">
4
+ <div class="flex items-center justify-between">
5
+ <div class="flex items-center gap-4">
6
+ <button
7
+ @click="router.back()"
8
+ class="p-2 text-gray-500 rounded-full hover:text-gray-900 dark:text-gray-400 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700"><svg
9
+ xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
10
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
11
+ class="lucide lucide-arrow-left-icon w-5 h-5">
12
+ <path d="m12 19-7-7 7-7"></path>
13
+ <path d="M19 12H5"></path>
14
+ </svg>
15
+ </button>
16
+ <h1 class="text-2xl font-semibold text-gray-900 dark:text-white">{{ id ? $t("users.editUser" ) : $t("users.createUser") }}</h1>
17
+ </div>
18
+ <div class="flex items-center space-x-3">
19
+ <button
20
+ @click="handleSubmit"
21
+ class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-9 px-4 py-2 bg-blue-600 text-white shadow-md transition-all duration-200 transform hover:bg-blue-700 hover:shadow-lg hover:scale-105 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50">
22
+ <svg
23
+ xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
24
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
25
+ class="lucide lucide-save-icon w-4 h-4 mr-2">
26
+ <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
27
+ <polyline points="17 21 17 13 7 13 7 21"></polyline>
28
+ <polyline points="7 3 7 8 15 8"></polyline>
29
+ </svg>
30
+ {{ $t("common.actions.save") }}</button>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ <div class="container mx-auto bg-white rounded-md pb-4 px-5">
35
+ <VsForm v-if="schema" v-model="formValue" v-model:form="form" :schema="schema" />
36
+ </div>
37
+ </div>
38
+ </template>
39
+
40
+ <script setup>
41
+ import { onMounted, ref } from 'vue';
42
+ import VsForm from '@opengis/form';
43
+ import { useRouter, useRoute } from 'vue-router';
44
+ import { useI18n } from 'vue-i18n';
45
+ import { notify } from '@opengis/core';
46
+ const schema = ref(null);
47
+ const formValue = ref({});
48
+ const form = ref({});
49
+ const loading = ref(true);
50
+ const router = useRouter();
51
+ const { t } = useI18n();
52
+ const token = ref(null);
53
+ const id = useRoute().params.id;
54
+
55
+ const handleSubmit = async () => {
56
+ let method = id ? 'PUT' : 'POST';
57
+
58
+ try {
59
+ let result = await form.value.validate();
60
+
61
+ if (result) {
62
+ notify({
63
+ type: "warning",
64
+ title: t("common.actions.warning"),
65
+ message: t("users.createUserFailed"),
66
+ });
67
+ return;
68
+ }
69
+
70
+ await fetch(`/api/table/${token.value}`, { method: method, body: JSON.stringify(formValue.value), headers: { 'Content-Type': 'application/json' } });
71
+
72
+ notify({
73
+ type: "success",
74
+ title: t("common.actions.success"),
75
+ message: t("users.createUserSuccess"),
76
+ });
77
+
78
+ router.back();
79
+ } catch (error) {
80
+ console.error(error);
81
+ }
82
+ }
83
+
84
+
85
+ const fetchData = async () => {
86
+ let url = id ? `/api/form/admin.users.table/${id}` : '/api/form/admin.users.table';
87
+
88
+ try {
89
+ const response = await fetch(url);
90
+ const data = await response.json();
91
+
92
+ formValue.value = data?.data || {};
93
+ token.value = data.token;
94
+ schema.value = data.schema
95
+ } catch (error) {
96
+ schema.value = [];
97
+ } finally {
98
+ loading.value = false;
99
+ }
100
+ }
101
+
102
+
103
+ onMounted(() => {
104
+ fetchData();
105
+ })
106
+ </script>