mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat(tg): Mini App V6 — premium overhaul (#609)
* feat(tg): Mini App V6 — premium overhaul (design system, Chat parity, Metrics drilldown, CEO verbs) Design system: native type with tabular-numeral heroes (mono demoted to the wordmark), borderless elevated cards, floating dock, Telegram window-chrome painting via the theme bridge; Inbox moves behind a header bell with humanized notifications (UUIDs resolve to task names). Chat: honest Mine/Fleet split — participant-scoped CEO threads with real unread counts and mark-read, watched fleet threads with reply-as-CEO on task-linked conversations (watch-only otherwise), markdown transcripts, live pulse flashes, and a pinned Secretary live chat on the panel's SSE session runtime. Metrics: new tab with period-segmented spend hero, by-agent/team/model breakdowns, delivery + efficiency health, and a per-agent drilldown over usage time-series (agent_slug) + member scorecard. Board: tg-native grouped pipeline replacing the MobileTaskBoard wrapper; task sheet gains the CEO decide verbs (approve / request changes / unblock). Security: /api/dashboard router now require_panel_token-gated at router level (mirrors /api/usage), closing unauthenticated metrics exposure. * fix(tg): restore Share Tech Mono brand voice, Phosphor icon set, borderless avatars The mono returns as the numeral/brand voice (.tg-display — heroes, stat values, wordmark) while labels stay native sentence case. The hand-drawn duotone glyphs and lucide feature icons are replaced by Phosphor (MIT): duotone at rest via an IconContext at the shell, filled weight on the dock's active tab; row glyph maps (board statuses, inbox kinds, approval kinds, quick actions) all move over. Team avatar tiles drop their borders — tint-only squircles. * fix(tg): fleet avatar strip breathes — spaced tiles instead of overlap * polish(tg): taste-skill audit pass — em-dash purge, one icon family, separator rationing Applied the design-taste audit against the cockpit: every em-dash in visible UI copy rewritten (periods/commas/colons), the remaining lucide chrome (carets, arrows, send, close, spinners) moved to Phosphor so the tg tree ships one icon family (send is the native paper-plane, carets bold), the hand-rolled chevron SVG deleted, and metadata lines rationed to a single middle-dot separator. * polish(tg): pipeline chip strip scrolls without a visible scrollbar * fix(tests): metrics observability fixture uses a relative timestamp The hardcoded _T0 (2026-06-20) aged out of the service's 30-day window exactly 30 days later, detonating the suite on every branch. Two days back from now() stays inside every window (30d metrics, 7d scorecards) permanently. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -29,7 +29,9 @@ const CUT_LABELS: Record<VideoCut, string> = {
|
||||
*/
|
||||
function CutPlayer({ post }: { post: VideoPost }) {
|
||||
const paths = post.mp4_paths ?? {};
|
||||
const [cut, setCut] = useState<VideoCut>(paths.vertical ? "vertical" : "square");
|
||||
const [cut, setCut] = useState<VideoCut>(
|
||||
paths.vertical ? "vertical" : "square",
|
||||
);
|
||||
// url === null means the fetch for that cut failed; a stale entry for a
|
||||
// different cut is simply ignored in render, so no synchronous state
|
||||
// reset is needed when the cut changes.
|
||||
@@ -87,7 +89,9 @@ function CutPlayer({ post }: { post: VideoPost }) {
|
||||
playsInline
|
||||
className={cn(
|
||||
"w-full rounded-md bg-black",
|
||||
cut === "vertical" ? "aspect-[9/16] max-h-[60dvh]" : "aspect-square",
|
||||
cut === "vertical"
|
||||
? "aspect-[9/16] max-h-[60dvh]"
|
||||
: "aspect-square",
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
@@ -134,15 +138,15 @@ function CaptionEditor({
|
||||
disabled={!editing}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
rows={3}
|
||||
className={cn("text-sm", editing && overLimit && "border-destructive")}
|
||||
className={cn("text-sm", editing && overLimit && "border-rose-400/60")}
|
||||
/>
|
||||
<p
|
||||
className={cn(
|
||||
"text-right text-[11px] tabular-nums",
|
||||
editing && overLimit ? "text-destructive" : "text-muted-foreground",
|
||||
editing && overLimit ? "text-rose-400" : "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{caption.length}/{maxChars}
|
||||
{caption.length} / {maxChars}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -196,7 +200,8 @@ export function VideoPostDetail({
|
||||
|
||||
const reject = useMutation({
|
||||
mutationFn: (reason: string) => videoApi.reject(post.task_id, reason),
|
||||
onSuccess: () => finish(true, "Draft rejected — feedback goes back to the author."),
|
||||
onSuccess: () =>
|
||||
finish(true, "Draft rejected. Feedback goes back to the author."),
|
||||
onError: (err) => {
|
||||
haptics.error();
|
||||
toast.error(getErrorMessage(err));
|
||||
@@ -213,7 +218,9 @@ export function VideoPostDetail({
|
||||
</Badge>
|
||||
))}
|
||||
{post.render_status === "failed" && (
|
||||
<Badge variant="destructive">render failed</Badge>
|
||||
<span className="rounded-full bg-rose-500/15 px-2 py-0.5 text-xs font-medium text-rose-300">
|
||||
render failed
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user