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
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mktcms",
3
3
  "configKey": "mktcms",
4
- "version": "0.1.21",
4
+ "version": "0.1.23",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -45,22 +45,52 @@ const module$1 = defineNuxtModule({
45
45
  handler: resolver.resolve("./runtime/server/api/admin/logout")
46
46
  });
47
47
  addServerHandler({
48
- route: "/api/admin/content/list",
49
- handler: resolver.resolve("./runtime/server/api/admin/content/list")
48
+ route: "/api/admin/list",
49
+ handler: resolver.resolve("./runtime/server/api/admin/list")
50
50
  });
51
51
  addServerHandler({
52
- route: "/api/admin/content/:path",
53
- method: "get",
54
- handler: resolver.resolve("./runtime/server/api/admin/content/[path]")
52
+ route: "/api/admin/delete",
53
+ method: "delete",
54
+ handler: resolver.resolve("./runtime/server/api/admin/delete")
55
55
  });
56
56
  addServerHandler({
57
- route: "/api/admin/content/:path",
57
+ route: "/api/admin/csv",
58
+ handler: resolver.resolve("./runtime/server/api/admin/csv")
59
+ });
60
+ addServerHandler({
61
+ route: "/api/admin/csv",
62
+ method: "post",
63
+ handler: resolver.resolve("./runtime/server/api/admin/csv.post")
64
+ });
65
+ addServerHandler({
66
+ route: "/api/admin/md",
67
+ handler: resolver.resolve("./runtime/server/api/admin/md")
68
+ });
69
+ addServerHandler({
70
+ route: "/api/admin/md",
71
+ method: "post",
72
+ handler: resolver.resolve("./runtime/server/api/admin/md.post")
73
+ });
74
+ addServerHandler({
75
+ route: "/api/admin/txt",
76
+ handler: resolver.resolve("./runtime/server/api/admin/txt")
77
+ });
78
+ addServerHandler({
79
+ route: "/api/admin/txt",
58
80
  method: "post",
59
- handler: resolver.resolve("./runtime/server/api/admin/content/[path].post")
81
+ handler: resolver.resolve("./runtime/server/api/admin/txt.post")
82
+ });
83
+ addServerHandler({
84
+ route: "/api/admin/blob",
85
+ handler: resolver.resolve("./runtime/server/api/admin/blob")
86
+ });
87
+ addServerHandler({
88
+ route: "/api/admin/upload",
89
+ handler: resolver.resolve("./runtime/server/api/admin/upload")
60
90
  });
61
91
  addServerHandler({
62
- route: "/api/admin/content/upload",
63
- handler: resolver.resolve("./runtime/server/api/admin/content/upload")
92
+ route: "/api/admin/download",
93
+ handler: resolver.resolve("./runtime/server/api/admin/download")
64
94
  });
65
95
  addServerHandler({
66
96
  route: "/api/content/list",
@@ -77,14 +107,14 @@ const module$1 = defineNuxtModule({
77
107
  file: resolver.resolve("./runtime/app/pages/admin/index.vue")
78
108
  });
79
109
  pages.push({
80
- name: "Admin Text Editor",
81
- path: "/admin/edit/text/:path",
82
- file: resolver.resolve("./runtime/app/pages/admin/edit/text/[path].vue")
110
+ name: "Admin Editor",
111
+ path: "/admin/edit/:path",
112
+ file: resolver.resolve("./runtime/app/pages/admin/edit/[path].vue")
83
113
  });
84
114
  pages.push({
85
- name: "Admin Blob Editor",
86
- path: "/admin/edit/blob/:path",
87
- file: resolver.resolve("./runtime/app/pages/admin/edit/blob/[path].vue")
115
+ name: "Admin Delete",
116
+ path: "/admin/delete/:path",
117
+ file: resolver.resolve("./runtime/app/pages/admin/delete/[path].vue")
88
118
  });
89
119
  pages.push({
90
120
  name: "Admin New Content",
@@ -5,5 +5,5 @@
5
5
  </template>
6
6
 
7
7
  <style>
8
- *{box-sizing:border-box}body{font-family:Arial,sans-serif;margin:0}#mktcms-admin{--mktcms-primary-color:#3cb371;--mktcms-primary-color-hover:#45a049;background-color:#f9f9f9;margin:0 auto;max-width:800px;padding:20px}#mktcms-admin h1{color:#333}#mktcms-admin .button,#mktcms-admin button{background-color:var(--mktcms-primary-color);border:none;border-radius:5px;color:#fff;cursor:pointer;font-size:16px;padding:10px 20px;text-align:center;text-decoration:none;transition:background-color .3s}#mktcms-admin .button:hover,#mktcms-admin button:hover{background-color:var(--mktcms-primary-color-hover)}#mktcms-admin .button.soft,#mktcms-admin button.soft{background-color:#eee;color:#666}#mktcms-admin .button.soft:hover,#mktcms-admin button.soft:hover{background-color:#ddd}#mktcms-admin .button-icon{height:20px;vertical-align:middle;width:20px}#mktcms-admin input[type=email],#mktcms-admin input[type=password],#mktcms-admin input[type=text],#mktcms-admin select,#mktcms-admin textarea{background-color:#fff;border:1px solid #ccc;border-radius:5px;font-size:16px;padding:10px;width:100%}#mktcms-admin .breadcrumbs{color:#888;font-size:1rem;margin:20px 0}#mktcms-admin .breadcrumbs a{background-color:#eee;border-radius:4px;color:#888;padding:2px 6px;text-decoration:none}#mktcms-admin .breadcrumbs a:hover{text-decoration:underline}#mktcms-admin .dirs,#mktcms-admin .files{display:flex;flex-direction:column;gap:8px}#mktcms-admin .dirs a,#mktcms-admin .files a{border-radius:4px;display:block;padding:8px 12px;text-decoration:none}#mktcms-admin .dirs a:hover,#mktcms-admin .files a:hover{text-decoration:underline}#mktcms-admin .files a{background-color:#fff;color:#555}#mktcms-admin .dirs a{background-color:#555;color:#fff;display:flex;justify-content:space-between}
8
+ @import "../styles/admin.min.css";
9
9
  </style>
@@ -1,6 +1,3 @@
1
- type __VLS_Props = {
2
- parts: 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>;
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>;
5
2
  declare const _default: typeof __VLS_export;
6
3
  export default _default;
@@ -1,20 +1,36 @@
1
1
  <script setup>
2
- defineProps({
3
- parts: { type: Array, required: true }
4
- });
2
+ import { useRoute } from "#app";
3
+ const path = useRoute().params.path || "";
4
+ const parts = path.split(":").filter((part) => part.length > 0);
5
+ parts.unshift("Hauptordner");
5
6
  </script>
6
7
 
7
8
  <template>
8
- <div class="breadcrumbs">
9
- <NuxtLink to="/admin">Hauptordner</NuxtLink>
10
- <span
9
+ <div class="text-gray-500 text-base mt-12 mb-6 flex items-center gap-1">
10
+ <div
11
11
  v-for="(part, index) in parts"
12
12
  :key="index"
13
+ class="flex items-center gap-1"
13
14
  >
14
- /
15
+ <svg
16
+ v-if="index > 0"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ fill="none"
19
+ viewBox="0 0 24 24"
20
+ stroke-width="1.5"
21
+ stroke="currentColor"
22
+ class="size-6"
23
+ >
24
+ <path
25
+ stroke-linecap="round"
26
+ stroke-linejoin="round"
27
+ d="m8.25 4.5 7.5 7.5-7.5 7.5"
28
+ />
29
+ </svg>
15
30
  <NuxtLink
16
31
  v-if="index < parts.length - 1"
17
- :to="`/admin/${parts.slice(0, index + 1).join(':')}`"
32
+ :to="`/admin/${index > 0 ? parts.slice(1, index + 1).join(':') : ''}`"
33
+ class="button secondary small"
18
34
  >
19
35
  {{ part }}
20
36
  </NuxtLink>
@@ -24,6 +40,6 @@ defineProps({
24
40
  >
25
41
  {{ part }}
26
42
  </span>
27
- </span>
43
+ </div>
28
44
  </div>
29
45
  </template>
@@ -1,6 +1,3 @@
1
- type __VLS_Props = {
2
- parts: 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>;
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>;
5
2
  declare const _default: typeof __VLS_export;
6
3
  export default _default;
@@ -0,0 +1,69 @@
1
+ <script setup>
2
+ import { navigateTo } from "#app";
3
+ import usePathParam from "../../composables/usePathParam";
4
+ const { path, pathParts } = usePathParam();
5
+ const parentPath = pathParts.slice(0, -1).join(":");
6
+ async function deleteContent() {
7
+ await fetch(`/api/admin/delete?path=${path}`, {
8
+ method: "DELETE"
9
+ });
10
+ await navigateTo(`/admin/${parentPath}`);
11
+ }
12
+ </script>
13
+
14
+ <template>
15
+ <div>
16
+ <h1 class="text-2xl font-bold mb-4">
17
+ Datei löschen
18
+ </h1>
19
+ <p class="mb-4">
20
+ Sind Sie sicher, dass Sie
21
+ <strong>{{ pathParts[pathParts.length - 1] }}</strong>
22
+ aus
23
+ <strong>{{ parentPath || "Hauptordner" }}</strong>
24
+ löschen möchten? Die Datei kann nicht wiederhergestellt werden!
25
+ </p>
26
+ <div class="flex gap-2">
27
+ <NuxtLink
28
+ :to="`/admin/${parentPath}`"
29
+ class="button secondary flex-1"
30
+ >
31
+ <svg
32
+ xmlns="http://www.w3.org/2000/svg"
33
+ fill="none"
34
+ viewBox="0 0 24 24"
35
+ stroke-width="1.5"
36
+ stroke="currentColor"
37
+ class="size-5"
38
+ >
39
+ <path
40
+ stroke-linecap="round"
41
+ stroke-linejoin="round"
42
+ d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18"
43
+ />
44
+ </svg>
45
+ Abbrechen
46
+ </NuxtLink>
47
+ <button
48
+ class="button danger"
49
+ @click="deleteContent"
50
+ >
51
+ <svg
52
+ xmlns="http://www.w3.org/2000/svg"
53
+ fill="none"
54
+ viewBox="0 0 24 24"
55
+ stroke-width="1.5"
56
+ stroke="currentColor"
57
+ class="size-5"
58
+ >
59
+ <path
60
+ stroke-linecap="round"
61
+ stroke-linejoin="round"
62
+ 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"
63
+ />
64
+ </svg>
65
+ Datei löschen
66
+ </button>
67
+ </div>
68
+ </div>
69
+ </template>
@@ -1,5 +1,4 @@
1
1
  type __VLS_Props = {
2
- path: string;
3
2
  dirs: 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,21 +1,37 @@
1
1
  <script setup>
2
+ import usePathParam from "../../composables/usePathParam";
2
3
  defineProps({
3
- path: { type: String, required: true },
4
4
  dirs: { type: Array, required: true }
5
5
  });
6
+ const { path } = usePathParam();
6
7
  </script>
7
8
 
8
9
  <template>
9
- <div class="dirs">
10
+ <div class="flex flex-col gap-2">
10
11
  <NuxtLink
11
12
  v-for="dir in dirs"
12
13
  :key="dir"
13
14
  :to="`/admin/${path ? path + ':' : ''}${dir}`"
15
+ class="button secondary"
14
16
  >
17
+ <svg
18
+ xmlns="http://www.w3.org/2000/svg"
19
+ fill="none"
20
+ viewBox="0 0 24 24"
21
+ stroke-width="1.5"
22
+ stroke="currentColor"
23
+ class="size-6 opacity-20"
24
+ >
25
+ <path
26
+ stroke-linecap="round"
27
+ stroke-linejoin="round"
28
+ 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"
29
+ />
30
+ </svg>
15
31
  <span>
16
32
  {{ dir }}
17
33
  </span>
18
- <span>
34
+ <span class="ml-auto">
19
35
  <svg
20
36
  xmlns="http://www.w3.org/2000/svg"
21
37
  fill="none"
@@ -1,5 +1,4 @@
1
1
  type __VLS_Props = {
2
- path: string;
3
2
  dirs: 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>;
@@ -0,0 +1,310 @@
1
+ <script setup>
2
+ import { ref } from "vue";
3
+ import Saved from "../saved.vue";
4
+ import usePathParam from "../../../composables/usePathParam";
5
+ import { useFetch } from "#imports";
6
+ const { path } = usePathParam();
7
+ const { data: table } = await useFetch(`/api/admin/csv?path=${path}`);
8
+ const isSaving = ref(false);
9
+ const savingSuccessful = ref(false);
10
+ async function saveCsv() {
11
+ if (!table.value) return;
12
+ isSaving.value = true;
13
+ savingSuccessful.value = false;
14
+ try {
15
+ await useFetch(`/api/admin/csv?path=${path}`, {
16
+ method: "POST",
17
+ body: {
18
+ table: table.value
19
+ }
20
+ });
21
+ savingSuccessful.value = true;
22
+ } catch (e) {
23
+ console.error("Fehler beim Speichern der CSV-Datei:", e);
24
+ } finally {
25
+ isSaving.value = false;
26
+ }
27
+ }
28
+ function insertRow(atIndex) {
29
+ if (!table.value) return;
30
+ const newRow = [];
31
+ const headerCount = table.value.headers.length;
32
+ for (let i = 0; i < headerCount; i++) {
33
+ newRow[i] = "-";
34
+ }
35
+ table.value = {
36
+ headers: table.value.headers,
37
+ rows: [
38
+ ...table.value.rows.slice(0, atIndex),
39
+ newRow,
40
+ ...table.value.rows.slice(atIndex)
41
+ ]
42
+ };
43
+ }
44
+ function removeRow(rowIndex) {
45
+ if (!table.value) return;
46
+ table.value = {
47
+ headers: table.value.headers,
48
+ rows: table.value.rows.filter((_, index) => index !== rowIndex)
49
+ };
50
+ }
51
+ function moveRowUp(rowIndex) {
52
+ if (!table.value || rowIndex <= 0) return;
53
+ const row = table.value.rows.splice(rowIndex, 1)[0];
54
+ if (!row) return;
55
+ table.value = {
56
+ headers: table.value.headers,
57
+ rows: [
58
+ ...table.value.rows.slice(0, rowIndex - 1),
59
+ row,
60
+ ...table.value.rows.slice(rowIndex - 1)
61
+ ]
62
+ };
63
+ }
64
+ function moveRowDown(rowIndex) {
65
+ if (!table.value || rowIndex >= table.value.rows.length - 1) return;
66
+ const row = table.value.rows.splice(rowIndex, 1)[0];
67
+ if (!row) return;
68
+ table.value = {
69
+ headers: table.value.headers,
70
+ rows: [
71
+ ...table.value.rows.slice(0, rowIndex + 1),
72
+ row,
73
+ ...table.value.rows.slice(rowIndex + 1)
74
+ ]
75
+ };
76
+ }
77
+ const editingCell = ref(null);
78
+ const editBuffer = ref("");
79
+ function startEdit(rowIndex, colIndex) {
80
+ if (!table.value) return;
81
+ editingCell.value = { rowIndex, colIndex };
82
+ editBuffer.value = table.value.rows[rowIndex][colIndex] || "";
83
+ }
84
+ function saveEdit() {
85
+ if (!table.value) return;
86
+ if (!editingCell.value) return;
87
+ const { rowIndex, colIndex } = editingCell.value;
88
+ table.value.rows[rowIndex][colIndex] = editBuffer.value;
89
+ cancelEdit();
90
+ }
91
+ function cancelEdit() {
92
+ editingCell.value = null;
93
+ editBuffer.value = "";
94
+ }
95
+ </script>
96
+
97
+ <template>
98
+ <div class="w-full">
99
+ <div
100
+ v-if="table"
101
+ class="bg-white"
102
+ >
103
+ <div class="flex items-center h-0 justify-center border border-gray-200 rounded-sm mb-6">
104
+ <button
105
+ type="button"
106
+ class="button small soft"
107
+ @click="insertRow(0)"
108
+ >
109
+ <svg
110
+ xmlns="http://www.w3.org/2000/svg"
111
+ fill="none"
112
+ viewBox="0 0 24 24"
113
+ stroke-width="1.5"
114
+ stroke="currentColor"
115
+ class="size-4"
116
+ >
117
+ <path
118
+ stroke-linecap="round"
119
+ stroke-linejoin="round"
120
+ d="M12 4.5v15m7.5-7.5h-15"
121
+ />
122
+ </svg>
123
+ </button>
124
+ </div>
125
+
126
+ <div>
127
+ <template
128
+ v-for="(r, rowIndex) in table.rows"
129
+ :key="rowIndex"
130
+ >
131
+ <div
132
+ class="flex items-stretch rounded-sm border border-gray-200"
133
+ >
134
+ <div class="flex-1 min-w-0 overflow-x-auto overflow-y-hidden">
135
+ <div class="grid grid-flow-col auto-cols-[minmax(160px,1fr)] max-[640px]:auto-cols-[minmax(220px,1fr)]">
136
+ <div
137
+ v-for="(cell, colIndex) in r"
138
+ :key="colIndex"
139
+ class="p-1.5 border-r border-gray-200 box-border last:border-r-0"
140
+ >
141
+ <div class="text-xs mb-1.5 leading-tight wrap-break-word flex items-center justify-between gap-1">
142
+ <div class="font-bold">
143
+ {{ table.headers[colIndex] || "Spalte " + (colIndex + 1) }}
144
+ </div>
145
+ </div>
146
+ <div
147
+ class="text-xs whitespace-pre-line wrap-break-word border border-gray-300/70 rounded bg-gray-50 p-1.5 min-h-9.5 leading-[1.35] box-border overflow-hidden line-clamp-4 h-[calc(4*1.35*1em+12px)] max-[640px]:line-clamp-3 max-[640px]:h-[calc(3*1.35*1em+12px)] cursor-pointer"
148
+ :title="cell"
149
+ @click="startEdit(rowIndex, colIndex)"
150
+ >
151
+ {{ cell || "\u2014" }}
152
+ </div>
153
+ </div>
154
+ </div>
155
+ </div>
156
+
157
+ <div class="flex-none flex flex-col items-start gap-1 p-1.5 border-l border-gray-200 bg-white max-[640px]:px-1 max-[640px]:gap-0.5">
158
+ <button
159
+ type="button"
160
+ class="button small"
161
+ :disabled="rowIndex === 0"
162
+ aria-label="Zeile nach oben"
163
+ title="Nach oben"
164
+ @click="moveRowUp(rowIndex)"
165
+ >
166
+ <svg
167
+ xmlns="http://www.w3.org/2000/svg"
168
+ fill="none"
169
+ viewBox="0 0 24 24"
170
+ stroke-width="1.5"
171
+ stroke="currentColor"
172
+ class="size-4"
173
+ >
174
+ <path
175
+ stroke-linecap="round"
176
+ stroke-linejoin="round"
177
+ d="M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18"
178
+ />
179
+ </svg>
180
+ </button>
181
+ <button
182
+ type="button"
183
+ class="button small"
184
+ :disabled="rowIndex === table.rows.length - 1"
185
+ aria-label="Zeile nach unten"
186
+ title="Nach unten"
187
+ @click="moveRowDown(rowIndex)"
188
+ >
189
+ <svg
190
+ xmlns="http://www.w3.org/2000/svg"
191
+ fill="none"
192
+ viewBox="0 0 24 24"
193
+ stroke-width="1.5"
194
+ stroke="currentColor"
195
+ class="size-4"
196
+ >
197
+ <path
198
+ stroke-linecap="round"
199
+ stroke-linejoin="round"
200
+ d="M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3"
201
+ />
202
+ </svg>
203
+ </button>
204
+ <button
205
+ type="button"
206
+ class="button small"
207
+ aria-label="Zeile löschen"
208
+ title="Löschen"
209
+ @click="removeRow(rowIndex)"
210
+ >
211
+ <svg
212
+ xmlns="http://www.w3.org/2000/svg"
213
+ fill="none"
214
+ viewBox="0 0 24 24"
215
+ stroke-width="1.5"
216
+ stroke="currentColor"
217
+ class="size-4"
218
+ >
219
+ <path
220
+ stroke-linecap="round"
221
+ stroke-linejoin="round"
222
+ 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"
223
+ />
224
+ </svg>
225
+ </button>
226
+ </div>
227
+ </div>
228
+
229
+ <div class="h-0 flex items-center justify-center border border-gray-200 rounded-sm my-6">
230
+ <button
231
+ type="button"
232
+ class="button small"
233
+ :disabled="table.headers.length === 0"
234
+ @click="insertRow(rowIndex + 1)"
235
+ >
236
+ <svg
237
+ xmlns="http://www.w3.org/2000/svg"
238
+ fill="none"
239
+ viewBox="0 0 24 24"
240
+ stroke-width="1.5"
241
+ stroke="currentColor"
242
+ class="size-4"
243
+ >
244
+ <path
245
+ stroke-linecap="round"
246
+ stroke-linejoin="round"
247
+ d="M12 4.5v15m7.5-7.5h-15"
248
+ />
249
+ </svg>
250
+ </button>
251
+ </div>
252
+ </template>
253
+ </div>
254
+ </div>
255
+
256
+ <button
257
+ type="button"
258
+ class="button w-full mt-3 justify-center"
259
+ @click="saveCsv"
260
+ >
261
+ <span v-if="isSaving">Speichern...</span>
262
+ <span v-else>Speichern</span>
263
+ </button>
264
+ <Saved v-if="savingSuccessful" />
265
+
266
+ <div
267
+ v-if="editingCell"
268
+ class="fixed inset-0 bg-black/45 flex items-center justify-center p-4 z-9999"
269
+ role="presentation"
270
+ @click.self="cancelEdit()"
271
+ >
272
+ <div
273
+ class="w-full max-w-180 bg-white rounded-[10px] border border-black/10 shadow-[0_10px_40px_rgba(0,0,0,0.28)] p-3.5 flex flex-col gap-2.5"
274
+ role="dialog"
275
+ aria-modal="true"
276
+ aria-label="Zelle bearbeiten"
277
+ >
278
+ <div class="flex flex-col gap-0.5">
279
+ <div class="font-bold">
280
+ {{ table?.headers[editingCell.colIndex] || "Spalte " + (editingCell.colIndex + 1) }}
281
+ </div>
282
+ </div>
283
+
284
+ <textarea
285
+ id="csv-edit-textarea"
286
+ v-model="editBuffer"
287
+ class="w-full box-border p-2.5 border border-gray-300 rounded-lg resize-y min-h-40 font-[inherit] focus:outline-none focus:ring-2 focus:ring-emerald-500/30"
288
+ rows="8"
289
+ />
290
+
291
+ <div class="flex justify-end gap-2">
292
+ <button
293
+ type="button"
294
+ class="button"
295
+ @click="saveEdit()"
296
+ >
297
+ schließen
298
+ </button>
299
+ <button
300
+ type="button"
301
+ class="button secondary"
302
+ @click="cancelEdit()"
303
+ >
304
+ zurücksetzen
305
+ </button>
306
+ </div>
307
+ </div>
308
+ </div>
309
+ </div>
310
+ </template>
@@ -1,10 +1,10 @@
1
1
  type __VLS_ModelProps = {
2
- 'path'?: string;
2
+ 'frontmatter': Record<string, any>;
3
3
  };
4
4
  declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
- "update:path": (value: string | undefined) => any;
5
+ "update:frontmatter": (value: Record<string, any>) => any;
6
6
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
- "onUpdate:path"?: ((value: string | undefined) => any) | undefined;
7
+ "onUpdate:frontmatter"?: ((value: Record<string, any>) => any) | undefined;
8
8
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
9
  declare const _default: typeof __VLS_export;
10
10
  export default _default;
@@ -0,0 +1,17 @@
1
+ <script setup>
2
+ import FrontmatterInput from "./input.vue";
3
+ const frontmatter = defineModel("frontmatter", { type: Object, ...{
4
+ required: true
5
+ } });
6
+ </script>
7
+
8
+ <template>
9
+ <div class="flex gap-2">
10
+ <FrontmatterInput
11
+ v-for="key in Object.keys(frontmatter)"
12
+ :key="key"
13
+ v-model:value="frontmatter[key]"
14
+ :label="key"
15
+ />
16
+ </div>
17
+ </template>
@@ -1,10 +1,10 @@
1
1
  type __VLS_ModelProps = {
2
- 'content'?: string;
2
+ 'frontmatter': Record<string, any>;
3
3
  };
4
4
  declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
- "update:content": (value: string) => any;
5
+ "update:frontmatter": (value: Record<string, any>) => any;
6
6
  }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
- "onUpdate:content"?: ((value: string) => any) | undefined;
7
+ "onUpdate:frontmatter"?: ((value: Record<string, any>) => any) | undefined;
8
8
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
9
  declare const _default: typeof __VLS_export;
10
10
  export default _default;