@opengis/cms 0.0.22 → 0.0.24

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 (211) hide show
  1. package/package.json +2 -10
  2. package/src/index.js +23 -0
  3. package/module/cms/card/cms.content.table/index.yml +0 -17
  4. package/module/cms/card/cms.content.table/main_info.hbs +0 -26
  5. package/module/cms/card/cms.menu.table/content_info.hbs +0 -16
  6. package/module/cms/card/cms.menu.table/index.yml +0 -18
  7. package/module/cms/card/cms.menu.table/main_info.hbs +0 -22
  8. package/module/cms/card/cms.settings.table/index.yml +0 -13
  9. package/module/cms/card/cms.settings.table/main_info.hbs +0 -20
  10. package/module/cms/cls/content.status.json +0 -18
  11. package/module/cms/cls/user_type.json +0 -10
  12. package/module/cms/form/admin.users.form.json +0 -78
  13. package/module/cms/form/cms.content.form.json +0 -79
  14. package/module/cms/form/cms.menu.form.json +0 -69
  15. package/module/cms/form/cms.settings.form.json +0 -32
  16. package/module/cms/menu.json +0 -24
  17. package/module/cms/router.js +0 -169
  18. package/module/cms/select/cms.page_type.sql +0 -2
  19. package/module/cms/select/collection.sql +0 -1
  20. package/module/cms/select/locale.sql +0 -17
  21. package/module/cms/select/news_tag_id.sql +0 -12
  22. package/module/cms/select/tag_id.sql +0 -1
  23. package/module/cms/table/admin.users.table.json +0 -54
  24. package/module/cms/table/cms.content.table.json +0 -106
  25. package/module/cms/table/cms.menu.table.json +0 -73
  26. package/module/cms/table/cms.settings.table.json +0 -57
  27. package/module/cms/table/collection.default.table.json +0 -102
  28. package/module/cms/table/single.default.table.json +0 -115
  29. package/src/App.css +0 -52
  30. package/src/App.vue +0 -62
  31. package/src/assets/image.png +0 -0
  32. package/src/assets/main.css +0 -3
  33. package/src/assets/tailwind-3.4.17.js +0 -113
  34. package/src/components/LanguageSwitcher.vue +0 -73
  35. package/src/components/SettingsCard.vue +0 -40
  36. package/src/components/builder/CreateForm.vue +0 -128
  37. package/src/components/builder/formTypeSchema.js +0 -145
  38. package/src/components/builder/tabs/index.ts +0 -9
  39. package/src/components/builder/tabs/vs-builder-edit.vue +0 -133
  40. package/src/components/builder/tabs/vs-builder-monaco.vue +0 -29
  41. package/src/components/builder/tabs/vs-builder-preview.vue +0 -39
  42. package/src/components/builder/vs-builder-datatable-controls.vue +0 -138
  43. package/src/components/builder/vs-builder-datatable-form.vue +0 -80
  44. package/src/components/builder/vs-builder-datatable.vue +0 -191
  45. package/src/components/builder/vs-builder-list-item.vue +0 -110
  46. package/src/components/collections/CollectionsBreadcrumb.vue +0 -52
  47. package/src/components/collections/CollectionsGrid.vue +0 -176
  48. package/src/components/collections/ContentBlock.vue +0 -75
  49. package/src/components/collections/formWrapper.vue +0 -156
  50. package/src/components/dashboard/ContentItem.vue +0 -82
  51. package/src/components/dashboard/DashboardHeader.vue +0 -33
  52. package/src/components/dashboard/QuickActions.vue +0 -84
  53. package/src/components/dashboard/RecentContent.vue +0 -54
  54. package/src/components/dashboard/StatCard.vue +0 -63
  55. package/src/components/dashboard/StatsGrid.vue +0 -28
  56. package/src/components/form-components/MonacoEditor.vue +0 -104
  57. package/src/components/form-components/VsFormMeta.vue +0 -40
  58. package/src/components/form-components/VsFormTags.vue +0 -150
  59. package/src/components/form-components/custom-datatable/vs-form-custom-datatable-add.vue +0 -84
  60. package/src/components/form-components/custom-datatable/vs-form-custom-datatable-controls.vue +0 -106
  61. package/src/components/form-components/index.js +0 -23
  62. package/src/components/form-components/reference/vs-form-reference-add.vue +0 -92
  63. package/src/components/form-components/reference/vs-form-reference-controls.vue +0 -101
  64. package/src/components/form-components/reference-list/referenceOptionList.js +0 -78
  65. package/src/components/form-components/reference-list/vs-form-reference-add.vue +0 -145
  66. package/src/components/form-components/reference-list/vs-form-reference-choce.vue +0 -39
  67. package/src/components/form-components/reference-list/vs-form-reference-controls.vue +0 -110
  68. package/src/components/form-components/reference-skeleton/about-skeleton.vue +0 -37
  69. package/src/components/form-components/reference-skeleton/banner-skeleton.vue +0 -29
  70. package/src/components/form-components/reference-skeleton/body-skeleton.vue +0 -56
  71. package/src/components/form-components/reference-skeleton/cards-skeleton.vue +0 -47
  72. package/src/components/form-components/reference-skeleton/documents-skeleton.vue +0 -64
  73. package/src/components/form-components/reference-skeleton/faq-skeleton.vue +0 -64
  74. package/src/components/form-components/reference-skeleton/form-skeleton.vue +0 -41
  75. package/src/components/form-components/reference-skeleton/index.js +0 -36
  76. package/src/components/form-components/reference-skeleton/infoLine-skeleton.vue +0 -37
  77. package/src/components/form-components/reference-skeleton/news-skeleton.vue +0 -54
  78. package/src/components/form-components/reference-skeleton/slider-skeleton.vue +0 -41
  79. package/src/components/form-components/reference-skeleton/tabs-skeleton.vue +0 -40
  80. package/src/components/form-components/reference-skeleton/team-skeleton.vue +0 -103
  81. package/src/components/form-components/reference-skeleton/usefulLinks-skeleton.vue +0 -52
  82. package/src/components/form-components/reference-skeleton/video-skeleton.vue +0 -36
  83. package/src/components/form-components/testReferenceTypes.js +0 -773
  84. package/src/components/form-components/vs-form-color-picker.vue +0 -29
  85. package/src/components/form-components/vs-form-custom-datatable.vue +0 -214
  86. package/src/components/form-components/vs-form-integer.vue +0 -86
  87. package/src/components/form-components/vs-form-key-value.vue +0 -201
  88. package/src/components/form-components/vs-form-marcdown-md.vue +0 -3
  89. package/src/components/form-components/vs-form-media-select.vue +0 -780
  90. package/src/components/form-components/vs-form-reference-list.vue +0 -97
  91. package/src/components/form-components/vs-form-reference.vue +0 -59
  92. package/src/components/form-components/vs-form-relation.vue +0 -30
  93. package/src/components/form-components/vs-form-reletion-link.vue +0 -34
  94. package/src/components/form-components/vs-form-select-collection.vue +0 -0
  95. package/src/components/form-components/vs-form-slug.vue +0 -72
  96. package/src/components/form-components/vs-form-tiptap.vue +0 -7
  97. package/src/components/form-components/vs-richtext-md.vue +0 -3
  98. package/src/components/icons/BellIcon.vue +0 -17
  99. package/src/components/icons/GlobeIcon.vue +0 -18
  100. package/src/components/icons/KeyIcon.vue +0 -20
  101. package/src/components/icons/PaletteIcon.vue +0 -22
  102. package/src/components/icons/SettingsIcon.vue +0 -19
  103. package/src/components/icons/ShieldIcon.vue +0 -18
  104. package/src/components/icons/UsersIcon.vue +0 -19
  105. package/src/components/icons/icon-chevron-right.vue +0 -16
  106. package/src/components/icons/icon-drag.vue +0 -20
  107. package/src/components/icons/icon-file-text.vue +0 -21
  108. package/src/components/icons/icon-folder.vue +0 -18
  109. package/src/components/icons/icon-grid.vue +0 -17
  110. package/src/components/icons/icon-group.vue +0 -19
  111. package/src/components/icons/icon-home.vue +0 -16
  112. package/src/components/icons/icon-image.vue +0 -18
  113. package/src/components/icons/icon-list.vue +0 -20
  114. package/src/components/icons/icon-more.vue +0 -17
  115. package/src/components/icons/icon-plus.vue +0 -17
  116. package/src/components/icons-types/icon-array.vue +0 -22
  117. package/src/components/icons-types/icon-boolean.vue +0 -18
  118. package/src/components/icons-types/icon-datalist.vue +0 -22
  119. package/src/components/icons-types/icon-date.vue +0 -20
  120. package/src/components/icons-types/icon-datetime.vue +0 -20
  121. package/src/components/icons-types/icon-file.vue +0 -21
  122. package/src/components/icons-types/icon-gallery.vue +0 -18
  123. package/src/components/icons-types/icon-image.vue +0 -19
  124. package/src/components/icons-types/icon-integer.vue +0 -20
  125. package/src/components/icons-types/icon-merkdown.vue +0 -18
  126. package/src/components/icons-types/icon-multiselect.vue +0 -22
  127. package/src/components/icons-types/icon-number.vue +0 -20
  128. package/src/components/icons-types/icon-radio.vue +0 -22
  129. package/src/components/icons-types/icon-reference-list.vue +0 -22
  130. package/src/components/icons-types/icon-reference.vue +0 -20
  131. package/src/components/icons-types/icon-relation.vue +0 -22
  132. package/src/components/icons-types/icon-richtext.vue +0 -18
  133. package/src/components/icons-types/icon-select.vue +0 -22
  134. package/src/components/icons-types/icon-slug.vue +0 -19
  135. package/src/components/icons-types/icon-text.vue +0 -19
  136. package/src/components/icons-types/index.js +0 -43
  137. package/src/components/layout/Layout.vue +0 -67
  138. package/src/components/layout/Sidebar.vue +0 -128
  139. package/src/components/media/FileUploadProgress.vue +0 -29
  140. package/src/components/media/MediaBreadcrumb.vue +0 -42
  141. package/src/components/media/MediaCreateFolder.vue +0 -59
  142. package/src/components/media/MediaFileInfo.vue +0 -148
  143. package/src/components/media/MediaGrid.vue +0 -148
  144. package/src/components/media/MediaList.vue +0 -148
  145. package/src/components/media/MediaViewControls.vue +0 -38
  146. package/src/components/media/TypeTag.vue +0 -23
  147. package/src/components/menu/AddNewItemInTree.vue +0 -75
  148. package/src/components/menu/MenuBody.vue +0 -149
  149. package/src/components/menu/MenuItem.vue +0 -73
  150. package/src/components/menu/MenuList.vue +0 -101
  151. package/src/components/referencec/index.ts +0 -7
  152. package/src/components/referencec/vs-reference-faq.vue +0 -61
  153. package/src/components/referencec/vs-reference-user-card.vue +0 -40
  154. package/src/components/settings/NotificationSettings.vue +0 -32
  155. package/src/components/settings/SettingsTable.vue +0 -50
  156. package/src/components/settings/SettingsTitle.vue +0 -33
  157. package/src/components/settings/SettingsToggleItem.vue +0 -25
  158. package/src/components/sidebar/DropdownMenu.vue +0 -34
  159. package/src/components/sidebar/SettingsSidebar.vue +0 -121
  160. package/src/components/sidebar/SidebarFooter.vue +0 -52
  161. package/src/components/sidebar/SidebarHeader.vue +0 -57
  162. package/src/components/sidebar/SidebarMenu.vue +0 -78
  163. package/src/components/ui/EmptyData.vue +0 -76
  164. package/src/components/ui/UniversalTable.vue +0 -310
  165. package/src/components/ui/UniversalTableFilters.vue +0 -0
  166. package/src/components/ui/UniversalTablePagination.vue +0 -118
  167. package/src/components/ui/VsPreview.vue +0 -75
  168. package/src/composables/useCollectionView.ts +0 -21
  169. package/src/composables/useDebounce.ts +0 -26
  170. package/src/composables/useMonaco.ts +0 -28
  171. package/src/composables/useTheme.ts +0 -40
  172. package/src/content/test-slug/metadata.json +0 -1
  173. package/src/i18n.ts +0 -75
  174. package/src/index.css +0 -3
  175. package/src/index.ts +0 -122
  176. package/src/locales/en.json +0 -778
  177. package/src/locales/uk.json +0 -797
  178. package/src/main.ts +0 -41
  179. package/src/pages/Dashboard.vue +0 -168
  180. package/src/pages/EmailPage.vue +0 -183
  181. package/src/pages/FeedbackPage.vue +0 -232
  182. package/src/pages/MediaPage.vue +0 -372
  183. package/src/pages/TagsPage.vue +0 -207
  184. package/src/pages/builder/BuilderPage.vue +0 -195
  185. package/src/pages/builder/EditCollectionPage.vue +0 -163
  186. package/src/pages/collections/ArticlesPage.vue +0 -385
  187. package/src/pages/collections/CollectionsPage.vue +0 -146
  188. package/src/pages/collections/SingletonsPage.vue +0 -119
  189. package/src/pages/collections/contentForm.vue +0 -484
  190. package/src/pages/collections/schema/seo.ts +0 -27
  191. package/src/pages/menu/MenuAddPage.vue +0 -123
  192. package/src/pages/menu/MenuItemPage.vue +0 -183
  193. package/src/pages/menu/MenuPage.vue +0 -133
  194. package/src/pages/settings/ApiKeys.vue +0 -75
  195. package/src/pages/settings/Appearance.vue +0 -80
  196. package/src/pages/settings/Logs.vue +0 -260
  197. package/src/pages/settings/PermissionsPage.vue +0 -237
  198. package/src/pages/settings/Settings.vue +0 -186
  199. package/src/pages/settings/Users.vue +0 -109
  200. package/src/pages/settings/general.vue +0 -154
  201. package/src/pages/settings/generalScheme.js +0 -132
  202. package/src/pages/users/AddUser.vue +0 -106
  203. package/src/pages/users/UsersPage.vue +0 -98
  204. package/src/props/builder.ts +0 -67
  205. package/src/props/content.ts +0 -56
  206. package/src/props/media.ts +0 -63
  207. package/src/router/index.ts +0 -181
  208. package/src/types/fastify-auth.d.ts +0 -4
  209. package/src/utils/getField.js +0 -270
  210. package/src/utils/translit.js +0 -19
  211. package/src/vite-env.d.ts +0 -1
@@ -1,310 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed, ref } from "vue";
3
- import { useI18n } from 'vue-i18n';
4
- const { t } = useI18n();
5
- import {
6
- Edit,
7
- Eye,
8
- Trash2,
9
- CheckCircle,
10
- XCircle,
11
- Pencil,
12
- Layers,
13
- File,
14
- } from "lucide-vue-next";
15
-
16
-
17
- const selectedRow = ref<any>(null);
18
-
19
- const props = defineProps<{
20
- rows: any[];
21
- // columns: Array<{ name: string; title?: string }>;
22
- query?: string;
23
- filterFn?: (row: any, query: string) => boolean;
24
- onEdit?: (row: any) => void;
25
- onView?: (row: any) => void;
26
- onDelete?: (row: any) => void;
27
- columns: any[];
28
- onMore?: (row: any) => void;
29
- customRender?: Record<string, (value: any, row: any) => any>;
30
- }>();
31
-
32
- const emit = defineEmits<{
33
- (e: "edit", row: any): void;
34
- (e: "view", row: any): void;
35
- (e: "delete", row: any): void;
36
- (e: "more", row: any): void;
37
- }>();
38
-
39
- const filteredRows = computed(() => {
40
- if (!props.query || !props.filterFn) return props.rows;
41
- return props.rows.filter((row) => props.filterFn!(row, props.query!));
42
- });
43
-
44
- const formatDate = (dateStr: string) => {
45
- if (!dateStr) return "";
46
- const date = new Date(dateStr);
47
- if (isNaN(date.getTime())) return '';
48
- return date.toISOString().slice(0, 16).replace('T', ' ');
49
- };
50
-
51
- const formatDateTime = (dateStr: string) => {
52
- if (!dateStr) return "";
53
- const date = new Date(dateStr);
54
- if (isNaN(date.getTime())) return '';
55
-
56
- const hasTime = dateStr.includes('T') || dateStr.includes(' ') || dateStr.includes(':');
57
-
58
- if (hasTime) {
59
- return date.toLocaleString('uk-UA', { dateStyle: 'medium', timeStyle: 'medium' });
60
- } else {
61
- return date.toLocaleDateString('uk-UA', { dateStyle: 'medium' });
62
- }
63
- };
64
-
65
- const getStatusBadge = (status: string) => {
66
- switch (status) {
67
- case "published":
68
- return {
69
- icon: CheckCircle,
70
- text: t("common.status.published"),
71
- classes:
72
- "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200 border-green-200 dark:border-green-700",
73
- };
74
- case "draft":
75
- return {
76
- icon: Edit,
77
- text: t("common.status.draft"),
78
- classes:
79
- "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-200 border-yellow-200 dark:border-yellow-700",
80
- };
81
- case "archived":
82
- return {
83
- icon: XCircle,
84
- text: t("common.status.archived"),
85
- classes:
86
- "bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-300 border-gray-200 dark:border-gray-700",
87
- };
88
- default:
89
- return null;
90
- }
91
- };
92
-
93
- const handleAction = (action: string, row: any) => {
94
- switch (action) {
95
- case "edit":
96
- emit("edit", row);
97
- if (props.onEdit) props.onEdit(row);
98
- break;
99
- case "view":
100
- emit("view", row);
101
- if (props.onView) props.onView(row);
102
- break;
103
- case "delete":
104
- emit("delete", row);
105
- break;
106
- case "more":
107
- emit("more", row);
108
- if (props.onMore) props.onMore(row);
109
- break;
110
- }
111
- };
112
-
113
- const handleRowClick = (row: any) => {
114
- handleAction("edit", row);
115
- };
116
-
117
- const timeAgo = (date: string) => {
118
- const now = new Date();
119
- const past = new Date(date);
120
- const diffMs = now - past;
121
- const diffSec = Math.floor(diffMs / 1000);
122
- const diffMin = Math.floor(diffSec / 60);
123
- const diffHour = Math.floor(diffMin / 60);
124
- const diffDay = Number(Math.floor(diffHour / 24));
125
- const diffWeek = Number(Math.floor(diffDay / 7));
126
-
127
- if (diffWeek >= 2) return t('table.time.weeksAgo', { count: diffWeek });
128
- if (diffWeek === 1) return t('table.time.weekAgo');
129
- if (diffDay >= 2) return t('table.time.daysAgo', { count: diffDay });
130
- if (diffDay === 1) return t('table.time.dayAgo');
131
- if (diffHour >= 2) return t('table.time.hoursAgo', { count: diffHour });
132
- if (diffHour === 1) return t('table.time.hourAgo');
133
- if (diffMin >= 2) return t('table.time.minutesAgo', { count: diffMin });
134
- if (diffMin === 1) return t('table.time.minuteAgo');
135
- return t('table.time.justNow');
136
- }
137
-
138
- </script>
139
-
140
- <template>
141
- <div class="text-card-foreground border-0 bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm" >
142
- <div class="p-0">
143
- <div class="table-scroll overflow-x-auto">
144
- <div class="relative w-full overflow-auto max-h-[calc(100vh-270px)]">
145
- <table class="w-full caption-bottom text-sm" >
146
- <thead>
147
- <tr class="border-b border-slate-200 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-700/50 hover:bg-slate-50/80 dark:hover:bg-slate-700/80 transition-colors">
148
- <th
149
- v-for="col in columns"
150
- :key="col.name"
151
- class="h-10 px-4 text-left align-middle font-semibold text-slate-700 dark:text-slate-300 max-w-80"
152
- :class="{
153
- 'min-w-[200px]': ['title','name'].includes(col.name ),
154
- 'min-w-[150px]': col.name === 'slug',
155
- 'min-w-[250px]': col.name === 'description',
156
- }"
157
- >
158
- <div class="flex items-center gap-2">
159
- <component :is="col.icon" v-if="col.icon" class="w-5 h-5" />
160
- {{ col.label || col.title || col.ua ||col.name }}
161
- </div>
162
- </th>
163
- <th
164
- class="h-10 text-center px-4 font-semibold text-slate-700 dark:text-slate-300 max-w-32"
165
- >
166
- {{ $t('table.actions') }}
167
- </th>
168
- </tr>
169
- </thead>
170
- <tbody>
171
- <tr
172
- v-for="row in filteredRows" :key="row.id || row._id || row.name"
173
- class="transition-all duration-200 hover:bg-slate-50/60 dark:hover:bg-slate-700/60"
174
- >
175
- <td
176
- v-for="(col, colIdx) in columns"
177
- :key="col.name"
178
- class="py-2 px-4 align-middle max-w-80"
179
- :class="{
180
- 'font-medium text-slate-800 dark:text-white cursor-pointer': colIdx === 0,
181
- 'text-slate-600 dark:text-slate-400 font-mono text-sm': colIdx !== 0,
182
- }"
183
- @click="colIdx === 0 ? handleRowClick(row) : undefined"
184
- >
185
- <template v-if="customRender && customRender[col.name]">
186
- <component :is="customRender[col.name](row[col.name], row)" />
187
- </template>
188
- <template v-if="[
189
- 'published_at',
190
- 'publish_at',
191
- 'created_at',
192
- 'date',
193
- ].includes(col.name) && row[col.name]
194
- ">
195
- <!-- <span class="whitespace-nowrap">
196
- {{ formatDate(row[col.name]) || '--' }}
197
- </span> -->
198
- <span class="whitespace-nowrap">
199
- {{ formatDateTime(row[col.name]) }}
200
- </span>
201
- </template>
202
- <template v-else-if="[
203
- 'updated_at',
204
- 'lastModified',
205
- 'last_edit',
206
- ].includes(col.name)
207
- ">
208
- <span class="whitespace-nowrap">
209
- {{ timeAgo(row[col.name]) || '--' }}
210
- </span>
211
- </template>
212
- <template v-else-if="col.name === 'views'">
213
- <div class="flex items-center">
214
- <Eye class="w-4 h-4 mr-1" />
215
- {{ row[col.name]?.toLocaleString?.() ?? row[col.name] }}
216
- </div>
217
- </template>
218
- <template v-else-if="['entries', 'fields'].includes(col.name)">
219
- <div class="inline-flex whitespace-nowrap items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors bg-white dark:bg-slate-700 border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-300">
220
- {{ row[col.name] || "-" }}
221
- </div>
222
- </template>
223
- <template v-else-if="col.name === 'type'">
224
- <div class="flex items-center">
225
- <div
226
- class="whitespace-nowrap inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors border-transparent hover:bg-secondary/80"
227
- :class="row[col.name] === 'pages' ? 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300' : 'bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300'"
228
- >
229
- {{ row[col.name] || "-" }}
230
- </div>
231
- </div>
232
- </template>
233
- <template v-else-if="col.name === 'name'">
234
- <div
235
- class="flex items-center gap-3"
236
- >
237
- <File v-if="row.type === 'single'" class="shrink-0 lucide lucide-page w-5 h-5 text-blue-600" />
238
- <Layers v-else class="shrink-0 lucide lucide-layers w-5 h-5 text-blue-600" />
239
- {{ row[col.name] }}
240
- </div>
241
- </template>
242
- <template v-else-if="col.name === 'status'">
243
- <div
244
- v-if="getStatusBadge(row[col.name])"
245
- class="whitespace-nowrap rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors hover:bg-secondary/80 flex items-center w-fit bg-green-100 text-green-800 border-green-200
246
- dark:bg-green-900/30 dark:text-green-300 dark:border-green-700"
247
- :class="getStatusBadge(row[col.name])?.classes"
248
- >
249
- <component
250
- :is="getStatusBadge(row[col.name])?.icon"
251
- class="w-3 h-3 mr-1"
252
- />
253
- {{ getStatusBadge(row[col.name])?.text }}
254
- </div>
255
- </template>
256
- <template v-else-if="col.name === 'slug'">
257
- <div class="flex items-center gap-3">
258
- {{ row[col.name] ? '/' + row[col.name] : "-" }}
259
- </div>
260
- </template>
261
- <template v-else-if="col.name === 'color'">
262
- <span
263
- class="w-4 h-4 rounded-full block ml-2"
264
- :class="'bg-[' + row[col.name] + ']'"
265
- >
266
- </span>
267
- </template>
268
- <template v-else-if="col.name === 'enabled'">
269
- <span class="whitespace-nowrap rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors hover:bg-secondary/80 flex items-center w-fit" :class="row[col.name] ? 'bg-green-100 text-green-800 border-green-200' : 'bg-red-100 text-red-800 border-red-200'">{{ row[col.name] ? 'Доступ' : 'Заборонено' }}</span>
270
- </template>
271
- <template v-else>
272
- <div class="flex items-center gap-3">
273
- <p class="line-clamp-2">
274
- {{ row[col.name] || "-" }}
275
- </p>
276
- </div>
277
- </template>
278
- </td>
279
- <td class="flex justify-end py-2 px-4 align-middle max-w-40">
280
- <div class="flex items-center justify-center gap-1">
281
- <button
282
- @click.stop="handleAction('edit', row)"
283
- class="inline-flex items-center justify-center whitespace-nowrap font-medium border rounded-md text-xs h-8 w-8 p-0 border-slate-300 bg-white transition-all duration-200 shadow-sm
284
- dark:border-slate-600 dark:bg-slate-700 dark:hover:bg-slate-600 dark:hover:text-slate-200
285
- hover:border-blue-300 hover:bg-blue-50 hover:text-blue-600"
286
- title="Edit"
287
- >
288
- <Pencil class="text-slate-800 dark:text-slate-200 w-4 h-4 group-hover:text-green-600 dark:group-hover:text-blue-50" />
289
- </button>
290
- <button
291
- @click.stop="handleAction('delete', row)"
292
- class="inline-flex items-center justify-center whitespace-nowrap font-medium border rounded-md text-xs h-8 w-8 p-0 border-slate-300 bg-white transition-all duration-200 shadow-sm
293
- dark:border-slate-600 dark:bg-slate-700 dark:hover:bg-slate-600 dark:hover:text-slate-200
294
- hover:border-blue-300 hover:bg-blue-50 hover:text-blue-600"
295
- title="Preview"
296
- >
297
- <Trash2 class="w-4 h-4 text-slate-800 dark:text-slate-200 group-hover:text-red-600 dark:group-hover:text-blue-50" />
298
- </button>
299
- <slot name="actions" :row="row" />
300
- </div>
301
- </td>
302
- </tr>
303
- </tbody>
304
- </table>
305
-
306
- </div>
307
- </div>
308
- </div>
309
- </div>
310
- </template>
File without changes
@@ -1,118 +0,0 @@
1
- <template>
2
- <div
3
- class="flex items-center justify-between px-6 py-4 border-t border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800"
4
- >
5
- <div class="flex justify-between flex-1 sm:hidden">
6
- <button
7
- class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
8
- >
9
- {{ $t("common.pagination.previous") }}
10
- </button>
11
- <button
12
- class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
13
- >
14
- {{ $t("common.pagination.next") }}
15
- </button>
16
- </div>
17
- <div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
18
- <div>
19
- <p class="text-sm text-slate-600 dark:text-slate-400">
20
- {{ $t("common.pagination.showing") }}
21
- <span class="font-medium text-slate-800 dark:text-slate-100">{{ count }}</span>
22
- {{ $t("common.pagination.of") }}
23
- <span class="font-medium text-slate-800 dark:text-slate-100">{{ total }}</span>
24
- {{ $t("common.pagination.results") }}
25
- </p>
26
- </div>
27
- <div>
28
- <nav
29
- class="relative z-0 inline-flex space-x-2 rounded-md shadow-sm"
30
- aria-label="Pagination"
31
- >
32
- <button
33
- @click="page = page > 1 ? page - 1 : 1"
34
- :disabled="page === 1"
35
- class="inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors border shadow-sm rounded-md text-xs h-8 w-8 p-0 bg-white border-slate-300 text-slate-600
36
- disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed
37
- hover:text-accent-foreground hover:bg-slate-50
38
- dark:bg-slate-700 dark:border-slate-600 dark:text-slate-300 dark:hover:bg-slate-600"
39
- >
40
- <span class="sr-only">{{ $t("common.pagination.previous") }}</span>
41
- <ChevronLeft class="w-5 h-5" />
42
- </button>
43
- <div class="space-x-1">
44
- <template v-for="(p, idx) in pagesToShow" :key="p + '-' + idx">
45
- <span v-if="p === 'ellipsis'" class="px-2 py-2 text-gray-400"
46
- >...</span
47
- >
48
- <button
49
- v-else
50
- @click="page = p"
51
- class="inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors h-8 w-8 p-0 text-sm rounded-md"
52
- :class="{
53
- 'border shadow-sm hover:text-accent-foreground bg-white dark:bg-slate-700 border-slate-300 dark:border-slate-600 text-slate-600 dark:text-slate-300 hover:border-blue-600 hover:bg-slate-50 dark:hover:bg-slate-600': p !== page,
54
- 'shadow bg-blue-600 hover:bg-blue-700 text-white border-blue-600': p === page
55
- }"
56
- >
57
- {{ p }}
58
- </button>
59
- </template>
60
- </div>
61
- <button
62
- @click="
63
- page =
64
- page < Math.ceil(total / limit)
65
- ? page + 1
66
- : Math.ceil(total / limit)
67
- "
68
- :disabled="page === Math.ceil(total / limit)"
69
- class="inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors border shadow-sm rounded-md text-xs h-8 w-8 p-0 bg-white border-slate-300 text-slate-600
70
- disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed
71
- hover:text-accent-foreground hover:bg-slate-50
72
- dark:bg-slate-700 dark:border-slate-600 dark:text-slate-300 dark:hover:bg-slate-600"
73
- >
74
- <span class="sr-only">{{ $t("common.pagination.next") }}</span>
75
- <ChevronRight class="w-5 h-5" />
76
- </button>
77
- </nav>
78
- </div>
79
- </div>
80
- </div>
81
- </template>
82
-
83
- <script setup lang="ts">
84
- import { ChevronLeft, ChevronRight } from "lucide-vue-next";
85
- import { computed } from "vue";
86
-
87
- const {
88
- limit,
89
- count,
90
- total,
91
- } = defineProps<{
92
- total: number;
93
- count: number;
94
- limit: number;
95
- }>();
96
-
97
- const page = defineModel<number>("page");
98
-
99
- const totalPages = computed(() => Math.ceil(total / limit));
100
-
101
- const pagesToShow = computed(() => {
102
- const maxPages = 4;
103
- const pages: (number | "ellipsis")[] = [];
104
- if (totalPages.value <= maxPages) {
105
- for (let i = 1; i <= totalPages.value; i++) pages.push(i);
106
- return pages;
107
- }
108
- // Always show first and last
109
- const left = Math.max(2, page.value - 2);
110
- const right = Math.min(totalPages.value - 1, page.value + 2);
111
- pages.push(1);
112
- if (left > 2) pages.push("ellipsis");
113
- for (let i = left; i <= right; i++) pages.push(i);
114
- if (right < totalPages.value - 1) pages.push("ellipsis");
115
- pages.push(totalPages.value);
116
- return pages;
117
- });
118
- </script>
@@ -1,75 +0,0 @@
1
- <template>
2
- <div>
3
- <div class="flex justify-end mb-4">
4
- <a
5
- :href="`${previewUrl}/uk/${preview_path}${slug}${draftKey ? `?draftKey=${draftKey}` : ''}`"
6
- target="_blank"
7
- class="inline-flex items-center gap-2 px-3 py-1.5 text-black-600 bg-white border border-blue-200 rounded hover:border-blue-300 hover:bg-blue-50 transition-colors duration-200 font-normal cursor-pointer"
8
- >
9
- Переглянути на сайті
10
- <ExternalLink class="w-4 h-4" />
11
- </a>
12
- </div>
13
- <iframe
14
- v-if="previewUrl && preview_path"
15
- :src="`${previewUrl}/uk/${preview_path}${slug}?hideHeader=true&hideFooter=true`"
16
- class="w-full h-[80vh]"
17
- ></iframe>
18
- <iframe
19
- v-else-if="previewUrl"
20
- :src="`${previewUrl}/uk/${slug}?hideHeader=true&hideFooter=true`"
21
- class="w-full h-[80vh]"
22
- ></iframe>
23
- <div
24
- v-else
25
- class="w-full h-[80vh] flex items-center justify-center text-gray-500"
26
- >
27
- Loading preview...
28
- </div>
29
- </div>
30
- </template>
31
-
32
- <script setup>
33
- import { ref, onMounted } from "vue";
34
- import { ExternalLink } from "lucide-vue-next";
35
-
36
- const props = defineProps({
37
- preview_path: {
38
- type: String,
39
- default: "",
40
- },
41
- slug: {
42
- type: String,
43
- default: "",
44
- },
45
- });
46
-
47
- const previewUrl = ref("");
48
- const draftKey = ref("");
49
-
50
- const fetchSettings = async () => {
51
- try {
52
- const response = await fetch("/api/settings");
53
- const data = await response.json();
54
- previewUrl.value =
55
- data?.settings?.site_info?.previewUrl || "http://ip.local.softpro.ua";
56
- } catch (error) {
57
- console.error("Failed to fetch settings:", error);
58
- previewUrl.value = "http://ip.local.softpro.ua"; // fallback
59
- }
60
- };
61
- const fetchUser = async () => {
62
- try {
63
- const response = await fetch("/user");
64
- const data = await response.json();
65
- draftKey.value = data?.user?.draftKey;
66
- } catch (error) {
67
- console.error("Failed to fetch user:", error);
68
- }
69
- };
70
-
71
- onMounted(() => {
72
- fetchSettings();
73
- fetchUser();
74
- });
75
- </script>
@@ -1,21 +0,0 @@
1
- import { ref, watch } from 'vue';
2
-
3
- export type CollectionView = 'list' | 'grid';
4
-
5
- const saved = localStorage.getItem('collectionView');
6
- const collectionView = ref<CollectionView>(saved === 'list' || saved === 'grid' ? saved as CollectionView : 'list');
7
-
8
- watch(collectionView, (newView) => {
9
- localStorage.setItem('collectionView', newView);
10
- });
11
-
12
- function toggleCollectionView() {
13
- collectionView.value = collectionView.value === 'grid' ? 'list' : 'grid';
14
- }
15
-
16
- export function useCollectionView() {
17
- return {
18
- collectionView,
19
- toggleCollectionView,
20
- };
21
- }
@@ -1,26 +0,0 @@
1
- import { ref, watch, type Ref } from 'vue';
2
-
3
- /**
4
- * Debounce composable for Vue 3 Composition API
5
- * @param value - The value to debounce
6
- * @param delay - The debounce delay in ms (default: 300)
7
- * @returns debouncedValue
8
- */
9
- export function useDebounce<T>(value: Ref<T>, delay = 300) {
10
- const debouncedValue = ref(value.value) as Ref<T>;
11
-
12
- let timeout: ReturnType<typeof setTimeout>;
13
-
14
- watch(
15
- value,
16
- (newValue) => {
17
- clearTimeout(timeout);
18
- timeout = setTimeout(() => {
19
- debouncedValue.value = newValue;
20
- }, delay);
21
- },
22
- { immediate: true }
23
- );
24
-
25
- return debouncedValue;
26
- }
@@ -1,28 +0,0 @@
1
- export async function useMonaco() {
2
- if (window.monaco) return window.monaco;
3
-
4
- const CDN = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min';
5
-
6
- if (!window.require) {
7
- window.require = { paths: { vs: `${CDN}/vs` } };
8
- await new Promise<void>((res, rej) => {
9
- const s = document.createElement('script');
10
- s.src = `${CDN}/vs/loader.min.js`;
11
- s.onload = () => res();
12
- s.onerror = rej;
13
- document.head.appendChild(s);
14
- });
15
- }
16
-
17
- return new Promise<typeof monaco>((resolve) => {
18
- window.require(['vs/editor/editor.main'], () => resolve(window.monaco));
19
- });
20
- }
21
-
22
- // Додаємо типи для TypeScript
23
- declare global {
24
- interface Window {
25
- monaco: any;
26
- require: any;
27
- }
28
- }
@@ -1,40 +0,0 @@
1
- import { ref, watch } from 'vue';
2
-
3
- type Theme = 'light' | 'dark';
4
- type Color = 'blue' | 'green' | 'purple';
5
-
6
- export function useTheme() {
7
- const theme = ref<Theme>(
8
- localStorage.getItem('theme') === 'light' ? 'light' : 'dark'
9
- );
10
-
11
- const color = ref<Color>(
12
- localStorage.getItem('color') as Color || 'blue'
13
- );
14
-
15
- watch(theme, (newTheme) => {
16
- document.documentElement.classList.toggle('dark');
17
- localStorage.setItem('theme', newTheme);
18
- });
19
-
20
- watch(color, (newColor) => {
21
- document.documentElement.classList.remove('theme-green', 'theme-purple', 'theme-blue');
22
- document.documentElement.classList.add(`theme-${newColor}`);
23
- localStorage.setItem('color', newColor);
24
- });
25
-
26
- const toggleTheme = () => {
27
- theme.value = theme.value === 'light' ? 'dark' : 'light';
28
- };
29
-
30
- const toggleColor = (newColor: Color) => {
31
- color.value = newColor;
32
- };
33
-
34
- return {
35
- theme,
36
- toggleTheme,
37
- color,
38
- toggleColor
39
- };
40
- }
@@ -1 +0,0 @@
1
- {"slug":"test-slug","title":"test-slug123466578"}