Explain the point to whoever opens the admin
The overview leads with what Logbuch is for, a new Anleitung page renders the editorial guide for humans, and the user list stops claiming accounts come from a shell command: they appear on first sign in.
This commit is contained in:
+14
-4
@@ -274,7 +274,8 @@
|
||||
"manage": "Verwalten",
|
||||
"postTypes": "Beitragsarten",
|
||||
"api": "API",
|
||||
"settings": "Einstellungen"
|
||||
"settings": "Einstellungen",
|
||||
"guide": "Anleitung"
|
||||
},
|
||||
"messages": {
|
||||
"created": "Angelegt.",
|
||||
@@ -635,9 +636,9 @@
|
||||
"back": "Zurück zur Liste",
|
||||
"saving": "Wird gespeichert",
|
||||
"emptyTitle": "Noch kein Konto",
|
||||
"empty": "Es gibt noch kein Konto. Leg eins mit dem Befehl pnpm admin:create an.",
|
||||
"empty": "Es gibt noch kein Konto. Sobald sich jemand zum ersten Mal anmeldet, erscheint er hier.",
|
||||
"newAccountTitle": "Neues Konto",
|
||||
"newAccount": "Konten entstehen über den Befehl pnpm admin:create. Danach vergibst du hier die Rechte.",
|
||||
"newAccount": "Konten entstehen bei der ersten Anmeldung. Wer eine Adresse auf einer erlaubten Domain hat, holt sich unter Anmelden selbst einen Link per E-Mail. Danach vergibst du hier die Rechte.",
|
||||
"adminSection": "Verwaltungsrecht",
|
||||
"isAdmin": "Dieses Konto verwaltet alles und hat Zugriff auf jedes Projekt.",
|
||||
"isNotAdmin": "Dieses Konto arbeitet nur in den Projekten, für die es freigeschaltet ist.",
|
||||
@@ -779,7 +780,16 @@
|
||||
"neverSent": "Noch nichts verschickt. Der erste Bericht umfasst die letzten sieben Tage.",
|
||||
"resetWindow": "Zeitraum zurücksetzen",
|
||||
"resetting": "Wird zurückgesetzt"
|
||||
}
|
||||
},
|
||||
"guide": {
|
||||
"eyebrow": "Verwaltung",
|
||||
"title": "Anleitung",
|
||||
"intro": "Wofür Logbuch da ist, wie ein Eintrag aufgebaut ist und wie er klingen soll. Derselbe Text, den auch ein KI-Zugang über die Schnittstelle liest.",
|
||||
"missing": "Der Leitfaden ist nicht auffindbar."
|
||||
},
|
||||
"welcomeTitle": "Worum es hier geht",
|
||||
"welcome": "Logbuch hält fest, was in den Produkten passiert ist. Ein Eintrag sagt in ganzen Sätzen, was sich geändert hat und was der Leser davon hat, ohne Ticketnummern und ohne Fachjargon. Geschrieben wird pro Projekt, veröffentlicht wird von Hand.",
|
||||
"welcomeAction": "Anleitung lesen"
|
||||
},
|
||||
"filter": {
|
||||
"type": "Art",
|
||||
|
||||
+14
-4
@@ -274,7 +274,8 @@
|
||||
"manage": "Manage",
|
||||
"postTypes": "Post types",
|
||||
"api": "API",
|
||||
"settings": "Settings"
|
||||
"settings": "Settings",
|
||||
"guide": "Guide"
|
||||
},
|
||||
"messages": {
|
||||
"created": "Created.",
|
||||
@@ -635,9 +636,9 @@
|
||||
"back": "Back to the list",
|
||||
"saving": "Saving",
|
||||
"emptyTitle": "No account yet",
|
||||
"empty": "There is no account yet. Create one with the command pnpm admin:create.",
|
||||
"empty": "No account yet. As soon as someone signs in for the first time, they show up here.",
|
||||
"newAccountTitle": "New account",
|
||||
"newAccount": "Accounts are created with the command pnpm admin:create. After that you hand out the rights here.",
|
||||
"newAccount": "Accounts appear on the first sign in. Anyone with an address on an allowed domain requests a link themselves. You grant the rights here afterwards.",
|
||||
"adminSection": "Administration right",
|
||||
"isAdmin": "This account administers everything and reaches every project.",
|
||||
"isNotAdmin": "This account works only in the projects it has been released for.",
|
||||
@@ -779,7 +780,16 @@
|
||||
"neverSent": "Nothing sent yet. The first digest covers the last seven days.",
|
||||
"resetWindow": "Reset the window",
|
||||
"resetting": "Resetting"
|
||||
}
|
||||
},
|
||||
"guide": {
|
||||
"eyebrow": "Administration",
|
||||
"title": "Guide",
|
||||
"intro": "What Logbuch is for, how an entry is built and how it should sound. The same text an API client reads.",
|
||||
"missing": "The guide could not be found."
|
||||
},
|
||||
"welcomeTitle": "What this is for",
|
||||
"welcome": "Logbuch records what happened in the products. An entry says in plain sentences what changed and what the reader gets from it, without ticket numbers or jargon. Writing happens per project, publishing by hand.",
|
||||
"welcomeAction": "Read the guide"
|
||||
},
|
||||
"filter": {
|
||||
"type": "Type",
|
||||
|
||||
@@ -14,6 +14,7 @@ const config: NextConfig = {
|
||||
'/api/v1/skill': ['./docs/skill.md'],
|
||||
'/admin/api/files/skill': ['./docs/skill.md'],
|
||||
'/admin/api/files/style-guide': ['./docs/style-guide.md'],
|
||||
'/admin/guide': ['./docs/style-guide.md'],
|
||||
'/': ['./node_modules/drizzle-orm/**'],
|
||||
'/api/v1/media': sharpNative,
|
||||
'/admin/media': sharpNative,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Metadata } from 'next'
|
||||
import Markdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { getTranslations } from 'next-intl/server'
|
||||
import { AdminHeading } from '~/components/admin/AdminHeading'
|
||||
import { AdminNotice } from '~/components/admin/AdminNotice'
|
||||
import { readStyleGuide } from '~/lib/api-docs'
|
||||
import { requireAdminArea } from '~/lib/auth-guards'
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const app = await getTranslations('app')
|
||||
const t = await getTranslations('admin.guide')
|
||||
|
||||
return { title: `${t('title')} - ${app('name')}` }
|
||||
}
|
||||
|
||||
export default async function AdminGuidePage() {
|
||||
await requireAdminArea()
|
||||
|
||||
const t = await getTranslations('admin.guide')
|
||||
const text = await readStyleGuide()
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-10 pt-12">
|
||||
<AdminHeading eyebrow={t('eyebrow')} title={t('title')} intro={t('intro')} />
|
||||
|
||||
{text === null ? (
|
||||
<AdminNotice tone="alert">{t('missing')}</AdminNotice>
|
||||
) : (
|
||||
<div className="prose-logbuch max-w-3xl">
|
||||
<Markdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
h1: () => null,
|
||||
h2: props => <h3 {...props} />,
|
||||
a: props => <a {...props} rel="noreferrer" />,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Markdown>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
adminNewEntryPath,
|
||||
adminEntriesPath,
|
||||
adminEntryPath,
|
||||
adminGuidePath,
|
||||
adminNewProjectPath,
|
||||
adminProjectEntriesPath,
|
||||
adminProjectPath,
|
||||
@@ -92,6 +93,12 @@ export default async function AdminPage() {
|
||||
}
|
||||
/>
|
||||
|
||||
<AdminNotice title={t('welcomeTitle')} action={
|
||||
<Link href={adminGuidePath} className={quietLinkClass}>{t('welcomeAction')}</Link>
|
||||
}>
|
||||
{t('welcome')}
|
||||
</AdminNotice>
|
||||
|
||||
{tiles.length > 0 ? (
|
||||
<ul className="m-0 grid list-none grid-cols-2 gap-px border border-rule bg-rule p-0 md:grid-cols-4 xl:grid-cols-7">
|
||||
{tiles.map(tile => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Link from 'next/link'
|
||||
import { getTranslations } from 'next-intl/server'
|
||||
import { TbAddressBook, TbArrowNarrowLeft, TbCode, TbKey, TbLayoutGrid, TbNotebook, TbPhoto, TbSettings, TbStack2, TbTags, TbUsers } from 'react-icons/tb'
|
||||
import { TbAddressBook, TbArrowNarrowLeft, TbBook, TbCode, TbKey, TbLayoutGrid, TbNotebook, TbPhoto, TbSettings, TbStack2, TbTags, TbUsers } from 'react-icons/tb'
|
||||
import { NavLink } from './NavLink'
|
||||
import { Wordmark } from './Wordmark'
|
||||
import { Scroller } from '~/components/ui/Scroller'
|
||||
@@ -11,6 +11,7 @@ import { canOpenAdmin, isAdmin, type Viewer } from '~/lib/auth-access'
|
||||
import { adminMediaPath, adminPath } from '~/lib/auth-routes'
|
||||
import {
|
||||
adminBrandsPath,
|
||||
adminGuidePath,
|
||||
adminClientsPath,
|
||||
adminEntriesPath,
|
||||
adminPostTypesPath,
|
||||
@@ -44,6 +45,7 @@ export async function AdminNav({ viewer, number }: AdminNavProps) {
|
||||
own.push({ href: adminPath, label: t('nav.overview'), icon: <TbLayoutGrid className="size-4" />, exact: true })
|
||||
own.push({ href: adminEntriesPath, label: t('nav.posts'), icon: <TbNotebook className="size-4" /> })
|
||||
own.push({ href: adminMediaPath, label: t('nav.media'), icon: <TbPhoto className="size-4" /> })
|
||||
own.push({ href: adminGuidePath, label: t('nav.guide'), icon: <TbBook className="size-4" /> })
|
||||
}
|
||||
|
||||
if (isAdmin(viewer)) {
|
||||
|
||||
@@ -11,6 +11,7 @@ export const adminUsersPath = '/admin/users' as Route
|
||||
export const adminClientsPath = '/admin/clients' as Route
|
||||
export const adminPostTypesPath = '/admin/post-types' as Route
|
||||
export const adminSettingsPath = '/admin/settings' as Route
|
||||
export const adminGuidePath = '/admin/guide' as Route
|
||||
export const adminNewPostTypePath = '/admin/post-types/new' as Route
|
||||
|
||||
export function adminApiFilePath(file: 'openapi' | 'style-guide' | 'skill'): Route {
|
||||
|
||||
Reference in New Issue
Block a user