nca-ai-cms-astro-plugin 1.1.1 → 1.1.2
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.
package/package.json
CHANGED
|
@@ -60,6 +60,18 @@ const { articleId, markdown, title, description, isAuthenticated } = Astro.props
|
|
|
60
60
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
61
61
|
</svg>
|
|
62
62
|
</button>
|
|
63
|
+
<span class="inline-toolbar-spacer"></span>
|
|
64
|
+
<button
|
|
65
|
+
type="button"
|
|
66
|
+
class="inline-delete-btn"
|
|
67
|
+
aria-label="Artikel löschen"
|
|
68
|
+
title="Artikel löschen"
|
|
69
|
+
>
|
|
70
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
71
|
+
<polyline points="3 6 5 6 21 6" />
|
|
72
|
+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
73
|
+
</svg>
|
|
74
|
+
</button>
|
|
63
75
|
</div>
|
|
64
76
|
)}
|
|
65
77
|
|
|
@@ -145,6 +157,22 @@ const { articleId, markdown, title, description, isAuthenticated } = Astro.props
|
|
|
145
157
|
background: #fecaca !important;
|
|
146
158
|
}
|
|
147
159
|
|
|
160
|
+
.inline-toolbar-spacer {
|
|
161
|
+
flex: 1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.inline-delete-btn {
|
|
165
|
+
background: transparent !important;
|
|
166
|
+
color: #94a3b8 !important;
|
|
167
|
+
border-color: transparent !important;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.inline-delete-btn:hover {
|
|
171
|
+
background: #fef2f2 !important;
|
|
172
|
+
color: #dc2626 !important;
|
|
173
|
+
border-color: #fecaca !important;
|
|
174
|
+
}
|
|
175
|
+
|
|
148
176
|
.inline-editor-field {
|
|
149
177
|
margin-bottom: 12px;
|
|
150
178
|
}
|
|
@@ -232,6 +260,7 @@ const { articleId, markdown, title, description, isAuthenticated } = Astro.props
|
|
|
232
260
|
const regenBtn = el.querySelector('.inline-regenerate-btn') as HTMLButtonElement;
|
|
233
261
|
const applyBtn = el.querySelector('.inline-apply-btn') as HTMLButtonElement;
|
|
234
262
|
const cancelBtn = el.querySelector('.inline-cancel-btn') as HTMLButtonElement;
|
|
263
|
+
const deleteBtn = el.querySelector('.inline-delete-btn') as HTMLButtonElement;
|
|
235
264
|
const rendered = el.querySelector('.inline-editor-rendered') as HTMLElement;
|
|
236
265
|
const editing = el.querySelector('.inline-editor-editing') as HTMLElement;
|
|
237
266
|
const preview = el.querySelector('.inline-editor-preview') as HTMLElement;
|
|
@@ -254,6 +283,7 @@ const { articleId, markdown, title, description, isAuthenticated } = Astro.props
|
|
|
254
283
|
regenBtn.hidden = mode !== 'view';
|
|
255
284
|
applyBtn.hidden = mode === 'view';
|
|
256
285
|
cancelBtn.hidden = mode === 'view';
|
|
286
|
+
deleteBtn.hidden = mode !== 'view';
|
|
257
287
|
};
|
|
258
288
|
|
|
259
289
|
// Edit mode
|
|
@@ -342,5 +372,25 @@ const { articleId, markdown, title, description, isAuthenticated } = Astro.props
|
|
|
342
372
|
}
|
|
343
373
|
});
|
|
344
374
|
|
|
375
|
+
// Delete article
|
|
376
|
+
deleteBtn?.addEventListener('click', async () => {
|
|
377
|
+
if (!confirm('Artikel wirklich löschen? Dies kann nicht rückgängig gemacht werden.')) return;
|
|
378
|
+
deleteBtn.disabled = true;
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
deleteBtn.classList.add('loading');
|
|
382
|
+
const res = await fetch(`/api/articles/${articleId}`, {
|
|
383
|
+
method: 'DELETE',
|
|
384
|
+
credentials: 'same-origin',
|
|
385
|
+
});
|
|
386
|
+
if (!res.ok) throw new Error('Löschen fehlgeschlagen');
|
|
387
|
+
window.location.href = '/';
|
|
388
|
+
} catch {
|
|
389
|
+
alert('Artikel konnte nicht gelöscht werden');
|
|
390
|
+
deleteBtn.disabled = false;
|
|
391
|
+
deleteBtn.classList.remove('loading');
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
345
395
|
});
|
|
346
396
|
</script>
|
package/src/pages/index.astro
CHANGED
|
@@ -4,7 +4,6 @@ import * as fs from 'fs/promises';
|
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import { db, SiteSettings, eq } from 'astro:db';
|
|
6
6
|
import Layout from '../layouts/Layout.astro';
|
|
7
|
-
import DeleteAction from '../components/frontend/DeleteAction.astro';
|
|
8
7
|
import { ArticleService } from '../services/ArticleService';
|
|
9
8
|
import { escapeJsonLd } from '../utils/sanitize';
|
|
10
9
|
|
|
@@ -128,14 +127,6 @@ const homeJsonLd = {
|
|
|
128
127
|
href={`/articles/${featuredArticle.articleId}`}
|
|
129
128
|
class="featured-card"
|
|
130
129
|
>
|
|
131
|
-
{isAuthenticated && (
|
|
132
|
-
<DeleteAction
|
|
133
|
-
articleId={
|
|
134
|
-
featuredArticle.articleId.split('/').pop() ||
|
|
135
|
-
featuredArticle.articleId
|
|
136
|
-
}
|
|
137
|
-
/>
|
|
138
|
-
)}
|
|
139
130
|
<div class="featured-card-image img-zoom">
|
|
140
131
|
{featuredArticle.image ? (
|
|
141
132
|
<Image
|
|
@@ -198,14 +189,6 @@ const homeJsonLd = {
|
|
|
198
189
|
{ 'article-card--wide': index % 5 === 0 },
|
|
199
190
|
]}
|
|
200
191
|
>
|
|
201
|
-
{isAuthenticated && (
|
|
202
|
-
<DeleteAction
|
|
203
|
-
articleId={
|
|
204
|
-
article.articleId.split('/').pop() ||
|
|
205
|
-
article.articleId
|
|
206
|
-
}
|
|
207
|
-
/>
|
|
208
|
-
)}
|
|
209
192
|
<div class="article-card-image img-zoom">
|
|
210
193
|
{article.image ? (
|
|
211
194
|
<Image
|
package/update.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
# v1.1.2
|
|
2
|
+
|
|
3
|
+
## Feature: Delete button in InlineEditor
|
|
4
|
+
|
|
5
|
+
- Trash icon in the toolbar, right-aligned, subtle gray until hover (then red)
|
|
6
|
+
- Confirm dialog before deleting: "Artikel wirklich löschen?"
|
|
7
|
+
- Calls `DELETE /api/articles/{id}` then redirects to homepage
|
|
8
|
+
- Only visible when authenticated
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
1
12
|
# v1.1.1
|
|
2
13
|
|
|
3
14
|
## Enhancement: Inline editing for title + description, context-aware toolbar
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
interface Props {
|
|
3
|
-
articleId: string;
|
|
4
|
-
}
|
|
5
|
-
const { articleId } = Astro.props;
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
<div class="delete-action" data-article-id={articleId}>
|
|
9
|
-
<button class="delete-icon" aria-label="Artikel löschen" type="button">
|
|
10
|
-
<svg
|
|
11
|
-
width="18"
|
|
12
|
-
height="18"
|
|
13
|
-
viewBox="0 0 24 24"
|
|
14
|
-
fill="none"
|
|
15
|
-
stroke="currentColor"
|
|
16
|
-
stroke-width="2"
|
|
17
|
-
stroke-linecap="round"
|
|
18
|
-
stroke-linejoin="round"
|
|
19
|
-
>
|
|
20
|
-
<polyline points="3 6 5 6 21 6"></polyline>
|
|
21
|
-
<path
|
|
22
|
-
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
|
|
23
|
-
></path>
|
|
24
|
-
<line x1="10" y1="11" x2="10" y2="17"></line>
|
|
25
|
-
<line x1="14" y1="11" x2="14" y2="17"></line>
|
|
26
|
-
</svg>
|
|
27
|
-
</button>
|
|
28
|
-
<button class="confirm-yes" type="button" hidden>Ja</button>
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<script>
|
|
32
|
-
document.querySelectorAll('.delete-action').forEach((container) => {
|
|
33
|
-
const articleId = (container as HTMLElement).dataset.articleId;
|
|
34
|
-
const icon = container.querySelector('.delete-icon') as HTMLButtonElement;
|
|
35
|
-
const confirmBtn = container.querySelector(
|
|
36
|
-
'.confirm-yes'
|
|
37
|
-
) as HTMLButtonElement;
|
|
38
|
-
|
|
39
|
-
const showConfirm = () => {
|
|
40
|
-
icon.hidden = true;
|
|
41
|
-
confirmBtn.hidden = false;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const resetState = () => {
|
|
45
|
-
confirmBtn.hidden = true;
|
|
46
|
-
confirmBtn.disabled = false;
|
|
47
|
-
confirmBtn.textContent = 'Ja';
|
|
48
|
-
icon.hidden = false;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const removeCard = () => {
|
|
52
|
-
const card = container.closest('.article-card, .featured-card');
|
|
53
|
-
card ? card.remove() : window.location.reload();
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const deleteArticle = async () => {
|
|
57
|
-
confirmBtn.disabled = true;
|
|
58
|
-
confirmBtn.textContent = '...';
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
const response = await fetch(`/api/articles/${articleId}`, {
|
|
62
|
-
method: 'DELETE',
|
|
63
|
-
credentials: 'same-origin',
|
|
64
|
-
});
|
|
65
|
-
response.ok
|
|
66
|
-
? removeCard()
|
|
67
|
-
: (alert('Löschen fehlgeschlagen'), resetState());
|
|
68
|
-
} catch {
|
|
69
|
-
alert('Löschen fehlgeschlagen');
|
|
70
|
-
resetState();
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
icon?.addEventListener('click', (e) => {
|
|
75
|
-
e.preventDefault();
|
|
76
|
-
e.stopPropagation();
|
|
77
|
-
showConfirm();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
confirmBtn?.addEventListener('click', (e) => {
|
|
81
|
-
e.preventDefault();
|
|
82
|
-
e.stopPropagation();
|
|
83
|
-
deleteArticle();
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
</script>
|
|
87
|
-
|
|
88
|
-
<style>
|
|
89
|
-
.delete-action {
|
|
90
|
-
position: absolute;
|
|
91
|
-
top: var(--space-3, 0.75rem);
|
|
92
|
-
right: var(--space-3, 0.75rem);
|
|
93
|
-
z-index: 10;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.delete-icon {
|
|
97
|
-
display: flex;
|
|
98
|
-
align-items: center;
|
|
99
|
-
justify-content: center;
|
|
100
|
-
width: 36px;
|
|
101
|
-
height: 36px;
|
|
102
|
-
background: var(--color-surface);
|
|
103
|
-
border: 1px solid var(--color-border);
|
|
104
|
-
border-radius: var(--radius-md);
|
|
105
|
-
color: var(--color-text-muted);
|
|
106
|
-
cursor: pointer;
|
|
107
|
-
transition:
|
|
108
|
-
color 0.2s ease,
|
|
109
|
-
background 0.2s ease,
|
|
110
|
-
border-color 0.2s ease;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.delete-icon:hover {
|
|
114
|
-
color: var(--color-error);
|
|
115
|
-
background: var(--color-error-muted);
|
|
116
|
-
border-color: var(--color-error);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.delete-icon:disabled {
|
|
120
|
-
opacity: 0.5;
|
|
121
|
-
cursor: not-allowed;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.confirm-yes {
|
|
125
|
-
padding: var(--space-1, 0.25rem) var(--space-3, 0.75rem);
|
|
126
|
-
font-family: var(--font-ui);
|
|
127
|
-
font-size: var(--text-sm, 0.875rem);
|
|
128
|
-
font-weight: 600;
|
|
129
|
-
border: none;
|
|
130
|
-
border-radius: var(--radius-sm, 4px);
|
|
131
|
-
cursor: pointer;
|
|
132
|
-
background: var(--color-error, #f87171);
|
|
133
|
-
color: white;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.confirm-yes:hover {
|
|
137
|
-
background: #ef4444;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.confirm-yes:disabled {
|
|
141
|
-
opacity: 0.6;
|
|
142
|
-
cursor: not-allowed;
|
|
143
|
-
}
|
|
144
|
-
</style>
|