aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/SubscriberTable/TwitchDataTable.tsx
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-05-06 00:05:25 -0700
committerPinapelz <yukais@pinapelz.com>2025-05-06 00:10:40 -0700
commit913f28e2f27830192a1c80270612d8314eed3353 (patch)
tree01bdf287a3b7c2370b37b3b1eba0be4ba5868479 /src/components/SubscriberTable/TwitchDataTable.tsx
parent83c3fa302adbc4c3adf63c59cfe87b51d83ecbcb (diff)
phase_tracker_only: implement the twitch table
Diffstat (limited to 'src/components/SubscriberTable/TwitchDataTable.tsx')
-rw-r--r--src/components/SubscriberTable/TwitchDataTable.tsx182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/components/SubscriberTable/TwitchDataTable.tsx b/src/components/SubscriberTable/TwitchDataTable.tsx
new file mode 100644
index 0000000..3c0680c
--- /dev/null
+++ b/src/components/SubscriberTable/TwitchDataTable.tsx
@@ -0,0 +1,182 @@
+"use client";
+import React, { useState } from "react";
+import ChannelRow from "./TwitchTableRow";
+
+interface TwitchChannelDataProp {
+ channel_name: string;
+ profile_pic: string;
+ subscribers: number;
+ sub_org: string;
+ twitch_followers: number;
+ total_sum: number;
+ max_following?: number;
+}
+
+
+interface TwitchDataTableProp {
+ channel_data: TwitchChannelDataProp[];
+ timestamp: string;
+}
+
+type SortKey = keyof TwitchChannelDataProp | "rank" | "max_following";
+
+const TwitchDataTable = ({ channel_data, timestamp }: TwitchDataTableProp) => {
+ const [sortKey, setSortKey] = useState<SortKey>("twitch_followers");
+ const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
+ const [indexName, setIndexName] = useState<string>("RANK");
+
+ const handleSort = (key: SortKey) => {
+ if (sortKey === key) {
+ setSortOrder(sortOrder === "asc" ? "desc" : "asc");
+ } else {
+ setSortKey(key);
+ setSortOrder("desc");
+ }
+ setIndexName(key === "sub_org" ? "INDEX" : "RANK");
+ };
+
+ const dataWithMax = channel_data.map((channel) => ({
+ ...channel,
+ max_following: Math.max(
+ channel.subscribers || 0,
+ channel.twitch_followers || 0
+ ),
+ }));
+
+ const sortedData = [...dataWithMax].sort((a, b) => {
+ let aValue: any, bValue: any;
+ if (sortKey === "rank") {
+ aValue = dataWithMax.indexOf(a) + 1;
+ bValue = dataWithMax.indexOf(b) + 1;
+ } else {
+ aValue = a[sortKey];
+ bValue = b[sortKey];
+ }
+ if (typeof aValue === "string") {
+ return sortOrder === "asc"
+ ? aValue.localeCompare(bValue)
+ : bValue.localeCompare(aValue);
+ }
+ return sortOrder === "asc" ? aValue - bValue : bValue - aValue;
+ });
+
+ return (
+ <>
+ <div className="sm:hidden text-center text-red-600 font-semibold my-2">
+ Limited data shown on mobile view!
+ </div>
+ <div className="text-center sm:mt-5" style={{ fontFamily: "Quantico, sans-serif" }}>
+ <h1 className="text-2xl font-bold text-gray-800">The Twitch Table</h1>
+ <p className="text-gray-500 text-sm">Updated Hourly. Retrieved at: {timestamp}</p>
+ </div>
+
+ <div className="flex justify-center mt-2 mb-4">
+ <div className="bg-gray-100 rounded-lg p-4 shadow-sm text-sm md:text-base max-w-2xl">
+ {/* Legend wrapper - stacked sections on all screen sizes */}
+ <div className="flex flex-col gap-3">
+
+ {/* Column explanations */}
+ <div>
+ <h3 className="font-bold text-gray-800 mb-1">Column Explanations</h3>
+ <div>
+ <div className="flex items-start mb-1">
+ <span className="font-medium text-gray-700 mr-2 whitespace-nowrap">SUM(YT+TTV):</span>
+ <span className="text-gray-600">Total combined followers across both platforms</span>
+ </div>
+ <div className="flex items-start">
+ <span className="font-medium text-gray-700 mr-2 whitespace-nowrap">MAX(YT,TTV):</span>
+ <span className="text-gray-600">Highest follower count between platforms</span>
+ </div>
+ </div>
+ </div>
+
+ {/* Horizontal divider */}
+ <div className="border-t border-gray-300 w-full"></div>
+
+ {/* Color key */}
+ <div>
+ <h3 className="font-bold text-gray-800 mb-1">MAX Column Color Key</h3>
+ <div>
+ <div className="flex items-center mb-1">
+ <div className="h-3 w-3 bg-red-600 rounded-full mr-2 flex-shrink-0"></div>
+ <span className="text-gray-600">YouTube subscriber count is higher</span>
+ </div>
+ <div className="flex items-center">
+ <div className="h-3 w-3 bg-purple-600 rounded-full mr-2 flex-shrink-0"></div>
+ <span className="text-gray-600">Twitch follower count is higher</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div className="px-2 sm:px-48 py-4 sm:py-8 relative rounded-l text-left overflow-auto">
+ <table className="w-full text-m sm:text-xl text-black bg-white">
+ <thead
+ className="text-m sm:text-lg text-white rounded-md select-none"
+ style={{ backgroundColor: "black" }}
+ >
+ <tr>
+ <th className="py-1 px-1 sm:px-3 hidden sm:table-cell">{indexName}</th>
+ <th className="py-1 px-1 sm:px-3">CHANNEL</th>
+ <th
+ className="py-1 px-1 sm:px-3 hidden sm:table-cell cursor-pointer"
+ onClick={() => handleSort("sub_org")}
+ >
+ GROUP
+ {sortKey === "sub_org" && (
+ <span className="ml-1">{sortOrder === "asc" ? "▲" : "▼"}</span>
+ )}
+ </th>
+ <th
+ className="py-1 px-1 sm:px-3 cursor-pointer"
+ onClick={() => handleSort("subscribers")}
+ >
+ YOUTUBE SUBS
+ {sortKey === "subscribers" && (
+ <span className="ml-1">{sortOrder === "asc" ? "▲" : "▼"}</span>
+ )}
+ </th>
+ <th
+ className="py-1 px-1 sm:px-3 cursor-pointer"
+ onClick={() => handleSort("twitch_followers")}
+ >
+ TWITCH FOLLOWS
+ {sortKey === "twitch_followers" && (
+ <span className="ml-1">{sortOrder === "asc" ? "▲" : "▼"}</span>
+ )}
+ </th>
+ <th
+ className="py-1 px-1 sm:px-3 cursor-pointer"
+ onClick={() => handleSort("total_sum")}
+ >
+ SUM(YT+TTV)
+ {sortKey === "total_sum" && (
+ <span className="ml-1">{sortOrder === "asc" ? "▲" : "▼"}</span>
+ )}
+ </th>
+ <th
+ className="py-1 px-1 sm:px-3 cursor-pointer"
+ onClick={() => handleSort("max_following")}
+ >
+ MAX(YT, TTV)
+ {sortKey === "max_following" && (
+ <span className="ml-1">{sortOrder === "asc" ? "▲" : "▼"}</span>
+ )}
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ {sortedData.map((channel, index) => (
+ <ChannelRow key={index} channel={channel} index={index} />
+ ))}
+ </tbody>
+ </table>
+ </div>
+ </>
+ );
+};
+
+export default TwitchDataTable;
+export type { TwitchDataTableProp, TwitchChannelDataProp };
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage