mirror of
https://github.com/arkorty/B.Tech-Project-III.git
synced 2026-04-19 12:41:48 +00:00
init
This commit is contained in:
19
negot8/dashboard/lib/api.ts
Normal file
19
negot8/dashboard/lib/api.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// Thin API client for the negoT8 FastAPI backend
|
||||
import type { Negotiation, Stats } from "./types";
|
||||
|
||||
const BASE = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
|
||||
|
||||
async function get<T>(path: string): Promise<T> {
|
||||
const res = await fetch(`${BASE}${path}`, { cache: "no-store" });
|
||||
if (!res.ok) throw new Error(`GET ${path} → ${res.status}`);
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
stats: () => get<Stats>("/api/stats"),
|
||||
negotiations: () =>
|
||||
get<{ negotiations: Negotiation[]; total: number }>("/api/negotiations"),
|
||||
negotiation: (id: string) => get<Negotiation>(`/api/negotiations/${id}`),
|
||||
analytics: (id: string) =>
|
||||
get<Negotiation["analytics"]>(`/api/negotiations/${id}/analytics`),
|
||||
};
|
||||
37
negot8/dashboard/lib/socket.ts
Normal file
37
negot8/dashboard/lib/socket.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// Socket.IO client singleton — import this anywhere to get the shared socket
|
||||
|
||||
import { io, Socket } from "socket.io-client";
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
|
||||
|
||||
let socket: Socket | null = null;
|
||||
|
||||
export function getSocket(): Socket {
|
||||
if (!socket) {
|
||||
socket = io(API_URL, {
|
||||
transports: ["websocket", "polling"],
|
||||
autoConnect: true,
|
||||
reconnectionAttempts: 5,
|
||||
reconnectionDelay: 1500,
|
||||
});
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log("[Socket.IO] connected:", socket?.id);
|
||||
});
|
||||
socket.on("disconnect", (reason) => {
|
||||
console.log("[Socket.IO] disconnected:", reason);
|
||||
});
|
||||
socket.on("connect_error", (err) => {
|
||||
console.warn("[Socket.IO] connection error:", err.message);
|
||||
});
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
export function joinNegotiation(negotiationId: string) {
|
||||
getSocket().emit("join_negotiation", { negotiation_id: negotiationId });
|
||||
}
|
||||
|
||||
export function leaveNegotiation(negotiationId: string) {
|
||||
getSocket().emit("leave_negotiation", { negotiation_id: negotiationId });
|
||||
}
|
||||
104
negot8/dashboard/lib/types.ts
Normal file
104
negot8/dashboard/lib/types.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
// Shared TypeScript types mirroring the backend data models
|
||||
|
||||
export interface User {
|
||||
telegram_id: number;
|
||||
username: string;
|
||||
display_name: string;
|
||||
personality: Personality;
|
||||
voice_id: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export type Personality =
|
||||
| "aggressive"
|
||||
| "people_pleaser"
|
||||
| "analytical"
|
||||
| "empathetic"
|
||||
| "balanced";
|
||||
|
||||
export type NegotiationStatus = "pending" | "active" | "resolved" | "escalated";
|
||||
|
||||
export type FeatureType =
|
||||
| "scheduling"
|
||||
| "expenses"
|
||||
| "freelance"
|
||||
| "roommate"
|
||||
| "trip"
|
||||
| "marketplace"
|
||||
| "collaborative"
|
||||
| "conflict"
|
||||
| "generic";
|
||||
|
||||
export interface Participant {
|
||||
negotiation_id: string;
|
||||
user_id: number;
|
||||
preferences: Record<string, unknown>;
|
||||
personality_used: Personality;
|
||||
username?: string;
|
||||
display_name?: string;
|
||||
personality?: Personality;
|
||||
voice_id?: string;
|
||||
}
|
||||
|
||||
export interface Round {
|
||||
id: number;
|
||||
negotiation_id: string;
|
||||
round_number: number;
|
||||
proposer_id: number;
|
||||
proposal: Record<string, unknown>;
|
||||
response_type: "propose" | "counter" | "accept" | "escalate";
|
||||
response: Record<string, unknown> | null;
|
||||
reasoning: string;
|
||||
satisfaction_a: number;
|
||||
satisfaction_b: number;
|
||||
concessions_made: string[];
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface SatisfactionPoint {
|
||||
round: number;
|
||||
score_a: number;
|
||||
score_b: number;
|
||||
}
|
||||
|
||||
export interface ConcessionEntry {
|
||||
round: number;
|
||||
by: "A" | "B";
|
||||
gave_up: string;
|
||||
}
|
||||
|
||||
export interface Analytics {
|
||||
negotiation_id: string;
|
||||
satisfaction_timeline: SatisfactionPoint[];
|
||||
concession_log: ConcessionEntry[];
|
||||
fairness_score: number;
|
||||
total_concessions_a: number;
|
||||
total_concessions_b: number;
|
||||
computed_at: string;
|
||||
}
|
||||
|
||||
export interface Negotiation {
|
||||
id: string;
|
||||
feature_type: FeatureType;
|
||||
status: NegotiationStatus;
|
||||
initiator_id: number;
|
||||
resolution: Record<string, unknown> | null;
|
||||
voice_summary_file: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
participant_count?: number;
|
||||
// Only present in detail view
|
||||
participants?: Participant[];
|
||||
rounds?: Round[];
|
||||
analytics?: Analytics;
|
||||
}
|
||||
|
||||
export interface Stats {
|
||||
total_negotiations: number;
|
||||
resolved: number;
|
||||
active: number;
|
||||
escalated: number;
|
||||
total_users: number;
|
||||
avg_fairness_score: number;
|
||||
feature_breakdown: { feature_type: FeatureType; c: number }[];
|
||||
}
|
||||
86
negot8/dashboard/lib/utils.ts
Normal file
86
negot8/dashboard/lib/utils.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
// Shared UI helpers — badges, labels, colour maps
|
||||
|
||||
import type { FeatureType, NegotiationStatus, Personality } from "@/lib/types";
|
||||
|
||||
// ─── Feature ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const FEATURE_LABELS: Record<FeatureType, string> = {
|
||||
scheduling: "📅 Scheduling",
|
||||
expenses: "💰 Expenses",
|
||||
freelance: "💼 Freelance",
|
||||
roommate: "🏠 Roommate",
|
||||
trip: "✈️ Trip",
|
||||
marketplace: "🛒 Marketplace",
|
||||
collaborative: "🍕 Collaborative",
|
||||
conflict: "⚖️ Conflict",
|
||||
generic: "🤝 Generic",
|
||||
};
|
||||
|
||||
// ─── Personality ─────────────────────────────────────────────────────────────
|
||||
|
||||
export const PERSONALITY_LABELS: Record<Personality, string> = {
|
||||
aggressive: "😤 Aggressive",
|
||||
people_pleaser: "🤝 Pleaser",
|
||||
analytical: "📊 Analytical",
|
||||
empathetic: "💚 Empathetic",
|
||||
balanced: "⚖️ Balanced",
|
||||
};
|
||||
|
||||
export const PERSONALITY_COLORS: Record<Personality, string> = {
|
||||
aggressive: "bg-red-500/20 text-red-300 border-red-500/30",
|
||||
people_pleaser: "bg-blue-500/20 text-blue-300 border-blue-500/30",
|
||||
analytical: "bg-yellow-500/20 text-yellow-300 border-yellow-500/30",
|
||||
empathetic: "bg-green-500/20 text-green-300 border-green-500/30",
|
||||
balanced: "bg-purple-500/20 text-purple-300 border-purple-500/30",
|
||||
};
|
||||
|
||||
// ─── Status ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export const STATUS_LABELS: Record<NegotiationStatus, string> = {
|
||||
pending: "⏳ Pending",
|
||||
active: "🔄 Active",
|
||||
resolved: "✅ Resolved",
|
||||
escalated: "⚠️ Escalated",
|
||||
};
|
||||
|
||||
export const STATUS_COLORS: Record<NegotiationStatus, string> = {
|
||||
pending: "bg-zinc-500/20 text-zinc-400 border-zinc-500/30",
|
||||
active: "bg-blue-500/20 text-blue-300 border-blue-500/30 animate-pulse",
|
||||
resolved: "bg-green-500/20 text-green-300 border-green-500/30",
|
||||
escalated: "bg-amber-500/20 text-amber-300 border-amber-500/30",
|
||||
};
|
||||
|
||||
// ─── Fairness colour ─────────────────────────────────────────────────────────
|
||||
|
||||
export function fairnessColor(score: number): string {
|
||||
if (score >= 80) return "text-green-400";
|
||||
if (score >= 60) return "text-yellow-400";
|
||||
return "text-red-400";
|
||||
}
|
||||
|
||||
export function fairnessBarColor(score: number): string {
|
||||
if (score >= 80) return "bg-green-500";
|
||||
if (score >= 60) return "bg-yellow-500";
|
||||
return "bg-red-500";
|
||||
}
|
||||
|
||||
// ─── Satisfaction colour ─────────────────────────────────────────────────────
|
||||
|
||||
export function satColor(score: number): string {
|
||||
if (score >= 70) return "text-green-400";
|
||||
if (score >= 40) return "text-yellow-400";
|
||||
return "text-red-400";
|
||||
}
|
||||
|
||||
// ─── Time formatting ─────────────────────────────────────────────────────────
|
||||
|
||||
export function relativeTime(iso: string): string {
|
||||
const diff = Date.now() - new Date(iso).getTime();
|
||||
const s = Math.floor(diff / 1000);
|
||||
if (s < 60) return `${s}s ago`;
|
||||
const m = Math.floor(s / 60);
|
||||
if (m < 60) return `${m}m ago`;
|
||||
const h = Math.floor(m / 60);
|
||||
if (h < 24) return `${h}h ago`;
|
||||
return `${Math.floor(h / 24)}d ago`;
|
||||
}
|
||||
Reference in New Issue
Block a user