vkashti / app / admin / quiz / OnlineTeams.tsx
OnlineTeams.tsx
Raw
'use client';
import React, { useEffect, useState } from 'react';
import { createClient } from '@/utils/supabase/client';
import { motion, AnimatePresence } from 'framer-motion';

type OnlineTeam = {
  team: string;
  online_at: string;
  status?: 'active' | 'inactive';
};

type PresenceState = {
  [key: string]: {
    team: string;
    online_at: string;
    status?: 'active' | 'inactive';
  }[];
};

export default function OnlineTeams() {
  const [onlineTeams, setOnlineTeams] = useState<OnlineTeam[]>([]);
  const supabase = createClient();

  useEffect(() => {
    const channel = supabase.channel('online_teams');

    channel.on('presence', { event: 'sync' }, () => {
      const newState = channel.presenceState() as PresenceState;
      const teams = Object.values(newState).flat();
      setOnlineTeams(teams);
    });

    channel.on('presence', { event: 'join' }, ({ newPresences }) => {
      const newTeams = newPresences as unknown as OnlineTeam[];
      setOnlineTeams((current) => [...current, ...newTeams]);
    });

    channel.on('presence', { event: 'leave' }, ({ leftPresences }) => {
      const leftTeams = leftPresences as unknown as OnlineTeam[];
      setOnlineTeams((current) => 
        current.filter((team) => 
          !leftTeams.some((left) => left.team === team.team)
        )
      );
    });

    channel.subscribe();

    return () => {
      channel.unsubscribe();
    };
  }, [supabase]);

  return (
    <div className="bg-white/60 backdrop-blur-sm rounded-xl p-6 shadow-sm border border-amber-100/50">
      <div className="text-2xl font-semibold text-amber-900 mb-4">
        Активни отбори ({onlineTeams.length})
      </div>
      <AnimatePresence>
        <div className="space-y-2">
          {onlineTeams.map((team) => (
            <motion.div
              key={team.team}
              initial={{ opacity: 0, x: -20 }}
              animate={{ opacity: 1, x: 0 }}
              exit={{ opacity: 0, x: 20 }}
              className={`flex items-center justify-between p-3 rounded-lg border ${
                team.status === 'inactive' 
                  ? 'bg-gray-200/70 border-gray-300/30 text-gray-500' 
                  : 'bg-white/40 border-amber-100/30'
              }`}
            >
              <div className="flex items-center space-x-3">
                <div className={`h-2 w-2 rounded-full ${
                  team.status === 'inactive' ? 'bg-gray-400' : 'bg-green-500'
                }`} />
                <span className={`text-lg ${
                  team.status === 'inactive' ? 'text-gray-600' : 'text-amber-900'
                }`}>{team.team}</span>
              </div>
              <span className={`text-sm ${
                team.status === 'inactive' ? 'text-gray-500/60' : 'text-amber-700/60'
              }`}>
                {new Date(team.online_at).toLocaleTimeString()}
              </span>
            </motion.div>
          ))}
        </div>
      </AnimatePresence>
    </div>
  );
}