This commit is contained in:
gpt-engineer-app[bot] 2025-12-18 09:02:39 +00:00
parent 394bf32820
commit 78528fbe8d

View file

@ -2,6 +2,7 @@ import { useState, useMemo } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Slider } from "@/components/ui/slider";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -9,9 +10,10 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { RefreshCw } from "lucide-react"; import { RefreshCw, User, UserRound, Users, UserCheck } from "lucide-react";
const AGENTS = ["Alice", "Bob", "Charlie", "Dana"]; const AGENTS = ["Alice", "Bob", "Charlie", "Dana"];
const AGENT_ICONS = [User, UserRound, Users, UserCheck];
const ROOM_COLORS = [ const ROOM_COLORS = [
"hsl(142, 70%, 45%)", // green "hsl(142, 70%, 45%)", // green
"hsl(0, 70%, 50%)", // red "hsl(0, 70%, 50%)", // red
@ -31,13 +33,17 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
) )
); );
// Total rent (controlled by slider)
const [totalRent, setTotalRent] = useState(20);
// Rents for each room // Rents for each room
const [rents, setRents] = useState<number[]>([5, 5, 5, 5]); const [rents, setRents] = useState<number[]>([5, 5, 5, 5]);
// Allocation: assignment[agent] = room index (-1 = unassigned) // Allocation: assignment[agent] = room index (-1 = unassigned)
const [assignment, setAssignment] = useState<number[]>([-1, -1, -1, -1]); const [assignment, setAssignment] = useState<number[]>([-1, -1, -1, -1]);
const totalRent = useMemo(() => rents.reduce((a, b) => a + b, 0), [rents]); const currentTotal = useMemo(() => rents.reduce((a, b) => a + b, 0), [rents]);
const rentDiff = totalRent - currentTotal;
// Compute U-matrix: utility[agent][room] = valuation - rent // Compute U-matrix: utility[agent][room] = valuation - rent
const utilities = useMemo(() => { const utilities = useMemo(() => {
@ -120,10 +126,25 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
const resetAll = () => { const resetAll = () => {
randomizeValuations(); randomizeValuations();
setTotalRent(20);
setRents([5, 5, 5, 5]); setRents([5, 5, 5, 5]);
setAssignment([-1, -1, -1, -1]); setAssignment([-1, -1, -1, -1]);
}; };
const handleTotalRentChange = (value: number[]) => {
const newTotal = value[0];
setTotalRent(newTotal);
// Distribute evenly
const base = Math.floor(newTotal / 4);
const remainder = newTotal % 4;
setRents([
base + (remainder > 0 ? 1 : 0),
base + (remainder > 1 ? 1 : 0),
base + (remainder > 2 ? 1 : 0),
base,
]);
};
const getAgentColor = (agentIdx: number) => { const getAgentColor = (agentIdx: number) => {
const room = assignment[agentIdx]; const room = assignment[agentIdx];
return room === -1 ? "hsl(0, 0%, 60%)" : ROOM_COLORS[room]; return room === -1 ? "hsl(0, 0%, 60%)" : ROOM_COLORS[room];
@ -147,42 +168,75 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
<CardHeader className="pb-3"> <CardHeader className="pb-3">
<CardTitle className="text-lg">Agents & Rooms</CardTitle> <CardTitle className="text-lg">Agents & Rooms</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent className="space-y-4">
<div className="flex gap-8 justify-center"> {/* Total Rent Slider */}
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Total Rent</span>
<span className="font-medium">${totalRent}</span>
</div>
<Slider
value={[totalRent]}
onValueChange={handleTotalRentChange}
min={0}
max={100}
step={1}
className="w-full"
/>
{rentDiff !== 0 && (
<p className={`text-xs ${rentDiff > 0 ? 'text-amber-600' : 'text-destructive'}`}>
{rentDiff > 0 ? `+$${rentDiff} unallocated` : `-$${Math.abs(rentDiff)} over budget`}
</p>
)}
</div>
<div className="flex gap-6 justify-center pt-2">
{/* Agents Column */} {/* Agents Column */}
<div className="space-y-3"> <div className="space-y-3">
<div className="text-sm font-medium text-muted-foreground text-center mb-2"> <div className="text-sm font-medium text-muted-foreground text-center mb-2">
Agents Agents
</div> </div>
{AGENTS.map((name, idx) => ( {AGENTS.map((name, idx) => {
const Icon = AGENT_ICONS[idx];
return (
<div key={idx} className="flex items-center gap-2"> <div key={idx} className="flex items-center gap-2">
<span
className="text-xs font-medium w-14 text-right"
style={{ color: getAgentColor(idx) }}
>
{name}
</span>
<div <div
className="w-12 h-12 rounded-lg border-2 flex items-center justify-center text-xs font-medium" className="w-10 h-10 rounded-lg border-2 flex items-center justify-center"
style={{ style={{
borderColor: getAgentColor(idx), borderColor: getAgentColor(idx),
backgroundColor: `${getAgentColor(idx)}20`, backgroundColor: `${getAgentColor(idx)}20`,
}} }}
> >
{name.slice(0, 2)} <Icon
className="w-5 h-5"
style={{ color: getAgentColor(idx) }}
/>
</div> </div>
<Select <Select
value={assignment[idx] === -1 ? "none" : String(assignment[idx])} value={assignment[idx] === -1 ? "none" : String(assignment[idx])}
onValueChange={(v) => handleAssignmentChange(idx, v)} onValueChange={(v) => handleAssignmentChange(idx, v)}
> >
<SelectTrigger className="w-24 h-8 text-xs"> <SelectTrigger className="w-20 h-8 text-xs">
<SelectValue placeholder="Room" /> <SelectValue placeholder="Room" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="none">None</SelectItem> <SelectItem value="none">None</SelectItem>
{[0, 1, 2, 3].map((r) => ( {[0, 1, 2, 3].map((r) => (
<SelectItem key={r} value={String(r)}> <SelectItem key={r} value={String(r)}>
Room {r + 1} R{r + 1}
</SelectItem> </SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
))} );
})}
</div> </div>
{/* Rooms Column */} {/* Rooms Column */}
@ -193,7 +247,7 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
{[0, 1, 2, 3].map((roomIdx) => ( {[0, 1, 2, 3].map((roomIdx) => (
<div key={roomIdx} className="flex items-center gap-2"> <div key={roomIdx} className="flex items-center gap-2">
<div <div
className="w-12 h-12 rounded-lg border-2 flex items-center justify-center text-xs font-bold" className="w-10 h-10 rounded-lg border-2 flex items-center justify-center text-xs font-bold"
style={{ style={{
borderColor: ROOM_COLORS[roomIdx], borderColor: ROOM_COLORS[roomIdx],
backgroundColor: `${ROOM_COLORS[roomIdx]}30`, backgroundColor: `${ROOM_COLORS[roomIdx]}30`,
@ -207,7 +261,7 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
type="number" type="number"
value={rents[roomIdx]} value={rents[roomIdx]}
onChange={(e) => handleRentChange(roomIdx, e.target.value)} onChange={(e) => handleRentChange(roomIdx, e.target.value)}
className="w-14 h-8 text-xs text-center" className="w-12 h-8 text-xs text-center"
min={0} min={0}
max={99} max={99}
/> />
@ -215,7 +269,7 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
</div> </div>
))} ))}
<div className="text-sm font-medium pt-2 border-t"> <div className="text-sm font-medium pt-2 border-t">
Total: ${totalRent} Sum: ${currentTotal}
</div> </div>
</div> </div>
</div> </div>