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
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
46 lines
1.5 KiB
TypeScript
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>
|
|
);
|
|
}
|