refactor: large codebase cleanup - consolidate schemas, migrate to Drizzle ORM, extract frontend components, modernize Docker builds
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 2m22s
Build & Deploy / build-and-push (backend) (push) Failing after 3m22s
Build & Deploy / build-and-push (proxy) (push) Successful in 1m36s
Build & Deploy / deploy (push) Skipped

- Consolidate all DB schema definitions into packages/shared as single source of truth
- Migrate backend from raw SQL to Drizzle ORM across all modules
- Extract frontend inline UI into separate component files
- Refactor discord-gateway circuitBreaker into conversationState + moderationState
- Convert messageStore to Proxy singleton pattern
- Add validateBody/validateQuery middleware + Zod schemas for API endpoints
- Modernize Docker builds with multi-stage + pnpm deploy
- Migrate CI/CD from deployment to image-based pipeline
- Remove 60+ unused/dead files (~15K lines)
- Update color scheme from sky-blue to teal-cyan
- Move DB connection management to @bete/shared/database

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Developer
2026-07-27 21:54:31 +07:00
co-authored by Claude Opus 4.8
parent 63f21513bd
commit 5802d02e29
223 changed files with 11499 additions and 13350 deletions
@@ -0,0 +1,80 @@
"use client";
import { ChevronRight, Search, Users } from "lucide-react";
import Image from "next/image";
import { useState } from "react";
import { EmptyState, LoadingSkeleton } from "@/components/shared";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { useUsers } from "@/hooks";
export function UsersSection({ onSelect }: { onSelect: (id: string) => void }) {
const [search, setSearch] = useState("");
const { data: users, isLoading } = useUsers(search || undefined);
return (
<div className="space-y-4 animate-fade-in-up">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
<Input
placeholder="Search users…"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="pl-9 h-9"
/>
</div>
{isLoading ? (
<LoadingSkeleton count={6} height="h-20" columns={2} />
) : !users || users.length === 0 ? (
<EmptyState icon={Users} title="No users found." />
) : (
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{users.map((u) => (
<Card
key={u.user_id}
className="cursor-pointer hover:bg-accent/5 transition-colors"
onClick={() => onSelect(u.user_id)}
>
<CardContent className="p-3">
<div className="flex items-center gap-3">
<div className="size-10 shrink-0 rounded-full bg-muted flex items-center justify-center text-sm font-medium overflow-hidden ring-1 ring-border">
{u.avatar_url ? (
<Image
src={u.avatar_url}
alt=""
width={40}
height={40}
className="size-full object-cover"
/>
) : (
(u.username ?? "?").charAt(0).toUpperCase()
)}
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium truncate">
{u.username ?? "Unknown"}
</p>
<p className="text-xs text-muted-foreground flex items-center gap-2">
<span>{u.total_messages} messages</span>
{u.flagged_count > 0 && (
<Badge
variant="destructive"
className="text-[10px] px-1.5 py-0 h-4"
>
{u.flagged_count} flagged
</Badge>
)}
</p>
</div>
<ChevronRight className="size-4 text-muted-foreground shrink-0" />
</div>
</CardContent>
</Card>
))}
</div>
)}
</div>
);
}