mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
feat: add Alert component and migrate alert banners to it
Add a reusable Alert component (@librechat/client) with error/success/warning/ info/neutral variants backed by the status-color tokens, default per-variant icons, and role=alert. Migrate the duplicated colored-div banners to it: Auth ErrorMessage, RequestPasswordReset success, and the identical error boxes in ToolSelectDialog, AssistantToolsDialog, and MCPToolSelectDialog.
This commit is contained in:
parent
2357bb1f11
commit
95f7717329
5 changed files with 70 additions and 15 deletions
|
|
@ -1,9 +1,12 @@
|
|||
import { Alert } from '@librechat/client';
|
||||
|
||||
export const ErrorMessage = ({ children }: { children: React.ReactNode }) => (
|
||||
<div
|
||||
role="alert"
|
||||
<Alert
|
||||
variant="error"
|
||||
icon={false}
|
||||
aria-live="assertive"
|
||||
className="relative mt-6 rounded-xl border border-red-500/20 bg-red-50/50 px-6 py-4 text-red-700 shadow-sm transition-all dark:bg-red-950/30 dark:text-red-100"
|
||||
className="mt-6 px-6 py-4 shadow-sm transition-all"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Alert>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useForm } from 'react-hook-form';
|
||||
import { useState, ReactNode } from 'react';
|
||||
import { Spinner, Button } from '@librechat/client';
|
||||
import { Spinner, Button, Alert } from '@librechat/client';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import { useRequestPasswordResetMutation } from 'librechat-data-provider/react-query';
|
||||
import { loginPage } from 'librechat-data-provider';
|
||||
|
|
@ -11,12 +11,9 @@ import { useLocalize } from '~/hooks';
|
|||
|
||||
const BodyTextWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
return (
|
||||
<div
|
||||
className="relative mt-6 rounded-xl border border-green-500/20 bg-green-50/50 px-6 py-4 text-green-700 shadow-sm transition-all dark:bg-green-950/30 dark:text-green-100"
|
||||
role="alert"
|
||||
>
|
||||
<Alert variant="success" icon={false} className="mt-6 px-6 py-4 shadow-sm transition-all">
|
||||
{children}
|
||||
</div>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from 'react';
|
||||
import { Search, X } from 'lucide-react';
|
||||
import { Alert } from '@librechat/client';
|
||||
import { Dialog, DialogPanel, DialogTitle, Description } from '@headlessui/react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { isAgentsEndpoint } from 'librechat-data-provider';
|
||||
|
|
@ -186,12 +187,9 @@ function AssistantToolsDialog({
|
|||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<div
|
||||
className="relative m-4 rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700"
|
||||
role="alert"
|
||||
>
|
||||
<Alert variant="error" icon={false} className="m-4">
|
||||
{localize('com_nav_plugin_auth_error')} {errorMessage}
|
||||
</div>
|
||||
</Alert>
|
||||
)}
|
||||
{showPluginAuthForm && (
|
||||
<div className="p-4 sm:p-6 sm:pt-4">
|
||||
|
|
|
|||
56
packages/client/src/components/Alert.tsx
Normal file
56
packages/client/src/components/Alert.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import * as React from 'react';
|
||||
import { Info, AlertCircle, AlertTriangle, CheckCircle2 } from 'lucide-react';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const alertVariants = cva('relative flex gap-3 rounded-xl border px-4 py-3 text-sm', {
|
||||
variants: {
|
||||
variant: {
|
||||
info: 'border-status-info bg-status-info-subtle text-status-info',
|
||||
success: 'border-status-success bg-status-success-subtle text-status-success',
|
||||
warning: 'border-status-warning bg-status-warning-subtle text-status-warning',
|
||||
error: 'border-status-error bg-status-error-subtle text-status-error',
|
||||
neutral: 'border-status-neutral bg-status-neutral-subtle text-status-neutral',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'info',
|
||||
},
|
||||
});
|
||||
|
||||
type AlertVariant = NonNullable<VariantProps<typeof alertVariants>['variant']>;
|
||||
|
||||
const defaultIcons: Record<AlertVariant, React.ComponentType<{ className?: string }>> = {
|
||||
info: Info,
|
||||
success: CheckCircle2,
|
||||
warning: AlertTriangle,
|
||||
error: AlertCircle,
|
||||
neutral: Info,
|
||||
};
|
||||
|
||||
export interface AlertProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof alertVariants> {
|
||||
/** Override the default per-variant icon, or pass `false` to hide it. */
|
||||
icon?: React.ReactNode | false;
|
||||
}
|
||||
|
||||
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
||||
({ className, variant = 'info', icon, role = 'alert', children, ...props }, ref) => {
|
||||
const resolvedVariant = (variant ?? 'info') as AlertVariant;
|
||||
const DefaultIcon = defaultIcons[resolvedVariant];
|
||||
return (
|
||||
<div ref={ref} role={role} className={cn(alertVariants({ variant }), className)} {...props}>
|
||||
{icon !== false && (
|
||||
<span className="mt-0.5 shrink-0" aria-hidden="true">
|
||||
{icon ?? <DefaultIcon className="size-4" />}
|
||||
</span>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">{children}</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
Alert.displayName = 'Alert';
|
||||
|
||||
export { Alert, alertVariants };
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
export * from './Accordion';
|
||||
export * from './AnimatedTabs';
|
||||
export * from './Alert';
|
||||
export * from './AlertDialog';
|
||||
export * from './Breadcrumb';
|
||||
export * from './Button';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue