Fix and UI imporvements 2
This commit is contained in:
@@ -31,9 +31,9 @@ interface NetworkSummaryCardsProps {
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
data,
|
||||
isLoading
|
||||
const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
data,
|
||||
isLoading
|
||||
}) => {
|
||||
const { t } = useTranslation('dashboard');
|
||||
|
||||
@@ -43,10 +43,10 @@ const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
{[...Array(5)].map((_, index) => (
|
||||
<Card key={index} className="animate-pulse">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm text-gray-500 h-4 bg-gray-200 rounded w-3/4"></CardTitle>
|
||||
<CardTitle className="text-sm text-[var(--text-tertiary)] h-4 bg-[var(--bg-tertiary)] rounded w-3/4"></CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="h-6 bg-gray-200 rounded w-1/2"></div>
|
||||
<div className="h-6 bg-[var(--bg-tertiary)] rounded w-1/2"></div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
@@ -56,7 +56,7 @@ const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
<div className="text-center py-8 text-[var(--text-secondary)]">
|
||||
{t('enterprise.no_network_data')}
|
||||
</div>
|
||||
);
|
||||
@@ -67,16 +67,16 @@ const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
{/* Network Outlets Card */}
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-500">
|
||||
<CardTitle className="text-sm font-medium text-[var(--text-secondary)]">
|
||||
{t('enterprise.network_outlets')}
|
||||
</CardTitle>
|
||||
<StoreIcon className="w-4 h-4 text-blue-500" />
|
||||
<StoreIcon className="w-5 h-5 text-[var(--color-primary)]" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
<div className="text-2xl font-bold text-[var(--text-primary)]">
|
||||
{data.child_tenant_count}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
<p className="text-xs text-[var(--text-secondary)] mt-1">
|
||||
{t('enterprise.outlets_in_network')}
|
||||
</p>
|
||||
</CardContent>
|
||||
@@ -85,16 +85,16 @@ const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
{/* Network Sales Card */}
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-500">
|
||||
<CardTitle className="text-sm font-medium text-[var(--text-secondary)]">
|
||||
{t('enterprise.network_sales')}
|
||||
</CardTitle>
|
||||
<DollarSign className="w-4 h-4 text-green-500" />
|
||||
<DollarSign className="w-5 h-5 text-[var(--color-success)]" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
<div className="text-2xl font-bold text-[var(--text-primary)]">
|
||||
{formatCurrency(data.network_sales_30d, 'EUR')}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
<p className="text-xs text-[var(--text-secondary)] mt-1">
|
||||
{t('enterprise.last_30_days')}
|
||||
</p>
|
||||
</CardContent>
|
||||
@@ -103,16 +103,16 @@ const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
{/* Production Volume Card */}
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-500">
|
||||
<CardTitle className="text-sm font-medium text-[var(--text-secondary)]">
|
||||
{t('enterprise.production_volume')}
|
||||
</CardTitle>
|
||||
<Package className="w-4 h-4 text-purple-500" />
|
||||
<Package className="w-5 h-5 text-[var(--color-secondary)]" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
<div className="text-2xl font-bold text-[var(--text-primary)]">
|
||||
{new Intl.NumberFormat('es-ES').format(data.production_volume_30d)} kg
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
<p className="text-xs text-[var(--text-secondary)] mt-1">
|
||||
{t('enterprise.last_30_days')}
|
||||
</p>
|
||||
</CardContent>
|
||||
@@ -121,16 +121,16 @@ const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
{/* Pending Internal Transfers Card */}
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-500">
|
||||
<CardTitle className="text-sm font-medium text-[var(--text-secondary)]">
|
||||
{t('enterprise.pending_orders')}
|
||||
</CardTitle>
|
||||
<ShoppingCart className="w-4 h-4 text-yellow-500" />
|
||||
<ShoppingCart className="w-5 h-5 text-[var(--color-warning)]" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
<div className="text-2xl font-bold text-[var(--text-primary)]">
|
||||
{data.pending_internal_transfers_count}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
<p className="text-xs text-[var(--text-secondary)] mt-1">
|
||||
{t('enterprise.internal_transfers')}
|
||||
</p>
|
||||
</CardContent>
|
||||
@@ -139,16 +139,16 @@ const NetworkSummaryCards: React.FC<NetworkSummaryCardsProps> = ({
|
||||
{/* Active Shipments Card */}
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-500">
|
||||
<CardTitle className="text-sm font-medium text-[var(--text-secondary)]">
|
||||
{t('enterprise.active_shipments')}
|
||||
</CardTitle>
|
||||
<Truck className="w-4 h-4 text-red-500" />
|
||||
<Truck className="w-5 h-5 text-[var(--color-info)]" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold text-gray-900">
|
||||
<div className="text-2xl font-bold text-[var(--text-primary)]">
|
||||
{data.active_shipments_count}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
<p className="text-xs text-[var(--text-secondary)] mt-1">
|
||||
{t('enterprise.today')}
|
||||
</p>
|
||||
</CardContent>
|
||||
|
||||
@@ -133,6 +133,10 @@ function getActionLabelKey(actionType: string, metadata?: Record<string, any>):
|
||||
key: 'alerts:actions.reject_po',
|
||||
extractParams: () => ({})
|
||||
},
|
||||
'modify_po': {
|
||||
key: 'alerts:actions.modify_po',
|
||||
extractParams: () => ({})
|
||||
},
|
||||
'view_po_details': {
|
||||
key: 'alerts:actions.view_po_details',
|
||||
extractParams: () => ({})
|
||||
@@ -252,6 +256,17 @@ function ActionCard({ alert, showEscalationBadge = false, onActionSuccess, onAct
|
||||
// Determine if this is a critical alert that needs stronger visual treatment
|
||||
const isCritical = alert.priority_level === 'critical' || alert.priority_level === 'CRITICAL';
|
||||
|
||||
// Detect if this is an AI-generated PO
|
||||
const isAIGeneratedPO = alert.event_type?.includes('po') &&
|
||||
(alert.orchestrator_context?.already_addressed ||
|
||||
alert.orchestrator_context?.action_type === 'create_po' ||
|
||||
alert.event_metadata?.source === 'orchestrator' ||
|
||||
alert.event_metadata?.auto_generated === true ||
|
||||
alert.event_metadata?.reasoning_data?.metadata?.ai_assisted === true ||
|
||||
alert.event_metadata?.reasoning_data?.metadata?.trigger_source === 'orchestrator_auto' ||
|
||||
alert.ai_reasoning?.details?.metadata?.ai_assisted === true ||
|
||||
alert.ai_reasoning?.details?.metadata?.trigger_source === 'orchestrator_auto');
|
||||
|
||||
// Extract reasoning from alert using new rendering utility
|
||||
const reasoningText = renderAIReasoning(alert, t) || '';
|
||||
|
||||
@@ -294,6 +309,16 @@ function ActionCard({ alert, showEscalationBadge = false, onActionSuccess, onAct
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* AI-Generated PO Badge */}
|
||||
{isAIGeneratedPO && (
|
||||
<div className="mb-2">
|
||||
<div className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-semibold bg-gradient-to-r from-[var(--color-info-100)] to-[var(--color-success-100)] text-[var(--color-info-900)] border-2 border-[var(--color-info-300)] shadow-sm">
|
||||
<Bot className="w-4 h-4" />
|
||||
<span>{t('alerts:orchestration.ai_generated_po')}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Escalation Badge Details */}
|
||||
{showEscalationBadge && <EscalationBadge alert={alert} />}
|
||||
|
||||
@@ -580,6 +605,7 @@ export function UnifiedActionQueueCard({
|
||||
// PO Details Modal state
|
||||
const [isPODetailsModalOpen, setIsPODetailsModalOpen] = useState(false);
|
||||
const [selectedPOId, setSelectedPOId] = useState<string | null>(null);
|
||||
const [poModalMode, setPOModalMode] = useState<'view' | 'edit'>('view');
|
||||
|
||||
// Subscribe to SSE notifications for real-time alerts
|
||||
const { notifications, isConnected } = useEventNotifications();
|
||||
@@ -638,13 +664,14 @@ export function UnifiedActionQueueCard({
|
||||
};
|
||||
}, [actionQueue]);
|
||||
|
||||
// Listen for PO details modal open events
|
||||
// Listen for PO details modal open events (view mode)
|
||||
useEffect(() => {
|
||||
const handlePODetailsOpen = (event: CustomEvent) => {
|
||||
const { po_id } = event.detail;
|
||||
const { po_id, mode } = event.detail;
|
||||
|
||||
if (po_id) {
|
||||
setSelectedPOId(po_id);
|
||||
setPOModalMode(mode || 'view');
|
||||
setIsPODetailsModalOpen(true);
|
||||
}
|
||||
};
|
||||
@@ -655,6 +682,24 @@ export function UnifiedActionQueueCard({
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Listen for PO edit modal open events (edit mode)
|
||||
useEffect(() => {
|
||||
const handlePOEditOpen = (event: CustomEvent) => {
|
||||
const { po_id, mode } = event.detail;
|
||||
|
||||
if (po_id) {
|
||||
setSelectedPOId(po_id);
|
||||
setPOModalMode(mode || 'edit');
|
||||
setIsPODetailsModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('po:open-edit' as any, handlePOEditOpen);
|
||||
return () => {
|
||||
window.removeEventListener('po:open-edit' as any, handlePOEditOpen);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Create a stable identifier for notifications to prevent infinite re-renders
|
||||
// Only recalculate when the actual notification IDs and read states change
|
||||
const notificationKey = useMemo(() => {
|
||||
@@ -976,7 +1021,7 @@ export function UnifiedActionQueueCard({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* PO Details Modal - Opened by "Ver detalles" action */}
|
||||
{/* PO Details Modal - Opened by "Ver detalles" or "Modificar PO" action */}
|
||||
{isPODetailsModalOpen && selectedPOId && tenantId && (
|
||||
<UnifiedPurchaseOrderModal
|
||||
poId={selectedPOId}
|
||||
@@ -985,9 +1030,10 @@ export function UnifiedActionQueueCard({
|
||||
onClose={() => {
|
||||
setIsPODetailsModalOpen(false);
|
||||
setSelectedPOId(null);
|
||||
setPOModalMode('view');
|
||||
}}
|
||||
showApprovalActions={true}
|
||||
initialMode="view"
|
||||
initialMode={poModalMode}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user