Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/controllers/profile_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func GetProfile(c *gin.Context) {
"opponent": transcript.Opponent,
"debateType": transcript.DebateType,
"date": transcript.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
"eloChange": 0, // TODO: Add actual Elo change tracking
"eloChange": transcript.EloChange,// TODO: Add actual Elo change tracking
})
}

Expand Down
4 changes: 3 additions & 1 deletion backend/models/transcript.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ type SavedDebateTranscript struct {
Topic string `bson:"topic" json:"topic"`
Opponent string `bson:"opponent" json:"opponent"` // Bot name or opponent email
Result string `bson:"result" json:"result"` // "win", "loss", "draw", "pending"
EloChange float64 `bson:"eloChange" json:"eloChange"`
Messages []Message `bson:"messages" json:"messages"`
Transcripts map[string]string `bson:"transcripts,omitempty" json:"transcripts,omitempty"` // For user vs user debates
Transcripts map[string]string `bson:"transcripts,omitempty" json:"transcripts,omitempty"`
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}


func (s SavedDebateTranscript) MarshalJSON() ([]byte, error) {
type Alias SavedDebateTranscript
a := Alias(s)
Expand Down
6 changes: 5 additions & 1 deletion backend/services/transcriptservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ func SubmitTranscripts(
resultFor,
[]models.Message{}, // You might want to reconstruct messages from transcripts
forSubmission.Transcripts,
forRecord.RatingChange
)
if err != nil {
}
Expand Down Expand Up @@ -659,7 +660,7 @@ func buildFallbackJudgeResult(merged map[string]string) string {
}

// SaveDebateTranscript saves a debate transcript for later viewing
func SaveDebateTranscript(userID primitive.ObjectID, email, debateType, topic, opponent, result string, messages []models.Message, transcripts map[string]string) error {
func SaveDebateTranscript(userID primitive.ObjectID, email, debateType, topic, opponent, result string, messages []models.Message, transcripts map[string]string, eloChange float64) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

Expand All @@ -682,11 +683,13 @@ func SaveDebateTranscript(userID primitive.ObjectID, email, debateType, topic, o

// If the result has changed or is "pending", update the transcript
if existingTranscript.Result != result || existingTranscript.Result == "pending" {

update := bson.M{
"$set": bson.M{
"result": result,
"messages": messages,
"transcripts": transcripts,
"eloChange": eloChange,
"updatedAt": time.Now(),
},
}
Expand All @@ -712,6 +715,7 @@ func SaveDebateTranscript(userID primitive.ObjectID, email, debateType, topic, o
Opponent: opponent,
Result: result,
Messages: messages,
EloChange: eloChange,
Transcripts: transcripts,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Expand Down
102 changes: 52 additions & 50 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,57 +50,59 @@ function AppRoutes() {
}
const { isAuthenticated } = authContext;
return (
<Routes>
{/* Public routes */}
<Route
path='/'
element={
isAuthenticated ? <Navigate to='/startDebate' replace /> : <Home />
}
/>
<Route path='/auth' element={<Authentication />} />
<Route path='/admin/login' element={<AdminSignup />} />
<Route path='/admin/dashboard' element={<AdminDashboard />} />
{/* Protected routes with layout */}
<Route element={<ProtectedRoute />}>
<Route path='/' element={<Layout />}>
<Route path='startDebate' element={<StartDebate />} />
<Route path='leaderboard' element={<Leaderboard />} />
<Route path='profile' element={<Profile />} />
<Route path='community' element={<CommunityFeed />} />
<Route path='about' element={<About />} />
<Route path='team-builder' element={<TeamBuilder />} />
<Route path='game/:userId' element={<DebateApp />} />
<Route path='bot-selection' element={<BotSelection />} />
<Route path='/tournaments' element={<TournamentHub />} />
<Route path='/coach' element={<CoachPage />} />
<Route
path='/tournament/:id/bracket'
element={<TournamentDetails />}
/>
<Route
path='coach/strengthen-argument'
element={<StrengthenArgument />}
/>
<Route path='/coach' element={<CoachPage />} />
<Route
path='coach/strengthen-argument'
element={<StrengthenArgument />}
/>{' '}
{/* Add this route */}
<Route path='coach/pros-cons' element={<ProsConsChallenge />} />
<Routes>
{/* Public routes */}
<Route
path="/"
element={
isAuthenticated ? <Navigate to="/startDebate" replace /> : <Home />
}
/>
<Route path="/auth" element={<Authentication />} />
<Route path="/admin/login" element={<AdminSignup />} />
<Route path="/admin/dashboard" element={<AdminDashboard />} />

{/* Protected routes */}
<Route element={<ProtectedRoute />}>
<Route element={<Layout />}>
<Route path="startDebate" element={<StartDebate />} />
<Route path="leaderboard" element={<Leaderboard />} />
<Route path="profile" element={<Profile />} />
<Route path="community" element={<CommunityFeed />} />
<Route path="about" element={<About />} />
<Route path="team-builder" element={<TeamBuilder />} />
<Route path="game/:userId" element={<DebateApp />} />
<Route path="bot-selection" element={<BotSelection />} />
<Route path="tournaments" element={<TournamentHub />} />
<Route
path="tournament/:id/bracket"
element={<TournamentDetails />}
/>

{/* Coach routes */}
<Route path="coach" element={<CoachPage />}>
<Route
path="strengthen-argument"
element={<StrengthenArgument />}
/>
<Route path="pros-cons" element={<ProsConsChallenge />} />
</Route>
</Route>

{/* Debate routes (outside layout) */}
<Route path="debate/:roomId" element={<DebateRoom />} />
<Route path="debate-room/:roomId" element={<OnlineDebateRoom />} />
<Route path="team-debate/:debateId" element={<TeamDebateRoom />} />
<Route path="spectator/:roomId" element={<ChatRoom />} />
<Route path="debate/:debateID/view" element={<ViewDebate />} />
<Route path="view-debate/:debateID" element={<ViewDebate />} />
<Route path="speech-test" element={<SpeechTest />} />
</Route>
<Route path='/debate/:roomId' element={<DebateRoom />} />
<Route path='/debate-room/:roomId' element={<OnlineDebateRoom />} />
<Route path='/team-debate/:debateId' element={<TeamDebateRoom />} />
<Route path='/spectator/:roomId' element={<ChatRoom />} />
<Route path='/debate/:debateID/view' element={<ViewDebate />} />
<Route path='/view-debate/:debateID' element={<ViewDebate />} />
<Route path='/speech-test' element={<SpeechTest />} />
</Route>
{/* Redirect unknown routes */}
<Route path='*' element={<Navigate to='/' replace />} />
</Routes>

{/* Redirect unknown routes */}
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>

);
}

Expand Down
74 changes: 41 additions & 33 deletions frontend/src/Pages/DebateRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import JudgmentPopup from "@/components/JudgementPopup";
import { Mic, MicOff } from "lucide-react";
import { useAtom } from "jotai";
import { userAtom } from "@/state/userAtom";
import JSON5 from "json5";

// Bot type definition (same as in BotSelection)
interface Bot {
Expand Down Expand Up @@ -217,8 +218,9 @@ const extractJSON = (response: string): string => {
};

const DebateRoom: React.FC = () => {
const location = useLocation();
const judgingRef = useRef(false);
const navigate = useNavigate();
const location = useLocation();
const debateData = location.state as DebateProps;
const phases = debateData.phaseTimings;
const debateKey = `debate_${debateData.userId}_${debateData.topic}_${debateData.debateId}`;
Expand Down Expand Up @@ -574,50 +576,49 @@ const DebateRoom: React.FC = () => {
}
};

const judgeDebateResult = async (messages: Message[]) => {
const judgeDebateResult = async (messages: Message[]) => {
if (judgingRef.current) {
console.log("Judging already in progress, skipping duplicate call");
return;
}

judgingRef.current = true;

try {
console.log("Starting judgment with messages:", messages);
const { result } = await judgeDebate({
history: messages,
userId: debateData.userId,
});

console.log("Raw judge result:", result);

const jsonString = extractJSON(result);
console.log("Extracted JSON string:", jsonString);

let judgment: JudgmentData;
try {
judgment = JSON.parse(jsonString);
} catch (parseError) {
console.error("JSON parse error:", parseError, "Trying to fix JSON...");
// Try to fix common JSON issues
const fixedJson = jsonString
.replace(/'/g, '"') // Replace single quotes with double quotes
.replace(/(\w+):/g, '"$1":') // Add quotes to keys
.replace(/,\s*}/g, '}') // Remove trailing commas
.replace(/,\s*]/g, ']'); // Remove trailing commas in arrays
try {
judgment = JSON.parse(fixedJson);
} catch (e) {
throw new Error(`Failed to parse JSON: ${e}`);
}
judgment = JSON5.parse(jsonString);
}catch (e) {
throw new Error(`Failed to parse judgment JSON: ${e}`);
}

console.log("Parsed judgment:", judgment);
setJudgmentData(judgment);
setPopup({ show: false, message: "" });
setShowJudgment(true);

} catch (error) {
console.error("Judging error:", error);
// Show error to user

setPopup({
show: true,
message: `Judgment error: ${error instanceof Error ? error.message : "Unknown error"}. Showing default results.`,
message: `Judgment error: ${
error instanceof Error ? error.message : "Unknown error"
}. Showing default results.`,
isJudging: false,
});

// Set default judgment data

setJudgmentData({
opening_statement: {
user: { score: 0, reason: "Error occurred during judgment" },
Expand All @@ -643,13 +644,18 @@ const DebateRoom: React.FC = () => {
opponent_analysis: "",
},
});

setTimeout(() => {
setPopup({ show: false, message: "" });
setShowJudgment(true);
}, 3000);

} finally {
judgingRef.current = false;
}
};


const formatTime = (seconds: number) => {
const timeStr = `${Math.floor(seconds / 60)}:${(seconds % 60)
.toString()
Expand Down Expand Up @@ -690,9 +696,12 @@ const DebateRoom: React.FC = () => {
const currentTurnType = turnTypes[state.currentPhase][state.phaseStep];

return (
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-200 p-4">
<div className="min-h-screen p-4 bg-gradient-to-br from-gray-50 to-gray-200
dark:from-zinc-900 dark:to-zinc-800">
<div className="w-full max-w-5xl mx-auto py-2">
<div className="bg-gradient-to-r from-orange-100 via-white to-orange-100 rounded-xl p-4 text-center transition-all duration-300 hover:shadow-lg">
<div className="rounded-xl p-4 text-center transition-all duration-300 hover:shadow-lg bg-gradient-to-r from-orange-100 via-white to-orange-100
dark:from-zinc-800 dark:via-zinc-900 dark:to-zinc-800
">
<h1 className="text-3xl font-bold text-gray-900 tracking-tight">
Debate: {debateData.topic}
</h1>
Expand All @@ -716,7 +725,7 @@ const DebateRoom: React.FC = () => {

{popup.show && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-70">
<div className="bg-white rounded-xl shadow-2xl p-6 max-w-md w-full transform transition-all duration-300 scale-105 border border-orange-200">
<div className="bg-white dark:bg-zinc-900 rounded-xl p-6 max-w-m w-full transform transition-all duration-300 scale-105 shadow-2xl border border-orange-200 dark:border-orange-400/30">
{popup.isJudging ? (
<div className="flex flex-col items-center">
<div className="animate-spin rounded-full h-16 w-16 border-t-4 border-blue500 mb-4"></div>
Expand Down Expand Up @@ -747,17 +756,16 @@ const DebateRoom: React.FC = () => {
userStance={state.userStance}
botStance={state.botStance}
botDesc={bot.desc}
onClose={() => setShowJudgment(false)}
onClose={() => {
setShowJudgment(false);
navigate("/");
}}
/>
)}

<div className="w-full max-w-5xl mx-auto flex flex-col md:flex-row gap-3">
{/* Bot Section */}
<div
className={`relative w-full md:w-1/2 ${
state.isBotTurn ? "animate-glow" : ""
} bg-white border border-gray-200 shadow-md h-[540px] flex flex-col`}
>
<div className={`relative w-full md:w-1/2 ${state.isBotTurn ? "animate-glow" : ""} h-[540px] flex flex-col shadow-md bg-white dark:bg-zinc-900 border border-gray-200 dark:border-zinc-700`}>
<div className="p-2 bg-gray-50 flex items-center gap-2">
<div className="w-12 h-12 flex-shrink-0">
<img
Expand Down Expand Up @@ -907,7 +915,7 @@ const DebateRoom: React.FC = () => {
box-shadow: 0 0 5px rgba(255, 149, 0, 0.5);
}
50% {
box-shadow: 0 0 20px rgba(255, 149, 0, 0.8);
box-shadow: 0 0 20px rgba(255, 180, 80, 0.9);
}
100% {
box-shadow: 0 0 5px rgba(255, 149, 0, 0.5);
Expand Down
Loading