dorfteich/apps/web/src/links/BacklinksPanel.tsx
Claude Opus 4.8 6c38abc20c
All checks were successful
CD / Build and push images (push) Successful in 3m3s
CI / Lint, typecheck, test (push) Successful in 2m15s
CI / Auth e2e pack (push) Successful in 2m49s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m12s
CD / Promote to Int (push) Successful in 11s
Add backlinks panel and phantom-pages view (#48)
Make wikilink relations visible: what links here, and which linked pages
do not exist yet.

- shared: `BacklinkView` gains a plain-text `snippet` for context.
- api: `LinksService` includes a short snippet (from the content cache) with
  each backlink and phantom referrer.
- web:
  - `BacklinksPanel` below a page in read mode: a collapsible "Linked from"
    list (title + snippet, links to the source), hidden when empty. Appears
    on load from the #47 index.
  - `PhantomPagesView` in pond settings: wikilink targets that do not exist
    yet, each with its referrers and a create shortcut that makes the page
    under the phantom slug — resolving those links (#47) and navigating to it.
  - i18n `links` namespace (de + en); backlinks + missing-pages styles.
- e2e `backlinks.spec.ts` (new CI pack): a link created in the editor appears
  as a backlink on the target; the missing-pages view lists a phantom slug and
  creating it navigates to the new page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGdhRiwU1WRL4XxJfZYipY
2026-07-09 13:05:32 +02:00

46 lines
1.5 KiB
TypeScript

import type { BacklinkView } from '@dorfteich/shared';
import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { apiGet } from '../lib/api';
/**
* "Linked from" panel below a page in read mode (issue #48): the pages that
* wikilink here, each with a short snippet, from the server-maintained index
* (#47). Collapsible; hidden entirely when nothing links here so it adds no
* noise. Backlinks are already permission-filtered by the api.
*/
export function BacklinksPanel({
pageId,
pondSlug,
}: {
pageId: string;
pondSlug: string;
}): React.JSX.Element | null {
const { t } = useTranslation('links');
const backlinks = useQuery({
queryKey: ['backlinks', pageId],
queryFn: () => apiGet<BacklinkView[]>(`/pages/${pageId}/backlinks`),
});
// Nothing to show until loaded; stay invisible when there are no backlinks.
if (!backlinks.data || backlinks.data.length === 0) return null;
return (
<details className="backlinks" open>
<summary>{t('backlinks.toggle', { count: backlinks.data.length })}</summary>
<ul className="backlinks__list">
{backlinks.data.map((link) => (
<li key={link.pageId} className="backlinks__item">
<Link to={`/p/${pondSlug}/${link.slug}`} className="backlinks__link">
{link.title}
</Link>
{link.snippet && <p className="backlinks__snippet">{link.snippet}</p>}
</li>
))}
</ul>
</details>
);
}