Files
ServerKit/frontend/src/components/EmptyState.jsx
T
Juan DenisandClaude Opus 5 33410950e8 chore(i18n): RTL plumbing ratchet; keep the bare t off the Vite-only path
Plan 79 §H + ratchet 7.

<html dir> already follows the language manifest and the locale probe asserts
it. What was missing is a stylesheet that could follow it: 468
direction-anchored declarations (margin/padding/border-left|right, bare
left:/right:, text-align and float) against 5 logical properties. An RTL
locale today would render a mirrored language inside an unmirrored layout, so
the plan ships plumbing and a ratchet, not a locale. The count is 468 rather
than the plan's grepped 412 because float and text-align are equally anchored.

Also splits `src/i18n/t.js` out of the i18n barrel. The API client's error
mapper imports the bare `t`, and the client is reachable from a node test --
which then loaded i18n/index.js and its `import.meta.glob`, a Vite
compile-time transform that throws under plain node. The obvious fix was the
runtime guard that had just been removed for silently discarding every locale,
so the module is split instead: t.js touches only the i18next singleton, and
index.js keeps initialisation and the glob for browser-only callers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 00:04:29 -04:00

50 lines
1.6 KiB
React

import { Inbox } from 'lucide-react';
import { PageSkeleton } from './PageSkeleton';
import { t } from '../i18n/t';
/**
* `loadingVariant` names the shape of the page that is loading — see
* PageSkeleton for the archetypes (table, feed, cards, detail, console, form,
* chart, tree, split). It defaults to the generic `panel`, which is a safe
* fallback but predicts nothing: prefer naming the real shape, so the skeleton
* resolves into the content instead of being replaced by it.
*/
export default function EmptyState({
icon: Icon = Inbox,
title = 'No items found',
description = '',
action = null,
size = 'default',
loading = false,
loadingVariant = 'panel',
loadingRows,
}) {
if (loading) {
return (
<div
className={`empty-state empty-state--${size} empty-state--loading`}
role="status"
aria-busy="true"
aria-label={title || t('common.state.loading', 'Loading')}
>
<PageSkeleton variant={loadingVariant} rows={loadingRows} />
</div>
);
}
return (
<div className={`empty-state empty-state--${size}`}>
<div className="empty-state__icon">
<Icon size={size === 'lg' ? 64 : 48} />
</div>
<h3 className="empty-state__title">{title}</h3>
{description && (
<p className="empty-state__description">{description}</p>
)}
{action && (
<div className="empty-state__action">{action}</div>
)}
</div>
);
}