File size: 9,052 Bytes
5f2460b
 
 
 
 
 
 
 
 
 
9e4bb05
5f2460b
a66ed25
 
5f2460b
 
 
a66ed25
5f2460b
 
 
 
9e4bb05
5f2460b
 
9e4bb05
 
 
 
 
 
 
 
5f2460b
9e4bb05
 
 
 
5f2460b
 
 
 
 
 
a66ed25
5f2460b
 
 
 
 
 
 
a66ed25
 
 
5f2460b
 
 
 
 
 
a66ed25
 
 
 
 
 
5f2460b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e4bb05
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f2460b
a66ed25
 
 
 
 
 
bd7895c
 
 
 
5f2460b
a66ed25
 
 
 
bd7895c
a66ed25
bd7895c
a66ed25
 
 
bd7895c
a66ed25
 
bd7895c
 
 
a66ed25
bd7895c
 
a66ed25
 
 
bd7895c
 
a66ed25
 
 
bd7895c
 
a66ed25
 
 
 
 
 
 
 
5f2460b
a66ed25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f2460b
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
"use client";

import { useEffect, useState } from "react";
import { fetchPurchaseOrders } from "../lib/api";
import { PurchaseOrder } from "../lib/types";
import BrandLoader from "./BrandLoader";

export default function MarketMonitor() {
  const [ocs, setOcs] = useState<PurchaseOrder[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [filter, setFilter] = useState("todos");
  const [page, setPage] = useState(1);
  const itemsPerPage = 50;

  useEffect(() => {
    loadOcs();
    setPage(1); // Reset page on filter change
  }, [filter]);

  async function loadOcs() {
    setIsLoading(true);
    setError(null);
    try {
      const data = await fetchPurchaseOrders(undefined, filter);
      if (!data || data.length === 0) {
        setError("No purchase orders found for today. Try again later or check your API connection.");
        setOcs([]);
      } else {
        // Sort by code descending (usually higher codes are newer)
        const sorted = [...data].sort((a, b) => b.code.localeCompare(a.code));
        setOcs(sorted);
      }
    } catch (e) {
      const errorMsg = e instanceof Error ? e.message : "Failed to load purchase orders. Check your backend connection.";
      console.error("OC Load Error:", e);
      setError(errorMsg);
      setOcs([]);
    } finally {
      setIsLoading(false);
    }
  }

  const formatCurrency = (amount: number | null, currency: string | null) => {
    if (!amount || amount === 0) return <span className="text-slate-600 italic">Pending...</span>;
    return new Intl.NumberFormat("es-CL", {
      style: "currency",
      currency: currency || "CLP",
      maximumFractionDigits: 0
    }).format(amount);
  };

  const paginatedOcs = ocs.slice((page - 1) * itemsPerPage, page * itemsPerPage);
  const totalPages = Math.ceil(ocs.length / itemsPerPage);

  return (
    <div className="space-y-8 animate-in fade-in duration-700">
      <div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
        <div>
          <p className="text-[10px] uppercase tracking-[0.4em] text-cyan/60 font-black mb-2">Real-Time Intelligence</p>
          <h2 className="text-4xl font-black text-white tracking-tight">Market Monitor</h2>
          <div className="flex items-center gap-3 mt-2">
             <span className="flex h-2 w-2 rounded-full bg-green-500 animate-pulse" />
             <p className="text-slate-400 text-sm">
                Monitoring <span className="text-white font-bold">{ocs.length.toLocaleString()}</span> active orders from today.
             </p>
          </div>
        </div>

        <div className="flex bg-slate-900/50 p-1 rounded-2xl border border-white/5 backdrop-blur-xl">
          {["todos", "aceptada", "enviadaproveedor"].map((f) => (
            <button
              key={f}
              onClick={() => setFilter(f)}
              className={`px-6 py-2.5 rounded-xl text-[10px] uppercase font-black tracking-widest transition-all ${
                filter === f ? "bg-cyan text-slate-950 shadow-lg shadow-cyan/20" : "text-slate-500 hover:text-white"
              }`}
            >
              {f === "todos" ? "Live Stream" : f}
            </button>
          ))}
        </div>
      </div>

      <div className="grid gap-6">
        {isLoading ? (
          <div className="py-20">
            <BrandLoader />
          </div>
        ) : error ? (
          <div className="glass-card rounded-[2rem] p-8 border border-red-500/20 bg-red-500/5">
            <div className="flex items-start gap-4">
              <div className="text-2xl">⚠️</div>
              <div className="flex-1">
                <h3 className="text-white font-bold mb-2">Connection Error</h3>
                <p className="text-slate-300 text-sm mb-4">{error}</p>
                <div className="flex gap-3">
                  <button
                    onClick={loadOcs}
                    className="px-6 py-2 bg-cyan text-slate-950 font-bold rounded-lg hover:bg-cyan/90 transition-all"
                  >
                    🔄 Retry
                  </button>
                  <a
                    href="#"
                    className="px-6 py-2 bg-white/5 border border-white/10 text-white font-bold rounded-lg hover:bg-white/10 transition-all"
                  >
                    Troubleshoot
                  </a>
                </div>
              </div>
            </div>
          </div>
        ) : ocs.length > 0 ? (
          <>
            <div className="glass-card rounded-[2rem] overflow-hidden border border-white/5 shadow-2xl shadow-black/50">
              <div className="overflow-x-auto custom-scrollbar max-h-[600px]">
                <table className="w-full text-left text-xs border-collapse sticky-header">
                  <thead className="sticky top-0 z-10">
                    <tr className="bg-slate-900/95 backdrop-blur-md text-slate-500 uppercase font-black tracking-tighter border-b border-white/5">
                      <th className="px-4 sm:px-6 py-5">Order ID / Description</th>
                      <th className="px-6 py-5 hidden md:table-cell">Buyer</th>
                      <th className="px-6 py-5 hidden lg:table-cell">Vendor</th>
                      <th className="px-4 sm:px-6 py-5 text-right">Total</th>
                    </tr>
                  </thead>
                  <tbody className="divide-y divide-white/5">
                    {paginatedOcs.map((oc) => (
                      <tr key={oc.code} className="hover:bg-white/[0.03] transition-colors group">
                        <td className="px-4 sm:px-6 py-5 max-w-md">
                          <div className="flex items-center gap-2 mb-1">
                            <span className="text-cyan font-bold font-mono text-[9px] bg-cyan/5 px-2 py-0.5 rounded border border-cyan/10">
                              {oc.code}
                            </span>
                          </div>
                          <div className="text-white font-bold line-clamp-1 group-hover:line-clamp-none transition-all cursor-help text-xs sm:text-sm" title={oc.name}>
                            {oc.name || "Orden de Compra"}
                          </div>
                          <div className="md:hidden text-[10px] text-slate-500 mt-1 truncate max-w-[200px]">
                            {oc.buyer}
                          </div>
                        </td>
                        <td className="px-6 py-5 hidden md:table-cell">
                          <div className="text-slate-300 font-medium truncate max-w-[150px] text-[11px]">
                            {oc.buyer !== "Unknown" ? oc.buyer : <span className="opacity-30">...</span>}
                          </div>
                        </td>
                        <td className="px-6 py-5 hidden lg:table-cell">
                          <div className="text-sky-400 font-bold truncate max-w-[150px] text-[11px]">
                            {oc.provider !== "Unknown" ? oc.provider : <span className="opacity-30">...</span>}
                          </div>
                        </td>
                        <td className="px-4 sm:px-6 py-5 text-right">
                          <div className="text-white font-black text-xs sm:text-sm">
                            {formatCurrency(oc.total_amount, oc.currency)}
                          </div>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>

            {/* Pagination Controls */}
            <div className="flex items-center justify-between px-4">
              <div className="text-[10px] text-slate-500 font-bold uppercase tracking-widest">
                Showing {((page - 1) * itemsPerPage) + 1} to {Math.min(page * itemsPerPage, ocs.length)} of {ocs.length}
              </div>
              <div className="flex gap-2">
                <button 
                  disabled={page === 1}
                  onClick={() => setPage(p => p - 1)}
                  className="px-4 py-2 rounded-lg bg-white/5 border border-white/10 text-xs font-bold disabled:opacity-30 hover:bg-white/10"
                >
                  Previous
                </button>
                <button 
                  disabled={page === totalPages}
                  onClick={() => setPage(p => p + 1)}
                  className="px-4 py-2 rounded-lg bg-white/5 border border-white/10 text-xs font-bold disabled:opacity-30 hover:bg-white/10"
                >
                  Next
                </button>
              </div>
            </div>
          </>
        ) : (
          <div className="py-40 text-center glass-card rounded-[2rem] border border-white/5">
            <div className="text-4xl mb-4">🛒</div>
            <p className="text-slate-500 font-medium italic">No purchase orders detected in the last hour.</p>
          </div>
        )}
      </div>
    </div>
  );
}