mktcms 0.1.21 → 0.1.23

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 (113) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +45 -15
  3. package/dist/runtime/app/components/admin.vue +1 -1
  4. package/dist/runtime/app/components/content/breadcrumb.d.vue.ts +1 -4
  5. package/dist/runtime/app/components/content/breadcrumb.vue +25 -9
  6. package/dist/runtime/app/components/content/breadcrumb.vue.d.ts +1 -4
  7. package/dist/runtime/app/components/content/delete.vue +69 -0
  8. package/dist/runtime/app/components/content/dirs.d.vue.ts +0 -1
  9. package/dist/runtime/app/components/content/dirs.vue +19 -3
  10. package/dist/runtime/app/components/content/dirs.vue.d.ts +0 -1
  11. package/dist/runtime/app/components/content/editor/csv.vue +310 -0
  12. package/dist/runtime/app/components/content/editor/{blob/image.d.vue.ts → frontmatter/form.d.vue.ts} +3 -3
  13. package/dist/runtime/app/components/content/editor/frontmatter/form.vue +17 -0
  14. package/dist/runtime/app/components/content/editor/{text/csv.d.vue.ts → frontmatter/form.vue.d.ts} +3 -3
  15. package/dist/runtime/app/components/content/editor/frontmatter/input.d.vue.ts +14 -0
  16. package/dist/runtime/app/components/content/editor/frontmatter/input.vue +53 -0
  17. package/dist/runtime/app/components/content/editor/frontmatter/input.vue.d.ts +14 -0
  18. package/dist/runtime/app/components/content/editor/image.vue +27 -0
  19. package/dist/runtime/app/components/content/editor/markdown.vue +114 -0
  20. package/dist/runtime/app/components/content/editor/pdf.d.vue.ts +3 -0
  21. package/dist/runtime/app/components/content/editor/pdf.vue +26 -0
  22. package/dist/runtime/app/components/content/editor/pdf.vue.d.ts +3 -0
  23. package/dist/runtime/app/components/content/editor/selectFile/breadcrumb.d.vue.ts +10 -0
  24. package/dist/runtime/app/components/content/editor/selectFile/breadcrumb.vue +51 -0
  25. package/dist/runtime/app/components/content/editor/selectFile/breadcrumb.vue.d.ts +10 -0
  26. package/dist/runtime/app/components/content/editor/selectFile/index.d.vue.ts +12 -0
  27. package/dist/runtime/app/components/content/editor/selectFile/index.vue +89 -0
  28. package/dist/runtime/app/components/content/editor/selectFile/index.vue.d.ts +12 -0
  29. package/dist/runtime/app/components/content/editor/txt.d.vue.ts +3 -0
  30. package/dist/runtime/app/components/content/editor/txt.vue +41 -0
  31. package/dist/runtime/app/components/content/editor/txt.vue.d.ts +3 -0
  32. package/dist/runtime/app/components/content/fileButtons.d.vue.ts +6 -0
  33. package/dist/runtime/app/components/content/fileButtons.vue +92 -0
  34. package/dist/runtime/app/components/content/fileButtons.vue.d.ts +6 -0
  35. package/dist/runtime/app/components/content/fileIcon.d.vue.ts +6 -0
  36. package/dist/runtime/app/components/content/fileIcon.vue +80 -0
  37. package/dist/runtime/app/components/content/fileIcon.vue.d.ts +6 -0
  38. package/dist/runtime/app/components/content/files.d.vue.ts +0 -1
  39. package/dist/runtime/app/components/content/files.vue +26 -92
  40. package/dist/runtime/app/components/content/files.vue.d.ts +0 -1
  41. package/dist/runtime/app/components/content/index.vue +8 -34
  42. package/dist/runtime/app/components/content/saved.d.vue.ts +3 -0
  43. package/dist/runtime/app/components/content/saved.vue +19 -0
  44. package/dist/runtime/app/components/content/saved.vue.d.ts +3 -0
  45. package/dist/runtime/app/components/content/upload.vue +40 -28
  46. package/dist/runtime/app/components/header.vue +55 -25
  47. package/dist/runtime/app/composables/useAdminUpload.d.ts +0 -1
  48. package/dist/runtime/app/composables/useAdminUpload.js +4 -15
  49. package/dist/runtime/app/composables/useFileType.d.ts +7 -0
  50. package/dist/runtime/app/composables/useFileType.js +14 -0
  51. package/dist/runtime/app/composables/usePathParam.d.ts +9 -0
  52. package/dist/runtime/app/composables/usePathParam.js +16 -0
  53. package/dist/runtime/app/pages/admin/delete/[path].d.vue.ts +3 -0
  54. package/dist/runtime/app/pages/admin/delete/[path].vue +14 -0
  55. package/dist/runtime/app/pages/admin/delete/[path].vue.d.ts +3 -0
  56. package/dist/runtime/app/pages/admin/edit/[path].d.vue.ts +3 -0
  57. package/dist/runtime/app/pages/admin/edit/[path].vue +27 -0
  58. package/dist/runtime/app/pages/admin/edit/[path].vue.d.ts +3 -0
  59. package/dist/runtime/app/pages/admin/index.vue +2 -0
  60. package/dist/runtime/app/pages/admin/login.vue +68 -21
  61. package/dist/runtime/app/styles/admin.css +1 -0
  62. package/dist/runtime/app/styles/admin.min.css +1 -0
  63. package/dist/runtime/server/api/admin/blob.js +37 -0
  64. package/dist/runtime/server/api/admin/csv.d.ts +5 -0
  65. package/dist/runtime/server/api/admin/csv.js +28 -0
  66. package/dist/runtime/server/api/admin/csv.post.d.ts +4 -0
  67. package/dist/runtime/server/api/admin/csv.post.js +28 -0
  68. package/dist/runtime/server/api/admin/delete.d.ts +4 -0
  69. package/dist/runtime/server/api/admin/delete.js +15 -0
  70. package/dist/runtime/server/api/admin/{content/[path].post.d.ts → download.d.ts} +1 -1
  71. package/dist/runtime/server/api/admin/download.js +29 -0
  72. package/dist/runtime/server/api/admin/list.d.ts +5 -0
  73. package/dist/runtime/server/api/admin/list.js +24 -0
  74. package/dist/runtime/server/api/admin/md.d.ts +5 -0
  75. package/dist/runtime/server/api/admin/md.js +22 -0
  76. package/dist/runtime/server/api/admin/md.post.d.ts +4 -0
  77. package/dist/runtime/server/api/admin/md.post.js +31 -0
  78. package/dist/runtime/server/api/admin/{content/list.d.ts → txt.d.ts} +1 -1
  79. package/dist/runtime/server/api/admin/txt.js +21 -0
  80. package/dist/runtime/server/api/admin/txt.post.d.ts +4 -0
  81. package/dist/runtime/server/api/admin/txt.post.js +19 -0
  82. package/dist/runtime/server/api/admin/{content/upload.js → upload.js} +1 -1
  83. package/dist/runtime/server/api/content/[path].js +15 -14
  84. package/dist/runtime/server/api/content/list.d.ts +11 -1
  85. package/dist/runtime/server/api/content/list.js +35 -10
  86. package/dist/runtime/server/plugins/storage.js +0 -3
  87. package/dist/runtime/server/utils/parseFrontmatter.d.ts +4 -0
  88. package/dist/runtime/server/utils/parseFrontmatter.js +24 -0
  89. package/package.json +10 -2
  90. package/dist/runtime/app/components/content/editor/blob/image.vue +0 -12
  91. package/dist/runtime/app/components/content/editor/blob/image.vue.d.ts +0 -10
  92. package/dist/runtime/app/components/content/editor/blob/index.vue +0 -41
  93. package/dist/runtime/app/components/content/editor/text/csv.vue +0 -235
  94. package/dist/runtime/app/components/content/editor/text/csv.vue.d.ts +0 -10
  95. package/dist/runtime/app/components/content/editor/text/index.vue +0 -58
  96. package/dist/runtime/app/components/content/editor/text/markdown.d.vue.ts +0 -10
  97. package/dist/runtime/app/components/content/editor/text/markdown.vue +0 -69
  98. package/dist/runtime/app/components/content/editor/text/markdown.vue.d.ts +0 -10
  99. package/dist/runtime/app/pages/admin/edit/blob/[path].vue +0 -12
  100. package/dist/runtime/app/pages/admin/edit/text/[path].vue +0 -12
  101. package/dist/runtime/server/api/admin/content/[path].js +0 -34
  102. package/dist/runtime/server/api/admin/content/[path].post.js +0 -18
  103. package/dist/runtime/server/api/admin/content/list.js +0 -20
  104. /package/dist/runtime/app/components/content/{editor/blob/index.d.vue.ts → delete.d.vue.ts} +0 -0
  105. /package/dist/runtime/app/components/content/{editor/blob/index.vue.d.ts → delete.vue.d.ts} +0 -0
  106. /package/dist/runtime/app/components/content/editor/{text/index.d.vue.ts → csv.d.vue.ts} +0 -0
  107. /package/dist/runtime/app/components/content/editor/{text/index.vue.d.ts → csv.vue.d.ts} +0 -0
  108. /package/dist/runtime/app/{pages/admin/edit/blob/[path].d.vue.ts → components/content/editor/image.d.vue.ts} +0 -0
  109. /package/dist/runtime/app/{pages/admin/edit/blob/[path].vue.d.ts → components/content/editor/image.vue.d.ts} +0 -0
  110. /package/dist/runtime/app/{pages/admin/edit/text/[path].d.vue.ts → components/content/editor/markdown.d.vue.ts} +0 -0
  111. /package/dist/runtime/app/{pages/admin/edit/text/[path].vue.d.ts → components/content/editor/markdown.vue.d.ts} +0 -0
  112. /package/dist/runtime/server/api/admin/{content/[path].d.ts → blob.d.ts} +0 -0
  113. /package/dist/runtime/server/api/admin/{content/upload.d.ts → upload.d.ts} +0 -0
@@ -0,0 +1,80 @@
1
+ <script setup>
2
+ import { useRuntimeConfig } from "#app";
3
+ import useFileType from "../../composables/useFileType";
4
+ const { filePath } = defineProps({
5
+ filePath: { type: String, required: true }
6
+ });
7
+ const { public: { mktcms: { siteUrl } } } = useRuntimeConfig();
8
+ const { isImage, isCsv, isMarkdown, isText, isPdf } = useFileType(filePath);
9
+ </script>
10
+
11
+ <template>
12
+ <img
13
+ v-if="isImage"
14
+ :src="`${siteUrl}/api/content/${filePath}`"
15
+ alt="Vorschaubild"
16
+ class="size-6 rounded-sm object-cover mr-1"
17
+ >
18
+ <svg
19
+ v-if="isCsv"
20
+ xmlns="http://www.w3.org/2000/svg"
21
+ width="24"
22
+ height="24"
23
+ viewBox="0 0 24 24"
24
+ fill="none"
25
+ stroke="currentColor"
26
+ stroke-width="2"
27
+ stroke-linecap="round"
28
+ stroke-linejoin="round"
29
+ class="size-6 opacity-20"
30
+ >
31
+ <path d="M12 3v18" />
32
+ <rect
33
+ width="18"
34
+ height="18"
35
+ x="3"
36
+ y="3"
37
+ rx="2"
38
+ />
39
+ <path d="M3 9h18" />
40
+ <path d="M3 15h18" />
41
+ </svg>
42
+ <svg
43
+ v-if="isMarkdown || isText"
44
+ xmlns="http://www.w3.org/2000/svg"
45
+ width="24"
46
+ height="24"
47
+ viewBox="0 0 24 24"
48
+ fill="none"
49
+ stroke="currentColor"
50
+ stroke-width="2"
51
+ stroke-linecap="round"
52
+ stroke-linejoin="round"
53
+ class="size-6 opacity-20"
54
+ >
55
+ <path
56
+ d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"
57
+ />
58
+ <path d="M14 2v5a1 1 0 0 0 1 1h5" />
59
+ <path d="M10 9H8" />
60
+ <path d="M16 13H8" />
61
+ <path d="M16 17H8" />
62
+ </svg>
63
+ <svg
64
+ v-if="isPdf"
65
+ xmlns="http://www.w3.org/2000/svg"
66
+ width="24"
67
+ height="24"
68
+ viewBox="0 0 24 24"
69
+ class="size-6 opacity-20"
70
+ >
71
+ <path
72
+ fill="none"
73
+ stroke="currentColor"
74
+ stroke-linecap="round"
75
+ stroke-linejoin="round"
76
+ stroke-width="2"
77
+ d="M10 8v8h2a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2zm-7 4h2a2 2 0 1 0 0-4H3v8m14-4h3m1-4h-4v8"
78
+ />
79
+ </svg>
80
+ </template>
@@ -0,0 +1,6 @@
1
+ type __VLS_Props = {
2
+ filePath: string;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare const _default: typeof __VLS_export;
6
+ export default _default;
@@ -1,5 +1,4 @@
1
1
  type __VLS_Props = {
2
- path: string;
3
2
  files: string[];
4
3
  };
5
4
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,109 +1,43 @@
1
1
  <script setup>
2
+ import usePathParam from "../../composables/usePathParam";
3
+ import FileIcon from "./fileIcon.vue";
4
+ import FileButtons from "./fileButtons.vue";
2
5
  defineProps({
3
- path: { type: String, required: true },
4
6
  files: { type: Array, required: true }
5
7
  });
8
+ const { path } = usePathParam();
9
+ function filenameWithoutExtension(filename) {
10
+ return filename.replace(/\.[^/.]+$/, "");
11
+ }
12
+ function fileExtension(filename) {
13
+ const match = filename.match(/\.([^.]+)$/);
14
+ return match && match[1] ? match[1] : "";
15
+ }
6
16
  </script>
7
17
 
8
18
  <template>
9
- <div class="files">
19
+ <div>
10
20
  <div
11
21
  v-for="file in files"
12
22
  :key="file"
13
- style="display: flex; align-items: center;"
23
+ class="flex gap-2 mb-2"
14
24
  >
15
25
  <NuxtLink
16
- :to="`/admin/edit/${file.match(/\.md$|\.csv$|\.txt$|\.json$/i) ? 'text' : 'blob'}/${path ? path + ':' : ''}${file}`"
17
- style="flex-grow: 1;"
26
+ :to="`/admin/edit/${path ? path + ':' : ''}${file}`"
27
+ class="flex-1 button secondary"
18
28
  >
19
- <img
20
- v-if="file.match(/\.png$|\.jpg$|\.jpeg$|\.gif$|\.svg$|\.webp$/i)"
21
- :src="`/api/content/${path ? path + ':' : ''}${file}`"
22
- alt="Vorschaubild"
23
- style="width: 64px; height: 64px; vertical-align: middle; margin-right: 4px; object-fit: cover;"
24
- >
25
- {{ file }}
26
- </NuxtLink>
27
- <NuxtLink
28
- v-if="file.match(/\.png$|\.jpg$|\.jpeg$|\.gif$|\.svg$|\.webp$/i)"
29
- style="margin-left: 8px;"
30
- title="Link kopieren"
31
- >
32
- <svg
33
- xmlns="http://www.w3.org/2000/svg"
34
- fill="none"
35
- viewBox="0 0 24 24"
36
- stroke-width="1.5"
37
- stroke="currentColor"
38
- class="button-icon"
39
- >
40
- <path
41
- stroke-linecap="round"
42
- stroke-linejoin="round"
43
- d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
44
- />
45
- </svg>
46
- </NuxtLink>
47
- <NuxtLink
48
- :to="`/admin/move/${path ? path + ':' : ''}${file}`"
49
- style="margin-left: 8px;"
50
- title="verschieben / umbenennen"
51
- >
52
- <svg
53
- xmlns="http://www.w3.org/2000/svg"
54
- fill="none"
55
- viewBox="0 0 24 24"
56
- stroke-width="1.5"
57
- stroke="currentColor"
58
- class="button-icon"
59
- >
60
- <path
61
- stroke-linecap="round"
62
- stroke-linejoin="round"
63
- d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 0 0-1.883 2.542l.857 6a2.25 2.25 0 0 0 2.227 1.932H19.05a2.25 2.25 0 0 0 2.227-1.932l.857-6a2.25 2.25 0 0 0-1.883-2.542m-16.5 0V6A2.25 2.25 0 0 1 6 3.75h3.879a1.5 1.5 0 0 1 1.06.44l2.122 2.12a1.5 1.5 0 0 0 1.06.44H18A2.25 2.25 0 0 1 20.25 9v.776"
64
- />
65
- </svg>
66
- </NuxtLink>
67
- <NuxtLink
68
- :to="`/admin/copy/${path ? path + ':' : ''}${file}`"
69
- style="margin-left: 8px;"
70
- title="kopieren"
71
- >
72
- <svg
73
- xmlns="http://www.w3.org/2000/svg"
74
- fill="none"
75
- viewBox="0 0 24 24"
76
- stroke-width="1.5"
77
- stroke="currentColor"
78
- class="button-icon"
79
- >
80
- <path
81
- stroke-linecap="round"
82
- stroke-linejoin="round"
83
- d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
84
- />
85
- </svg>
86
- </NuxtLink>
87
- <NuxtLink
88
- :to="`/admin/delete/${path ? path + ':' : ''}${file}`"
89
- style="margin-left: 8px;"
90
- title="löschen"
91
- >
92
- <svg
93
- xmlns="http://www.w3.org/2000/svg"
94
- fill="none"
95
- viewBox="0 0 24 24"
96
- stroke-width="1.5"
97
- stroke="currentColor"
98
- class="button-icon"
99
- >
100
- <path
101
- stroke-linecap="round"
102
- stroke-linejoin="round"
103
- d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
104
- />
105
- </svg>
29
+ <FileIcon :file-path="`${path ? path + ':' : ''}${file}`" />
30
+ <div class="w-full flex">
31
+ {{ filenameWithoutExtension(file) }}
32
+ <span
33
+ v-if="fileExtension(file)"
34
+ class="text-sm text-gray-400 ml-auto"
35
+ >
36
+ .{{ fileExtension(file) }}
37
+ </span>
38
+ </div>
106
39
  </NuxtLink>
40
+ <FileButtons :file-path="`${path ? path + ':' : ''}${file}`" />
107
41
  </div>
108
42
  </div>
109
43
  </template>
@@ -1,5 +1,4 @@
1
1
  type __VLS_Props = {
2
- path: string;
3
2
  files: string[];
4
3
  };
5
4
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,50 +1,24 @@
1
1
  <script setup>
2
- import { useFetch, useRoute } from "#app";
3
- import { computed } from "vue";
4
- import Breadcrumb from "./breadcrumb.vue";
2
+ import { useFetch } from "#app";
5
3
  import Files from "./files.vue";
6
4
  import Dirs from "./dirs.vue";
7
- const path = useRoute().params.path || "";
8
- const pathParts = path.split(":");
9
- const { data: keys } = await useFetch("/api/admin/content/list", {
5
+ import usePathParam from "../../composables/usePathParam";
6
+ const { path } = usePathParam();
7
+ const { data: list } = await useFetch("/api/admin/list", {
10
8
  query: { path }
11
9
  });
12
- const keysWithoutCurrentPath = computed(() => {
13
- return keys.value?.map(
14
- (key) => key.replace(new RegExp("^" + pathParts.join(":") + ":"), "")
15
- ) || [];
16
- });
17
- const files = computed(() => {
18
- return keysWithoutCurrentPath.value.filter((key) => !key.includes(":"));
19
- });
20
- const dirs = computed(() => {
21
- return keysWithoutCurrentPath.value.reduce((acc, key) => {
22
- const parts = key.split(":");
23
- if (parts.length > 1 && parts[0]) {
24
- const dir = parts[0];
25
- if (!acc.includes(dir)) {
26
- acc.push(dir);
27
- }
28
- }
29
- return acc;
30
- }, []);
31
- });
32
10
  </script>
33
11
 
34
12
  <template>
35
13
  <div>
36
- <Breadcrumb :parts="pathParts" />
37
-
38
14
  <Files
39
- v-if="files.length"
40
- :path="path"
41
- :files="files"
15
+ v-if="list && list.files.length"
16
+ :files="list.files"
42
17
  />
43
18
 
44
19
  <Dirs
45
- v-if="dirs.length"
46
- :path="path"
47
- :dirs="dirs"
20
+ v-if="list && list.dirs.length"
21
+ :dirs="list.dirs"
48
22
  style="margin-top: 8px;"
49
23
  />
50
24
  </div>
@@ -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,19 @@
1
+ <template>
2
+ <div class="text-emerald-700 text-center my-3 flex items-center justify-center gap-2">
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ fill="none"
6
+ viewBox="0 0 24 24"
7
+ stroke-width="1.5"
8
+ stroke="currentColor"
9
+ class="size-6"
10
+ >
11
+ <path
12
+ stroke-linecap="round"
13
+ stroke-linejoin="round"
14
+ d="m4.5 12.75 6 6 9-13.5"
15
+ />
16
+ </svg>
17
+ Gespeichert
18
+ </div>
19
+ </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,20 +1,7 @@
1
1
  <script setup>
2
- import { computed, onMounted, ref, useFetch, useRoute } from "#imports";
2
+ import { onMounted, ref, useFetch, useRoute } from "#imports";
3
3
  import useAdminUpload from "../../composables/useAdminUpload";
4
- const { data: keys } = await useFetch("/api/admin/content/list");
5
- const dirs = computed(() => {
6
- const dirSet = /* @__PURE__ */ new Set();
7
- keys.value?.forEach((key) => {
8
- const parts = key.split(":");
9
- if (parts.length > 1) {
10
- for (let i = 0; i < parts.length - 1; i++) {
11
- const dir2 = parts.slice(0, i + 1).join("/");
12
- dirSet.add(dir2);
13
- }
14
- }
15
- });
16
- return Array.from(dirSet).sort();
17
- });
4
+ const { data: list } = await useFetch("/api/admin/list");
18
5
  const route = useRoute();
19
6
  const dir = ref(route.query.dir || "");
20
7
  const newSubdir = ref("");
@@ -25,14 +12,33 @@ onMounted(() => {
25
12
  </script>
26
13
 
27
14
  <template>
28
- <div style="display: flex; flex-direction: column; gap: 8px; margin: 16px 0;">
29
- <h1 style="margin-bottom: 0;">
15
+ <div class="flex flex-col gap-2 my-4">
16
+ <h1 class="my-6">
30
17
  Datei hochladen
31
18
  </h1>
32
19
 
33
- <div style="display: flex; gap: 16px; align-items: center;">
34
- <div style="flex-grow: 1;">
35
- <h3 style="margin-bottom: 4px;">
20
+ <div class="bg-emerald-50 text-emerald-800 p-4 rounded flex gap-4 items-center">
21
+ <svg
22
+ xmlns="http://www.w3.org/2000/svg"
23
+ fill="none"
24
+ viewBox="0 0 24 24"
25
+ stroke-width="1.5"
26
+ stroke="currentColor"
27
+ class="size-10 opacity-50"
28
+ >
29
+ <path
30
+ stroke-linecap="round"
31
+ stroke-linejoin="round"
32
+ d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
33
+ />
34
+ </svg>
35
+ Wenn eine Datei mit gleichem Namen im ausgewählten Ordner bereits existiert, wird diese
36
+ überschrieben!
37
+ </div>
38
+
39
+ <div class="flex gap-4 items-center">
40
+ <div class="flex-1">
41
+ <h3 class="mb-1">
36
42
  Ordner
37
43
  </h3>
38
44
  <select
@@ -43,7 +49,7 @@ onMounted(() => {
43
49
  Hauptordner
44
50
  </option>
45
51
  <option
46
- v-for="d in dirs"
52
+ v-for="d in list?.dirs"
47
53
  :key="d"
48
54
  :value="d"
49
55
  >
@@ -51,32 +57,38 @@ onMounted(() => {
51
57
  </option>
52
58
  </select>
53
59
  </div>
54
- <div style="flex-grow: 1;">
55
- <h3 style="margin-bottom: 4px;">
56
- Neuen Unterordner erstellen
60
+ <div class="flex-1">
61
+ <h3 class="mb-1">
62
+ Unterordner erstellen
57
63
  </h3>
58
64
  <input
59
65
  v-model="newSubdir"
60
66
  type="text"
61
- placeholder="Unterordner (z.B. 'Produkte')"
67
+ placeholder="z.B. Produkte"
62
68
  @change="path = dir ? dir.replace(/\//g, ':') + ':' + newSubdir : newSubdir"
63
69
  >
64
70
  </div>
65
71
  </div>
66
72
 
67
73
  <button
74
+ type="button"
75
+ class="button"
68
76
  :disabled="isUploading"
69
77
  @click="fileInputImg?.click()"
70
78
  >
71
79
  Bild hochladen
72
80
  </button>
73
81
  <button
82
+ type="button"
83
+ class="button"
74
84
  :disabled="isUploading"
75
85
  @click="fileInputPdf?.click()"
76
86
  >
77
87
  PDF hochladen
78
88
  </button>
79
89
  <button
90
+ type="button"
91
+ class="button"
80
92
  :disabled="isUploading"
81
93
  @click="fileInput?.click()"
82
94
  >
@@ -84,7 +96,7 @@ onMounted(() => {
84
96
  </button>
85
97
  <input
86
98
  ref="fileInput"
87
- style="display: none"
99
+ class="hidden"
88
100
  type="file"
89
101
  accept=".pdf,.jpg,.jpeg,.png,.gif,.svg,.webp,.md,.docx,.txt"
90
102
  @change="async (e) => {
@@ -93,7 +105,7 @@ onMounted(() => {
93
105
  >
94
106
  <input
95
107
  ref="fileInputImg"
96
- style="display: none"
108
+ class="hidden"
97
109
  type="file"
98
110
  accept=".jpg,.jpeg,.png,.gif,.svg,.webp"
99
111
  @change="async (e) => {
@@ -102,7 +114,7 @@ onMounted(() => {
102
114
  >
103
115
  <input
104
116
  ref="fileInputPdf"
105
- style="display: none"
117
+ class="hidden"
106
118
  type="file"
107
119
  accept=".pdf"
108
120
  @change="async (e) => {
@@ -1,15 +1,41 @@
1
1
  <script setup>
2
- import { useRoute, useRuntimeConfig } from "#imports";
2
+ import { useRuntimeConfig } from "#imports";
3
3
  const { public: { mktcms: { siteUrl } } } = useRuntimeConfig();
4
- const route = useRoute();
5
4
  </script>
6
5
 
7
6
  <template>
8
- <div style="display: flex; justify-content: space-between; align-items: center; gap: 8px;">
7
+ <div class="flex items-start justify-between gap-2">
8
+ <div class="flex flex-col gap-1 items-start">
9
+ <NuxtLink
10
+ to="/admin"
11
+ class="font-bold text-xl"
12
+ >
13
+ {{ siteUrl.replace(/https?:\/\//, "") }}
14
+ </NuxtLink>
15
+ <NuxtLink
16
+ :to="siteUrl"
17
+ class="font-bold button small secondary justify-between"
18
+ >
19
+ zur Website
20
+ <svg
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ fill="none"
23
+ viewBox="0 0 24 24"
24
+ stroke-width="1.5"
25
+ stroke="currentColor"
26
+ class="size-4"
27
+ >
28
+ <path
29
+ stroke-linecap="round"
30
+ stroke-linejoin="round"
31
+ d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
32
+ />
33
+ </svg>
34
+ </NuxtLink>
35
+ </div>
9
36
  <NuxtLink
10
- v-if="route.fullPath != '/admin'"
11
- to="/admin"
12
- class="button soft"
37
+ to="/admin/new"
38
+ class="button small ml-auto"
13
39
  >
14
40
  <svg
15
41
  xmlns="http://www.w3.org/2000/svg"
@@ -17,36 +43,40 @@ const route = useRoute();
17
43
  viewBox="0 0 24 24"
18
44
  stroke-width="1.5"
19
45
  stroke="currentColor"
20
- class="button-icon"
46
+ class="size-4"
21
47
  >
22
48
  <path
23
49
  stroke-linecap="round"
24
50
  stroke-linejoin="round"
25
- d="M15.75 19.5 8.25 12l7.5-7.5"
51
+ d="M12 4.5v15m7.5-7.5h-15"
26
52
  />
27
53
  </svg>
28
- </NuxtLink>
29
- <NuxtLink
30
- to="/admin/new"
31
- class="button"
32
- style="margin-left: auto;"
33
- >
34
- Datei hochladen
35
- </NuxtLink>
36
- <NuxtLink
37
- :to="siteUrl"
38
- external
39
- target="_blank"
40
- class="button soft"
41
- >
42
- zur Website
54
+ <span class="hidden sm:inline">
55
+ Datei hochladen
56
+ </span>
43
57
  </NuxtLink>
44
58
  <NuxtLink
45
59
  external
46
60
  to="/api/admin/logout"
47
- class="button soft"
61
+ class="button small secondary"
48
62
  >
49
- Abmelden
63
+ <span class="hidden sm:inline">
64
+ abmelden
65
+ </span>
66
+ <svg
67
+ xmlns="http://www.w3.org/2000/svg"
68
+ width="24"
69
+ height="24"
70
+ viewBox="0 0 24 24"
71
+ fill="none"
72
+ stroke="currentColor"
73
+ stroke-width="2"
74
+ stroke-linecap="round"
75
+ stroke-linejoin="round"
76
+ class="size-4"
77
+ >
78
+ <path d="m16 17 5-5-5-5" /><path d="M21 12H9" /><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
79
+ </svg>
50
80
  </NuxtLink>
51
81
  </div>
52
82
  </template>
@@ -7,5 +7,4 @@ export default function useAdminUpload(): {
7
7
  fileInputImg: import("vue").Ref<HTMLInputElement | null, HTMLInputElement | null>;
8
8
  fileInputPdf: import("vue").Ref<HTMLInputElement | null, HTMLInputElement | null>;
9
9
  uploadFiles: (event: Event) => Promise<void>;
10
- deleteFile: (path: string) => Promise<void>;
11
10
  };
@@ -1,3 +1,4 @@
1
+ import { navigateTo } from "#imports";
1
2
  import { computed, ref } from "vue";
2
3
  export default function useAdminUpload() {
3
4
  const uploadError = ref(null);
@@ -25,7 +26,7 @@ export default function useAdminUpload() {
25
26
  const formData = new FormData();
26
27
  formData.append("file", file);
27
28
  try {
28
- const res = await $fetch("/api/admin/content/upload", {
29
+ const res = await $fetch("/api/admin/upload", {
29
30
  method: "POST",
30
31
  body: formData,
31
32
  query: { path: sanePath.value }
@@ -33,24 +34,13 @@ export default function useAdminUpload() {
33
34
  if (res?.success && res.path) {
34
35
  files.value.push(res.path);
35
36
  }
37
+ await navigateTo(`/admin/${sanePath.value ? sanePath.value : ""}`);
36
38
  } catch (error) {
37
39
  uploadError.value = error.data?.statusMessage || "Fehler beim Hochladen der Datei";
38
40
  input.value = "";
39
41
  }
40
42
  isUploading.value = false;
41
43
  }
42
- async function deleteFile(path2) {
43
- uploadError.value = null;
44
- try {
45
- await $fetch("/api/content/remove", {
46
- method: "DELETE",
47
- body: { path: path2 }
48
- });
49
- files.value = files.value.filter((p) => p !== path2);
50
- } catch (error) {
51
- uploadError.value = error.data?.statusMessage || "Fehler beim L\xF6schen der Datei";
52
- }
53
- }
54
44
  return {
55
45
  uploadError,
56
46
  isUploading,
@@ -59,7 +49,6 @@ export default function useAdminUpload() {
59
49
  fileInput,
60
50
  fileInputImg,
61
51
  fileInputPdf,
62
- uploadFiles,
63
- deleteFile
52
+ uploadFiles
64
53
  };
65
54
  }
@@ -0,0 +1,7 @@
1
+ export default function useFileType(path: string): {
2
+ isImage: boolean;
3
+ isPdf: boolean;
4
+ isMarkdown: boolean;
5
+ isCsv: boolean;
6
+ isText: boolean;
7
+ };
@@ -0,0 +1,14 @@
1
+ export default function useFileType(path) {
2
+ const isImage = path.match(/\.(png|jpg|jpeg|gif|svg|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;
7
+ return {
8
+ isImage,
9
+ isPdf,
10
+ isMarkdown,
11
+ isCsv,
12
+ isText
13
+ };
14
+ }
@@ -0,0 +1,9 @@
1
+ export default function usePathParam(): {
2
+ path: string;
3
+ pathParts: string[];
4
+ isImage: boolean;
5
+ isPdf: boolean;
6
+ isMarkdown: boolean;
7
+ isCsv: boolean;
8
+ isText: boolean;
9
+ };