Changes
This commit is contained in:
parent
394bf32820
commit
78528fbe8d
1 changed files with 89 additions and 35 deletions
|
|
@ -2,6 +2,7 @@ import { useState, useMemo } from "react";
|
|||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -9,9 +10,10 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} 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 AGENT_ICONS = [User, UserRound, Users, UserCheck];
|
||||
const ROOM_COLORS = [
|
||||
"hsl(142, 70%, 45%)", // green
|
||||
"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
|
||||
const [rents, setRents] = useState<number[]>([5, 5, 5, 5]);
|
||||
|
||||
// Allocation: assignment[agent] = room index (-1 = unassigned)
|
||||
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
|
||||
const utilities = useMemo(() => {
|
||||
|
|
@ -120,10 +126,25 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
|
|||
|
||||
const resetAll = () => {
|
||||
randomizeValuations();
|
||||
setTotalRent(20);
|
||||
setRents([5, 5, 5, 5]);
|
||||
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 room = assignment[agentIdx];
|
||||
return room === -1 ? "hsl(0, 0%, 60%)" : ROOM_COLORS[room];
|
||||
|
|
@ -147,42 +168,75 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
|
|||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg">Agents & Rooms</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex gap-8 justify-center">
|
||||
<CardContent className="space-y-4">
|
||||
{/* 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 */}
|
||||
<div className="space-y-3">
|
||||
<div className="text-sm font-medium text-muted-foreground text-center mb-2">
|
||||
Agents
|
||||
</div>
|
||||
{AGENTS.map((name, idx) => (
|
||||
<div key={idx} className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg border-2 flex items-center justify-center text-xs font-medium"
|
||||
style={{
|
||||
borderColor: getAgentColor(idx),
|
||||
backgroundColor: `${getAgentColor(idx)}20`,
|
||||
}}
|
||||
>
|
||||
{name.slice(0, 2)}
|
||||
{AGENTS.map((name, idx) => {
|
||||
const Icon = AGENT_ICONS[idx];
|
||||
return (
|
||||
<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
|
||||
className="w-10 h-10 rounded-lg border-2 flex items-center justify-center"
|
||||
style={{
|
||||
borderColor: getAgentColor(idx),
|
||||
backgroundColor: `${getAgentColor(idx)}20`,
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
className="w-5 h-5"
|
||||
style={{ color: getAgentColor(idx) }}
|
||||
/>
|
||||
</div>
|
||||
<Select
|
||||
value={assignment[idx] === -1 ? "none" : String(assignment[idx])}
|
||||
onValueChange={(v) => handleAssignmentChange(idx, v)}
|
||||
>
|
||||
<SelectTrigger className="w-20 h-8 text-xs">
|
||||
<SelectValue placeholder="Room" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
{[0, 1, 2, 3].map((r) => (
|
||||
<SelectItem key={r} value={String(r)}>
|
||||
R{r + 1}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<Select
|
||||
value={assignment[idx] === -1 ? "none" : String(assignment[idx])}
|
||||
onValueChange={(v) => handleAssignmentChange(idx, v)}
|
||||
>
|
||||
<SelectTrigger className="w-24 h-8 text-xs">
|
||||
<SelectValue placeholder="Room" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
{[0, 1, 2, 3].map((r) => (
|
||||
<SelectItem key={r} value={String(r)}>
|
||||
Room {r + 1}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Rooms Column */}
|
||||
|
|
@ -193,7 +247,7 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
|
|||
{[0, 1, 2, 3].map((roomIdx) => (
|
||||
<div key={roomIdx} className="flex items-center gap-2">
|
||||
<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={{
|
||||
borderColor: ROOM_COLORS[roomIdx],
|
||||
backgroundColor: `${ROOM_COLORS[roomIdx]}30`,
|
||||
|
|
@ -207,7 +261,7 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
|
|||
type="number"
|
||||
value={rents[roomIdx]}
|
||||
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}
|
||||
max={99}
|
||||
/>
|
||||
|
|
@ -215,7 +269,7 @@ const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
|
|||
</div>
|
||||
))}
|
||||
<div className="text-sm font-medium pt-2 border-t">
|
||||
Total: ${totalRent}
|
||||
Sum: ${currentTotal}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue