Files
GMW/services/frontend/src/components/dashboard/reactions-section.tsx
T
asepharyana 4f9d4a5c7d refactor: remove unused UI components and replace GlassCard with Card in voice components
- Deleted Item, Kbd, Marker, Message, NativeSelect, Questionnaire, Spinner components.
- Replaced GlassCard with Card in VoiceActivityTimeline, VoiceConnectionCard, ListenControl, MicControl, and SpeakerWaveform components.
- Introduced AppSidebar and ThemeToggle components for improved navigation and theme management.
2026-08-07 17:33:12 +07:00

142 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { Flame, Heart, SmilePlus } from "lucide-react";
import { EmptyState, LoadingSkeleton } from "@/components/shared";
import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import { useTopReactions, useTopReactors } from "@/hooks";
import { renderMessageContent } from "@/lib/format";
import { cn } from "@/lib/utils";
function formatReactionTime(ts: number | null): string {
if (!ts) return "";
const diff = Date.now() - ts;
const hours = Math.floor(diff / 3600000);
if (hours < 1) return "baru saja";
if (hours < 24) return `${hours} jam lalu`;
const days = Math.floor(hours / 24);
return `${days} hari lalu`;
}
export function ReactionsSection() {
const { data: reactions, isLoading: reactionsLoading } = useTopReactions();
const { data: reactors, isLoading: reactorsLoading } = useTopReactors();
return (
<div className="space-y-4">
<div>
<h3 className="mb-2 flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-text-secondary/50">
<Heart className="size-3" />
Top pesan paling di-reaksi
</h3>
{reactionsLoading ? (
<LoadingSkeleton count={5} height="h-16" />
) : !reactions || reactions.length === 0 ? (
<Card className={cn("p-6", "[--card-spacing:0px]", "rounded-2xl")}>
<EmptyState
icon={Heart}
title="Belum ada reaksi"
description="Pesan dengan reaksi emoji akan muncul di sini."
/>
</Card>
) : (
<div className="space-y-2">
{reactions.map((r, i) => (
<Card
key={r.message_id}
className={cn(
"flex items-center gap-3 p-3",
"[--card-spacing:0px]",
"rounded-2xl",
)}
>
<span className="w-6 shrink-0 text-center font-mono text-xs text-text-secondary/50">
{i + 1}
</span>
<div className="flex shrink-0 gap-0.5 text-base">
{r.top_emojis.map((e) => (
<span
key={`${r.message_id}-${e.emoji}`}
title={`${e.emoji} ×${e.count}`}
>
{e.emoji}
</span>
))}
{r.top_emojis.length === 0 && (
<Heart className="size-4 text-text-secondary/30" />
)}
</div>
<div className="min-w-0 flex-1">
<p className="line-clamp-1 text-xs text-text-secondary">
{renderMessageContent(r.content, undefined) ||
"(tanpa teks)"}
</p>
<p className="mt-0.5 truncate text-[10px] font-mono text-text-secondary/40">
{r.username ?? "unknown"} · #
{r.channel_name ?? r.channel_id?.slice(0, 8)} ·{" "}
{formatReactionTime(r.created_at)}
</p>
</div>
<Badge variant="secondary" className="shrink-0 gap-1">
<Heart className="size-3" />
{r.reaction_count}
</Badge>
</Card>
))}
</div>
)}
</div>
<div>
<h3 className="mb-2 flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-text-secondary/50">
<Flame className="size-3" />
Top reaktor paling sering ngasih reaksi
</h3>
{reactorsLoading ? (
<LoadingSkeleton count={5} height="h-14" />
) : !reactors || reactors.length === 0 ? (
<Card className={cn("p-6", "[--card-spacing:0px]", "rounded-2xl")}>
<EmptyState
icon={SmilePlus}
title="Belum ada reaktor"
description="User yang ngasih reaksi emoji akan muncul di sini."
/>
</Card>
) : (
<div className="space-y-2">
{reactors.map((r, i) => (
<Card
key={r.user_id}
className={cn(
"flex items-center gap-3 p-3",
"[--card-spacing:0px]",
"rounded-2xl",
)}
>
<span className="w-6 shrink-0 text-center font-mono text-xs text-text-secondary/50">
{i + 1}
</span>
<div className="min-w-0 flex-1">
<p className="truncate text-xs font-medium text-text-primary">
{r.username}
</p>
<p className="mt-0.5 truncate text-[10px] font-mono text-text-secondary/40">
{r.messages_reacted} pesan di-reaksi · {r.emojis_used} emoji
unik · {r.adds_count} total reaksi
</p>
</div>
<Badge variant="secondary" className="shrink-0 gap-1">
<Flame className="size-3" />
{r.net_count}
</Badge>
</Card>
))}
</div>
)}
</div>
</div>
);
}
export default ReactionsSection;