@jant/core 0.3.25 → 0.3.26
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/dist/app.js +67 -562
- package/dist/client.js +1 -0
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/lib/avatar-upload.js +134 -0
- package/dist/lib/config.js +39 -0
- package/dist/lib/constants.js +10 -10
- package/dist/lib/favicon.js +102 -0
- package/dist/lib/image.js +13 -17
- package/dist/lib/media-helpers.js +2 -2
- package/dist/lib/navigation.js +23 -3
- package/dist/lib/render.js +10 -1
- package/dist/lib/schemas.js +31 -0
- package/dist/lib/timezones.js +388 -0
- package/dist/lib/view.js +1 -1
- package/dist/routes/api/posts.js +1 -1
- package/dist/routes/api/upload.js +3 -3
- package/dist/routes/auth/reset.js +221 -0
- package/dist/routes/auth/setup.js +194 -0
- package/dist/routes/auth/signin.js +176 -0
- package/dist/routes/dash/collections.js +23 -415
- package/dist/routes/dash/media.js +12 -392
- package/dist/routes/dash/pages.js +7 -330
- package/dist/routes/dash/redirects.js +18 -12
- package/dist/routes/dash/settings.js +198 -577
- package/dist/routes/feed/rss.js +2 -1
- package/dist/routes/feed/sitemap.js +4 -2
- package/dist/routes/pages/featured.js +5 -1
- package/dist/routes/pages/home.js +26 -1
- package/dist/routes/pages/latest.js +45 -0
- package/dist/services/post.js +30 -50
- package/dist/types/bindings.js +3 -0
- package/dist/types/config.js +147 -0
- package/dist/types/constants.js +27 -0
- package/dist/types/entities.js +3 -0
- package/dist/types/operations.js +3 -0
- package/dist/types/props.js +3 -0
- package/dist/types/views.js +5 -0
- package/dist/types.js +8 -111
- package/dist/ui/color-themes.js +33 -33
- package/dist/ui/compose/ComposeDialog.js +36 -21
- package/dist/ui/dash/PageForm.js +21 -15
- package/dist/ui/dash/PostForm.js +22 -16
- package/dist/ui/dash/collections/CollectionForm.js +152 -0
- package/dist/ui/dash/collections/CollectionsListContent.js +68 -0
- package/dist/ui/dash/collections/ViewCollectionContent.js +96 -0
- package/dist/ui/dash/media/MediaListContent.js +166 -0
- package/dist/ui/dash/media/ViewMediaContent.js +212 -0
- package/dist/ui/dash/pages/LinkFormContent.js +130 -0
- package/dist/ui/dash/pages/UnifiedPagesContent.js +193 -0
- package/dist/ui/dash/settings/AccountContent.js +209 -0
- package/dist/ui/dash/settings/AppearanceContent.js +259 -0
- package/dist/ui/dash/settings/GeneralContent.js +536 -0
- package/dist/ui/dash/settings/SettingsNav.js +41 -0
- package/dist/ui/font-themes.js +36 -0
- package/dist/ui/layouts/BaseLayout.js +24 -2
- package/dist/ui/layouts/SiteLayout.js +47 -19
- package/package.json +1 -1
- package/src/app.tsx +93 -553
- package/src/client.ts +1 -0
- package/src/i18n/locales/en.po +240 -175
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +240 -175
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +240 -175
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/lib/__tests__/config.test.ts +192 -0
- package/src/lib/__tests__/favicon.test.ts +151 -0
- package/src/lib/__tests__/image.test.ts +2 -6
- package/src/lib/__tests__/timezones.test.ts +61 -0
- package/src/lib/__tests__/view.test.ts +2 -2
- package/src/lib/avatar-upload.ts +165 -0
- package/src/lib/config.ts +47 -0
- package/src/lib/constants.ts +19 -11
- package/src/lib/favicon.ts +115 -0
- package/src/lib/image.ts +13 -21
- package/src/lib/media-helpers.ts +2 -2
- package/src/lib/navigation.ts +33 -2
- package/src/lib/render.tsx +15 -1
- package/src/lib/schemas.ts +39 -0
- package/src/lib/timezones.ts +325 -0
- package/src/lib/view.ts +1 -1
- package/src/routes/api/posts.ts +1 -1
- package/src/routes/api/upload.ts +2 -3
- package/src/routes/auth/reset.tsx +239 -0
- package/src/routes/auth/setup.tsx +189 -0
- package/src/routes/auth/signin.tsx +163 -0
- package/src/routes/dash/__tests__/settings-avatar.test.ts +89 -0
- package/src/routes/dash/collections.tsx +17 -366
- package/src/routes/dash/media.tsx +12 -414
- package/src/routes/dash/pages.tsx +8 -348
- package/src/routes/dash/redirects.tsx +20 -14
- package/src/routes/dash/settings.tsx +243 -534
- package/src/routes/feed/__tests__/rss.test.ts +141 -0
- package/src/routes/feed/rss.ts +3 -1
- package/src/routes/feed/sitemap.ts +4 -2
- package/src/routes/pages/featured.tsx +7 -1
- package/src/routes/pages/home.tsx +25 -2
- package/src/routes/pages/latest.tsx +59 -0
- package/src/services/post.ts +34 -66
- package/src/styles/components.css +0 -65
- package/src/styles/tokens.css +1 -1
- package/src/styles/ui.css +24 -40
- package/src/types/bindings.ts +30 -0
- package/src/types/config.ts +183 -0
- package/src/types/constants.ts +26 -0
- package/src/types/entities.ts +109 -0
- package/src/types/operations.ts +88 -0
- package/src/types/props.ts +115 -0
- package/src/types/views.ts +172 -0
- package/src/types.ts +8 -644
- package/src/ui/__tests__/font-themes.test.ts +34 -0
- package/src/ui/color-themes.ts +34 -34
- package/src/ui/compose/ComposeDialog.tsx +40 -21
- package/src/ui/dash/PageForm.tsx +25 -19
- package/src/ui/dash/PostForm.tsx +26 -20
- package/src/ui/dash/collections/CollectionForm.tsx +153 -0
- package/src/ui/dash/collections/CollectionsListContent.tsx +85 -0
- package/src/ui/dash/collections/ViewCollectionContent.tsx +92 -0
- package/src/ui/dash/media/MediaListContent.tsx +201 -0
- package/src/ui/dash/media/ViewMediaContent.tsx +208 -0
- package/src/ui/dash/pages/LinkFormContent.tsx +119 -0
- package/src/ui/dash/pages/UnifiedPagesContent.tsx +203 -0
- package/src/ui/dash/settings/AccountContent.tsx +176 -0
- package/src/ui/dash/settings/AppearanceContent.tsx +254 -0
- package/src/ui/dash/settings/GeneralContent.tsx +533 -0
- package/src/ui/dash/settings/SettingsNav.tsx +56 -0
- package/src/ui/font-themes.ts +54 -0
- package/src/ui/layouts/BaseLayout.tsx +17 -0
- package/src/ui/layouts/SiteLayout.tsx +45 -31
|
@@ -1,229 +1,28 @@
|
|
|
1
|
-
import { getSiteName } from "../../lib/config.js";
|
|
2
1
|
/**
|
|
3
2
|
* Dashboard Pages & Navigation Routes
|
|
4
3
|
*
|
|
5
|
-
* Unified management for pages and navigation items
|
|
6
|
-
* Two sections: "Your site navigation" (draggable) and "Other pages".
|
|
4
|
+
* Unified management for pages and navigation items.
|
|
7
5
|
*/
|
|
8
6
|
|
|
9
7
|
import { Hono } from "hono";
|
|
10
8
|
import { useLingui } from "@lingui/react/macro";
|
|
11
|
-
import type { Bindings, Page
|
|
9
|
+
import type { Bindings, Page } from "../../types.js";
|
|
12
10
|
import type { AppVariables } from "../../app.js";
|
|
13
11
|
import { DashLayout } from "../../ui/layouts/DashLayout.js";
|
|
14
|
-
import {
|
|
15
|
-
PageForm,
|
|
16
|
-
ListItemRow,
|
|
17
|
-
ActionButtons,
|
|
18
|
-
CrudPageHeader,
|
|
19
|
-
DangerZone,
|
|
20
|
-
} from "../../ui/dash/index.js";
|
|
12
|
+
import { PageForm, ActionButtons, DangerZone } from "../../ui/dash/index.js";
|
|
21
13
|
import { dsRedirect, dsToast } from "../../lib/sse.js";
|
|
14
|
+
import { getSiteName } from "../../lib/config.js";
|
|
15
|
+
import { UnifiedPagesContent } from "../../ui/dash/pages/UnifiedPagesContent.js";
|
|
16
|
+
import { LinkFormContent } from "../../ui/dash/pages/LinkFormContent.js";
|
|
22
17
|
|
|
23
18
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
24
19
|
|
|
25
20
|
export const pagesRoutes = new Hono<Env>();
|
|
26
21
|
|
|
27
22
|
// =============================================================================
|
|
28
|
-
//
|
|
23
|
+
// Inline components (small, tightly coupled to route params)
|
|
29
24
|
// =============================================================================
|
|
30
25
|
|
|
31
|
-
function UnifiedPagesContent({
|
|
32
|
-
navItems,
|
|
33
|
-
otherPages,
|
|
34
|
-
}: {
|
|
35
|
-
navItems: NavItem[];
|
|
36
|
-
otherPages: Page[];
|
|
37
|
-
}) {
|
|
38
|
-
const { t } = useLingui();
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<>
|
|
42
|
-
<CrudPageHeader
|
|
43
|
-
title={t({
|
|
44
|
-
message: "Pages",
|
|
45
|
-
comment: "@context: Pages main heading",
|
|
46
|
-
})}
|
|
47
|
-
>
|
|
48
|
-
<div class="flex gap-2">
|
|
49
|
-
<a href="/dash/pages/links/new" class="btn-outline">
|
|
50
|
-
{t({
|
|
51
|
-
message: "Add Link",
|
|
52
|
-
comment: "@context: Button to add a navigation link",
|
|
53
|
-
})}
|
|
54
|
-
</a>
|
|
55
|
-
<a href="/dash/pages/new" class="btn">
|
|
56
|
-
{t({
|
|
57
|
-
message: "New Page",
|
|
58
|
-
comment: "@context: Button to create new page",
|
|
59
|
-
})}
|
|
60
|
-
</a>
|
|
61
|
-
</div>
|
|
62
|
-
</CrudPageHeader>
|
|
63
|
-
|
|
64
|
-
{/* Navigation section */}
|
|
65
|
-
<section class="mb-8">
|
|
66
|
-
<h2 class="text-lg font-medium mb-3">
|
|
67
|
-
{t({
|
|
68
|
-
message: "Your site navigation",
|
|
69
|
-
comment: "@context: Section heading for navigation items",
|
|
70
|
-
})}
|
|
71
|
-
</h2>
|
|
72
|
-
{navItems.length === 0 ? (
|
|
73
|
-
<p class="text-sm text-muted-foreground py-4">
|
|
74
|
-
{t({
|
|
75
|
-
message:
|
|
76
|
-
"No navigation links yet. Add pages to navigation or create links.",
|
|
77
|
-
comment: "@context: Empty state for navigation section",
|
|
78
|
-
})}
|
|
79
|
-
</p>
|
|
80
|
-
) : (
|
|
81
|
-
<div id="nav-links-list" class="flex flex-col divide-y">
|
|
82
|
-
{navItems.map((item) => (
|
|
83
|
-
<ListItemRow
|
|
84
|
-
key={item.id}
|
|
85
|
-
actions={
|
|
86
|
-
item.type === "page" ? (
|
|
87
|
-
<>
|
|
88
|
-
<ActionButtons
|
|
89
|
-
editHref={
|
|
90
|
-
item.pageId
|
|
91
|
-
? `/dash/pages/${item.pageId}/edit`
|
|
92
|
-
: undefined
|
|
93
|
-
}
|
|
94
|
-
editLabel={t({
|
|
95
|
-
message: "Edit",
|
|
96
|
-
comment: "@context: Button to edit page",
|
|
97
|
-
})}
|
|
98
|
-
/>
|
|
99
|
-
<button
|
|
100
|
-
type="button"
|
|
101
|
-
class="btn-sm-ghost"
|
|
102
|
-
data-on:click__prevent={`@post('/dash/pages/${item.pageId}/remove-from-nav')`}
|
|
103
|
-
>
|
|
104
|
-
{t({
|
|
105
|
-
message: "Un-nav",
|
|
106
|
-
comment:
|
|
107
|
-
"@context: Button to remove page from navigation",
|
|
108
|
-
})}
|
|
109
|
-
</button>
|
|
110
|
-
</>
|
|
111
|
-
) : (
|
|
112
|
-
<>
|
|
113
|
-
<ActionButtons
|
|
114
|
-
editHref={`/dash/pages/links/${item.id}/edit`}
|
|
115
|
-
editLabel={t({
|
|
116
|
-
message: "Edit",
|
|
117
|
-
comment: "@context: Button to edit link",
|
|
118
|
-
})}
|
|
119
|
-
deleteAction={`/dash/pages/links/${item.id}/delete`}
|
|
120
|
-
deleteLabel={t({
|
|
121
|
-
message: "Delete",
|
|
122
|
-
comment: "@context: Button to delete link",
|
|
123
|
-
})}
|
|
124
|
-
/>
|
|
125
|
-
</>
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
>
|
|
129
|
-
<div
|
|
130
|
-
class="flex items-center gap-3 cursor-grab"
|
|
131
|
-
data-id={item.id}
|
|
132
|
-
>
|
|
133
|
-
<span class="text-muted-foreground select-none">⠿</span>
|
|
134
|
-
<div class="flex items-center gap-2">
|
|
135
|
-
<span class="font-medium">{item.label}</span>
|
|
136
|
-
<code class="text-sm text-muted-foreground bg-muted px-1 rounded">
|
|
137
|
-
{item.url}
|
|
138
|
-
</code>
|
|
139
|
-
<span class="badge badge-sm">
|
|
140
|
-
{item.type === "page"
|
|
141
|
-
? t({
|
|
142
|
-
message: "page",
|
|
143
|
-
comment: "@context: Nav item type badge",
|
|
144
|
-
})
|
|
145
|
-
: t({
|
|
146
|
-
message: "link",
|
|
147
|
-
comment: "@context: Nav item type badge",
|
|
148
|
-
})}
|
|
149
|
-
</span>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
</ListItemRow>
|
|
153
|
-
))}
|
|
154
|
-
</div>
|
|
155
|
-
)}
|
|
156
|
-
</section>
|
|
157
|
-
|
|
158
|
-
{/* Other pages section */}
|
|
159
|
-
<section>
|
|
160
|
-
<h2 class="text-lg font-medium mb-3">
|
|
161
|
-
{t({
|
|
162
|
-
message: "Other pages",
|
|
163
|
-
comment: "@context: Section heading for pages not in navigation",
|
|
164
|
-
})}
|
|
165
|
-
</h2>
|
|
166
|
-
{otherPages.length === 0 ? (
|
|
167
|
-
<p class="text-sm text-muted-foreground py-4">
|
|
168
|
-
{t({
|
|
169
|
-
message: "All pages are in your navigation.",
|
|
170
|
-
comment: "@context: Empty state when all pages are in nav",
|
|
171
|
-
})}
|
|
172
|
-
</p>
|
|
173
|
-
) : (
|
|
174
|
-
<div class="flex flex-col divide-y">
|
|
175
|
-
{otherPages.map((page) => (
|
|
176
|
-
<ListItemRow
|
|
177
|
-
key={page.id}
|
|
178
|
-
actions={
|
|
179
|
-
<>
|
|
180
|
-
<button
|
|
181
|
-
type="button"
|
|
182
|
-
class="btn-sm-outline"
|
|
183
|
-
data-on:click__prevent={`@post('/dash/pages/${page.id}/add-to-nav')`}
|
|
184
|
-
>
|
|
185
|
-
{t({
|
|
186
|
-
message: "Add to nav",
|
|
187
|
-
comment: "@context: Button to add page to navigation",
|
|
188
|
-
})}
|
|
189
|
-
</button>
|
|
190
|
-
<ActionButtons
|
|
191
|
-
editHref={`/dash/pages/${page.id}/edit`}
|
|
192
|
-
editLabel={t({
|
|
193
|
-
message: "Edit",
|
|
194
|
-
comment: "@context: Button to edit page",
|
|
195
|
-
})}
|
|
196
|
-
viewHref={
|
|
197
|
-
page.status !== "draft" ? `/${page.slug}` : undefined
|
|
198
|
-
}
|
|
199
|
-
viewLabel={t({
|
|
200
|
-
message: "View",
|
|
201
|
-
comment: "@context: Button to view page on public site",
|
|
202
|
-
})}
|
|
203
|
-
/>
|
|
204
|
-
</>
|
|
205
|
-
}
|
|
206
|
-
>
|
|
207
|
-
<a
|
|
208
|
-
href={`/dash/pages/${page.id}`}
|
|
209
|
-
class="font-medium hover:underline"
|
|
210
|
-
>
|
|
211
|
-
{page.title ||
|
|
212
|
-
t({
|
|
213
|
-
message: "Untitled",
|
|
214
|
-
comment: "@context: Default title for untitled page",
|
|
215
|
-
})}
|
|
216
|
-
</a>
|
|
217
|
-
<p class="text-sm text-muted-foreground mt-1">/{page.slug}</p>
|
|
218
|
-
</ListItemRow>
|
|
219
|
-
))}
|
|
220
|
-
</div>
|
|
221
|
-
)}
|
|
222
|
-
</section>
|
|
223
|
-
</>
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
26
|
function NewPageContent() {
|
|
228
27
|
const { t } = useLingui();
|
|
229
28
|
return (
|
|
@@ -301,118 +100,10 @@ function EditPageContent({ page }: { page: Page }) {
|
|
|
301
100
|
);
|
|
302
101
|
}
|
|
303
102
|
|
|
304
|
-
function LinkFormContent({
|
|
305
|
-
item,
|
|
306
|
-
isEdit,
|
|
307
|
-
}: {
|
|
308
|
-
item?: NavItem;
|
|
309
|
-
isEdit?: boolean;
|
|
310
|
-
}) {
|
|
311
|
-
const { t } = useLingui();
|
|
312
|
-
const title = isEdit
|
|
313
|
-
? t({ message: "Edit Link", comment: "@context: Page heading" })
|
|
314
|
-
: t({ message: "New Link", comment: "@context: Page heading" });
|
|
315
|
-
|
|
316
|
-
const signals = JSON.stringify({
|
|
317
|
-
label: item?.label ?? "",
|
|
318
|
-
url: item?.url ?? "",
|
|
319
|
-
}).replace(/</g, "\\u003c");
|
|
320
|
-
|
|
321
|
-
const action = isEdit ? `/dash/pages/links/${item?.id}` : "/dash/pages/links";
|
|
322
|
-
|
|
323
|
-
return (
|
|
324
|
-
<>
|
|
325
|
-
<h1 class="text-2xl font-semibold mb-6">{title}</h1>
|
|
326
|
-
|
|
327
|
-
<form
|
|
328
|
-
data-signals={signals}
|
|
329
|
-
data-on:submit__prevent={`@post('${action}')`}
|
|
330
|
-
data-indicator="_loading"
|
|
331
|
-
class="flex flex-col gap-4 max-w-lg"
|
|
332
|
-
>
|
|
333
|
-
<div class="field">
|
|
334
|
-
<label class="label">
|
|
335
|
-
{t({
|
|
336
|
-
message: "Label",
|
|
337
|
-
comment: "@context: Navigation link form field",
|
|
338
|
-
})}
|
|
339
|
-
</label>
|
|
340
|
-
<input
|
|
341
|
-
type="text"
|
|
342
|
-
data-bind="label"
|
|
343
|
-
class="input"
|
|
344
|
-
placeholder="Home"
|
|
345
|
-
required
|
|
346
|
-
/>
|
|
347
|
-
<p class="text-xs text-muted-foreground mt-1">
|
|
348
|
-
{t({
|
|
349
|
-
message: "Display text for the link",
|
|
350
|
-
comment: "@context: Navigation label help text",
|
|
351
|
-
})}
|
|
352
|
-
</p>
|
|
353
|
-
</div>
|
|
354
|
-
|
|
355
|
-
<div class="field">
|
|
356
|
-
<label class="label">
|
|
357
|
-
{t({
|
|
358
|
-
message: "URL",
|
|
359
|
-
comment: "@context: Navigation link form field",
|
|
360
|
-
})}
|
|
361
|
-
</label>
|
|
362
|
-
<input
|
|
363
|
-
type="text"
|
|
364
|
-
data-bind="url"
|
|
365
|
-
class="input"
|
|
366
|
-
placeholder="/archive or https://..."
|
|
367
|
-
required
|
|
368
|
-
/>
|
|
369
|
-
<p class="text-xs text-muted-foreground mt-1">
|
|
370
|
-
{t({
|
|
371
|
-
message:
|
|
372
|
-
"Path (e.g. /archive) or full URL (e.g. https://example.com)",
|
|
373
|
-
comment: "@context: Navigation URL help text",
|
|
374
|
-
})}
|
|
375
|
-
</p>
|
|
376
|
-
</div>
|
|
377
|
-
|
|
378
|
-
<div class="flex gap-2">
|
|
379
|
-
<button type="submit" class="btn" data-attr-disabled="$_loading">
|
|
380
|
-
<span data-show="!$_loading">
|
|
381
|
-
{isEdit
|
|
382
|
-
? t({
|
|
383
|
-
message: "Save Changes",
|
|
384
|
-
comment: "@context: Button to save edited navigation link",
|
|
385
|
-
})
|
|
386
|
-
: t({
|
|
387
|
-
message: "Create Link",
|
|
388
|
-
comment: "@context: Button to save new navigation link",
|
|
389
|
-
})}
|
|
390
|
-
</span>
|
|
391
|
-
<span data-show="$_loading">
|
|
392
|
-
{t({
|
|
393
|
-
message: "Processing...",
|
|
394
|
-
comment:
|
|
395
|
-
"@context: Loading text shown on submit button while request is in progress",
|
|
396
|
-
})}
|
|
397
|
-
</span>
|
|
398
|
-
</button>
|
|
399
|
-
<a href="/dash/pages" class="btn-outline">
|
|
400
|
-
{t({
|
|
401
|
-
message: "Cancel",
|
|
402
|
-
comment: "@context: Button to cancel form",
|
|
403
|
-
})}
|
|
404
|
-
</a>
|
|
405
|
-
</div>
|
|
406
|
-
</form>
|
|
407
|
-
</>
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
103
|
// =============================================================================
|
|
412
|
-
//
|
|
104
|
+
// Route handlers
|
|
413
105
|
// =============================================================================
|
|
414
106
|
|
|
415
|
-
// List pages (unified view)
|
|
416
107
|
pagesRoutes.get("/", async (c) => {
|
|
417
108
|
const [navItems, otherPages] = await Promise.all([
|
|
418
109
|
c.var.services.navItems.list(),
|
|
@@ -432,10 +123,8 @@ pagesRoutes.get("/", async (c) => {
|
|
|
432
123
|
);
|
|
433
124
|
});
|
|
434
125
|
|
|
435
|
-
// New page form
|
|
436
126
|
pagesRoutes.get("/new", async (c) => {
|
|
437
127
|
const siteName = await getSiteName(c);
|
|
438
|
-
|
|
439
128
|
return c.html(
|
|
440
129
|
<DashLayout
|
|
441
130
|
c={c}
|
|
@@ -448,10 +137,8 @@ pagesRoutes.get("/new", async (c) => {
|
|
|
448
137
|
);
|
|
449
138
|
});
|
|
450
139
|
|
|
451
|
-
// New link form
|
|
452
140
|
pagesRoutes.get("/links/new", async (c) => {
|
|
453
141
|
const siteName = await getSiteName(c);
|
|
454
|
-
|
|
455
142
|
return c.html(
|
|
456
143
|
<DashLayout
|
|
457
144
|
c={c}
|
|
@@ -464,10 +151,8 @@ pagesRoutes.get("/links/new", async (c) => {
|
|
|
464
151
|
);
|
|
465
152
|
});
|
|
466
153
|
|
|
467
|
-
// Create link
|
|
468
154
|
pagesRoutes.post("/links", async (c) => {
|
|
469
155
|
const body = await c.req.json<{ label: string; url: string }>();
|
|
470
|
-
|
|
471
156
|
if (!body.label || !body.url) {
|
|
472
157
|
return dsToast("Label and URL are required", "error");
|
|
473
158
|
}
|
|
@@ -477,24 +162,18 @@ pagesRoutes.post("/links", async (c) => {
|
|
|
477
162
|
label: body.label,
|
|
478
163
|
url: body.url,
|
|
479
164
|
});
|
|
480
|
-
|
|
481
165
|
return dsRedirect("/dash/pages");
|
|
482
166
|
});
|
|
483
167
|
|
|
484
|
-
// Reorder nav items (must be before /:id to avoid matching)
|
|
485
168
|
pagesRoutes.post("/reorder", async (c) => {
|
|
486
169
|
const body = await c.req.json<{ ids: number[] }>();
|
|
487
|
-
|
|
488
170
|
if (!Array.isArray(body.ids)) {
|
|
489
171
|
return dsToast("Invalid request", "error");
|
|
490
172
|
}
|
|
491
|
-
|
|
492
173
|
await c.var.services.navItems.reorder(body.ids);
|
|
493
|
-
|
|
494
174
|
return dsToast("Order saved");
|
|
495
175
|
});
|
|
496
176
|
|
|
497
|
-
// Edit link form
|
|
498
177
|
pagesRoutes.get("/links/:id/edit", async (c) => {
|
|
499
178
|
const id = parseInt(c.req.param("id"), 10);
|
|
500
179
|
if (isNaN(id)) return c.notFound();
|
|
@@ -503,7 +182,6 @@ pagesRoutes.get("/links/:id/edit", async (c) => {
|
|
|
503
182
|
if (!item) return c.notFound();
|
|
504
183
|
|
|
505
184
|
const siteName = await getSiteName(c);
|
|
506
|
-
|
|
507
185
|
return c.html(
|
|
508
186
|
<DashLayout
|
|
509
187
|
c={c}
|
|
@@ -516,13 +194,11 @@ pagesRoutes.get("/links/:id/edit", async (c) => {
|
|
|
516
194
|
);
|
|
517
195
|
});
|
|
518
196
|
|
|
519
|
-
// Update link
|
|
520
197
|
pagesRoutes.post("/links/:id", async (c) => {
|
|
521
198
|
const id = parseInt(c.req.param("id"), 10);
|
|
522
199
|
if (isNaN(id)) return c.notFound();
|
|
523
200
|
|
|
524
201
|
const body = await c.req.json<{ label: string; url: string }>();
|
|
525
|
-
|
|
526
202
|
if (!body.label || !body.url) {
|
|
527
203
|
return dsToast("Label and URL are required", "error");
|
|
528
204
|
}
|
|
@@ -531,23 +207,19 @@ pagesRoutes.post("/links/:id", async (c) => {
|
|
|
531
207
|
label: body.label,
|
|
532
208
|
url: body.url,
|
|
533
209
|
});
|
|
534
|
-
|
|
535
210
|
if (!updated) return c.notFound();
|
|
536
211
|
|
|
537
212
|
return dsRedirect("/dash/pages");
|
|
538
213
|
});
|
|
539
214
|
|
|
540
|
-
// Delete link
|
|
541
215
|
pagesRoutes.post("/links/:id/delete", async (c) => {
|
|
542
216
|
const id = parseInt(c.req.param("id"), 10);
|
|
543
217
|
if (!isNaN(id)) {
|
|
544
218
|
await c.var.services.navItems.delete(id);
|
|
545
219
|
}
|
|
546
|
-
|
|
547
220
|
return dsRedirect("/dash/pages");
|
|
548
221
|
});
|
|
549
222
|
|
|
550
|
-
// Create page
|
|
551
223
|
pagesRoutes.post("/", async (c) => {
|
|
552
224
|
const body = await c.req.json<{
|
|
553
225
|
title: string;
|
|
@@ -566,7 +238,6 @@ pagesRoutes.post("/", async (c) => {
|
|
|
566
238
|
return dsRedirect(`/dash/pages/${page.id}`);
|
|
567
239
|
});
|
|
568
240
|
|
|
569
|
-
// Add page to navigation
|
|
570
241
|
pagesRoutes.post("/:id/add-to-nav", async (c) => {
|
|
571
242
|
const id = parseInt(c.req.param("id"), 10);
|
|
572
243
|
if (isNaN(id)) return c.notFound();
|
|
@@ -580,26 +251,21 @@ pagesRoutes.post("/:id/add-to-nav", async (c) => {
|
|
|
580
251
|
url: `/${page.slug}`,
|
|
581
252
|
pageId: page.id,
|
|
582
253
|
});
|
|
583
|
-
|
|
584
254
|
return dsRedirect("/dash/pages");
|
|
585
255
|
});
|
|
586
256
|
|
|
587
|
-
// Remove page from navigation (keeps the page, deletes the nav item)
|
|
588
257
|
pagesRoutes.post("/:id/remove-from-nav", async (c) => {
|
|
589
258
|
const pageId = parseInt(c.req.param("id"), 10);
|
|
590
259
|
if (isNaN(pageId)) return c.notFound();
|
|
591
260
|
|
|
592
|
-
// Find nav item by pageId
|
|
593
261
|
const navItems = await c.var.services.navItems.list();
|
|
594
262
|
const navItem = navItems.find((item) => item.pageId === pageId);
|
|
595
263
|
if (navItem) {
|
|
596
264
|
await c.var.services.navItems.delete(navItem.id);
|
|
597
265
|
}
|
|
598
|
-
|
|
599
266
|
return dsRedirect("/dash/pages");
|
|
600
267
|
});
|
|
601
268
|
|
|
602
|
-
// View single page
|
|
603
269
|
pagesRoutes.get("/:id", async (c) => {
|
|
604
270
|
const id = parseInt(c.req.param("id"), 10);
|
|
605
271
|
if (isNaN(id)) return c.notFound();
|
|
@@ -608,7 +274,6 @@ pagesRoutes.get("/:id", async (c) => {
|
|
|
608
274
|
if (!page) return c.notFound();
|
|
609
275
|
|
|
610
276
|
const siteName = await getSiteName(c);
|
|
611
|
-
|
|
612
277
|
return c.html(
|
|
613
278
|
<DashLayout
|
|
614
279
|
c={c}
|
|
@@ -621,7 +286,6 @@ pagesRoutes.get("/:id", async (c) => {
|
|
|
621
286
|
);
|
|
622
287
|
});
|
|
623
288
|
|
|
624
|
-
// Edit page form
|
|
625
289
|
pagesRoutes.get("/:id/edit", async (c) => {
|
|
626
290
|
const id = parseInt(c.req.param("id"), 10);
|
|
627
291
|
if (isNaN(id)) return c.notFound();
|
|
@@ -630,7 +294,6 @@ pagesRoutes.get("/:id/edit", async (c) => {
|
|
|
630
294
|
if (!page) return c.notFound();
|
|
631
295
|
|
|
632
296
|
const siteName = await getSiteName(c);
|
|
633
|
-
|
|
634
297
|
return c.html(
|
|
635
298
|
<DashLayout
|
|
636
299
|
c={c}
|
|
@@ -643,7 +306,6 @@ pagesRoutes.get("/:id/edit", async (c) => {
|
|
|
643
306
|
);
|
|
644
307
|
});
|
|
645
308
|
|
|
646
|
-
// Update page
|
|
647
309
|
pagesRoutes.post("/:id", async (c) => {
|
|
648
310
|
const id = parseInt(c.req.param("id"), 10);
|
|
649
311
|
if (isNaN(id)) return c.notFound();
|
|
@@ -665,12 +327,10 @@ pagesRoutes.post("/:id", async (c) => {
|
|
|
665
327
|
return dsRedirect(`/dash/pages/${id}`);
|
|
666
328
|
});
|
|
667
329
|
|
|
668
|
-
// Delete page
|
|
669
330
|
pagesRoutes.post("/:id/delete", async (c) => {
|
|
670
331
|
const id = parseInt(c.req.param("id"), 10);
|
|
671
332
|
if (isNaN(id)) return c.notFound();
|
|
672
333
|
|
|
673
334
|
await c.var.services.pages.delete(id);
|
|
674
|
-
|
|
675
335
|
return dsRedirect("/dash/pages");
|
|
676
336
|
});
|
|
@@ -158,20 +158,26 @@ function NewRedirectContent() {
|
|
|
158
158
|
</div>
|
|
159
159
|
|
|
160
160
|
<div class="flex gap-2">
|
|
161
|
-
<button type="submit" class="btn" data-attr
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
161
|
+
<button type="submit" class="btn" data-attr:disabled="$_loading">
|
|
162
|
+
<svg
|
|
163
|
+
data-show="$_loading"
|
|
164
|
+
style="display:none"
|
|
165
|
+
class="animate-spin size-4"
|
|
166
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
167
|
+
viewBox="0 0 24 24"
|
|
168
|
+
fill="none"
|
|
169
|
+
stroke="currentColor"
|
|
170
|
+
stroke-width="2"
|
|
171
|
+
stroke-linecap="round"
|
|
172
|
+
stroke-linejoin="round"
|
|
173
|
+
role="status"
|
|
174
|
+
>
|
|
175
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
176
|
+
</svg>
|
|
177
|
+
{t({
|
|
178
|
+
message: "Create Redirect",
|
|
179
|
+
comment: "@context: Button to save new redirect",
|
|
180
|
+
})}
|
|
175
181
|
</button>
|
|
176
182
|
<a href="/dash/redirects" class="btn-outline">
|
|
177
183
|
{t({
|