mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
* feat(api): x/video post history endpoints Approved or rejected drafts vanished from both queues permanently -- the listers exclude terminal statuses and no history surface existed, so a posted tweet or video was only findable in the raw task list. GET /x/posts/history and GET /video/posts/history (CEO-gated, bounded) return acted-on drafts newest-first with the posted platform ids and reject reasons from the draft markers. Route tests assert by identity, not emptiness: approve/reject commits the whole session, so prior tests' rows legitimately persist in the shared test DB. * feat(panel): Social page aggregating post queues and history New dashboard page composing the X and video post queues with one unified history section beneath them -- both platforms interleaved newest-first, kind and outcome badges, posted X ids linking to the live tweet, reject reasons shown. The command center's two full queue cards become a compact pending-counts card linking to the page, so the queues have one home instead of duplicated surfaces. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { xApi, videoApi } from "@/lib/api";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Share2 } from "lucide-react";
|
|
|
|
// Compact stand-in for the full X/video queues on the command center — the
|
|
// full queues (plus the unified history) moved to /social to avoid a
|
|
// duplicated surface. Reuses the same queries the queues themselves run
|
|
// (["x","posts"] / ["video","posts"]), so react-query dedups the fetch once
|
|
// /social's own queues are also mounted.
|
|
export function SocialSummaryCard({ className }: { className?: string }) {
|
|
const { data: xPosts } = useQuery({
|
|
queryKey: ["x", "posts"],
|
|
queryFn: () => xApi.listPosts(),
|
|
refetchInterval: 30000,
|
|
});
|
|
const { data: videoPosts } = useQuery({
|
|
queryKey: ["video", "posts"],
|
|
queryFn: () => videoApi.listPosts(),
|
|
refetchInterval: 30000,
|
|
});
|
|
|
|
const xCount = xPosts?.length ?? 0;
|
|
const videoCount = videoPosts?.length ?? 0;
|
|
const total = xCount + videoCount;
|
|
|
|
return (
|
|
<Card className={className}>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Share2 className="h-5 w-5" />
|
|
Social
|
|
{total > 0 && <Badge variant="secondary">{total}</Badge>}
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Held X and video drafts awaiting your approval.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="flex flex-wrap items-center justify-between gap-3">
|
|
<div className="flex gap-4 text-sm text-muted-foreground">
|
|
<span>
|
|
{xCount} X draft{xCount === 1 ? "" : "s"}
|
|
</span>
|
|
<span>
|
|
{videoCount} video draft{videoCount === 1 ? "" : "s"}
|
|
</span>
|
|
</div>
|
|
<Link href="/social" prefetch={false}>
|
|
<Button variant="outline" size="sm">
|
|
Open Social
|
|
</Button>
|
|
</Link>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|