bugfix: all the toasts unfied inside organizations feature. other features still need a refactor for toasts though.
This commit is contained in:
@@ -24,3 +24,70 @@ export function Toast({ children, variant = 'default', className = '' }: ToastPr
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export type ToastMessages = {
|
||||
error?: string;
|
||||
success?: string;
|
||||
info?: string;
|
||||
default?: string;
|
||||
};
|
||||
|
||||
export type ToastStackProps = ToastMessages & {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function hasToastMessages(messages: ToastMessages): boolean {
|
||||
return Boolean(messages.error || messages.success || messages.info || messages.default);
|
||||
}
|
||||
|
||||
/** Renders active toast messages with shared badge colors (success / warning / danger / default). */
|
||||
export function ToastStack({ error, success, info, default: defaultMessage, className = '' }: ToastStackProps) {
|
||||
if (!hasToastMessages({ error, success, info, default: defaultMessage })) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`space-y-2 ${className}`.trim()} aria-live="polite">
|
||||
{error && <Toast variant="danger">{error}</Toast>}
|
||||
{info && <Toast variant="warning">{info}</Toast>}
|
||||
{success && <Toast variant="success">{success}</Toast>}
|
||||
{defaultMessage && <Toast variant="default">{defaultMessage}</Toast>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export type ToastViewportPosition = 'inline' | 'top' | 'bottom';
|
||||
|
||||
export type ToastViewportProps = ToastStackProps & {
|
||||
position?: ToastViewportPosition;
|
||||
};
|
||||
|
||||
const viewportPositionClass: Record<Exclude<ToastViewportPosition, 'inline'>, string> = {
|
||||
top: 'fixed top-4 left-0 right-0 z-[70] px-4 pointer-events-none',
|
||||
bottom: 'fixed bottom-4 left-0 right-0 z-[70] px-4 pointer-events-none',
|
||||
};
|
||||
|
||||
/**
|
||||
* Positions a ToastStack on the page. Use `inline` below a heading; `bottom` / `top` for overlays.
|
||||
*/
|
||||
export function ToastViewport({
|
||||
position = 'inline',
|
||||
className = '',
|
||||
...messages
|
||||
}: ToastViewportProps) {
|
||||
if (!hasToastMessages(messages)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stack = <ToastStack {...messages} className={className} />;
|
||||
|
||||
if (position === 'inline') {
|
||||
return stack;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={viewportPositionClass[position]}>
|
||||
<div className="pointer-events-auto w-full">{stack}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { DialogCloseButton } from '@/components/ui/common/DialogCloseButton';
|
||||
import { ToastStack, type ToastMessages } from '@/components/ui/common/Toast';
|
||||
import type { OrganizationInvitationHistoryItemDto } from '@/lib/api/organization';
|
||||
import { Badge, organizationConnectionStatusVariant } from '@/components/ui/common/Badge';
|
||||
import { Table } from '@/components/ui/common/Table';
|
||||
@@ -28,8 +29,8 @@ type InvitationHistoryDialogProps = {
|
||||
copiedId: string | null;
|
||||
copyingInvitationId: string | null;
|
||||
onCopy: (invitation: OrganizationInvitationHistoryItemDto) => void;
|
||||
copyError?: string;
|
||||
copySuccess?: string;
|
||||
/** Same page-level toasts, rendered at top of dialog while it is open. */
|
||||
toastMessages?: ToastMessages;
|
||||
};
|
||||
|
||||
export function InvitationHistoryDialog({
|
||||
@@ -40,8 +41,7 @@ export function InvitationHistoryDialog({
|
||||
copiedId,
|
||||
copyingInvitationId,
|
||||
onCopy,
|
||||
copyError,
|
||||
copySuccess,
|
||||
toastMessages,
|
||||
}: InvitationHistoryDialogProps) {
|
||||
if (!open) return null;
|
||||
|
||||
@@ -60,17 +60,7 @@ export function InvitationHistoryDialog({
|
||||
<DialogCloseButton onClick={onClose} />
|
||||
</div>
|
||||
|
||||
{copyError && (
|
||||
<div className="rounded-[var(--radius-md)] border border-red-500/40 bg-red-500/10 px-4 py-3 text-sm text-red-700 dark:text-red-300">
|
||||
{copyError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{copySuccess && (
|
||||
<div className="rounded-[var(--radius-md)] border border-primary/30 bg-primary-soft/40 px-4 py-3 text-sm text-text-primary">
|
||||
{copySuccess}
|
||||
</div>
|
||||
)}
|
||||
{toastMessages && <ToastStack {...toastMessages} />}
|
||||
|
||||
{loading ? (
|
||||
<p className="text-sm text-text-secondary">Loading invitation history...</p>
|
||||
|
||||
Reference in New Issue
Block a user