← Components/Tables

DataTable

datatable-1779378817400.tsx
'use client'
import { useState, useMemo } from 'react'

const ROWS = Array.from({ length: 32 }, (_, i) => ({
  id: i + 1,
  name: ['Aurora Dashboard', 'Nexus Portal', 'Helix Analytics', 'Vertex CRM', 'Pulse Monitor', 'Orbit Tracker', 'Zenith Suite', 'Apex Workflow'][i % 8] + (i >= 8 ? ' Pro' : ''),
  status: (['active', 'paused', 'draft', 'archived'] as const)[i % 4],
  users: Math.floor(Math.random() * 10000 + 100),
  revenue: Math.floor(Math.random() * 50000 + 1000),
  updated: `${2024 + (i % 2)}-${String((i % 12) + 1).padStart(2,'0')}-${String((i % 28) + 1).padStart(2,'0')}`,
}))

const STATUS_COLORS = { active: '#22c55e', paused: '#f59e0b', draft: '#6366f1', archived: '#64748b' }

type SortKey = 'name' | 'users' | 'revenue' | 'updated'

export default function DataTable() {
  const [sort, setSort] = useState<{ key: SortKey; dir: 1 | -1 }>({ key: 'revenue', dir: -1 })
  const [page, setPage] = useState(0)
  const [filter, setFilter] = useState('')
  const [selected, setSelected] = useState<Set<number>>(new Set())
  const PER = 8

  const rows = useMemo(() => {
    let r = ROWS.filter(row => row.name.toLowerCase().includes(filter.toLowerCase()))
    r.sort((a, b) => {
      const av = a[sort.key], bv = b[sort.key]
      return typeof av === 'number' ? (av - (bv as number)) * sort.dir : String(av).localeCompare(String(bv)) * sort.dir
    })
    return r
  }, [sort, filter])

  const page_rows = rows.slice(page * PER, (page + 1) * PER)
  const total_pages = Math.ceil(rows.length / PER)

  function toggleSort(key: SortKey) {
    setSort(s => s.key === key ? { key, dir: s.dir === 1 ? -1 : 1 } : { key, dir: -1 })
    setPage(0)
  }

  const SortIcon = ({ k }: { k: SortKey }) => (
    <span style={{ marginLeft: 4, opacity: sort.key === k ? 1 : 0.3, fontSize: 10 }}>{sort.key === k ? (sort.dir === 1 ? '▲' : '▼') : '▼'}</span>
  )

  return (
    <div style={{ background: '#0D0D0D', border: '1px solid rgba(255,255,255,0.06)', borderRadius: 16, overflow: 'hidden' }}>
      <div style={{ padding: '16px 20px', borderBottom: '1px solid rgba(255,255,255,0.06)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ color: '#F5F5F0', fontSize: 15, fontWeight: 600 }}>Projects {selected.size > 0 && <span style={{ color: '#C9A84C', fontSize: 13, marginLeft: 8 }}>{selected.size} selected</span>}</div>
        <input placeholder="Search..." value={filter} onChange={e => { setFilter(e.target.value); setPage(0) }}
          style={{ background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 8, color: '#F5F5F0', padding: '7px 12px', fontSize: 13, outline: 'none', width: 180 }} />
      </div>

      <div style={{ overflowX: 'auto' }}>
        <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
          <thead>
            <tr style={{ borderBottom: '1px solid rgba(255,255,255,0.06)' }}>
              <th style={th}><input type="checkbox" onChange={e => setSelected(e.target.checked ? new Set(page_rows.map(r => r.id)) : new Set())} /></th>
              {(['name', 'status', 'users', 'revenue', 'updated'] as const).map(k => (
                <th key={k} onClick={() => k !== 'status' && toggleSort(k as SortKey)} style={{ ...th, cursor: k !== 'status' ? 'pointer' : 'default', textAlign: k === 'users' || k === 'revenue' ? 'right' : 'left' }}>
                  {k.charAt(0).toUpperCase() + k.slice(1)}{k !== 'status' && <SortIcon k={k as SortKey} />}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {page_rows.map(row => (
              <tr key={row.id} style={{ borderBottom: '1px solid rgba(255,255,255,0.04)', background: selected.has(row.id) ? 'rgba(201,168,76,0.05)' : 'transparent' }}>
                <td style={td}><input type="checkbox" checked={selected.has(row.id)} onChange={e => setSelected(s => { const n = new Set(s); e.target.checked ? n.add(row.id) : n.delete(row.id); return n })} /></td>
                <td style={td}><span style={{ color: '#F5F5F0', fontWeight: 500 }}>{row.name}</span></td>
                <td style={td}><span style={{ background: STATUS_COLORS[row.status] + '20', color: STATUS_COLORS[row.status], padding: '2px 8px', borderRadius: 20, fontSize: 11, fontWeight: 600 }}>{row.status}</span></td>
                <td style={{ ...td, textAlign: 'right', color: 'rgba(255,255,255,0.7)' }}>{row.users.toLocaleString()}</td>
                <td style={{ ...td, textAlign: 'right', color: '#C9A84C', fontWeight: 600 }}>${row.revenue.toLocaleString()}</td>
                <td style={{ ...td, color: 'rgba(255,255,255,0.4)' }}>{row.updated}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <div style={{ padding: '12px 20px', borderTop: '1px solid rgba(255,255,255,0.06)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <span style={{ color: 'rgba(255,255,255,0.4)', fontSize: 12 }}>{rows.length} results</span>
        <div style={{ display: 'flex', gap: 4 }}>
          {Array.from({ length: total_pages }, (_, i) => (
            <button key={i} onClick={() => setPage(i)} style={{ width: 28, height: 28, borderRadius: 6, border: '1px solid rgba(255,255,255,0.1)', background: page === i ? '#C9A84C' : 'transparent', color: page === i ? '#0A0A0A' : 'rgba(255,255,255,0.5)', cursor: 'pointer', fontSize: 12, fontWeight: 600 }}>{i + 1}</button>
          ))}
        </div>
      </div>
    </div>
  )
}
const th: React.CSSProperties = { padding: '10px 12px', color: 'rgba(255,255,255,0.4)', fontWeight: 600, textAlign: 'left', fontSize: 11, userSelect: 'none', textTransform: 'uppercase', letterSpacing: '0.05em' }
const td: React.CSSProperties = { padding: '11px 12px', color: 'rgba(255,255,255,0.7)' }

Component info

CategoryTables
Frameworkreact
TierFREE
Views0
Copies0

About

Sortable data table with pagination, column filters, row selection, and status badges