New alert service

This commit is contained in:
Urtzi Alfaro
2025-12-05 20:07:01 +01:00
parent 1fe3a73549
commit 667e6e0404
393 changed files with 26002 additions and 61033 deletions

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { Loader2 } from 'lucide-react';
interface LoaderProps {
export interface LoaderProps {
size?: 'sm' | 'md' | 'lg' | 'default';
text?: string;
className?: string;

View File

@@ -16,14 +16,15 @@ import {
Phone,
ExternalLink
} from 'lucide-react';
import { EnrichedAlert, AlertTypeClass } from '../../../types/alerts';
import { Alert, AlertTypeClass, getPriorityColor, getTypeClassBadgeVariant, formatTimeUntilConsequence } from '../../../api/types/events';
import { renderEventTitle, renderEventMessage, renderActionLabel, renderAIReasoning } from '../../../utils/i18n/alertRendering';
import { useSmartActionHandler } from '../../../utils/smartActionHandlers';
import { getPriorityColor, getTypeClassBadgeVariant, formatTimeUntilConsequence } from '../../../types/alerts';
import { useAuthUser } from '../../../stores/auth.store';
import { useTranslation } from 'react-i18next';
export interface NotificationPanelProps {
notifications: NotificationData[];
enrichedAlerts?: EnrichedAlert[];
enrichedAlerts?: Alert[];
isOpen: boolean;
onClose: () => void;
onMarkAsRead: (id: string) => void;
@@ -50,12 +51,13 @@ const formatTimestamp = (timestamp: string) => {
// Enriched Alert Item Component
const EnrichedAlertItem: React.FC<{
alert: EnrichedAlert;
alert: Alert;
isMobile: boolean;
onMarkAsRead: (id: string) => void;
onRemove: (id: string) => void;
actionHandler: any;
}> = ({ alert, isMobile, onMarkAsRead, onRemove, actionHandler }) => {
const { t } = useTranslation();
const isUnread = alert.status === 'active';
const priorityColor = getPriorityColor(alert.priority_level);
@@ -109,14 +111,14 @@ const EnrichedAlertItem: React.FC<{
<p className={`font-medium leading-tight text-[var(--text-primary)] ${
isMobile ? 'text-base mb-2' : 'text-sm mb-1'
}`}>
{alert.title}
{renderEventTitle(alert, t)}
</p>
{/* Message */}
<p className={`leading-relaxed text-[var(--text-secondary)] ${
isMobile ? 'text-sm mb-3' : 'text-xs mb-2'
}`}>
{alert.message}
{renderEventMessage(alert, t)}
</p>
{/* Context Badges */}
@@ -133,10 +135,10 @@ const EnrichedAlertItem: React.FC<{
<span>{alert.business_impact.financial_impact_eur.toFixed(0)} en riesgo</span>
</div>
)}
{alert.urgency_context?.time_until_consequence_hours && (
{alert.urgency?.hours_until_consequence && (
<div className="flex items-center gap-1 px-2 py-1 rounded-md bg-error/10 text-error text-xs">
<Clock className="w-3 h-3" />
<span>{formatTimeUntilConsequence(alert.urgency_context.time_until_consequence_hours)}</span>
<span>{formatTimeUntilConsequence(alert.urgency.hours_until_consequence)}</span>
</div>
)}
{alert.trend_context && (
@@ -148,21 +150,21 @@ const EnrichedAlertItem: React.FC<{
</div>
{/* AI Reasoning Summary */}
{alert.ai_reasoning_summary && (
{renderAIReasoning(alert, t) && (
<div className="mb-3 p-2 rounded-md bg-primary/5 border border-primary/20">
<div className="flex items-start gap-2">
<Bot className="w-4 h-4 text-primary mt-0.5 flex-shrink-0" />
<p className="text-xs text-[var(--text-secondary)] italic">
{alert.ai_reasoning_summary}
{renderAIReasoning(alert, t)}
</p>
</div>
</div>
)}
{/* Smart Actions */}
{alert.actions && alert.actions.length > 0 && (
{alert.smart_actions && alert.smart_actions.length > 0 && (
<div className={`flex flex-wrap gap-2 ${isMobile ? 'mb-3' : 'mb-2'}`}>
{alert.actions.slice(0, 3).map((action, idx) => (
{alert.smart_actions.slice(0, 3).map((action, idx) => (
<Button
key={idx}
size={isMobile ? "sm" : "xs"}
@@ -173,9 +175,9 @@ const EnrichedAlertItem: React.FC<{
action.variant === 'danger' ? 'text-error hover:text-error-dark' : ''
}`}
>
{action.type === 'call_supplier' && <Phone className="w-3 h-3 mr-1" />}
{action.type === 'navigate' && <ExternalLink className="w-3 h-3 mr-1" />}
{action.label}
{action.action_type === 'call_supplier' && <Phone className="w-3 h-3 mr-1" />}
{action.action_type === 'navigate' && <ExternalLink className="w-3 h-3 mr-1" />}
{renderActionLabel(action, t)}
{action.estimated_time_minutes && (
<span className="ml-1 opacity-60">({action.estimated_time_minutes}m)</span>
)}
@@ -344,17 +346,23 @@ export const NotificationPanel: React.FC<NotificationPanelProps> = ({
key={notification.id}
alert={{
...notification,
event_class: 'alert',
tenant_id: user?.tenant_id || '',
status: notification.read ? 'acknowledged' : 'active',
created_at: notification.timestamp,
enriched_at: notification.timestamp,
alert_metadata: notification.metadata || {},
event_metadata: notification.metadata || {},
service: 'notification-service',
alert_type: notification.item_type,
actions: notification.actions || [],
is_group_summary: false,
placement: notification.placement || ['notification_panel']
} as EnrichedAlert}
event_type: notification.item_type,
event_domain: 'notification',
smart_actions: notification.actions || [],
entity_links: {},
i18n: {
title_key: notification.title || '',
message_key: notification.message || '',
title_params: {},
message_params: {}
}
} as Alert}
isMobile={isMobile}
onMarkAsRead={onMarkAsRead}
onRemove={onRemoveNotification}

View File

@@ -2,7 +2,7 @@
export { default as Button } from './Button';
export { default as Input } from './Input';
export { default as Textarea } from './Textarea/Textarea';
export { default as Card, CardHeader, CardBody, CardFooter } from './Card';
export { default as Card, CardHeader, CardBody, CardFooter, CardContent, CardTitle, CardDescription } from './Card';
export { default as Modal, ModalHeader, ModalBody, ModalFooter } from './Modal';
export { default as Table } from './Table';
export { Badge, CountBadge, StatusDot, SeverityBadge } from './Badge';
@@ -48,9 +48,9 @@ export { SettingsSearch } from './SettingsSearch';
export type { ButtonProps } from './Button';
export type { InputProps } from './Input';
export type { TextareaProps } from './Textarea';
export type { CardProps, CardHeaderProps, CardBodyProps, CardFooterProps } from './Card';
export type { CardProps, CardHeaderProps, CardBodyProps, CardFooterProps, CardContentProps, CardTitleProps } from './Card';
export type { ModalProps, ModalHeaderProps, ModalBodyProps, ModalFooterProps } from './Modal';
export type { TableProps, TableColumn, TableRow } from './Table';
export type { TableProps, TableColumn } from './Table';
export type { BadgeProps, CountBadgeProps, StatusDotProps, SeverityBadgeProps, SeverityLevel } from './Badge';
export type { AvatarProps } from './Avatar';
export type { TooltipProps } from './Tooltip';