Restructures frontend to a feature-based folder layout
Continuous Integration / config (pull_request) Successful in 11s
Continuous Integration / changes (pull_request) Successful in 21s
Continuous Integration / backend-build (pull_request) Skipped
Continuous Integration / backend-test (pull_request) Skipped
Continuous Integration / vulnerability-scan (pull_request) Skipped
Continuous Integration / frontend-prepare (pull_request) Successful in 1m37s
Continuous Integration / frontend-build (pull_request) Successful in 2m12s
Continuous Integration / frontend-test (pull_request) Successful in 4m42s
Continuous Integration / frontend-lint (pull_request) Successful in 2m2s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 6m51s
Continuous Integration / deploy-test (pull_request) Skipped
Continuous Integration / config (pull_request) Successful in 11s
Continuous Integration / changes (pull_request) Successful in 21s
Continuous Integration / backend-build (pull_request) Skipped
Continuous Integration / backend-test (pull_request) Skipped
Continuous Integration / vulnerability-scan (pull_request) Skipped
Continuous Integration / frontend-prepare (pull_request) Successful in 1m37s
Continuous Integration / frontend-build (pull_request) Successful in 2m12s
Continuous Integration / frontend-test (pull_request) Successful in 4m42s
Continuous Integration / frontend-lint (pull_request) Successful in 2m2s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 6m51s
Continuous Integration / deploy-test (pull_request) Skipped
Groups auth, setup, invitation, profile, users, cms, availability and system code (services/hooks, components, schemas, mocks, pages) under src/features/<name> instead of splitting by technical layer (api/, components/, lib/schemas/, mocks/, pages/). Renames the old connection-oriented `api` layer to `services` per feature, and splits the monolithic api/types.ts into per-feature types.ts files (with ProblemDetails/ApiResult merged into lib/api-client.ts as shared infra). Layout-agnostic code (ui primitives, app shell, i18n, test utils, lib) stays at the top level. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { AlertTriangle, CheckCircle, Clock, XCircle } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { AvailabilityStatus } from '@/features/availability/services/types';
|
||||
|
||||
interface AvailabilityStatusBadgeProps {
|
||||
status: AvailabilityStatus;
|
||||
message?: string;
|
||||
stale?: boolean;
|
||||
}
|
||||
|
||||
const STATUS_CONFIG: Record<
|
||||
AvailabilityStatus,
|
||||
{
|
||||
labelKey: string;
|
||||
defaultMessageKey: string;
|
||||
/** When true the default translated message is always shown, ignoring any backend message. */
|
||||
alwaysDefault: boolean;
|
||||
colorClass: string;
|
||||
Icon: React.ComponentType<{ className?: string }>;
|
||||
}
|
||||
> = {
|
||||
Available: {
|
||||
labelKey: 'availability.available',
|
||||
defaultMessageKey: 'availability.messageAvailable',
|
||||
alwaysDefault: true,
|
||||
colorClass: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
|
||||
Icon: CheckCircle,
|
||||
},
|
||||
Maintenance: {
|
||||
labelKey: 'availability.maintenance',
|
||||
defaultMessageKey: 'availability.messageMaintenance',
|
||||
alwaysDefault: false,
|
||||
colorClass: 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200',
|
||||
Icon: AlertTriangle,
|
||||
},
|
||||
NotAvailable: {
|
||||
labelKey: 'availability.unavailable',
|
||||
defaultMessageKey: 'availability.messageUnavailable',
|
||||
alwaysDefault: false,
|
||||
colorClass: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
|
||||
Icon: XCircle,
|
||||
},
|
||||
};
|
||||
|
||||
export function AvailabilityStatusBadge({
|
||||
status,
|
||||
message,
|
||||
stale = false,
|
||||
}: AvailabilityStatusBadgeProps) {
|
||||
const { t } = useTranslation();
|
||||
const { labelKey, defaultMessageKey, alwaysDefault, colorClass, Icon } = STATUS_CONFIG[status];
|
||||
const displayMessage = alwaysDefault ? t(defaultMessageKey) : (message || t(defaultMessageKey));
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="availability-badge"
|
||||
className={stale ? 'rounded-lg border-2 border-dashed border-amber-400 p-3' : undefined}
|
||||
>
|
||||
<div className={`inline-flex items-center gap-2 rounded-full px-3 py-1 text-sm font-medium ${colorClass}`}>
|
||||
<Icon className="size-4" />
|
||||
<span data-testid="availability-status-label">{t(labelKey)}</span>
|
||||
</div>
|
||||
|
||||
<p
|
||||
data-testid="availability-message"
|
||||
className="mt-2 text-sm text-muted-foreground"
|
||||
>
|
||||
{displayMessage}
|
||||
</p>
|
||||
|
||||
{stale && (
|
||||
<div
|
||||
data-testid="availability-stale-indicator"
|
||||
className="mt-2 flex items-center gap-1 text-xs text-amber-600 dark:text-amber-400"
|
||||
>
|
||||
<Clock className="size-3" />
|
||||
<span>{t('availability.staleLabel')}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user