2import { useState, useEffect } from "react";
3import { Icon } from '@/contexts/IconContext';
5interface AdvisorEvent {
6 f101: number; // Event ID
7 f110: string; // Description
9 f120?: string; // Documentation link
10 f121?: string; // Action link
11 [key: string]: any; // Additional fields (f1000-f1009, etc)
14interface AdvisorStat {
15 f120: number; // Current progress
16 f121: number; // Total items
17 f122: number; // Errors
20interface AdvisorResponse {
21 DATS?: AdvisorEvent[];
26 { id: 'accounts', label: 'Accounts', icon: 'account_balance' },
27 { id: 'customers', label: 'Customers', icon: 'group' },
28 { id: 'other', label: 'Other', icon: 'more_horiz' },
29 { id: 'products', label: 'Products', icon: 'inventory_2' },
30 { id: 'staff', label: 'Staff', icon: 'badge' },
31 { id: 'reference', label: 'Reference', icon: 'book' },
32 { id: 'security', label: 'Security', icon: 'security' },
33 { id: 'technical', label: 'Technical', icon: 'settings' },
36export default function AdvisorPage() {
37 const [selectedCategories, setSelectedCategories] = useState<string[]>(
38 categories.map(c => c.id)
40 const [loading, setLoading] = useState(true);
41 const [events, setEvents] = useState<AdvisorEvent[]>([]);
42 const [progress, setProgress] = useState<AdvisorStat | null>(null);
43 const [error, setError] = useState<string | null>(null);
47 }, [selectedCategories]);
49 // Navigate all advisor "Show me" links to reports page
50 const handleReportClick = () => {
51 window.open('/pages/reports?source=advisor', '_blank');
54 const loadAdvisorData = async () => {
59 const areas = selectedCategories.join(',');
60 const response = await fetch(`/api/v1/elink/advisor?areas=${areas}`);
61 const result = await response.json();
63 if (result.success && result.data) {
64 const data: AdvisorResponse = result.data;
65 setEvents(data.DATS || []);
66 setProgress(data.STAT?.[0] || null);
68 setError(result.error || 'Failed to load advisor data');
71 console.error('Error loading advisor data:', err);
72 setError(err.message || 'An error occurred');
78 const toggleCategory = (categoryId: string) => {
79 setSelectedCategories(prev =>
80 prev.includes(categoryId)
81 ? prev.filter(id => id !== categoryId)
82 : [...prev, categoryId]
86 const toggleAll = (selectAll: boolean) => {
87 setSelectedCategories(selectAll ? categories.map(c => c.id) : []);
90 const getProgressPercentage = () => {
91 if (!progress || progress.f121 === 0) return 0;
92 return Math.round((progress.f120 / progress.f121) * 100);
96 <div className="min-h-screen bg-bg">
97 <div className="max-w-7xl mx-auto p-6">
99 <div className="mb-8">
100 <div className="flex items-center justify-between mb-4">
102 <h1 className="text-3xl font-bold text-text mb-2 flex items-center gap-3">
103 <Icon name="psychology" size={36} className="text-brand" />
106 <p className="text-muted">
107 Advisor analyses your retail data and displays potentially interesting tidbits
111 {/* Progress Indicator */}
113 <div className="flex items-center gap-3 bg-info/10 border border-info/30 rounded-lg px-4 py-3">
114 <div className="relative">
115 <Icon name="progress_activity" size={24} className="text-info animate-spin" />
117 <div className="text-sm">
118 <div className="font-medium text-text">Loading...</div>
120 <div className="text-muted">
121 {progress.f120} of {progress.f121} ({getProgressPercentage()}%)
128 {!loading && progress && (
129 <div className="flex items-center gap-3 bg-success/10 border border-success/30 rounded-lg px-4 py-3">
130 <Icon name="check_circle" size={24} className="text-success" />
131 <div className="text-sm">
132 <div className="font-medium text-text">Complete</div>
133 <div className="text-muted">
134 {progress.f121} items analyzed
142 {/* Category Filters */}
143 <div className="bg-surface rounded-lg shadow-sm border border-border p-6 mb-6">
144 <div className="flex items-center justify-between mb-4">
145 <h2 className="text-lg font-semibold text-text">Filter Categories</h2>
146 <div className="flex gap-2">
148 onClick={() => toggleAll(true)}
149 className="px-3 py-1 text-sm bg-brand text-surface rounded hover:bg-brand2 transition-colors"
154 onClick={() => toggleAll(false)}
155 className="px-3 py-1 text-sm bg-surface-2 text-text rounded hover:bg-surface-2/80 transition-colors"
162 <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
163 {categories.map(category => (
166 className={`flex items-center gap-2 px-4 py-3 rounded-lg border-2 cursor-pointer transition-all ${
167 selectedCategories.includes(category.id)
168 ? 'border-brand bg-brand/5 text-brand font-medium'
169 : 'border-border bg-surface text-text hover:border-brand/30'
174 checked={selectedCategories.includes(category.id)}
175 onChange={() => toggleCategory(category.id)}
176 className="w-4 h-4 text-brand focus:ring-brand focus:ring-2 rounded"
178 <Icon name={category.icon} size={20} />
179 <span className="text-sm">{category.label}</span>
187 <div className="bg-danger/10 border border-danger/30 rounded-lg p-6 mb-6">
188 <div className="flex items-start gap-3">
189 <Icon name="error" size={24} className="text-danger flex-shrink-0" />
191 <h3 className="font-semibold text-text mb-1">Error Loading Data</h3>
192 <p className="text-sm text-muted">{error}</p>
194 onClick={loadAdvisorData}
195 className="mt-3 px-4 py-2 bg-danger text-surface rounded hover:bg-danger/80 transition-colors text-sm"
204 {/* Loading State */}
205 {loading && !events.length && (
206 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
207 {[1, 2, 3, 4, 5, 6].map(i => (
208 <div key={i} className="bg-surface rounded-lg shadow-sm border border-border p-6 animate-pulse">
209 <div className="h-6 bg-surface-2 rounded w-3/4 mb-3"></div>
210 <div className="h-4 bg-surface-2 rounded w-full mb-2"></div>
211 <div className="h-4 bg-surface-2 rounded w-5/6"></div>
218 {!loading && events.length === 0 && !error && (
219 <div className="bg-surface rounded-lg shadow-sm border border-border p-12 text-center">
220 <Icon name="check_circle" size={64} className="text-success mx-auto mb-4" />
221 <h3 className="text-xl font-semibold text-text mb-2">No Issues Found</h3>
222 <p className="text-muted">
223 {selectedCategories.length === 0
224 ? 'Select at least one category to analyze your data.'
225 : 'Advisor has analyzed your data and found no issues to report.'}
231 {!loading && events.length > 0 && (
232 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
233 {events.map((event, index) => (
235 key={`${event.f101}-${index}`}
236 className="bg-surface rounded-lg shadow-sm border border-border hover:border-brand/50 transition-all hover:shadow-md"
238 <div className="p-6">
239 <div className="flex items-start justify-between mb-3">
240 <h3 className="text-lg font-semibold text-text flex-1">
249 className="flex items-center gap-1 text-xs bg-brand text-surface px-3 py-1 rounded hover:bg-brand2 transition-colors flex-shrink-0 ml-2"
251 <Icon name="open_in_new" size={14} />
257 <p className="text-sm text-text mb-4 leading-relaxed">
262 <div className="pt-3 border-t border-border">
266 rel="noopener noreferrer"
267 className="text-xs text-info hover:text-info/80 flex items-center gap-1 transition-colors"
269 <Icon name="help_outline" size={14} />