{"version":3,"sources":["../src/CustomTable.tsx"],"sourcesContent":["import { ChangeEvent, useMemo, useState } from \"react\";\nimport \"./CustomTable.css\";\n\n// Column interface: describes a table column, with label, key, and optional sortable flag\ninterface Column<T> {\n  label: string;\n  key: keyof T;\n  sortable?: boolean;\n}\n\n// Props for the CustomTable: array of data and columns\ninterface CustomTableProps<T> {\n  data: T[];\n  columns: Column<T>[];\n}\n\n// Generic CustomTable component\nconst CustomTable = <T extends Record<string, unknown>>({\n  data,\n  columns,\n}: CustomTableProps<T>) => {\n  // State for sorting, pagination, and search\n  const [sortKey, setSortKey] = useState<keyof T | null>(null);\n  const [sortDirection, setSortDirection] = useState<\"asc\" | \"desc\">(\"asc\");\n  const [rowsPerPage, setRowsPerPage] = useState<number>(10);\n  const [currentPage, setCurrentPage] = useState<number>(1);\n  const [searchQuery, setSearchQuery] = useState<string>(\"\");\n\n  // Filter data based on search query\n  const filteredData = useMemo(() => {\n    if (!searchQuery) return data;\n\n    return data.filter((row) =>\n      columns.some((col) => {\n        const cellValue = row[col.key];\n        // If value is a Date, format it before searching\n        if (cellValue instanceof Date) {\n          return new Intl.DateTimeFormat(\"en-us\")\n            .format(cellValue)\n            .toLowerCase()\n            .includes(searchQuery.toLowerCase());\n        }\n        // Otherwise, convert to string and search\n        return String(cellValue)\n          .toLowerCase()\n          .includes(searchQuery.toLowerCase());\n      })\n    );\n  }, [data, columns, searchQuery]);\n\n  // Sort filtered data based on sortKey and sortDirection\n  const sortedData = useMemo(() => {\n    if (!sortKey) return filteredData;\n    return [...filteredData].sort((a, b) => {\n      const aValue = a[sortKey] as string | number | Date;\n      const bValue = b[sortKey] as string | number | Date;\n\n      // Handle Date sorting\n      if (aValue instanceof Date || bValue instanceof Date) {\n        return sortDirection === \"asc\"\n          ? new Date(aValue).getTime() - new Date(bValue).getTime()\n          : new Date(bValue).getTime() - new Date(aValue).getTime();\n      }\n\n      // String/number sorting\n      return sortDirection === \"asc\"\n        ? String(aValue).localeCompare(String(bValue))\n        : String(bValue).localeCompare(String(aValue));\n    });\n  }, [filteredData, sortKey, sortDirection]);\n\n  // Paginate sorted data\n  const paginatedData = useMemo(() => {\n    const startIndex = (currentPage - 1) * rowsPerPage;\n    return sortedData.slice(startIndex, startIndex + rowsPerPage);\n  }, [sortedData, currentPage, rowsPerPage]);\n\n  // Calculate total number of pages\n  const totalPages = Math.ceil(filteredData.length / rowsPerPage);\n\n  // Handle sorting when a column header is clicked\n  const handleSort = (key: keyof T) => {\n    if (sortKey === key) {\n      setSortDirection(sortDirection === \"asc\" ? \"desc\" : \"asc\");\n    } else {\n      setSortKey(key);\n      setSortDirection(\"asc\");\n    }\n  };\n\n  // Handle page change (pagination)\n  const handlePageChange = (page: number) => {\n    if (page < 1 || page > totalPages) return;\n    setCurrentPage(page);\n  };\n\n  // Handle change in rows per page\n  const handleRowsPerPageChange = (event: ChangeEvent<HTMLSelectElement>) => {\n    setRowsPerPage(Number(event.target.value));\n    setCurrentPage(1);\n  };\n\n  // Handle search input change\n  const handleSearchChange = (event: ChangeEvent<HTMLInputElement>) => {\n    setSearchQuery(event.target.value);\n    setCurrentPage(1);\n  };\n\n  // No data to show\n  if (data.length === 0) return <p>No data to show</p>\n\n  return (\n    <div className=\"custom-table-wrapper\">\n      {/* Filter and search controls */}\n      <div className=\"custom-table-filter\">\n        <label htmlFor=\"rowsPerPage\">\n          Show\n          <select\n            id=\"rowsPerPage\"\n            value={rowsPerPage}\n            onChange={handleRowsPerPageChange}\n          >\n            {[10, 25, 50, 100].map((size) => (\n              <option key={size} value={size}>\n                {size}\n              </option>\n            ))}\n          </select>\n          entries\n        </label>\n        <div>\n          <label htmlFor=\"search\">Search:</label>\n          <input\n            id=\"search\"\n            type=\"text\"\n            value={searchQuery}\n            onChange={handleSearchChange}\n            placeholder=\"type to search...\"\n          />\n        </div>\n      </div>\n\n      {/* Table rendering */}\n      <table className=\"custom-table\">\n        <thead>\n          <tr>\n            {columns.map((column) => (\n              <th\n                key={String(column.key)}\n                onClick={() => column.sortable && handleSort(column.key)}\n              >\n                <div>\n                  {column.label}\n                  {/* Sort icon */}\n                  {column.sortable && (\n                    <span\n                      className={`custom-table-sort-icon ${\n                        sortKey === column.key ? \"custom-table-sort-active\" : \"\"\n                      }`}\n                    >\n                      {sortDirection === \"asc\" ? \"▲\" : \"▼\"}\n                    </span>\n                  )}\n                </div>\n              </th>\n            ))}\n          </tr>\n        </thead>\n        <tbody>\n          {paginatedData.map((row, index) => (\n            <tr key={index}>\n              {columns.map((column) => (\n                <td key={String(column.key)}>\n                  {/* Format Date values */}\n                  {row[column.key] instanceof Date\n                    ? new Intl.DateTimeFormat(\"en-us\").format(\n                        new Date(row[column.key] as Date)\n                      )\n                    : String(row[column.key])}\n                </td>\n              ))}\n            </tr>\n          ))}\n        </tbody>\n      </table>\n\n      {/* Pagination controls */}\n      <div className=\"custom-table-pagination\">\n        <span>\n          Showing {(currentPage - 1) * rowsPerPage + 1} to{\" \"}\n          {Math.min(currentPage * rowsPerPage, filteredData.length)} of{\" \"}\n          {filteredData.length} entries\n        </span>\n        <div>\n          Page {currentPage} of {totalPages}\n          <button\n            onClick={() => handlePageChange(currentPage - 1)}\n            disabled={currentPage === 1}\n          >\n            Previous\n          </button>\n          <button\n            onClick={() => handlePageChange(currentPage + 1)}\n            disabled={currentPage === totalPages}\n          >\n            Next\n          </button>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default CustomTable;\n"],"mappings":"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","names":[]}