mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(panel): resolve undefined font vars + remove dead client code
- layout.tsx/globals.css: font-sans/font-mono pointed at undefined --font-geist-* vars (Geist-starter leftover); now resolve to the actually-loaded Inter via --font-inter. No visible change. - remove orphaned company-goals-card.tsx (unmounted; live charter UI is goals-tab.tsx) - remove dead streamApi transcription REST client (zero consumers) + its barrel export; AgentStreamViewer (a different, live websocket-based component) untouched.
This commit is contained in:
@@ -7,8 +7,8 @@
|
|||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--font-sans: var(--font-geist-sans);
|
--font-sans: var(--font-inter);
|
||||||
--font-mono: var(--font-geist-mono);
|
--font-mono: var(--font-inter);
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Inter } from "next/font/google";
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { Providers } from "@/components/providers";
|
import { Providers } from "@/components/providers";
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "RoboCo Control Panel",
|
title: "RoboCo Control Panel",
|
||||||
@@ -17,7 +17,7 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body className={inter.className}>
|
<body className={`${inter.className} ${inter.variable}`}>
|
||||||
<Providers>{children}</Providers>
|
<Providers>{children}</Providers>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
||||||
import {
|
|
||||||
companyGoalsApi,
|
|
||||||
type CompanyGoalsUpdate,
|
|
||||||
} from "@/lib/api/company-goals";
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Target, Save } from "lucide-react";
|
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
function parseError(e: unknown): string {
|
|
||||||
return e instanceof Error ? e.message : "parse error";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CompanyGoalsCard() {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
// null = "show the server value"; deriving the displayed value avoids syncing
|
|
||||||
// query state into local state with an effect (react-hooks/set-state-in-effect).
|
|
||||||
const [northStar, setNorthStar] = useState<string | null>(null);
|
|
||||||
const [constraints, setConstraints] = useState<string | null>(null);
|
|
||||||
const [objectives, setObjectives] = useState<string | null>(null);
|
|
||||||
const [policy, setPolicy] = useState<string | null>(null);
|
|
||||||
const [brandVoice, setBrandVoice] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const { data: goals, isLoading } = useQuery({
|
|
||||||
queryKey: ["company-goals"],
|
|
||||||
queryFn: companyGoalsApi.get,
|
|
||||||
});
|
|
||||||
|
|
||||||
const northStarVal = northStar ?? goals?.north_star ?? "";
|
|
||||||
const constraintsVal = constraints ?? (goals?.constraints ?? []).join("\n");
|
|
||||||
const objectivesVal =
|
|
||||||
objectives ?? JSON.stringify(goals?.objectives ?? [], null, 2);
|
|
||||||
const policyVal =
|
|
||||||
policy ?? JSON.stringify(goals?.operating_policy ?? {}, null, 2);
|
|
||||||
const brandVoiceVal = brandVoice ?? goals?.brand_voice ?? "";
|
|
||||||
|
|
||||||
const saveMutation = useMutation({
|
|
||||||
mutationFn: (update: CompanyGoalsUpdate) => companyGoalsApi.update(update),
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["company-goals"] });
|
|
||||||
setNorthStar(null);
|
|
||||||
setConstraints(null);
|
|
||||||
setObjectives(null);
|
|
||||||
setPolicy(null);
|
|
||||||
setBrandVoice(null);
|
|
||||||
toast.success("Company charter updated");
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
toast.error(`Failed to save: ${parseError(error)}`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
let parsedObjectives: Record<string, unknown>[];
|
|
||||||
let parsedPolicy: Record<string, unknown>;
|
|
||||||
try {
|
|
||||||
parsedObjectives = JSON.parse(objectivesVal);
|
|
||||||
if (!Array.isArray(parsedObjectives)) {
|
|
||||||
throw new Error("must be a JSON array");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`Objectives: invalid JSON — ${parseError(e)}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
parsedPolicy = JSON.parse(policyVal);
|
|
||||||
if (
|
|
||||||
typeof parsedPolicy !== "object" ||
|
|
||||||
parsedPolicy === null ||
|
|
||||||
Array.isArray(parsedPolicy)
|
|
||||||
) {
|
|
||||||
throw new Error("must be a JSON object");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`Operating policy: invalid JSON — ${parseError(e)}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
saveMutation.mutate({
|
|
||||||
north_star: northStarVal,
|
|
||||||
objectives: parsedObjectives,
|
|
||||||
constraints: constraintsVal
|
|
||||||
.split("\n")
|
|
||||||
.map((c) => c.trim())
|
|
||||||
.filter(Boolean),
|
|
||||||
operating_policy: parsedPolicy,
|
|
||||||
brand_voice: brandVoiceVal,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Target className="h-5 w-5" />
|
|
||||||
Company Charter
|
|
||||||
</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
The CEO-owned north star, objectives, constraints, operating policy,
|
|
||||||
and brand voice. Injected into every agent's briefing so all
|
|
||||||
work stays goal-aware.
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="north-star">North star</Label>
|
|
||||||
<Textarea
|
|
||||||
id="north-star"
|
|
||||||
rows={3}
|
|
||||||
value={northStarVal}
|
|
||||||
disabled={isLoading}
|
|
||||||
onChange={(e) => setNorthStar(e.target.value)}
|
|
||||||
placeholder="The long-term vision in one or two sentences..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="brand-voice">Brand voice</Label>
|
|
||||||
<Textarea
|
|
||||||
id="brand-voice"
|
|
||||||
rows={3}
|
|
||||||
value={brandVoiceVal}
|
|
||||||
disabled={isLoading}
|
|
||||||
onChange={(e) => setBrandVoice(e.target.value)}
|
|
||||||
placeholder="Sample posts or a style description for the Head of Marketing's drafts..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="constraints">Constraints (one per line)</Label>
|
|
||||||
<Textarea
|
|
||||||
id="constraints"
|
|
||||||
rows={3}
|
|
||||||
value={constraintsVal}
|
|
||||||
disabled={isLoading}
|
|
||||||
onChange={(e) => setConstraints(e.target.value)}
|
|
||||||
placeholder={"AGPL only\nNo external data egress"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="objectives">Objectives (JSON array)</Label>
|
|
||||||
<Textarea
|
|
||||||
id="objectives"
|
|
||||||
rows={6}
|
|
||||||
value={objectivesVal}
|
|
||||||
disabled={isLoading}
|
|
||||||
onChange={(e) => setObjectives(e.target.value)}
|
|
||||||
className="font-mono text-xs"
|
|
||||||
placeholder='[{"metric": "NPS", "target": 50, "status": "active"}]'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="policy">Operating policy (JSON object)</Label>
|
|
||||||
<Textarea
|
|
||||||
id="policy"
|
|
||||||
rows={6}
|
|
||||||
value={policyVal}
|
|
||||||
disabled={isLoading}
|
|
||||||
onChange={(e) => setPolicy(e.target.value)}
|
|
||||||
className="font-mono text-xs"
|
|
||||||
placeholder='{"autonomy_level": "assisted", "monthly_budget_cap": 500}'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onClick={handleSave}
|
|
||||||
disabled={saveMutation.isPending || isLoading}
|
|
||||||
>
|
|
||||||
<Save className="h-4 w-4 mr-2" />
|
|
||||||
{saveMutation.isPending ? "Saving..." : "Save charter"}
|
|
||||||
</Button>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,6 @@ export { productsApi } from "./products";
|
|||||||
export { workSessionsApi } from "./work-sessions";
|
export { workSessionsApi } from "./work-sessions";
|
||||||
export { gitApi } from "./git";
|
export { gitApi } from "./git";
|
||||||
export { a2aApi } from "./a2a";
|
export { a2aApi } from "./a2a";
|
||||||
export { streamApi } from "./stream";
|
|
||||||
export { settingsApi } from "./settings";
|
export { settingsApi } from "./settings";
|
||||||
export { companyGoalsApi } from "./company-goals";
|
export { companyGoalsApi } from "./company-goals";
|
||||||
export { releaseApi } from "./release";
|
export { releaseApi } from "./release";
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
/**
|
|
||||||
* Stream API Client
|
|
||||||
*
|
|
||||||
* API functions for transcription and extraction operations.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import api from "./client";
|
|
||||||
import { isMockMode } from "@/lib/mock-data";
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Types
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export interface StreamChunkRequest {
|
|
||||||
channel_name: string;
|
|
||||||
audio_data: string; // Base64 encoded
|
|
||||||
chunk_index: number;
|
|
||||||
is_final?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StreamChunkResponse {
|
|
||||||
status: string;
|
|
||||||
chunk_index: number;
|
|
||||||
queued: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StreamCompleteRequest {
|
|
||||||
channel_name: string;
|
|
||||||
session_id?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StreamCompleteResponse {
|
|
||||||
status: string;
|
|
||||||
channel_name: string;
|
|
||||||
total_chunks: number;
|
|
||||||
transcription_id?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExtractionRequest {
|
|
||||||
content: string;
|
|
||||||
extraction_type: "tasks" | "decisions" | "action_items" | "summary";
|
|
||||||
context?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExtractionResponse {
|
|
||||||
extraction_type: string;
|
|
||||||
results: Record<string, unknown>[];
|
|
||||||
confidence: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TranscriptionStats {
|
|
||||||
total_transcriptions: number;
|
|
||||||
active_channels: number;
|
|
||||||
chunks_processed: number;
|
|
||||||
avg_processing_time_ms: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChannelPermissions {
|
|
||||||
channel_name: string;
|
|
||||||
can_transcribe: boolean;
|
|
||||||
can_extract: boolean;
|
|
||||||
rate_limit?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// API Client
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export const streamApi = {
|
|
||||||
// ===========================================================================
|
|
||||||
// TRANSCRIPTION ENDPOINTS
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit an audio chunk for transcription
|
|
||||||
*/
|
|
||||||
submitChunk: async (
|
|
||||||
request: StreamChunkRequest,
|
|
||||||
): Promise<StreamChunkResponse> => {
|
|
||||||
if (isMockMode()) {
|
|
||||||
return {
|
|
||||||
status: "queued",
|
|
||||||
chunk_index: request.chunk_index,
|
|
||||||
queued: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const { data } = await api.post<StreamChunkResponse>(
|
|
||||||
"/stream/chunk",
|
|
||||||
request,
|
|
||||||
);
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Complete a transcription session
|
|
||||||
*/
|
|
||||||
complete: async (
|
|
||||||
request: StreamCompleteRequest,
|
|
||||||
): Promise<StreamCompleteResponse> => {
|
|
||||||
if (isMockMode()) {
|
|
||||||
return {
|
|
||||||
status: "completed",
|
|
||||||
channel_name: request.channel_name,
|
|
||||||
total_chunks: 10,
|
|
||||||
transcription_id: `trans-${Date.now()}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const { data } = await api.post<StreamCompleteResponse>(
|
|
||||||
"/stream/complete",
|
|
||||||
request,
|
|
||||||
);
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// EXTRACTION ENDPOINTS
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract structured information from content
|
|
||||||
*/
|
|
||||||
extract: async (request: ExtractionRequest): Promise<ExtractionResponse> => {
|
|
||||||
if (isMockMode()) {
|
|
||||||
return {
|
|
||||||
extraction_type: request.extraction_type,
|
|
||||||
results: [],
|
|
||||||
confidence: 0.85,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const { data } = await api.post<ExtractionResponse>(
|
|
||||||
"/stream/extract",
|
|
||||||
request,
|
|
||||||
);
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// STATS & PERMISSIONS
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get transcription statistics
|
|
||||||
*/
|
|
||||||
getStats: async (): Promise<TranscriptionStats> => {
|
|
||||||
if (isMockMode()) {
|
|
||||||
return {
|
|
||||||
total_transcriptions: 100,
|
|
||||||
active_channels: 5,
|
|
||||||
chunks_processed: 1500,
|
|
||||||
avg_processing_time_ms: 250,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const { data } = await api.get<TranscriptionStats>("/stream/stats");
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all channel permissions
|
|
||||||
*/
|
|
||||||
getPermissions: async (): Promise<ChannelPermissions[]> => {
|
|
||||||
if (isMockMode()) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const { data } = await api.get<ChannelPermissions[]>("/stream/permissions");
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user