Files
the-order/packages/ui/src/components/Toast.tsx
T
defiQUG 8649ad4124 feat: implement naming convention, deployment automation, and infrastructure updates
- Add comprehensive naming convention (provider-region-resource-env-purpose)
- Implement Terraform locals for centralized naming
- Update all Terraform resources to use new naming convention
- Create deployment automation framework (18 phase scripts)
- Add Azure setup scripts (provider registration, quota checks)
- Update deployment scripts config with naming functions
- Create complete deployment documentation (guide, steps, quick reference)
- Add frontend portal implementations (public and internal)
- Add UI component library (18 components)
- Enhance Entra VerifiedID integration with file utilities
- Add API client package for all services
- Create comprehensive documentation (naming, deployment, next steps)

Infrastructure:
- Resource groups, storage accounts with new naming
- Terraform configuration updates
- Outputs with naming convention examples

Deployment:
- Automated deployment scripts for all 15 phases
- State management and logging
- Error handling and validation

Documentation:
- Naming convention guide and implementation summary
- Complete deployment guide (296 steps)
- Next steps and quick start guides
- Azure prerequisites and setup completion docs

Note: ESLint warnings present - will be addressed in follow-up commit
2025-11-12 08:22:51 -08:00

145 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
import { cn } from '../lib/utils';
import { Alert, AlertDescription } from './Alert';
export type ToastVariant = 'default' | 'success' | 'error' | 'warning' | 'info';
export interface Toast {
id: string;
title?: string;
message: string;
variant: ToastVariant;
duration?: number;
}
interface ToastContextType {
toasts: Toast[];
addToast: (toast: Omit<Toast, 'id'>) => void;
removeToast: (id: string) => void;
success: (message: string, title?: string) => void;
error: (message: string, title?: string) => void;
warning: (message: string, title?: string) => void;
info: (message: string, title?: string) => void;
}
const ToastContext = createContext<ToastContextType | undefined>(undefined);
export function useToast() {
const context = useContext(ToastContext);
if (!context) {
throw new Error('useToast must be used within a ToastProvider');
}
return context;
}
export function ToastProvider({ children }: { children: ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([]);
const removeToast = useCallback((id: string) => {
setToasts((prev) => prev.filter((toast) => toast.id !== id));
}, []);
const addToast = useCallback(
(toast: Omit<Toast, 'id'>) => {
const id = Math.random().toString(36).substring(2, 9);
const newToast: Toast = {
...toast,
id,
duration: toast.duration || 5000,
};
setToasts((prev) => [...prev, newToast]);
if (newToast.duration && newToast.duration > 0) {
setTimeout(() => {
removeToast(id);
}, newToast.duration);
}
},
[removeToast]
);
const success = useCallback(
(message: string, title?: string) => {
addToast({ message, title, variant: 'success' });
},
[addToast]
);
const error = useCallback(
(message: string, title?: string) => {
addToast({ message, title, variant: 'error' });
},
[addToast]
);
const warning = useCallback(
(message: string, title?: string) => {
addToast({ message, title, variant: 'warning' });
},
[addToast]
);
const info = useCallback(
(message: string, title?: string) => {
addToast({ message, title, variant: 'info' });
},
[addToast]
);
return (
<ToastContext.Provider value={{ toasts, addToast, removeToast, success, error, warning, info }}>
{children}
<ToastContainer toasts={toasts} removeToast={removeToast} />
</ToastContext.Provider>
);
}
function ToastContainer({ toasts, removeToast }: { toasts: Toast[]; removeToast: (id: string) => void }) {
if (toasts.length === 0) return null;
return (
<div className="fixed top-4 right-4 z-50 flex flex-col gap-2 w-full max-w-sm">
{toasts.map((toast) => (
<ToastItem key={toast.id} toast={toast} onRemove={removeToast} />
))}
</div>
);
}
function ToastItem({ toast, onRemove }: { toast: Toast; onRemove: (id: string) => void }) {
const variantMap: Record<ToastVariant, { alert: 'default' | 'destructive' | 'success' | 'warning'; icon: string }> = {
default: { alert: 'default', icon: 'ℹ️' },
success: { alert: 'success', icon: '✓' },
error: { alert: 'destructive', icon: '✗' },
warning: { alert: 'warning', icon: '⚠' },
info: { alert: 'default', icon: 'ℹ️' },
};
const variant = variantMap[toast.variant];
return (
<Alert
variant={variant.alert}
className="animate-in slide-in-from-top-5 fade-in shadow-lg"
>
<div className="flex items-start justify-between">
<div className="flex-1">
{toast.title && (
<AlertDescription className="font-semibold mb-1">{toast.title}</AlertDescription>
)}
<AlertDescription>{toast.message}</AlertDescription>
</div>
<button
onClick={() => onRemove(toast.id)}
className="ml-4 text-gray-500 hover:text-gray-700 text-xl leading-none"
aria-label="Close"
>
×
</button>
</div>
</Alert>
);
}