File size: 7,115 Bytes
1f7ead8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import React, { useState, useMemo } from 'react';
import { X, Download, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react';
import html2canvas from 'html2canvas';

const DataTableModal = ({ isOpen, onClose, data }) => {
  const [sortConfig, setSortConfig] = useState({ key: null, direction: 'ascending' });

  if (!isOpen || !data) return null;

  const sortedRows = useMemo(() => {
    let sortableItems = [...(data.rows || [])];
    if (sortConfig.key !== null) {
      sortableItems.sort((a, b) => {
        let aValue = a.values[sortConfig.key] || '';
        let bValue = b.values[sortConfig.key] || '';
        
        // Try numeric sort
        const aNum = parseFloat(aValue.replace(/[^0-9.-]+/g, ""));
        const bNum = parseFloat(bValue.replace(/[^0-9.-]+/g, ""));

        if (!isNaN(aNum) && !isNaN(bNum)) {
             if (aNum < bNum) return sortConfig.direction === 'ascending' ? -1 : 1;
             if (aNum > bNum) return sortConfig.direction === 'ascending' ? 1 : -1;
             return 0;
        }

        if (aValue < bValue) {
          return sortConfig.direction === 'ascending' ? -1 : 1;
        }
        if (aValue > bValue) {
          return sortConfig.direction === 'ascending' ? 1 : -1;
        }
        return 0;
      });
    }
    return sortableItems;
  }, [data.rows, sortConfig]);

  const requestSort = (key) => {
    let direction = 'ascending';
    if (sortConfig.key === key && sortConfig.direction === 'ascending') {
      direction = 'descending';
    }
    setSortConfig({ key, direction });
  };

  const handleDownloadImage = async () => {
    const element = document.getElementById('data-table-export');
    if (!element) return;
    try {
        const canvas = await html2canvas(element, { backgroundColor: '#1f1f1f' }); // Dark bg match
        const dataUrl = canvas.toDataURL('image/png');
        const link = document.createElement('a');
        link.download = `${data.title || 'data-table'}.png`;
        link.href = dataUrl;
        link.click();
    } catch (err) {
        console.error("Export failed:", err);
    }
  };

  return (
    <div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">

      <div className="bg-[#1f1f1f] rounded-2xl w-full max-w-5xl h-[85vh] flex flex-col shadow-2xl border border-white/10 overflow-hidden">

        

        {/* Header */}

        <div className="p-6 border-b border-white/10 flex justify-between items-center bg-[#1f1f1f]">

          <div>

             <h2 className="text-2xl font-semibold text-white mb-1">{data.title || "Data Table"}</h2>

             <p className="text-gray-400 text-sm">AI-extracted comparative analysis</p>

          </div>

          <div className="flex gap-3">

             <button

               onClick={handleDownloadImage}

               className="flex items-center gap-2 px-4 py-2 bg-purple-600/20 hover:bg-purple-600/30 text-purple-300 rounded-lg transition-colors border border-purple-500/30 font-medium"

             >

               <Download className="w-4 h-4" />

               Export PNG

             </button>

            <button 

              onClick={onClose}

              className="p-2 hover:bg-white/10 rounded-full transition-colors text-gray-400 hover:text-white"

            >

              <X className="w-6 h-6" />

            </button>

          </div>

        </div>



        {/* Content - Scrollable */}

        <div className="flex-1 overflow-auto p-6 bg-[#121212]" id="data-table-export">

            <div className="bg-[#1f1f1f] rounded-xl border border-white/10 overflow-hidden">

                 <div className="overflow-x-auto">

                    <table className="w-full text-left border-collapse">

                        <thead>

                            <tr className="bg-white/5 border-b border-white/10">

                                <th className="p-4 text-sm font-semibold text-gray-300 border-r border-white/10 min-w-[200px]">

                                    Entity

                                </th>

                                {data.columns?.map(col => (

                                    <th 

                                        key={col.key}

                                        onClick={() => requestSort(col.key)}

                                        className="p-4 text-sm font-semibold text-gray-300 border-r border-white/10 cursor-pointer hover:bg-white/5 min-w-[150px] group select-none"

                                    >

                                        <div className="flex items-center gap-2">

                                            {col.label}

                                            <span className="text-gray-600 group-hover:text-gray-400">

                                                {sortConfig.key === col.key ? (

                                                    sortConfig.direction === 'ascending' ? <ArrowUp className="w-3 h-3"/> : <ArrowDown className="w-3 h-3"/>

                                                ) : <ArrowUpDown className="w-3 h-3"/>}

                                            </span>

                                        </div>

                                    </th>

                                ))}

                            </tr>

                        </thead>

                        <tbody>

                            {sortedRows.length > 0 ? (

                                sortedRows.map((row, idx) => (

                                    <tr key={idx} className="border-b border-white/5 hover:bg-white/5 transition-colors">

                                        <td className="p-4 text-white font-medium border-r border-white/10 bg-white/5">

                                            {row.entity}

                                        </td>

                                        {data.columns?.map(col => (

                                            <td key={col.key} className="p-4 text-gray-300 border-r border-white/10 text-sm">

                                                {row.values[col.key] || '-'}

                                            </td>

                                        ))}

                                    </tr>

                                ))

                            ) : (

                                <tr>

                                    <td colSpan={data.columns?.length + 1} className="p-8 text-center text-gray-500 italic">

                                        No data available.

                                    </td>

                                </tr>

                            )}

                        </tbody>

                    </table>

                 </div>

            </div>

            

            <div className="mt-4 text-center text-xs text-gray-600">

                Generated by Vertical.ai • Research Assistant

            </div>

        </div>



      </div>

    </div>
  );
};

export default DataTableModal;