@meith/web 0.36.1 → 0.37.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.
@@ -1,7 +1,6 @@
1
1
  import type { Metadata } from 'next'
2
2
 
3
3
  import type { TaskHealthStatus } from '@meith/tasks'
4
- import { cn } from '@meith/ui'
5
4
 
6
5
  import {
7
6
  ApplyMigrationsForm,
@@ -12,6 +11,7 @@ import {
12
11
  ReindexSearchForm,
13
12
  RetryJobForm,
14
13
  } from '@/components/admin/system-forms'
14
+ import { SystemRunDetails } from '@/components/admin/system-run-details'
15
15
  import { PANEL_CARD } from '@/components/shell/panel-list'
16
16
  import { PanelPage } from '@/components/shell/panel-page'
17
17
  import { adminPageContext } from '@/server/admin'
@@ -35,9 +35,14 @@ export async function generateMetadata(): Promise<Metadata> {
35
35
  return { title: await tr('page.system-health') }
36
36
  }
37
37
 
38
- export default async function AdminSystemPage() {
38
+ export default async function AdminSystemPage({
39
+ searchParams,
40
+ }: {
41
+ searchParams: Promise<{ maintenance?: string }>
42
+ }) {
39
43
  if ((await adminPageContext()) === null) return null
40
44
 
45
+ const maintenanceOpen = (await searchParams).maintenance === '1'
41
46
  const now = new Date()
42
47
  const [translator, view, upgradeNotice] = await Promise.all([
43
48
  getTranslator(),
@@ -55,12 +60,19 @@ export default async function AdminSystemPage() {
55
60
  }
56
61
 
57
62
  const { mail, scheduler, volumes, legacyPasswordHashes } = view
63
+ const taskTitles = new Map(scheduler.tasks.map((task) => [task.key, task.titleKey]))
64
+ const taskCounts = Object.entries(TASK_STATUS_KEYS).map(([status, key]) => ({
65
+ status,
66
+ label: translator.t(key),
67
+ count: scheduler.tasks.filter((task) => task.status === status).length,
68
+ }))
58
69
 
59
70
  return (
60
71
  <PanelPage
61
72
  title={await tr('page.system-health')}
62
73
  lede={await tr('page.what-board-does-schedule-whether')}
63
- gap="loose"
74
+ width="wide"
75
+ gap="normal"
64
76
  >
65
77
  {scheduler.schedulerStopped && (
66
78
  <section
@@ -129,116 +141,6 @@ export default async function AdminSystemPage() {
129
141
  </section>
130
142
  )}
131
143
 
132
- <section className={PANEL_CARD}>
133
- <h2 className="font-heading text-lg font-semibold">{translator.t('adminSystem.mail')}</h2>
134
- <p className="text-sm">
135
- {mail.summary}
136
- {!mail.sends && (
137
- <span className="text-muted-foreground">
138
- {' '}
139
- {translator.t('adminSystem.mailDoesNotSend')}
140
- </span>
141
- )}{' '}
142
- {translator.t('adminSystem.activationMethod')} <code>{mail.activationMethod}</code>{' '}
143
- {translator.t('adminSystem.configuredFrom')}{' '}
144
- <code>
145
- {mail.source === 'environment'
146
- ? translator.t('adminSystem.environment')
147
- : translator.t('adminSystem.boardSettings')}
148
- </code>
149
- </p>
150
- {mail.source === 'board' && (
151
- <p className="text-sm text-muted-foreground">
152
- {translator.t('adminSystem.changeMailBefore')}{' '}
153
- <a href="/admin/settings?group=mail" className="underline">
154
- {translator.t('page.board-settings')}
155
- </a>
156
- {translator.t('adminSystem.changeMailEnd')}
157
- </p>
158
- )}
159
- <p className="text-sm text-muted-foreground">{translator.t('adminSystem.mailSchedule')}</p>
160
- </section>
161
-
162
- <section className={PANEL_CARD}>
163
- <h2 className="font-heading text-lg font-semibold">{await tr('page.scheduled-tasks')}</h2>
164
- {scheduler.tasks.length === 0 ? (
165
- <p className="text-sm text-muted-foreground">{translator.t('adminSystem.noTasks')}</p>
166
- ) : (
167
- <ul className="flex flex-col divide-y divide-border">
168
- {scheduler.tasks.map((task) => (
169
- <li
170
- key={task.key}
171
- className="flex flex-wrap items-center justify-between gap-x-3 gap-y-2 py-3 first:pt-0 last:pb-0"
172
- >
173
- <span className="flex min-w-0 flex-col">
174
- <span className="truncate text-sm font-medium">
175
- {task.titleKey === undefined ? task.key : translator.t(task.titleKey)}
176
- </span>
177
- {task.descriptionKey !== undefined && (
178
- <span className="text-xs text-muted-foreground">
179
- {translator.t(task.descriptionKey)}
180
- </span>
181
- )}
182
- <span className="truncate text-xs text-muted-foreground">
183
- <code>{task.key}</code> ·{' '}
184
- {translator.t('adminSystem.taskInterval', { seconds: task.intervalSeconds })} ·{' '}
185
- {task.lastRunAt === null
186
- ? translator.t('adminSystem.neverRun')
187
- : translator.t('adminSystem.taskLastRun', {
188
- time: formatTime(task.lastRunAt, now, translator).label,
189
- })}
190
- {task.consecutiveFailures > 0 &&
191
- ` · ${translator.t('adminSystem.taskFailures', {
192
- count: task.consecutiveFailures,
193
- })}`}
194
- </span>
195
- </span>
196
- <span
197
- className={
198
- task.status === 'healthy' ||
199
- task.status === 'running' ||
200
- task.status === 'disabled'
201
- ? 'shrink-0 text-xs text-muted-foreground'
202
- : 'shrink-0 text-xs font-medium text-destructive'
203
- }
204
- >
205
- {translator.t(TASK_STATUS_KEYS[task.status])}
206
- </span>
207
- </li>
208
- ))}
209
- </ul>
210
- )}
211
- </section>
212
-
213
- <section className={PANEL_CARD}>
214
- <h2 className="font-heading text-lg font-semibold">{await tr('page.recent-runs')}</h2>
215
- {view.runs.length === 0 ? (
216
- <p className="text-sm text-muted-foreground">{await tr('page.nothing-has-run-yet')}</p>
217
- ) : (
218
- <ul className="flex flex-col gap-1 text-sm">
219
- {view.runs.map((run, index) => (
220
- // biome-ignore lint/suspicious/noArrayIndexKey: the tiebreaker between two runs of one task recorded at the same instant
221
- <li key={`${run.taskKey}:${run.ranAt.toISOString()}:${index}`}>
222
- <code className="text-xs">{run.taskKey}</code>{' '}
223
- <span className="text-muted-foreground">
224
- {run.succeeded
225
- ? translator.t('adminSystem.taskRunOk')
226
- : translator.t('adminSystem.taskRunFailed')}
227
- {run.durationMs !== null && ` · ${run.durationMs}ms`} ·{' '}
228
- <time dateTime={run.ranAt.toISOString()}>
229
- {formatTime(run.ranAt, now, translator).label}
230
- </time>
231
- {run.detail !== null && ` · ${run.detail}`}
232
- </span>
233
- {run.error !== null && (
234
- <span className="block text-xs text-destructive">{run.error}</span>
235
- )}
236
- </li>
237
- ))}
238
- </ul>
239
- )}
240
- </section>
241
-
242
144
  <section className={PANEL_CARD}>
243
145
  <h2 className="font-heading text-lg font-semibold">
244
146
  {translator.t('adminSystem.volumes')}
@@ -255,65 +157,201 @@ export default async function AdminSystemPage() {
255
157
  </ul>
256
158
  </section>
257
159
 
258
- {legacyPasswordHashes > 0 && (
160
+ <div className="grid items-start gap-4 xl:grid-cols-2">
259
161
  <section className={PANEL_CARD}>
260
- <h2 className="font-heading text-lg font-semibold">
261
- {translator.t('adminSystem.legacyPasswords')}
262
- </h2>
162
+ <h2 className="font-heading text-lg font-semibold">{translator.t('adminSystem.mail')}</h2>
263
163
  <p className="text-sm">
264
- {translator.t('adminSystem.legacyPasswordsCount', { count: legacyPasswordHashes })}
164
+ {mail.summary}
165
+ {!mail.sends && (
166
+ <span className="text-muted-foreground">
167
+ {' '}
168
+ {translator.t('adminSystem.mailDoesNotSend')}
169
+ </span>
170
+ )}{' '}
171
+ {translator.t('adminSystem.activationMethod')} <code>{mail.activationMethod}</code>{' '}
172
+ {translator.t('adminSystem.configuredFrom')}{' '}
173
+ <code>
174
+ {mail.source === 'environment'
175
+ ? translator.t('adminSystem.environment')
176
+ : translator.t('adminSystem.boardSettings')}
177
+ </code>
265
178
  </p>
179
+ {mail.source === 'board' && (
180
+ <p className="text-sm text-muted-foreground">
181
+ {translator.t('adminSystem.changeMailBefore')}{' '}
182
+ <a href="/admin/settings?group=mail" className="underline">
183
+ {translator.t('page.board-settings')}
184
+ </a>
185
+ {translator.t('adminSystem.changeMailEnd')}
186
+ </p>
187
+ )}
266
188
  <p className="text-sm text-muted-foreground">
267
- {translator.t('adminSystem.legacyPasswordsHint')}
189
+ {translator.t('adminSystem.mailSchedule')}
268
190
  </p>
269
191
  </section>
270
- )}
271
192
 
272
- <section className={PANEL_CARD}>
273
- <h2 className="font-heading text-lg font-semibold">
274
- {translator.t('adminSystem.migrations')}
275
- </h2>
276
- <p className="text-sm">
277
- {translator.t('adminSystem.runningVersion')} <code>{CODE_VERSION}</code>.
278
- </p>
279
- {upgradeNotice === null ? (
280
- <p className="text-sm text-muted-foreground">
281
- {translator.t('adminSystem.migrationsUpToDate')}
193
+ <section className={PANEL_CARD}>
194
+ <h2 className="font-heading text-lg font-semibold">
195
+ {translator.t('adminSystem.migrations')}
196
+ </h2>
197
+ <p className="text-sm">
198
+ {translator.t('adminSystem.runningVersion')} <code>{CODE_VERSION}</code>.
282
199
  </p>
283
- ) : (
284
- <>
285
- <p className="text-sm text-destructive">{upgradeNotice}</p>
200
+ {upgradeNotice === null ? (
286
201
  <p className="text-sm text-muted-foreground">
287
- {translator.t('adminSystem.migrationsHint')}
202
+ {translator.t('adminSystem.migrationsUpToDate')}
288
203
  </p>
289
- <ApplyMigrationsForm copy={copy} />
290
- </>
291
- )}
292
- </section>
204
+ ) : (
205
+ <>
206
+ <p className="text-sm text-destructive">{upgradeNotice}</p>
207
+ <p className="text-sm text-muted-foreground">
208
+ {translator.t('adminSystem.migrationsHint')}
209
+ </p>
210
+ <ApplyMigrationsForm copy={copy} />
211
+ </>
212
+ )}
213
+ </section>
214
+ </div>
293
215
 
294
216
  <section className={PANEL_CARD}>
295
- <h2 className="font-heading text-lg font-semibold">
296
- {translator.t('adminSystem.recount')}
297
- </h2>
298
- <p className="text-sm text-muted-foreground">{translator.t('adminSystem.recountHint')}</p>
299
- {view.recount.length > 0 && (
300
- <ul className="flex flex-col gap-1 text-xs text-muted-foreground">
301
- {view.recount.map((row) => (
302
- <li key={row.id}>
303
- {translator.t('adminSystem.recountProgress', {
304
- id: row.id,
305
- phase: row.phase,
306
- cursor: row.cursor,
307
- passes: row.passes,
308
- corrected: row.corrected,
309
- })}
217
+ <h2 className="font-heading text-lg font-semibold">{await tr('page.scheduled-tasks')}</h2>
218
+ <p className="text-sm text-muted-foreground">
219
+ {translator.t('adminSystem.taskCount', { count: scheduler.tasks.length })}
220
+ </p>
221
+ <ul className="flex flex-wrap gap-x-4 gap-y-2 text-sm">
222
+ {taskCounts
223
+ .filter(({ count }) => count > 0)
224
+ .map(({ status, label, count }) => (
225
+ <li
226
+ key={status}
227
+ className={
228
+ status === 'stale' || status === 'failing'
229
+ ? 'font-medium text-destructive'
230
+ : 'text-muted-foreground'
231
+ }
232
+ >
233
+ {translator.t('adminSystem.tasksInState', { count, status: label })}
310
234
  </li>
311
235
  ))}
312
- </ul>
313
- )}
314
- <RecountForm copy={copy} />
236
+ </ul>
237
+ <details open={scheduler.stale > 0 || scheduler.failing > 0}>
238
+ <summary className="cursor-pointer text-sm font-medium">
239
+ {translator.t('adminSystem.showTasks')}
240
+ </summary>
241
+ {scheduler.tasks.length === 0 ? (
242
+ <p className="text-sm text-muted-foreground">{translator.t('adminSystem.noTasks')}</p>
243
+ ) : (
244
+ <ul className="grid gap-x-6 sm:grid-cols-2">
245
+ {scheduler.tasks.map((task) => (
246
+ <li
247
+ key={task.key}
248
+ className="flex min-w-0 items-start justify-between gap-3 border-t border-border py-3"
249
+ >
250
+ <div className="flex min-w-0 flex-1 flex-col gap-1">
251
+ <span className="text-sm font-medium [overflow-wrap:anywhere]">
252
+ {task.titleKey === undefined ? task.key : translator.t(task.titleKey)}
253
+ </span>
254
+ <span className="text-xs text-muted-foreground">
255
+ {translator.t('adminSystem.taskInterval', { seconds: task.intervalSeconds })}{' '}
256
+ ·{' '}
257
+ {task.lastRunAt === null
258
+ ? translator.t('adminSystem.neverRun')
259
+ : translator.t('adminSystem.taskLastRun', {
260
+ time: formatTime(task.lastRunAt, now, translator).label,
261
+ })}
262
+ {task.consecutiveFailures > 0 &&
263
+ ` · ${translator.t('adminSystem.taskFailures', {
264
+ count: task.consecutiveFailures,
265
+ })}`}
266
+ </span>
267
+ <details className="text-xs text-muted-foreground">
268
+ <summary className="cursor-pointer">
269
+ {translator.t('adminSystem.taskDetails')}
270
+ </summary>
271
+ <div className="mt-2 flex flex-col gap-1 [overflow-wrap:anywhere]">
272
+ <code>{task.key}</code>
273
+ {task.descriptionKey !== undefined && (
274
+ <p>{translator.t(task.descriptionKey)}</p>
275
+ )}
276
+ </div>
277
+ </details>
278
+ </div>
279
+ <span
280
+ className={
281
+ task.status === 'healthy' ||
282
+ task.status === 'running' ||
283
+ task.status === 'disabled'
284
+ ? 'shrink-0 text-xs text-muted-foreground'
285
+ : 'shrink-0 text-xs font-medium text-destructive'
286
+ }
287
+ >
288
+ {translator.t(TASK_STATUS_KEYS[task.status])}
289
+ </span>
290
+ </li>
291
+ ))}
292
+ </ul>
293
+ )}
294
+ </details>
315
295
  </section>
316
296
 
297
+ <section className={PANEL_CARD}>
298
+ <h2 className="font-heading text-lg font-semibold">{await tr('page.recent-runs')}</h2>
299
+ <details open={view.runs.some((run) => !run.succeeded)}>
300
+ <summary className="cursor-pointer text-sm font-medium">
301
+ {translator.t('adminSystem.showRuns')}
302
+ <span className="ml-2 font-normal text-muted-foreground">
303
+ {translator.t('adminSystem.runCount', { count: view.runs.length })}
304
+ </span>
305
+ </summary>
306
+ {view.runs.length === 0 ? (
307
+ <p className="text-sm text-muted-foreground">{await tr('page.nothing-has-run-yet')}</p>
308
+ ) : (
309
+ <ul className="mt-3 flex flex-col divide-y divide-border text-sm">
310
+ {view.runs.map((run, index) => (
311
+ <li
312
+ // biome-ignore lint/suspicious/noArrayIndexKey: the tiebreaker between two runs of one task recorded at the same instant
313
+ key={`${run.taskKey}:${run.ranAt.toISOString()}:${index}`}
314
+ className="min-w-0 py-3 [overflow-wrap:anywhere]"
315
+ >
316
+ <span className="font-medium">
317
+ {taskTitles.get(run.taskKey) === undefined
318
+ ? run.taskKey
319
+ : translator.t(taskTitles.get(run.taskKey)!)}
320
+ </span>{' '}
321
+ <span className="text-muted-foreground">
322
+ {run.succeeded
323
+ ? translator.t('adminSystem.taskRunOk')
324
+ : translator.t('adminSystem.taskRunFailed')}
325
+ {run.durationMs !== null && ` · ${run.durationMs}ms`} ·{' '}
326
+ <time dateTime={run.ranAt.toISOString()}>
327
+ {formatTime(run.ranAt, now, translator).label}
328
+ </time>
329
+ </span>
330
+ <SystemRunDetails detail={run.detail} translator={translator} />
331
+ {run.error !== null && (
332
+ <span className="block text-xs text-destructive">{run.error}</span>
333
+ )}
334
+ </li>
335
+ ))}
336
+ </ul>
337
+ )}
338
+ </details>
339
+ </section>
340
+
341
+ {legacyPasswordHashes > 0 && (
342
+ <section className={PANEL_CARD}>
343
+ <h2 className="font-heading text-lg font-semibold">
344
+ {translator.t('adminSystem.legacyPasswords')}
345
+ </h2>
346
+ <p className="text-sm">
347
+ {translator.t('adminSystem.legacyPasswordsCount', { count: legacyPasswordHashes })}
348
+ </p>
349
+ <p className="text-sm text-muted-foreground">
350
+ {translator.t('adminSystem.legacyPasswordsHint')}
351
+ </p>
352
+ </section>
353
+ )}
354
+
317
355
  <section className={PANEL_CARD}>
318
356
  <h2 className="font-heading text-lg font-semibold">{await tr('page.search-index')}</h2>
319
357
  <p className="text-sm text-muted-foreground">
@@ -332,18 +370,57 @@ export default async function AdminSystemPage() {
332
370
  <ReindexSearchForm pending={view.searchIndex.pending} copy={copy} />
333
371
  </section>
334
372
 
335
- <section className={cn(PANEL_CARD, 'gap-4')}>
373
+ <section id="maintenance" className={PANEL_CARD}>
336
374
  <h2 className="font-heading text-lg font-semibold">
337
375
  {translator.t('adminSystem.maintenance')}
338
376
  </h2>
339
- <p className="text-sm text-muted-foreground">
340
- {translator.t('adminSystem.maintenanceHint')}
341
- </p>
377
+ <a
378
+ href={
379
+ maintenanceOpen
380
+ ? '/admin/system#maintenance'
381
+ : '/admin/system?maintenance=1#maintenance'
382
+ }
383
+ className="w-fit text-sm font-medium underline underline-offset-4"
384
+ >
385
+ {translator.t(
386
+ maintenanceOpen ? 'adminSystem.closeMaintenance' : 'adminSystem.openMaintenance',
387
+ )}
388
+ </a>
389
+ {maintenanceOpen && (
390
+ <div className="flex flex-col gap-6">
391
+ <section className="flex flex-col gap-3">
392
+ <h3 className="font-heading font-semibold">{translator.t('adminSystem.recount')}</h3>
393
+ <p className="text-sm text-muted-foreground">
394
+ {translator.t('adminSystem.recountHint')}
395
+ </p>
396
+ {view.recount.length > 0 && (
397
+ <ul className="flex flex-col gap-1 text-xs text-muted-foreground">
398
+ {view.recount.map((row) => (
399
+ <li key={row.id}>
400
+ {translator.t('adminSystem.recountProgress', {
401
+ id: row.id,
402
+ phase: row.phase,
403
+ cursor: row.cursor,
404
+ passes: row.passes,
405
+ corrected: row.corrected,
406
+ })}
407
+ </li>
408
+ ))}
409
+ </ul>
410
+ )}
411
+ <RecountForm copy={copy} />
412
+ </section>
413
+
414
+ <p className="text-sm text-muted-foreground">
415
+ {translator.t('adminSystem.maintenanceHint')}
416
+ </p>
342
417
 
343
- <PruneSessionsForm prunable={view.prunableSessions} copy={copy} />
344
- <PruneTokensForm copy={copy} />
345
- <ClearCacheForm copy={copy} />
346
- <RetryJobForm copy={copy} />
418
+ <PruneSessionsForm prunable={view.prunableSessions} copy={copy} />
419
+ <PruneTokensForm copy={copy} />
420
+ <ClearCacheForm copy={copy} />
421
+ <RetryJobForm copy={copy} />
422
+ </div>
423
+ )}
347
424
  </section>
348
425
  </PanelPage>
349
426
  )
@@ -0,0 +1,30 @@
1
+ import type { Metadata } from 'next'
2
+ import { notFound } from 'next/navigation'
3
+
4
+ import { env } from '@meith/core'
5
+ import { isSlotName } from '@meith/theme-kit'
6
+
7
+ import { getTranslator } from '@/server/i18n'
8
+ import { FixtureGallery } from '@/theme/gallery.fixture'
9
+ import { fixtureVariants } from '@/theme/preview.fixture'
10
+
11
+ export async function generateMetadata(): Promise<Metadata> {
12
+ return {
13
+ title: (await getTranslator()).t('nav.themeFixtures'),
14
+ robots: { index: false, follow: false },
15
+ }
16
+ }
17
+
18
+ export default async function FixturesPage({
19
+ searchParams,
20
+ }: {
21
+ searchParams: Promise<Record<string, string | string[] | undefined>>
22
+ }) {
23
+ if (env.DATA_SOURCE !== 'fixture') notFound()
24
+ const query = await searchParams
25
+ const name = query.slot ?? 'BoardIndex'
26
+ if (typeof name !== 'string' || !isSlotName(name)) notFound()
27
+ const variant = query.variant ?? 'default'
28
+ if (typeof variant !== 'string' || !fixtureVariants(name).includes(variant)) notFound()
29
+ return <FixtureGallery name={name} variant={variant} />
30
+ }
package/app/layout.tsx CHANGED
@@ -9,7 +9,6 @@ import { NavTabsEnhancer } from '@meith/ui/nav-tabs-enhancer'
9
9
 
10
10
  import { CopyProvider } from '@/components/shell/copy'
11
11
  import { CrashNoticeProvider } from '@/components/shell/crash-notice'
12
- import { DemoBanner } from '@/components/shell/demo-banner'
13
12
  import { GroupNameStyle } from '@/components/shell/group-name-style'
14
13
  import { ServiceWorkerRegistrar } from '@/components/shell/service-worker'
15
14
  import { ThemeRuntimeStyle } from '@/components/shell/theme-runtime-style'
@@ -110,7 +109,6 @@ export default async function RootLayout({
110
109
  </head>
111
110
  <body className="font-sans antialiased">
112
111
  <NavTabsEnhancer />
113
- <DemoBanner />
114
112
  <CrashNoticeProvider
115
113
  notice={notice}
116
114
  requestId={currentRequestId() ?? null}
package/next.config.mjs CHANGED
@@ -63,7 +63,6 @@ const nextConfig = {
63
63
  '@meith/board-digest',
64
64
  '@meith/core',
65
65
  '@meith/db',
66
- '@meith/demo',
67
66
  '@meith/drafts',
68
67
  '@meith/drivers',
69
68
  '@meith/events',
@@ -78,6 +77,7 @@ const nextConfig = {
78
77
  '@meith/messages',
79
78
  '@meith/moderation',
80
79
  '@meith/notifications',
80
+ '@meith/plugin-awards',
81
81
  '@meith/plugin-calendar',
82
82
  '@meith/plugin-dues',
83
83
  '@meith/plugin-kit',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meith/web",
3
- "version": "0.36.1",
3
+ "version": "0.37.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,55 +43,55 @@
43
43
  "react-dom": "19.2.8",
44
44
  "tailwindcss": "^4.3.3",
45
45
  "typescript": "7.0.2",
46
- "@meith/accounts": "0.36.1",
47
- "@meith/admin": "0.36.1",
48
- "@meith/antispam": "0.36.1",
49
- "@meith/api": "0.36.1",
50
- "@meith/attachments": "0.36.1",
51
- "@meith/authorization": "0.36.1",
52
- "@meith/avatars": "0.36.1",
53
- "@meith/backup": "0.36.1",
54
- "@meith/board-digest": "0.36.1",
55
- "@meith/core": "0.36.1",
56
- "@meith/db": "0.36.1",
57
- "@meith/demo": "0.36.1",
58
- "@meith/drafts": "0.36.1",
59
- "@meith/drivers": "0.36.1",
60
- "@meith/events": "0.36.1",
61
- "@meith/forums": "0.36.1",
62
- "@meith/groups": "0.36.1",
63
- "@meith/i18n": "0.36.1",
64
- "@meith/import": "0.36.1",
65
- "@meith/install": "0.36.1",
66
- "@meith/mail": "0.36.1",
67
- "@meith/markdown": "0.36.1",
68
- "@meith/marketplace": "0.36.1",
69
- "@meith/messages": "0.36.1",
70
- "@meith/moderation": "0.36.1",
71
- "@meith/notifications": "0.36.1",
72
- "@meith/plugin-calendar": "0.36.1",
73
- "@meith/plugin-dues": "0.36.1",
74
- "@meith/plugin-kit": "0.36.1",
75
- "@meith/polls": "0.36.1",
76
- "@meith/posts": "0.36.1",
77
- "@meith/profile-fields": "0.36.1",
78
- "@meith/relations": "0.36.1",
79
- "@meith/reputation": "0.36.1",
80
- "@meith/runtime": "0.36.1",
81
- "@meith/search": "0.36.1",
82
- "@meith/settings": "0.36.1",
83
- "@meith/signatures": "0.36.1",
84
- "@meith/subscriptions": "0.36.1",
85
- "@meith/tasks": "0.36.1",
86
- "@meith/theme-clubhouse": "0.36.1",
87
- "@meith/theme-default": "0.36.1",
88
- "@meith/theme-kit": "0.36.1",
89
- "@meith/theme-midnight": "0.36.1",
90
- "@meith/theme-phasebook": "0.36.1",
91
- "@meith/theme-raidframe": "0.36.1",
92
- "@meith/threads": "0.36.1",
93
- "@meith/ui": "0.36.1",
94
- "@meith/upgrade": "0.36.1"
46
+ "@meith/accounts": "0.37.0",
47
+ "@meith/admin": "0.37.0",
48
+ "@meith/antispam": "0.37.0",
49
+ "@meith/api": "0.37.0",
50
+ "@meith/attachments": "0.37.0",
51
+ "@meith/authorization": "0.37.0",
52
+ "@meith/avatars": "0.37.0",
53
+ "@meith/backup": "0.37.0",
54
+ "@meith/board-digest": "0.37.0",
55
+ "@meith/core": "0.37.0",
56
+ "@meith/db": "0.37.0",
57
+ "@meith/drafts": "0.37.0",
58
+ "@meith/drivers": "0.37.0",
59
+ "@meith/events": "0.37.0",
60
+ "@meith/forums": "0.37.0",
61
+ "@meith/i18n": "0.37.0",
62
+ "@meith/groups": "0.37.0",
63
+ "@meith/import": "0.37.0",
64
+ "@meith/install": "0.37.0",
65
+ "@meith/mail": "0.37.0",
66
+ "@meith/markdown": "0.37.0",
67
+ "@meith/marketplace": "0.37.0",
68
+ "@meith/messages": "0.37.0",
69
+ "@meith/moderation": "0.37.0",
70
+ "@meith/notifications": "0.37.0",
71
+ "@meith/plugin-awards": "0.37.0",
72
+ "@meith/plugin-calendar": "0.37.0",
73
+ "@meith/plugin-dues": "0.37.0",
74
+ "@meith/plugin-kit": "0.37.0",
75
+ "@meith/polls": "0.37.0",
76
+ "@meith/posts": "0.37.0",
77
+ "@meith/profile-fields": "0.37.0",
78
+ "@meith/relations": "0.37.0",
79
+ "@meith/reputation": "0.37.0",
80
+ "@meith/runtime": "0.37.0",
81
+ "@meith/search": "0.37.0",
82
+ "@meith/settings": "0.37.0",
83
+ "@meith/signatures": "0.37.0",
84
+ "@meith/subscriptions": "0.37.0",
85
+ "@meith/theme-clubhouse": "0.37.0",
86
+ "@meith/tasks": "0.37.0",
87
+ "@meith/theme-default": "0.37.0",
88
+ "@meith/theme-kit": "0.37.0",
89
+ "@meith/theme-midnight": "0.37.0",
90
+ "@meith/theme-phasebook": "0.37.0",
91
+ "@meith/theme-raidframe": "0.37.0",
92
+ "@meith/threads": "0.37.0",
93
+ "@meith/ui": "0.37.0",
94
+ "@meith/upgrade": "0.37.0"
95
95
  },
96
96
  "scripts": {
97
97
  "dev": "next dev",