@meith/web 0.25.0 → 0.26.0
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/app/(board)/member/[id]/reputation/page.tsx +2 -2
- package/app/(board)/messages/[id]/page.tsx +28 -20
- package/app/(board)/messages/page.tsx +2 -2
- package/app/(board)/notifications/page.tsx +3 -1
- package/app/(board)/subscriptions/page.tsx +6 -2
- package/app/(board)/usercp/contacts/page.tsx +3 -7
- package/app/admin/api-tokens/page.tsx +3 -2
- package/app/admin/forums/[id]/page.tsx +25 -22
- package/app/admin/groups/memberships/page.tsx +6 -6
- package/app/admin/groups/promotions/page.tsx +14 -16
- package/app/admin/log/page.tsx +5 -3
- package/app/admin/plugins/page.tsx +1 -1
- package/app/admin/security/page.tsx +4 -3
- package/app/admin/system/page.tsx +13 -10
- package/app/admin/themes/page.tsx +1 -1
- package/bin/forum-web.mjs +3 -3
- package/bin/forum-web.test.ts +2 -2
- package/package.json +48 -48
- package/src/components/account/active-sessions.tsx +9 -6
- package/src/components/account/credential-proof-form.tsx +6 -7
- package/src/components/account/logout-form.tsx +5 -3
- package/src/components/account/notification-forms.tsx +9 -9
- package/src/components/account/relation-forms.tsx +5 -5
- package/src/components/account/reputation-forms.tsx +10 -10
- package/src/components/account/sign-in-methods.tsx +12 -6
- package/src/components/account/subscription-forms.tsx +7 -7
- package/src/components/account/two-factor-forms.tsx +12 -12
- package/src/components/account/usercp-forms.tsx +13 -13
- package/src/components/admin/admin-forms.tsx +7 -7
- package/src/components/admin/badge-forms.tsx +3 -3
- package/src/components/admin/ban-filter-forms.tsx +4 -4
- package/src/components/admin/branding-forms.tsx +3 -3
- package/src/components/admin/content-forms.tsx +15 -15
- package/src/components/admin/forum-forms.tsx +7 -7
- package/src/components/admin/group-forms.tsx +5 -5
- package/src/components/admin/mail-test-card.tsx +3 -3
- package/src/components/admin/marketplace-forms.tsx +4 -4
- package/src/components/admin/navigation-forms.tsx +3 -3
- package/src/components/admin/plugin-forms.tsx +7 -7
- package/src/components/admin/settings-form.tsx +5 -2
- package/src/components/admin/theme-forms.tsx +7 -10
- package/src/components/auth/form-controls.tsx +23 -1
- package/src/components/auth/second-factor-form.tsx +4 -4
- package/src/components/content/composer-intents.tsx +7 -6
- package/src/components/content/edit-post-form.tsx +7 -7
- package/src/components/content/poll-edit-form.tsx +3 -1
- package/src/components/content/poll.tsx +4 -2
- package/src/components/content/thanks-button.tsx +3 -3
- package/src/components/content/thread-rating.tsx +3 -3
- package/src/components/messages/message-forms.tsx +15 -15
- package/src/components/moderation/inline-moderation-form.tsx +19 -20
- package/src/components/moderation/queue-form.tsx +5 -7
- package/src/components/moderation/report-forms.tsx +8 -10
- package/src/components/moderation/signature-lock-form.tsx +5 -5
- package/src/components/moderation/thread-surgery-form.tsx +8 -5
- package/src/components/moderation/thread-tools-form.tsx +11 -22
- package/src/components/moderation/warning-forms.tsx +6 -6
- package/src/components/shell/onboarding-banner.tsx +6 -2
- package/src/components/shell/theme-switcher.tsx +6 -6
- package/src/components/shell/view-tabs.tsx +12 -7
- package/src/server/theme.ts +1 -1
- package/src/server/upgrade-notice.ts +1 -1
|
@@ -119,11 +119,11 @@ export default async function ReputationPage({
|
|
|
119
119
|
)}
|
|
120
120
|
|
|
121
121
|
{view.rows.length === 0 ? (
|
|
122
|
-
<p className="text-sm text-muted-foreground">
|
|
122
|
+
<p className="rounded-lg border border-border bg-card p-4 text-sm text-muted-foreground">
|
|
123
123
|
{await tr('page.nobody-has-rated-them-yet')}
|
|
124
124
|
</p>
|
|
125
125
|
) : (
|
|
126
|
-
<ul className="flex flex-col divide-y divide-border rounded-lg border border-border">
|
|
126
|
+
<ul className="flex flex-col divide-y divide-border rounded-lg border border-border bg-card">
|
|
127
127
|
{view.rows.map((row) => (
|
|
128
128
|
<li key={row.id} className="flex flex-col gap-1 px-4 py-3">
|
|
129
129
|
<div className="flex flex-wrap items-baseline gap-2 text-sm">
|
|
@@ -2,6 +2,7 @@ import type { Metadata } from 'next'
|
|
|
2
2
|
import { notFound } from 'next/navigation'
|
|
3
3
|
|
|
4
4
|
import { postBodyHtml } from '@meith/markdown'
|
|
5
|
+
import { Card, CardContent, CardFooter } from '@meith/ui'
|
|
5
6
|
|
|
6
7
|
import { PanelPage } from '@/components/shell/panel-page'
|
|
7
8
|
import { getContainer } from '@/server/container'
|
|
@@ -63,27 +64,34 @@ export default async function MessagePage({ params }: { params: Promise<{ id: st
|
|
|
63
64
|
}
|
|
64
65
|
: {})}
|
|
65
66
|
>
|
|
66
|
-
<
|
|
67
|
-
className="
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
<Card>
|
|
68
|
+
<CardContent className="p-4 sm:p-5">
|
|
69
|
+
<article
|
|
70
|
+
className="prose prose-sm max-w-none break-words"
|
|
71
|
+
dangerouslySetInnerHTML={{ __html: view.bodyHtml }}
|
|
72
|
+
/>
|
|
73
|
+
</CardContent>
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
<CardFooter className="gap-4">
|
|
76
|
+
{view.actions.map((action) => (
|
|
77
|
+
<a
|
|
78
|
+
key={action.href}
|
|
79
|
+
href={action.href}
|
|
80
|
+
className="text-sm font-medium text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground"
|
|
81
|
+
>
|
|
82
|
+
{action.label}
|
|
83
|
+
</a>
|
|
84
|
+
))}
|
|
85
|
+
{view.reportHref !== null && (
|
|
86
|
+
<a
|
|
87
|
+
href={view.reportHref}
|
|
88
|
+
className="text-sm text-muted-foreground hover:text-foreground"
|
|
89
|
+
>
|
|
90
|
+
{await tr('page.report-this-message')}
|
|
91
|
+
</a>
|
|
92
|
+
)}
|
|
93
|
+
</CardFooter>
|
|
94
|
+
</Card>
|
|
87
95
|
|
|
88
96
|
{view.tracking.length > 0 && (
|
|
89
97
|
<section className="flex flex-col gap-2 rounded-lg border border-border bg-card p-4">
|
|
@@ -110,7 +110,7 @@ export default async function MessagesPage({
|
|
|
110
110
|
) : null}
|
|
111
111
|
|
|
112
112
|
{view.rows.length === 0 ? (
|
|
113
|
-
<p className="text-sm text-muted-foreground">
|
|
113
|
+
<p className="rounded-lg border border-border bg-card p-4 text-sm text-muted-foreground">
|
|
114
114
|
{folder === 'inbox'
|
|
115
115
|
? translator.t('board.messages.emptyInbox')
|
|
116
116
|
: folder === 'sent'
|
|
@@ -125,7 +125,7 @@ export default async function MessagesPage({
|
|
|
125
125
|
copy={messageFormsCopy(await getTranslator())}
|
|
126
126
|
/>
|
|
127
127
|
|
|
128
|
-
<ul className="flex flex-col divide-y divide-border rounded-lg border border-border">
|
|
128
|
+
<ul className="flex flex-col divide-y divide-border rounded-lg border border-border bg-card">
|
|
129
129
|
{view.rows.map((row) => (
|
|
130
130
|
<li key={row.copyId} className="flex items-start gap-3 px-4 py-3">
|
|
131
131
|
<input
|
|
@@ -79,7 +79,9 @@ export default async function NotificationsPage({
|
|
|
79
79
|
{notice !== null && <BoardNotice kind="info" message={notice} dismissHref="/notifications" />}
|
|
80
80
|
|
|
81
81
|
{view.rows.length === 0 ? (
|
|
82
|
-
<p className="text-sm text-muted-foreground">
|
|
82
|
+
<p className="rounded-lg border border-border bg-card p-4 text-sm text-muted-foreground">
|
|
83
|
+
{translator.t('board.notifications.empty')}
|
|
84
|
+
</p>
|
|
83
85
|
) : (
|
|
84
86
|
<ul className="flex flex-col gap-3">
|
|
85
87
|
{view.rows.map((row) => (
|
|
@@ -65,7 +65,9 @@ export default async function SubscriptionsPage({
|
|
|
65
65
|
{notice !== null && <BoardNotice kind="info" message={notice} dismissHref="/subscriptions" />}
|
|
66
66
|
|
|
67
67
|
{view.total === 0 ? (
|
|
68
|
-
<p className="text-sm text-muted-foreground">
|
|
68
|
+
<p className="rounded-lg border border-border bg-card p-4 text-sm text-muted-foreground">
|
|
69
|
+
{await tr('page.subscriptions.none')}
|
|
70
|
+
</p>
|
|
69
71
|
) : (
|
|
70
72
|
<>
|
|
71
73
|
<Section
|
|
@@ -110,7 +112,9 @@ function Section({
|
|
|
110
112
|
<h2 className="font-heading text-lg font-semibold">{title}</h2>
|
|
111
113
|
|
|
112
114
|
{rows.length === 0 ? (
|
|
113
|
-
<p className="text-sm text-muted-foreground">
|
|
115
|
+
<p className="rounded-lg border border-border bg-card p-4 text-sm text-muted-foreground">
|
|
116
|
+
{empty}
|
|
117
|
+
</p>
|
|
114
118
|
) : (
|
|
115
119
|
<ul className="flex flex-col gap-3">
|
|
116
120
|
{rows.map((row) => (
|
|
@@ -5,7 +5,7 @@ import { MAX_RELATIONS } from '@meith/relations'
|
|
|
5
5
|
|
|
6
6
|
import { RemoveRelationForm } from '@/components/account/relation-forms'
|
|
7
7
|
import { BoardNotice } from '@/components/shell/board-notice'
|
|
8
|
-
import { PANEL_LIST } from '@/components/shell/panel-list'
|
|
8
|
+
import { PANEL_LIST, PANEL_NOTE } from '@/components/shell/panel-list'
|
|
9
9
|
import { PanelPage } from '@/components/shell/panel-page'
|
|
10
10
|
import { getActor } from '@/server/context'
|
|
11
11
|
import { getTranslator, tr } from '@/server/i18n'
|
|
@@ -68,9 +68,7 @@ export default async function ContactsPage({
|
|
|
68
68
|
</h2>
|
|
69
69
|
|
|
70
70
|
{view.buddies.length === 0 ? (
|
|
71
|
-
<p className=
|
|
72
|
-
{translator.t('board.contacts.emptyBuddies')}
|
|
73
|
-
</p>
|
|
71
|
+
<p className={PANEL_NOTE}>{translator.t('board.contacts.emptyBuddies')}</p>
|
|
74
72
|
) : (
|
|
75
73
|
<ul className={PANEL_LIST}>
|
|
76
74
|
{view.buddies.map((row) => (
|
|
@@ -89,9 +87,7 @@ export default async function ContactsPage({
|
|
|
89
87
|
</p>
|
|
90
88
|
|
|
91
89
|
{view.ignored.length === 0 ? (
|
|
92
|
-
<p className=
|
|
93
|
-
{translator.t('board.contacts.emptyIgnored')}
|
|
94
|
-
</p>
|
|
90
|
+
<p className={PANEL_NOTE}>{translator.t('board.contacts.emptyIgnored')}</p>
|
|
95
91
|
) : (
|
|
96
92
|
<ul className={PANEL_LIST}>
|
|
97
93
|
{view.ignored.map((row) => (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Metadata } from 'next'
|
|
2
2
|
|
|
3
3
|
import { IssueTokenForm, RevokeTokenForm } from '@/components/admin/api-token-forms'
|
|
4
|
+
import { PANEL_CARD } from '@/components/shell/panel-list'
|
|
4
5
|
import { PanelPage } from '@/components/shell/panel-page'
|
|
5
6
|
import { adminPageContext } from '@/server/admin'
|
|
6
7
|
import { buildApiTokenView } from '@/server/api-tokens-admin'
|
|
@@ -47,12 +48,12 @@ export default async function AdminApiTokensPage() {
|
|
|
47
48
|
}
|
|
48
49
|
gap="loose"
|
|
49
50
|
>
|
|
50
|
-
<section className=
|
|
51
|
+
<section className={PANEL_CARD}>
|
|
51
52
|
<h2 className="font-heading text-lg font-semibold">{await tr('page.issue-token')}</h2>
|
|
52
53
|
<IssueTokenForm scopes={view.scopes} copy={copy} />
|
|
53
54
|
</section>
|
|
54
55
|
|
|
55
|
-
<section className=
|
|
56
|
+
<section className={PANEL_CARD}>
|
|
56
57
|
<h2 className="font-heading text-lg font-semibold">{await tr('page.existing-tokens')}</h2>
|
|
57
58
|
|
|
58
59
|
{view.tokens.length === 0 ? (
|
|
@@ -47,28 +47,31 @@ export default async function AdminForumPage({ params }: { params: Promise<{ id:
|
|
|
47
47
|
lede={t.t('adminForums.optionsLede')}
|
|
48
48
|
gap="loose"
|
|
49
49
|
>
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
50
|
+
<section className={PANEL_CARD}>
|
|
51
|
+
<h2 className="font-heading text-lg font-semibold">{await tr('page.forum-options')}</h2>
|
|
52
|
+
<ForumOptionsForm
|
|
53
|
+
forum={{
|
|
54
|
+
id: forum.id,
|
|
55
|
+
type: forum.type,
|
|
56
|
+
title: options.title,
|
|
57
|
+
slug: options.slug,
|
|
58
|
+
description: options.description ?? '',
|
|
59
|
+
linkUrl: options.linkUrl ?? '',
|
|
60
|
+
displayOrder: options.displayOrder,
|
|
61
|
+
flags: {
|
|
62
|
+
isOpen: options.isOpen,
|
|
63
|
+
allowThreads: options.allowThreads,
|
|
64
|
+
allowReplies: options.allowReplies,
|
|
65
|
+
allowPolls: options.allowPolls,
|
|
66
|
+
allowAttachments: options.allowAttachments,
|
|
67
|
+
requiresPrefix: options.requiresPrefix,
|
|
68
|
+
moderateNewThreads: options.moderateNewThreads,
|
|
69
|
+
moderateNewPosts: options.moderateNewPosts,
|
|
70
|
+
},
|
|
71
|
+
}}
|
|
72
|
+
copy={forumAdminCopy(t)}
|
|
73
|
+
/>
|
|
74
|
+
</section>
|
|
72
75
|
|
|
73
76
|
<section className={PANEL_CARD}>
|
|
74
77
|
<h2 className="font-heading text-lg font-semibold">{t.t('adminForums.move')}</h2>
|
|
@@ -44,13 +44,13 @@ export default async function AdminMembershipsPage() {
|
|
|
44
44
|
}))}
|
|
45
45
|
copy={groupAdminCopy(translator)}
|
|
46
46
|
/>
|
|
47
|
-
</section>
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
<p className="border-t border-border pt-4 text-xs text-muted-foreground">
|
|
49
|
+
{translator.t('adminGroups.membershipsBefore')}{' '}
|
|
50
|
+
<strong>{translator.t('adminGroups.primary')}</strong>
|
|
51
|
+
{translator.t('adminGroups.membershipsAfter')}
|
|
52
|
+
</p>
|
|
53
|
+
</section>
|
|
54
54
|
</PanelPage>
|
|
55
55
|
)
|
|
56
56
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import type { Metadata } from 'next'
|
|
2
2
|
|
|
3
|
-
import { cn } from '@meith/ui'
|
|
4
|
-
|
|
5
3
|
import {
|
|
6
4
|
ApplyPromotionsForm,
|
|
7
5
|
NewPromotionRuleForm,
|
|
8
6
|
PromotionRuleRowForm,
|
|
9
7
|
} from '@/components/admin/group-forms'
|
|
10
|
-
import { PANEL_CARD
|
|
8
|
+
import { PANEL_CARD } from '@/components/shell/panel-list'
|
|
11
9
|
import { PanelPage } from '@/components/shell/panel-page'
|
|
12
10
|
import { adminPageContext } from '@/server/admin'
|
|
13
11
|
import {
|
|
@@ -93,7 +91,7 @@ export default async function AdminPromotionsPage() {
|
|
|
93
91
|
<NewPromotionRuleForm groups={options} copy={copy} />
|
|
94
92
|
</section>
|
|
95
93
|
|
|
96
|
-
<section className=
|
|
94
|
+
<section className={PANEL_CARD}>
|
|
97
95
|
<h2 className="font-heading text-lg font-semibold">
|
|
98
96
|
{await tr('page.what-would-happen-now')}
|
|
99
97
|
</h2>
|
|
@@ -102,24 +100,24 @@ export default async function AdminPromotionsPage() {
|
|
|
102
100
|
</p>
|
|
103
101
|
|
|
104
102
|
{result.complete ? null : (
|
|
105
|
-
<p className=
|
|
103
|
+
<p className="text-sm text-muted-foreground">
|
|
106
104
|
{translator.t('adminGroups.previewCapped', { count: result.examined })}
|
|
107
105
|
</p>
|
|
108
106
|
)}
|
|
109
107
|
|
|
110
108
|
{result.outcomes.length === 0 ? (
|
|
111
|
-
<p className=
|
|
109
|
+
<p className="text-sm text-muted-foreground">
|
|
112
110
|
{stored.length === 0
|
|
113
111
|
? translator.t('adminGroups.nobodyNoRules')
|
|
114
112
|
: translator.t('adminGroups.nobodyQualified')}
|
|
115
113
|
</p>
|
|
116
114
|
) : (
|
|
117
115
|
<>
|
|
118
|
-
<ul className=
|
|
116
|
+
<ul className="flex flex-col divide-y divide-border text-sm">
|
|
119
117
|
{result.outcomes.map((outcome) => (
|
|
120
118
|
<li
|
|
121
119
|
key={`${outcome.userId}:${outcome.ruleId}`}
|
|
122
|
-
className="flex flex-wrap items-baseline gap-2
|
|
120
|
+
className="flex flex-wrap items-baseline gap-2 py-2 first:pt-0 last:pb-0"
|
|
123
121
|
>
|
|
124
122
|
<span className="font-medium">
|
|
125
123
|
{translator.t('adminGroups.member', { id: outcome.userId })}
|
|
@@ -132,18 +130,18 @@ export default async function AdminPromotionsPage() {
|
|
|
132
130
|
))}
|
|
133
131
|
</ul>
|
|
134
132
|
|
|
135
|
-
<
|
|
136
|
-
<h3 className="font-heading text-
|
|
133
|
+
<div className="flex flex-col gap-3 border-t border-border pt-4">
|
|
134
|
+
<h3 className="font-heading text-base font-semibold">{await tr('page.run-it')}</h3>
|
|
137
135
|
<ApplyPromotionsForm count={result.outcomes.length} copy={copy} />
|
|
138
|
-
</
|
|
136
|
+
</div>
|
|
139
137
|
</>
|
|
140
138
|
)}
|
|
141
|
-
</section>
|
|
142
139
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
140
|
+
<p className="border-t border-border pt-4 text-xs text-muted-foreground">
|
|
141
|
+
{translator.t('adminGroups.promotionNoteBefore')} <code>promotions.apply</code>
|
|
142
|
+
{translator.t('adminGroups.promotionNoteEnd')}
|
|
143
|
+
</p>
|
|
144
|
+
</section>
|
|
147
145
|
</PanelPage>
|
|
148
146
|
)
|
|
149
147
|
}
|
package/app/admin/log/page.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Metadata } from 'next'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { cn } from '@meith/ui'
|
|
4
|
+
|
|
5
|
+
import { PANEL_CARD, PANEL_LIST, PANEL_NOTE } from '@/components/shell/panel-list'
|
|
4
6
|
import { PanelPage } from '@/components/shell/panel-page'
|
|
5
7
|
import { PanelPagination } from '@/components/shell/panel-pagination'
|
|
6
8
|
import { adminPageContext } from '@/server/admin'
|
|
@@ -52,7 +54,7 @@ export default async function AdminLogPage({
|
|
|
52
54
|
lede={await tr('page.every-administrative-moderation-action-newest')}
|
|
53
55
|
width="wide"
|
|
54
56
|
>
|
|
55
|
-
<form method="get" className=
|
|
57
|
+
<form method="get" className={cn(PANEL_CARD, 'flex-row flex-wrap items-end')}>
|
|
56
58
|
<label className="flex flex-col gap-1 text-sm">
|
|
57
59
|
<span className="font-medium">{translator.t('adminLog.action')}</span>
|
|
58
60
|
<select
|
|
@@ -77,7 +79,7 @@ export default async function AdminLogPage({
|
|
|
77
79
|
</form>
|
|
78
80
|
|
|
79
81
|
{view.rows.length === 0 ? (
|
|
80
|
-
<p className=
|
|
82
|
+
<p className={PANEL_NOTE}>{await tr('page.nothing-logged')}</p>
|
|
81
83
|
) : (
|
|
82
84
|
<ul className={PANEL_LIST}>
|
|
83
85
|
{view.rows.map((row) => (
|
|
@@ -33,7 +33,7 @@ export default async function AdminPluginsPage() {
|
|
|
33
33
|
title={t.t('page.plugins')}
|
|
34
34
|
lede={
|
|
35
35
|
<>
|
|
36
|
-
{t.t('adminPlugins.ledeBefore')} <code className="text-xs">
|
|
36
|
+
{t.t('adminPlugins.ledeBefore')} <code className="text-xs">meith.config.ts</code>{' '}
|
|
37
37
|
{t.t('adminPlugins.ledeAfter')}
|
|
38
38
|
</>
|
|
39
39
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Metadata } from 'next'
|
|
2
2
|
|
|
3
3
|
import { AUTH_EVENT_KINDS } from '@meith/accounts'
|
|
4
|
+
import { cn } from '@meith/ui'
|
|
4
5
|
|
|
5
|
-
import { PANEL_LIST, PANEL_ROW } from '@/components/shell/panel-list'
|
|
6
|
+
import { PANEL_CARD, PANEL_LIST, PANEL_NOTE, PANEL_ROW } from '@/components/shell/panel-list'
|
|
6
7
|
import { PanelPage } from '@/components/shell/panel-page'
|
|
7
8
|
import { adminPageContext } from '@/server/admin'
|
|
8
9
|
import { boardSecurityActivity } from '@/server/auth-events'
|
|
@@ -51,7 +52,7 @@ export default async function AdminSecurityPage({
|
|
|
51
52
|
lede={translator.t('adminSecurity.lede')}
|
|
52
53
|
width="wide"
|
|
53
54
|
>
|
|
54
|
-
<form method="get" className=
|
|
55
|
+
<form method="get" className={cn(PANEL_CARD, 'flex-row flex-wrap items-end')}>
|
|
55
56
|
<label className="flex flex-col gap-1 text-sm">
|
|
56
57
|
<span className="font-medium">{translator.t('adminSecurity.kind')}</span>
|
|
57
58
|
<select
|
|
@@ -76,7 +77,7 @@ export default async function AdminSecurityPage({
|
|
|
76
77
|
</form>
|
|
77
78
|
|
|
78
79
|
{page.length === 0 ? (
|
|
79
|
-
<p className=
|
|
80
|
+
<p className={PANEL_NOTE}>{await tr('page.nothing-recorded-yet')}</p>
|
|
80
81
|
) : (
|
|
81
82
|
<ul className={PANEL_LIST}>
|
|
82
83
|
{page.map((event) => (
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ReindexSearchForm,
|
|
12
12
|
RetryJobForm,
|
|
13
13
|
} from '@/components/admin/system-forms'
|
|
14
|
-
import { PANEL_CARD
|
|
14
|
+
import { PANEL_CARD } from '@/components/shell/panel-list'
|
|
15
15
|
import { PanelPage } from '@/components/shell/panel-page'
|
|
16
16
|
import { adminPageContext } from '@/server/admin'
|
|
17
17
|
import { getTranslator, tr } from '@/server/i18n'
|
|
@@ -122,7 +122,7 @@ export default async function AdminSystemPage() {
|
|
|
122
122
|
</section>
|
|
123
123
|
)}
|
|
124
124
|
|
|
125
|
-
<section className=
|
|
125
|
+
<section className={PANEL_CARD}>
|
|
126
126
|
<h2 className="font-heading text-lg font-semibold">{translator.t('adminSystem.mail')}</h2>
|
|
127
127
|
<p className="text-sm">
|
|
128
128
|
{mail.summary}
|
|
@@ -152,14 +152,17 @@ export default async function AdminSystemPage() {
|
|
|
152
152
|
<p className="text-sm text-muted-foreground">{translator.t('adminSystem.mailSchedule')}</p>
|
|
153
153
|
</section>
|
|
154
154
|
|
|
155
|
-
<section className=
|
|
155
|
+
<section className={PANEL_CARD}>
|
|
156
156
|
<h2 className="font-heading text-lg font-semibold">{await tr('page.scheduled-tasks')}</h2>
|
|
157
157
|
{scheduler.tasks.length === 0 ? (
|
|
158
|
-
<p className=
|
|
158
|
+
<p className="text-sm text-muted-foreground">{translator.t('adminSystem.noTasks')}</p>
|
|
159
159
|
) : (
|
|
160
|
-
<ul className=
|
|
160
|
+
<ul className="flex flex-col divide-y divide-border">
|
|
161
161
|
{scheduler.tasks.map((task) => (
|
|
162
|
-
<li
|
|
162
|
+
<li
|
|
163
|
+
key={task.key}
|
|
164
|
+
className="flex flex-wrap items-center justify-between gap-x-3 gap-y-2 py-3 first:pt-0 last:pb-0"
|
|
165
|
+
>
|
|
163
166
|
<span className="flex min-w-0 flex-col">
|
|
164
167
|
<span className="truncate text-sm font-medium">
|
|
165
168
|
{task.titleKey === undefined ? task.key : translator.t(task.titleKey)}
|
|
@@ -198,7 +201,7 @@ export default async function AdminSystemPage() {
|
|
|
198
201
|
)}
|
|
199
202
|
</section>
|
|
200
203
|
|
|
201
|
-
<section className=
|
|
204
|
+
<section className={PANEL_CARD}>
|
|
202
205
|
<h2 className="font-heading text-lg font-semibold">{await tr('page.recent-runs')}</h2>
|
|
203
206
|
{view.runs.length === 0 ? (
|
|
204
207
|
<p className="text-sm text-muted-foreground">{await tr('page.nothing-has-run-yet')}</p>
|
|
@@ -227,7 +230,7 @@ export default async function AdminSystemPage() {
|
|
|
227
230
|
)}
|
|
228
231
|
</section>
|
|
229
232
|
|
|
230
|
-
<section className=
|
|
233
|
+
<section className={PANEL_CARD}>
|
|
231
234
|
<h2 className="font-heading text-lg font-semibold">
|
|
232
235
|
{translator.t('adminSystem.volumes')}
|
|
233
236
|
</h2>
|
|
@@ -243,7 +246,7 @@ export default async function AdminSystemPage() {
|
|
|
243
246
|
</ul>
|
|
244
247
|
</section>
|
|
245
248
|
|
|
246
|
-
<section className=
|
|
249
|
+
<section className={PANEL_CARD}>
|
|
247
250
|
<h2 className="font-heading text-lg font-semibold">
|
|
248
251
|
{translator.t('adminSystem.recount')}
|
|
249
252
|
</h2>
|
|
@@ -266,7 +269,7 @@ export default async function AdminSystemPage() {
|
|
|
266
269
|
<RecountForm copy={copy} />
|
|
267
270
|
</section>
|
|
268
271
|
|
|
269
|
-
<section className=
|
|
272
|
+
<section className={PANEL_CARD}>
|
|
270
273
|
<h2 className="font-heading text-lg font-semibold">{await tr('page.search-index')}</h2>
|
|
271
274
|
<p className="text-sm text-muted-foreground">
|
|
272
275
|
{translator.t('adminSystem.searchIndexHint')}
|
|
@@ -169,7 +169,7 @@ export default async function AdminThemesPage() {
|
|
|
169
169
|
{translator.t('adminThemes.deployBefore')} <em>exist</em>
|
|
170
170
|
{translator.t('adminThemes.deployAfterExist')} <code className="text-xs">pnpm add</code>{' '}
|
|
171
171
|
{translator.t('adminThemes.deployAfterAdd')}{' '}
|
|
172
|
-
<code className="text-xs">
|
|
172
|
+
<code className="text-xs">meith.config.ts</code>
|
|
173
173
|
{translator.t('adminThemes.deployAfterConfig')}{' '}
|
|
174
174
|
<code className="text-xs">defaultTheme</code> {translator.t('adminThemes.deployEnd')}
|
|
175
175
|
</p>
|
package/bin/forum-web.mjs
CHANGED
|
@@ -233,12 +233,12 @@ function monorepoAliases() {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
function materialize() {
|
|
236
|
-
const boardConfig = join(workspaceRoot, '
|
|
237
|
-
const boardPlugins = join(workspaceRoot, '
|
|
236
|
+
const boardConfig = join(workspaceRoot, 'meith.config.ts')
|
|
237
|
+
const boardPlugins = join(workspaceRoot, 'meith.plugins.ts')
|
|
238
238
|
|
|
239
239
|
if (!existsSync(boardConfig)) {
|
|
240
240
|
fail(
|
|
241
|
-
`no
|
|
241
|
+
`no meith.config.ts in ${workspaceRoot}. Run forum-web from a board's own ` +
|
|
242
242
|
'directory — the one create-meith scaffolded, or one shaped like it.',
|
|
243
243
|
)
|
|
244
244
|
}
|
package/bin/forum-web.test.ts
CHANGED
|
@@ -166,8 +166,8 @@ describe('materializing at the workspace root', () => {
|
|
|
166
166
|
|
|
167
167
|
function board(): string {
|
|
168
168
|
const dir = mkdtempSync(join(tmpdir(), 'forum-web-at-root-'))
|
|
169
|
-
writeFileSync(join(dir, '
|
|
170
|
-
writeFileSync(join(dir, '
|
|
169
|
+
writeFileSync(join(dir, 'meith.config.ts'), 'export default {}\n')
|
|
170
|
+
writeFileSync(join(dir, 'meith.plugins.ts'), 'export const INSTALLED_PLUGINS = []\n')
|
|
171
171
|
boards.push(dir)
|
|
172
172
|
return dir
|
|
173
173
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meith/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "The board itself: the Next.js app, and the forum-web bin that materializes it into an external board workspace.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -43,53 +43,53 @@
|
|
|
43
43
|
"react-dom": "19.2.8",
|
|
44
44
|
"tailwindcss": "^4.3.3",
|
|
45
45
|
"typescript": "7.0.2",
|
|
46
|
-
"@meith/accounts": "0.
|
|
47
|
-
"@meith/admin": "0.
|
|
48
|
-
"@meith/antispam": "0.
|
|
49
|
-
"@meith/api": "0.
|
|
50
|
-
"@meith/attachments": "0.
|
|
51
|
-
"@meith/authorization": "0.
|
|
52
|
-
"@meith/avatars": "0.
|
|
53
|
-
"@meith/core": "0.
|
|
54
|
-
"@meith/db": "0.
|
|
55
|
-
"@meith/demo": "0.
|
|
56
|
-
"@meith/drafts": "0.
|
|
57
|
-
"@meith/drivers": "0.
|
|
58
|
-
"@meith/events": "0.
|
|
59
|
-
"@meith/forums": "0.
|
|
60
|
-
"@meith/groups": "0.
|
|
61
|
-
"@meith/i18n": "0.
|
|
62
|
-
"@meith/import": "0.
|
|
63
|
-
"@meith/install": "0.
|
|
64
|
-
"@meith/mail": "0.
|
|
65
|
-
"@meith/markdown": "0.
|
|
66
|
-
"@meith/marketplace": "0.
|
|
67
|
-
"@meith/messages": "0.
|
|
68
|
-
"@meith/moderation": "0.
|
|
69
|
-
"@meith/notifications": "0.
|
|
70
|
-
"@meith/plugin-calendar": "0.
|
|
71
|
-
"@meith/plugin-dues": "0.
|
|
72
|
-
"@meith/plugin-kit": "0.
|
|
73
|
-
"@meith/polls": "0.
|
|
74
|
-
"@meith/posts": "0.
|
|
75
|
-
"@meith/profile-fields": "0.
|
|
76
|
-
"@meith/relations": "0.
|
|
77
|
-
"@meith/reputation": "0.
|
|
78
|
-
"@meith/runtime": "0.
|
|
79
|
-
"@meith/search": "0.
|
|
80
|
-
"@meith/settings": "0.
|
|
81
|
-
"@meith/
|
|
82
|
-
"@meith/
|
|
83
|
-
"@meith/tasks": "0.
|
|
84
|
-
"@meith/theme-clubhouse": "0.
|
|
85
|
-
"@meith/theme-default": "0.
|
|
86
|
-
"@meith/theme-kit": "0.
|
|
87
|
-
"@meith/theme-midnight": "0.
|
|
88
|
-
"@meith/theme-phasebook": "0.
|
|
89
|
-
"@meith/theme-raidframe": "0.
|
|
90
|
-
"@meith/threads": "0.
|
|
91
|
-
"@meith/ui": "0.
|
|
92
|
-
"@meith/upgrade": "0.
|
|
46
|
+
"@meith/accounts": "0.26.0",
|
|
47
|
+
"@meith/admin": "0.26.0",
|
|
48
|
+
"@meith/antispam": "0.26.0",
|
|
49
|
+
"@meith/api": "0.26.0",
|
|
50
|
+
"@meith/attachments": "0.26.0",
|
|
51
|
+
"@meith/authorization": "0.26.0",
|
|
52
|
+
"@meith/avatars": "0.26.0",
|
|
53
|
+
"@meith/core": "0.26.0",
|
|
54
|
+
"@meith/db": "0.26.0",
|
|
55
|
+
"@meith/demo": "0.26.0",
|
|
56
|
+
"@meith/drafts": "0.26.0",
|
|
57
|
+
"@meith/drivers": "0.26.0",
|
|
58
|
+
"@meith/events": "0.26.0",
|
|
59
|
+
"@meith/forums": "0.26.0",
|
|
60
|
+
"@meith/groups": "0.26.0",
|
|
61
|
+
"@meith/i18n": "0.26.0",
|
|
62
|
+
"@meith/import": "0.26.0",
|
|
63
|
+
"@meith/install": "0.26.0",
|
|
64
|
+
"@meith/mail": "0.26.0",
|
|
65
|
+
"@meith/markdown": "0.26.0",
|
|
66
|
+
"@meith/marketplace": "0.26.0",
|
|
67
|
+
"@meith/messages": "0.26.0",
|
|
68
|
+
"@meith/moderation": "0.26.0",
|
|
69
|
+
"@meith/notifications": "0.26.0",
|
|
70
|
+
"@meith/plugin-calendar": "0.26.0",
|
|
71
|
+
"@meith/plugin-dues": "0.26.0",
|
|
72
|
+
"@meith/plugin-kit": "0.26.0",
|
|
73
|
+
"@meith/polls": "0.26.0",
|
|
74
|
+
"@meith/posts": "0.26.0",
|
|
75
|
+
"@meith/profile-fields": "0.26.0",
|
|
76
|
+
"@meith/relations": "0.26.0",
|
|
77
|
+
"@meith/reputation": "0.26.0",
|
|
78
|
+
"@meith/runtime": "0.26.0",
|
|
79
|
+
"@meith/search": "0.26.0",
|
|
80
|
+
"@meith/settings": "0.26.0",
|
|
81
|
+
"@meith/subscriptions": "0.26.0",
|
|
82
|
+
"@meith/signatures": "0.26.0",
|
|
83
|
+
"@meith/tasks": "0.26.0",
|
|
84
|
+
"@meith/theme-clubhouse": "0.26.0",
|
|
85
|
+
"@meith/theme-default": "0.26.0",
|
|
86
|
+
"@meith/theme-kit": "0.26.0",
|
|
87
|
+
"@meith/theme-midnight": "0.26.0",
|
|
88
|
+
"@meith/theme-phasebook": "0.26.0",
|
|
89
|
+
"@meith/theme-raidframe": "0.26.0",
|
|
90
|
+
"@meith/threads": "0.26.0",
|
|
91
|
+
"@meith/ui": "0.26.0",
|
|
92
|
+
"@meith/upgrade": "0.26.0"
|
|
93
93
|
},
|
|
94
94
|
"scripts": {
|
|
95
95
|
"dev": "next dev",
|