mktcms 0.2.7 → 0.2.8

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 (37) hide show
  1. package/README.md +2 -8
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +19 -1
  4. package/dist/runtime/app/components/content/editor/frontmatter/form.d.vue.ts +14 -6
  5. package/dist/runtime/app/components/content/editor/frontmatter/form.vue +285 -17
  6. package/dist/runtime/app/components/content/editor/frontmatter/form.vue.d.ts +14 -6
  7. package/dist/runtime/app/components/content/editor/frontmatter/input.d.vue.ts +7 -5
  8. package/dist/runtime/app/components/content/editor/frontmatter/input.vue +27 -8
  9. package/dist/runtime/app/components/content/editor/frontmatter/input.vue.d.ts +7 -5
  10. package/dist/runtime/app/components/content/editor/frontmatter/{toggle.d.vue.ts → modal.d.vue.ts} +7 -4
  11. package/dist/runtime/app/components/content/editor/frontmatter/modal.vue +113 -0
  12. package/dist/runtime/app/components/content/editor/frontmatter/{toggle.vue.d.ts → modal.vue.d.ts} +7 -4
  13. package/dist/runtime/app/components/content/editor/markdown.vue +14 -3
  14. package/dist/runtime/app/components/content/versioning.d.vue.ts +3 -0
  15. package/dist/runtime/app/components/content/versioning.vue +359 -0
  16. package/dist/runtime/app/components/content/versioning.vue.d.ts +3 -0
  17. package/dist/runtime/app/composables/useFileType.js +6 -5
  18. package/dist/runtime/app/pages/admin/delete/[path].vue +4 -2
  19. package/dist/runtime/app/pages/admin/edit/file/[path].vue +9 -7
  20. package/dist/runtime/app/pages/admin/edit/markdown/[path].vue +7 -5
  21. package/dist/runtime/app/pages/admin/index.vue +14 -5
  22. package/dist/runtime/app/pages/admin/new.vue +4 -2
  23. package/dist/runtime/app/styles/admin.css +1 -1
  24. package/dist/runtime/app/styles/admin.min.css +1 -1
  25. package/dist/runtime/server/api/admin/git-branch.d.ts +18 -0
  26. package/dist/runtime/server/api/admin/git-branch.js +44 -0
  27. package/dist/runtime/server/api/admin/git-history.d.ts +2 -0
  28. package/dist/runtime/server/api/admin/git-history.js +27 -0
  29. package/dist/runtime/server/api/admin/git-update-status.d.ts +2 -0
  30. package/dist/runtime/server/api/admin/git-update-status.js +21 -0
  31. package/dist/runtime/server/api/admin/git-update.post.d.ts +6 -0
  32. package/dist/runtime/server/api/admin/git-update.post.js +45 -0
  33. package/dist/runtime/server/utils/gitVersioning.d.ts +54 -0
  34. package/dist/runtime/server/utils/gitVersioning.js +205 -0
  35. package/dist/runtime/server/utils/syncGitContent.js +20 -10
  36. package/package.json +2 -1
  37. package/dist/runtime/app/components/content/editor/frontmatter/toggle.vue +0 -22
@@ -1,14 +1,17 @@
1
1
  type __VLS_Props = {
2
- label: string;
2
+ isOpen: boolean;
3
3
  };
4
4
  type __VLS_ModelProps = {
5
- 'value': boolean;
5
+ 'frontmatter': any;
6
6
  };
7
7
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
8
8
  declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
- "update:value": (value: boolean) => any;
9
+ "update:frontmatter": (value: any) => any;
10
+ } & {
11
+ close: () => any;
10
12
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
11
- "onUpdate:value"?: ((value: boolean) => any) | undefined;
13
+ onClose?: (() => any) | undefined;
14
+ "onUpdate:frontmatter"?: ((value: any) => any) | undefined;
12
15
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
16
  declare const _default: typeof __VLS_export;
14
17
  export default _default;
@@ -4,7 +4,7 @@ import { refDebounced } from "@vueuse/core";
4
4
  import Saved from "../saved.vue";
5
5
  import usePathParam from "../../../composables/usePathParam";
6
6
  import { useFetch } from "#imports";
7
- import FrontmatterForm from "./frontmatter/form.vue";
7
+ import FrontmatterModal from "./frontmatter/modal.vue";
8
8
  import MonacoEditor from "./monacoEditor.vue";
9
9
  const { path } = usePathParam();
10
10
  const { data: content } = await useFetch(`/api/admin/md?path=${path}`);
@@ -14,6 +14,7 @@ const commitMessage = ref("Inhaltliche \xC4nderungen");
14
14
  const debouncedMarkdown = refDebounced(markdown, 250);
15
15
  const isSaving = ref(false);
16
16
  const savingSuccessful = ref(false);
17
+ const showFrontmatterModal = ref(false);
17
18
  async function saveMarkdown() {
18
19
  if (!content.value) return;
19
20
  isSaving.value = true;
@@ -37,10 +38,20 @@ const mode = ref("preview");
37
38
  v-if="content"
38
39
  class="flex-1 min-h-0 flex flex-col"
39
40
  >
40
- <FrontmatterForm
41
+ <button
42
+ type="button"
43
+ class="button secondary small mb-3 self-start"
44
+ @click="showFrontmatterModal = true"
45
+ >
46
+ Metadaten bearbeiten
47
+ </button>
48
+
49
+ <FrontmatterModal
41
50
  v-model:frontmatter="frontmatter"
42
- class="mb-2"
51
+ :is-open="showFrontmatterModal"
52
+ @close="showFrontmatterModal = false"
43
53
  />
54
+
44
55
  <div class="flex gap-2 my-2 lg:hidden">
45
56
  <button
46
57
  type="button"
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,359 @@
1
+ <script setup>
2
+ import { computed, ref } from "vue";
3
+ import { useFetch } from "#app";
4
+ const HISTORY_PER_PAGE = 25;
5
+ const isHistoryModalOpen = ref(false);
6
+ const isUpdateModalOpen = ref(false);
7
+ const selectedUpdateBranch = ref("");
8
+ const historyPage = ref(1);
9
+ const isUpdating = ref(false);
10
+ const updateError = ref("");
11
+ const updateSuccess = ref("");
12
+ const { data: branchData, pending: branchPending, error: branchError, refresh: refreshBranch } = await useFetch("/api/admin/git-branch", {
13
+ key: "mktcms-git-branch"
14
+ });
15
+ const { data: updateStatusData, pending: updateStatusPending, error: updateStatusError, refresh: refreshUpdateStatus } = await useFetch("/api/admin/git-update-status", {
16
+ key: "mktcms-git-update-status"
17
+ });
18
+ const { data: historyData, pending: historyPending, error: historyError, refresh: refreshHistory } = await useFetch("/api/admin/git-history", {
19
+ key: "mktcms-git-history",
20
+ query: computed(() => ({
21
+ page: historyPage.value,
22
+ perPage: HISTORY_PER_PAGE
23
+ })),
24
+ immediate: false
25
+ });
26
+ const currentBranch = computed(() => branchData.value?.currentBranch ?? "unbekannt");
27
+ const isSupportedBranch = computed(() => branchData.value?.isSupported ?? false);
28
+ const sourceBranch = computed(() => branchData.value?.sourceBranch ?? "");
29
+ const sourceAheadCount = computed(() => updateStatusData.value?.sourceAheadCount ?? 0);
30
+ const isIdentical = computed(() => updateStatusData.value?.isIdentical ?? false);
31
+ const canUpdate = computed(() => updateStatusData.value?.canUpdate ?? false);
32
+ const updateBlockedReason = computed(() => updateStatusData.value?.updateBlockedReason || branchData.value?.updateBlockedReason || "");
33
+ const updateButtonText = computed(() => {
34
+ const count = sourceAheadCount.value;
35
+ return `Aktualisieren (${count})`;
36
+ });
37
+ const updateTitle = computed(() => currentBranch.value === "main" ? "\xC4nderungen aus Vorschau \xFCbernehmen" : currentBranch.value === "staging" ? "\xC4nderungen von Live in Vorschau \xFCbernehmen" : "Branch aktualisieren");
38
+ const historyEntries = computed(() => historyData.value?.entries ?? []);
39
+ const hasNextHistoryPage = computed(() => historyData.value?.hasNextPage ?? false);
40
+ function formatRelativeDate(isoDate) {
41
+ const target = new Date(isoDate).getTime();
42
+ if (!Number.isFinite(target)) {
43
+ return isoDate;
44
+ }
45
+ const diffMs = target - Date.now();
46
+ const absMs = Math.abs(diffMs);
47
+ const minute = 6e4;
48
+ const hour = 60 * minute;
49
+ const day = 24 * hour;
50
+ const rtf = new Intl.RelativeTimeFormat("de-DE", { numeric: "auto" });
51
+ if (absMs < hour) {
52
+ return rtf.format(Math.round(diffMs / minute), "minute");
53
+ }
54
+ if (absMs < day) {
55
+ return rtf.format(Math.round(diffMs / hour), "hour");
56
+ }
57
+ return rtf.format(Math.round(diffMs / day), "day");
58
+ }
59
+ async function openHistoryModal() {
60
+ isHistoryModalOpen.value = true;
61
+ historyPage.value = 1;
62
+ await refreshHistory();
63
+ }
64
+ function openUpdateModal() {
65
+ selectedUpdateBranch.value = updateStatusData.value?.sourceBranch || sourceBranch.value;
66
+ updateError.value = "";
67
+ updateSuccess.value = "";
68
+ isUpdateModalOpen.value = true;
69
+ }
70
+ function closeHistoryModal() {
71
+ isHistoryModalOpen.value = false;
72
+ }
73
+ async function goToHistoryPage(nextPage) {
74
+ if (nextPage < 1 || historyPending.value) {
75
+ return;
76
+ }
77
+ historyPage.value = nextPage;
78
+ await refreshHistory();
79
+ }
80
+ function closeUpdateModal() {
81
+ isUpdateModalOpen.value = false;
82
+ }
83
+ async function runUpdate() {
84
+ if (!isSupportedBranch.value || isUpdating.value) {
85
+ return;
86
+ }
87
+ if (!canUpdate.value) {
88
+ updateError.value = updateBlockedReason.value || "Aktualisierung aktuell nicht m\xF6glich.";
89
+ return;
90
+ }
91
+ isUpdating.value = true;
92
+ updateError.value = "";
93
+ updateSuccess.value = "";
94
+ try {
95
+ await $fetch("/api/admin/git-update", {
96
+ method: "POST"
97
+ });
98
+ updateSuccess.value = `Merge erfolgreich: ${sourceBranch.value} \u2192 ${currentBranch.value}`;
99
+ await refreshBranch();
100
+ await refreshUpdateStatus();
101
+ } catch (error) {
102
+ updateError.value = error?.data?.statusMessage || error?.message || "Aktualisierung fehlgeschlagen.";
103
+ } finally {
104
+ isUpdating.value = false;
105
+ }
106
+ }
107
+ </script>
108
+
109
+ <template>
110
+ <div class="text-sm text-gray-700">
111
+ <div class="flex items-center justify-end gap-3">
112
+ <span class="text-xs text-gray-500 mr-auto">
113
+ Version: <strong class="capitalize">{{ currentBranch }}</strong>
114
+ </span>
115
+ <button
116
+ type="button"
117
+ class="button small tertiary"
118
+ @click="openHistoryModal"
119
+ >
120
+ <svg
121
+ xmlns="http://www.w3.org/2000/svg"
122
+ width="16"
123
+ height="16"
124
+ viewBox="0 0 24 24"
125
+ fill="none"
126
+ stroke="currentColor"
127
+ stroke-width="2"
128
+ stroke-linecap="round"
129
+ stroke-linejoin="round"
130
+ class="text-gray-400"
131
+ ><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" /><path d="M3 3v5h5" /><path d="M12 7v5l4 2" /></svg>
132
+ Änderungshistorie
133
+ </button>
134
+ <button
135
+ type="button"
136
+ class="button small tertiary"
137
+ :disabled="branchPending || updateStatusPending || !!branchError || !!updateStatusError"
138
+ @click="openUpdateModal"
139
+ >
140
+ <svg
141
+ xmlns="http://www.w3.org/2000/svg"
142
+ width="16"
143
+ height="16"
144
+ viewBox="0 0 24 24"
145
+ fill="none"
146
+ stroke="currentColor"
147
+ stroke-width="2"
148
+ stroke-linecap="round"
149
+ stroke-linejoin="round"
150
+ class="text-gray-400"
151
+ ><path d="M12 2a10 10 0 0 1 7.38 16.75" /><path d="m16 12-4-4-4 4" /><path d="M12 16V8" /><path d="M2.5 8.875a10 10 0 0 0-.5 3" /><path d="M2.83 16a10 10 0 0 0 2.43 3.4" /><path d="M4.636 5.235a10 10 0 0 1 .891-.857" /><path d="M8.644 21.42a10 10 0 0 0 7.631-.38" /></svg>
152
+ {{ updateButtonText }}
153
+ </button>
154
+ </div>
155
+
156
+ <div
157
+ v-if="isHistoryModalOpen"
158
+ class="fixed inset-0 bg-black/45 flex items-start justify-center p-4 z-9999 overflow-y-auto"
159
+ role="presentation"
160
+ @click.self="closeHistoryModal"
161
+ >
162
+ <div
163
+ class="w-full max-w-140 bg-white rounded-[10px] border border-black/10 shadow-[0_10px_40px_rgba(0,0,0,0.28)] p-6 flex flex-col gap-4 max-h-[calc(100vh-2rem)] my-auto"
164
+ role="dialog"
165
+ aria-modal="true"
166
+ aria-label="Änderungshistorie"
167
+ >
168
+ <div class="flex items-center justify-between gap-2">
169
+ <h2 class="font-bold text-xl">
170
+ Änderungshistorie
171
+ </h2>
172
+ <button
173
+ type="button"
174
+ class="button secondary small"
175
+ @click="closeHistoryModal"
176
+ >
177
+ Schließen
178
+ </button>
179
+ </div>
180
+
181
+ <div class="min-h-0 overflow-y-auto pr-1">
182
+ <div
183
+ v-if="historyError"
184
+ class="text-sm p-3 bg-red-100 text-red-700 rounded"
185
+ >
186
+ Konnte Änderungshistorie nicht laden.
187
+ </div>
188
+
189
+ <div
190
+ v-else-if="historyPending"
191
+ class="text-sm text-gray-500"
192
+ >
193
+ lädt…
194
+ </div>
195
+
196
+ <div
197
+ v-else-if="historyEntries.length === 0"
198
+ class="text-sm text-gray-500"
199
+ >
200
+ Keine Einträge vorhanden.
201
+ </div>
202
+
203
+ <div
204
+ v-else
205
+ class="flex flex-col gap-3"
206
+ >
207
+ <div
208
+ v-for="entry in historyEntries"
209
+ :key="entry.hash"
210
+ class="flex flex-col justify-between gap-3 p-3 bg-gray-50 border border-gray-200 rounded"
211
+ >
212
+ <div class="flex items-start justify-between gap-3 flex-1 w-full">
213
+ <div>
214
+ <p class="font-bold">
215
+ {{ entry.authorName }}
216
+ </p>
217
+ <p class="text-xs text-gray-500 break-all">
218
+ {{ entry.authorEmail }}
219
+ </p>
220
+ </div>
221
+ <p class="text-sm text-gray-500">
222
+ {{ formatRelativeDate(entry.date) }}
223
+ </p>
224
+ </div>
225
+ <p>
226
+ Änderung: "{{ entry.subject }}"
227
+ </p>
228
+ </div>
229
+
230
+ <div class="flex items-center justify-between pt-1">
231
+ <button
232
+ type="button"
233
+ class="button secondary small"
234
+ :disabled="historyPage <= 1 || historyPending"
235
+ @click="goToHistoryPage(historyPage - 1)"
236
+ >
237
+ Zurück
238
+ </button>
239
+ <span class="text-xs text-gray-500">
240
+ Seite {{ historyPage }}
241
+ </span>
242
+ <button
243
+ type="button"
244
+ class="button secondary small"
245
+ :disabled="!hasNextHistoryPage || historyPending"
246
+ @click="goToHistoryPage(historyPage + 1)"
247
+ >
248
+ Weiter
249
+ </button>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </div>
254
+ </div>
255
+
256
+ <div
257
+ v-if="isUpdateModalOpen"
258
+ class="fixed inset-0 bg-black/45 flex items-start justify-center p-4 z-9999 overflow-y-auto"
259
+ role="presentation"
260
+ @click.self="closeUpdateModal"
261
+ >
262
+ <div
263
+ class="w-full max-w-140 bg-white rounded-[10px] border border-black/10 shadow-[0_10px_40px_rgba(0,0,0,0.28)] p-6 flex flex-col gap-4 max-h-[calc(100vh-2rem)] my-auto overflow-y-auto"
264
+ role="dialog"
265
+ aria-modal="true"
266
+ aria-label="Version aktualisieren"
267
+ >
268
+ <div class="flex items-center justify-between gap-2">
269
+ <h2 class="font-bold text-xl">
270
+ {{ updateTitle }}
271
+ </h2>
272
+ <button
273
+ type="button"
274
+ class="button secondary small"
275
+ @click="closeUpdateModal"
276
+ >
277
+ Schließen
278
+ </button>
279
+ </div>
280
+
281
+ <p class="text-sm p-3 bg-gray-50 border border-gray-200 rounded">
282
+ Achtung: Eine Aktualisierung ist sofort auf der Website sichtbar.
283
+ </p>
284
+
285
+ <p
286
+ v-if="branchError"
287
+ class="text-sm p-3 bg-red-100 text-red-700 rounded"
288
+ >
289
+ Konnte aktuellen Branch nicht laden.
290
+ </p>
291
+
292
+ <p
293
+ v-else-if="updateStatusError"
294
+ class="text-sm p-3 bg-red-100 text-red-700 rounded"
295
+ >
296
+ Konnte Branch-Status nicht laden.
297
+ </p>
298
+
299
+ <p
300
+ v-else-if="isIdentical"
301
+ class="text-sm p-3 bg-gray-50 border border-gray-200 rounded"
302
+ >
303
+ Die Branches sind identisch. Es sind keine neuen Änderungen verfügbar.
304
+ </p>
305
+
306
+ <p
307
+ v-else-if="!isSupportedBranch || !canUpdate"
308
+ class="text-sm p-3 bg-red-100 text-red-700 rounded"
309
+ >
310
+ {{ updateBlockedReason || `Unterst\xFCtzt sind nur main oder staging. Aktuell: ${currentBranch}` }}
311
+ </p>
312
+
313
+ <label class="text-sm font-medium text-gray-700">
314
+ Merge von Branch
315
+ </label>
316
+ <select
317
+ v-model="selectedUpdateBranch"
318
+ class="w-full border border-gray-200 rounded-sm px-3 py-2"
319
+ :disabled="true"
320
+ >
321
+ <option :value="selectedUpdateBranch || ''">
322
+ {{ selectedUpdateBranch || "unbekannt" }}
323
+ </option>
324
+ </select>
325
+
326
+ <p
327
+ v-if="updateError"
328
+ class="text-sm p-3 bg-red-100 text-red-700 rounded"
329
+ >
330
+ {{ updateError }}
331
+ </p>
332
+ <p
333
+ v-if="updateSuccess"
334
+ class="text-sm p-3 bg-emerald-100 text-emerald-800 rounded"
335
+ >
336
+ {{ updateSuccess }}
337
+ </p>
338
+
339
+ <div class="flex items-center justify-end gap-2">
340
+ <button
341
+ type="button"
342
+ class="button secondary small"
343
+ @click="closeUpdateModal"
344
+ >
345
+ Abbrechen
346
+ </button>
347
+ <button
348
+ type="button"
349
+ class="button small"
350
+ :disabled="!isSupportedBranch || !canUpdate || isUpdating || isIdentical"
351
+ @click="runUpdate"
352
+ >
353
+ {{ isUpdating ? "Aktualisiert\u2026" : "Aktualisieren" }}
354
+ </button>
355
+ </div>
356
+ </div>
357
+ </div>
358
+ </div>
359
+ </template>
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -1,9 +1,10 @@
1
1
  export default function useFileType(path) {
2
- const isImage = path.match(/\.(png|jpg|jpeg|gif|webp)$/i) !== null;
3
- const isPdf = path.endsWith(".pdf");
4
- const isMarkdown = path.endsWith(".md");
5
- const isCsv = path.endsWith(".csv");
6
- const isText = path.match(/\.(txt|json)$/i) !== null;
2
+ const normalizedPath = path.toLowerCase();
3
+ const isImage = normalizedPath.match(/\.(png|jpg|jpeg|gif|webp)$/) !== null;
4
+ const isPdf = normalizedPath.endsWith(".pdf");
5
+ const isMarkdown = normalizedPath.endsWith(".md");
6
+ const isCsv = normalizedPath.endsWith(".csv");
7
+ const isText = normalizedPath.match(/\.(txt|json)$/) !== null;
7
8
  return {
8
9
  isImage,
9
10
  isPdf,
@@ -6,7 +6,9 @@ import Delete from "../../../components/content/delete.vue";
6
6
 
7
7
  <template>
8
8
  <Admin>
9
- <Header />
10
- <Delete />
9
+ <div class="boxed">
10
+ <Header />
11
+ <Delete />
12
+ </div>
11
13
  </Admin>
12
14
  </template>
@@ -11,13 +11,15 @@ const { isImage, isPdf, isCsv, isText } = usePathParam();
11
11
 
12
12
  <template>
13
13
  <Admin>
14
- <Header />
15
- <Image v-if="isImage" />
16
- <Pdf v-else-if="isPdf" />
17
- <Csv v-else-if="isCsv" />
18
- <Txt v-else-if="isText" />
19
- <div v-else>
20
- Unbekannter Dateityp.
14
+ <div class="boxed">
15
+ <Header />
16
+ <Image v-if="isImage" />
17
+ <Pdf v-else-if="isPdf" />
18
+ <Csv v-else-if="isCsv" />
19
+ <Txt v-else-if="isText" />
20
+ <div v-else>
21
+ Unbekannter Dateityp.
22
+ </div>
21
23
  </div>
22
24
  </Admin>
23
25
  </template>
@@ -7,11 +7,13 @@ const { isMarkdown } = usePathParam();
7
7
  </script>
8
8
 
9
9
  <template>
10
- <Admin class="fullscreen">
11
- <Header />
12
- <Markdown v-if="isMarkdown" />
13
- <div v-else>
14
- Unbekannter Dateityp.
10
+ <Admin>
11
+ <div class="fullscreen">
12
+ <Header />
13
+ <Markdown v-if="isMarkdown" />
14
+ <div v-else>
15
+ Unbekannter Dateityp.
16
+ </div>
15
17
  </div>
16
18
  </Admin>
17
19
  </template>
@@ -2,15 +2,24 @@
2
2
  import Admin from "../../components/admin.vue";
3
3
  import Header from "../../components/header.vue";
4
4
  import Content from "../../components/content/index.vue";
5
+ import Versioning from "../../components/content/versioning.vue";
5
6
  import Usage from "../../components/content/usage.vue";
7
+ import { useRuntimeConfig } from "#imports";
8
+ const { public: { mktcms: { showVersioning } } } = useRuntimeConfig();
6
9
  </script>
7
10
 
8
11
  <template>
9
12
  <Admin>
10
- <Header class="mb-4" />
11
- <Content />
13
+ <div class="boxed">
14
+ <Header class="mb-4" />
15
+ <Content />
16
+ <Versioning
17
+ v-if="showVersioning"
18
+ class="mt-6"
19
+ />
20
+ </div>
21
+ <div class="mt-6 max-w-3xl mx-auto p-4 border border-gray-200 rounded-2xl">
22
+ <Usage />
23
+ </div>
12
24
  </Admin>
13
- <div class="mt-6 max-w-3xl mx-auto">
14
- <Usage />
15
- </div>
16
25
  </template>
@@ -6,7 +6,9 @@ import ContentUpload from "../../components/content/upload.vue";
6
6
 
7
7
  <template>
8
8
  <Admin>
9
- <Header />
10
- <ContentUpload />
9
+ <div class="boxed">
10
+ <Header />
11
+ <ContentUpload />
12
+ </div>
11
13
  </Admin>
12
14
  </template>
@@ -1 +1 @@
1
- @import "tailwindcss";@plugin "@tailwindcss/typography";body:has(#mktcms-admin){@apply bg-gray-100}body:has(#mktcms-admin:not(.fullscreen)){@apply p-4 md:p-12}#mktcms-admin{@apply p-4 max-w-3xl mx-auto bg-white md:rounded-lg shadow-lg flex flex-col}#mktcms-admin.fullscreen{@apply max-w-none rounded-none max-h-screen h-screen overflow-hidden}#mktcms-admin h1{@apply text-gray-800 text-3xl font-semibold}#mktcms-admin input[type=email],#mktcms-admin input[type=password],#mktcms-admin input[type=text],#mktcms-admin select,#mktcms-admin textarea{@apply w-full bg-white p-2.5 text-base border border-gray-300 rounded}#mktcms-admin .button{@apply inline-flex items-center gap-2 px-4 py-2.5 bg-emerald-600 text-white rounded hover:bg-emerald-700 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed}#mktcms-admin .button.secondary{@apply bg-gray-50 text-gray-700 border border-gray-300 hover:bg-gray-100}#mktcms-admin .button.directory{@apply bg-gray-700 text-white border border-gray-600 hover:bg-gray-600}#mktcms-admin .button.danger{@apply bg-red-600 hover:bg-red-700}#mktcms-admin .button.small{@apply px-2.5 py-1.5 text-sm}#mktcms-admin .button :disabled{@apply cursor-not-allowed opacity-50}
1
+ @import "tailwindcss";@plugin "@tailwindcss/typography";body:has(#mktcms-admin){@apply bg-gray-100}#mktcms-admin .boxed{@apply p-4 max-w-3xl mx-auto md:my-6 bg-white md:rounded-lg shadow-lg flex flex-col}#mktcms-admin .fullscreen{@apply max-w-none rounded-none max-h-screen h-screen overflow-hidden bg-white p-3 md:p-6 flex flex-col}#mktcms-admin h1{@apply text-gray-800 text-3xl font-semibold}#mktcms-admin input[type=email],#mktcms-admin input[type=password],#mktcms-admin input[type=text],#mktcms-admin select,#mktcms-admin textarea{@apply w-full bg-white p-2.5 text-base border border-gray-300 rounded}#mktcms-admin .button{@apply inline-flex items-center gap-2 px-4 py-2.5 bg-emerald-600 text-white rounded hover:bg-emerald-700 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed}#mktcms-admin .button.secondary{@apply bg-gray-50 text-gray-700 border border-gray-300 hover:bg-gray-100}#mktcms-admin .button.tertiary{@apply bg-transparent text-gray-500 border border-gray-100 hover:bg-gray-100 hover:text-gray-700}#mktcms-admin .button.directory{@apply bg-gray-700 text-white border border-gray-600 hover:bg-gray-600}#mktcms-admin .button.danger{@apply bg-red-600 hover:bg-red-700}#mktcms-admin .button.small{@apply px-2.5 py-1.5 text-sm}#mktcms-admin .button :disabled{@apply cursor-not-allowed opacity-50}