Skip to content

Commit

Permalink
Fix pr #390: Fix issue #389: Jutsu Level Transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
openhands-agent committed Feb 21, 2025
1 parent 325f0b2 commit 50c03ab
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 137 deletions.
134 changes: 26 additions & 108 deletions app/src/app/jutsus/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { useState } from "react";
import { JutsuTransferModal } from "@/components/JutsuTransferModal";
import { Trash2, CircleFadingArrowUp, ArrowRightLeft } from "lucide-react";
import ItemWithEffects from "@/layout/ItemWithEffects";
import ContentBox from "@/layout/ContentBox";
Expand Down Expand Up @@ -50,6 +51,8 @@ export default function MyJutsu() {
const now = new Date();
const { data: userData, updateUser } = useRequiredUserData();
const [isOpen, setIsOpen] = useState<boolean>(false);
const [showTransferModal, setShowTransferModal] = useState<boolean>(false);
const [selectedJutsu, setSelectedJutsu] = useState<(Jutsu & UserJutsu) | undefined>(undefined);
const [userjutsu, setUserJutsu] = useState<(Jutsu & UserJutsu) | undefined>(
undefined,
);
Expand Down Expand Up @@ -232,6 +235,26 @@ export default function MyJutsu() {
const canUpgrade = userData.reputationPoints >= COST_EXTRA_JUTSU_SLOT;

return (
<>
{showTransferModal && selectedJutsu && (
<JutsuTransferModal
isOpen={showTransferModal}
onClose={() => setShowTransferModal(false)}
sourceJutsu={selectedJutsu}
availableJutsus={allJutsu || []}
maxTransferLevel={JUTSU_TRANSFER_MAX_LEVEL}
usedTransfers={usedTransfers}
freeTransfers={freeTransfers}
transferCost={JUTSU_TRANSFER_COST}
onTransfer={(toJutsuId, levels) => {
transferLevel({
fromJutsuId: selectedJutsu.jutsuId,
toJutsuId,
levels,
});
}}
/>
)}
<ContentBox
title="Jutsu Management"
subtitle={subtitle}
Expand Down Expand Up @@ -361,114 +384,8 @@ export default function MyJutsu() {
variant="secondary"
onClick={() => {
setIsOpen(false);


const transferModal = document.createElement('div');
transferModal.innerHTML = `
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white p-6 rounded-lg max-w-lg w-full">
<h2 class="text-xl font-bold mb-4">Transfer Level</h2>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<label for="target-jutsu">Target Jutsu:</label>
<select
id="target-jutsu"
class="p-2 border rounded"
>
<option value="">Select a jutsu...</option>
${allJutsu
?.filter(
(j) =>
j.jutsuId !== userjutsu.jutsuId &&
j.jutsuType === userjutsu.jutsuType &&
j.jutsuRank === userjutsu.jutsuRank
)
.map((j) => `
<option value="${j.jutsuId}">
${j.name} (Level ${j.level})
</option>
`).join('')}
</select>
</div>
<div id="transfer-details" class="hidden">
<div class="flex flex-col gap-2">
<label for="transfer-levels">Levels to Transfer:</label>
<input
id="transfer-levels"
type="number"
class="p-2 border rounded"
min="1"
max="${Math.min(userjutsu.level - 1, JUTSU_TRANSFER_MAX_LEVEL)}"
value="1"
/>
</div>
<div id="transfer-info" class="mt-4"></div>
<p class="mt-2">
Cost: ${usedTransfers >= freeTransfers
? `${JUTSU_TRANSFER_COST} reputation points`
: "Free"}
</p>
<div class="flex justify-end gap-2 mt-4">
<button class="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300" onclick="document.querySelector('.fixed').remove()">
Cancel
</button>
<button id="confirm-transfer" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
Confirm Transfer
</button>
</div>
</div>
</div>
</div>
</div>
`;
document.body.appendChild(transferModal);

// Add event listeners
const targetSelect = document.getElementById('target-jutsu');
const transferDetails = document.getElementById('transfer-details');
const transferInfo = document.getElementById('transfer-info');
const transferLevelsInput = document.getElementById('transfer-levels');
const confirmButton = document.getElementById('confirm-transfer');

targetSelect?.addEventListener('change', (e) => {
const selected = allJutsu?.find(
(j) => j.jutsuId === (e.target as HTMLSelectElement).value
);
if (selected) {
transferDetails?.classList.remove('hidden');
const levels = parseInt((document.getElementById('transfer-levels') as HTMLInputElement).value);
if (transferInfo) transferInfo.innerHTML = `
<p>Transfer ${levels} level(s) from ${userjutsu.name} to ${selected.name}?</p>
<p>This will reduce ${userjutsu.name} to level ${userjutsu.level - levels} and increase ${selected.name} to level ${selected.level + levels}.</p>
`;
}
});

transferLevelsInput?.addEventListener('input', (e) => {
const selected = allJutsu?.find(
(j) => j.jutsuId === (targetSelect as HTMLSelectElement).value
);
if (selected) {
const levels = parseInt((e.target as HTMLInputElement).value);
if (transferInfo) transferInfo.innerHTML = `
<p>Transfer ${levels} level(s) from ${userjutsu.name} to ${selected.name}?</p>
<p>This will reduce ${userjutsu.name} to level ${userjutsu.level - levels} and increase ${selected.name} to level ${selected.level + levels}.</p>
`;
}
});

confirmButton?.addEventListener('click', () => {
const selectedJutsuId = (targetSelect as HTMLSelectElement).value;
const levels = parseInt((transferLevelsInput as HTMLInputElement).value);
if (selectedJutsuId && levels > 0) {
transferLevel({
fromJutsuId: userjutsu.jutsuId,
toJutsuId: selectedJutsuId,
levels: levels,
});
document.querySelector('.fixed')?.remove();
}
});
setSelectedJutsu(userjutsu);
setShowTransferModal(true);
}}
>
<ArrowRightLeft className="h-6 w-6 mr-2" />
Expand Down Expand Up @@ -498,5 +415,6 @@ export default function MyJutsu() {
)}
{isPending && <Loader explanation="Loading Jutsu" />}
</ContentBox>
</>
);
}
109 changes: 109 additions & 0 deletions app/src/components/JutsuTransferModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Input } from "@/components/ui/input";
import type { Jutsu, UserJutsu } from "@/drizzle/schema";

interface JutsuTransferModalProps {
isOpen: boolean;
onClose: () => void;
sourceJutsu: Jutsu & UserJutsu;
availableJutsus: (Jutsu & UserJutsu)[];
maxTransferLevel: number;
usedTransfers: number;
freeTransfers: number;
transferCost: number;
onTransfer: (toJutsuId: string, levels: number) => void;
}

export function JutsuTransferModal({
isOpen,
onClose,
sourceJutsu,
availableJutsus,
maxTransferLevel,
usedTransfers,
freeTransfers,
transferCost,
onTransfer,
}: JutsuTransferModalProps) {
const [selectedJutsuId, setSelectedJutsuId] = useState<string>("");
const [levels, setLevels] = useState<number>(1);

const selectedJutsu = availableJutsus.find(j => j.jutsuId === selectedJutsuId);
const maxLevels = Math.min(sourceJutsu.level - 1, maxTransferLevel);

return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent>
<DialogHeader>
<DialogTitle>Transfer Level</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<label htmlFor="target-jutsu">Target Jutsu:</label>
<Select value={selectedJutsuId} onValueChange={setSelectedJutsuId}>
<SelectTrigger>
<SelectValue placeholder="Select a jutsu..." />
</SelectTrigger>
<SelectContent>
{availableJutsus
.filter(
j =>
j.jutsuId !== sourceJutsu.jutsuId &&
j.jutsuType === sourceJutsu.jutsuType &&
j.jutsuRank === sourceJutsu.jutsuRank
)
.map(j => (
<SelectItem key={j.jutsuId} value={j.jutsuId}>
{j.name} (Level {j.level})
</SelectItem>
))}
</SelectContent>
</Select>
</div>

{selectedJutsu && (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<label htmlFor="transfer-levels">Levels to Transfer:</label>
<Input
id="transfer-levels"
type="number"
min={1}
max={maxLevels}
value={levels}
onChange={e => setLevels(parseInt(e.target.value))}
/>
</div>

<div className="mt-4">
<p>Transfer {levels} level(s) from {sourceJutsu.name} to {selectedJutsu.name}?</p>
<p>This will reduce {sourceJutsu.name} to level {sourceJutsu.level - levels} and increase {selectedJutsu.name} to level {selectedJutsu.level + levels}.</p>
</div>

<p className="mt-2">
Cost: {usedTransfers >= freeTransfers ? `${transferCost} reputation points` : "Free"}
</p>

<div className="flex justify-end gap-2 mt-4">
<Button variant="secondary" onClick={onClose}>
Cancel
</Button>
<Button
onClick={() => {
onTransfer(selectedJutsuId, levels);
onClose();
}}
>
Confirm Transfer
</Button>
</div>
</div>
)}
</div>
</DialogContent>
</Dialog>
);
}
6 changes: 3 additions & 3 deletions app/src/server/api/routers/jutsu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,10 @@ export const fetchUserJutsus = async (
.orderBy(desc(userJutsu.level));

return userjutsus.map((result) => ({
...result.UserJutsu,
...result.userJutsu,
jutsu: {
...result.Jutsu,
bloodline: result.Bloodline,
...result.jutsu,
bloodline: result.bloodline,
},
}));
};
Expand Down
Loading

0 comments on commit 50c03ab

Please sign in to comment.