1
0
forked from a/lifeto-shop
This commit is contained in:
a 2025-06-24 18:44:08 -05:00
parent 0e759282b3
commit 3f58fac4a2
No known key found for this signature in database
GPG Key ID: 2F22877AA4DFDADB

View File

@ -12,7 +12,7 @@ import {
useRole, useRole,
} from '@floating-ui/react' } from '@floating-ui/react'
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import { useAtom } from 'jotai' import { useAtom, useAtomValue } from 'jotai'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { TricksterCharacter } from '../lib/trickster' import { TricksterCharacter } from '../lib/trickster'
import { charactersAtom, selectedCharacterAtom } from '../state/atoms' import { charactersAtom, selectedCharacterAtom } from '../state/atoms'
@ -46,6 +46,8 @@ export const CharacterCard = ({ character, noTopBorder = false }: { character: T
const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, dismiss, role]) const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, dismiss, role])
const [selectedCharacter, setSelectedCharacter] = useAtom(selectedCharacterAtom) const [selectedCharacter, setSelectedCharacter] = useAtom(selectedCharacterAtom)
const isBank = character.base_job === -8
return ( return (
<> <>
<button <button
@ -62,9 +64,9 @@ export const CharacterCard = ({ character, noTopBorder = false }: { character: T
p-2 ${character.path === selectedCharacter?.path ? `bg-blue-200 hover:bg-blue-100` : ''}`} p-2 ${character.path === selectedCharacter?.path ? `bg-blue-200 hover:bg-blue-100` : ''}`}
> >
<div className="flex flex-col justify-between h-full"> <div className="flex flex-col justify-between h-full">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-1 items-center">
<div className="flex flex-row justify-center"> <div className="flex flex-row justify-center">
{character.base_job === -8 ? ( {isBank ? (
<img <img
className="h-8" className="h-8"
src="https://beta.lifeto.co/item_img/gel.nri.003.000.png" src="https://beta.lifeto.co/item_img/gel.nri.003.000.png"
@ -72,16 +74,15 @@ export const CharacterCard = ({ character, noTopBorder = false }: { character: T
/> />
) : ( ) : (
<img <img
className="h-16" className="h-16 bg-gray-200"
src={`https://knowledge.lifeto.co/animations/character/chr${( src=""
character.current_type || character.base_job
)
.toString()
.padStart(3, '0')}_13.png`}
alt={`Character ${character.name}`} alt={`Character ${character.name}`}
/> />
)} )}
</div> </div>
<div className="text-xs font-semibold text-center">
{isBank ? 'Bank' : character.name}
</div>
<FloatingPortal> <FloatingPortal>
{isOpen && ( {isOpen && (
<div <div
@ -103,6 +104,100 @@ export const CharacterCard = ({ character, noTopBorder = false }: { character: T
) )
} }
const SelectedCharacterDisplay = () => {
const selectedCharacter = useAtomValue(selectedCharacterAtom)
const [{ data: rawCharacters }] = useAtom(charactersAtom)
if (!selectedCharacter) {
return (
<div className="flex flex-col items-center gap-2 p-4 border border-gray-300 rounded bg-gray-50 h-[140px] justify-center">
<div className="text-sm text-gray-400">No character selected</div>
</div>
)
}
if (selectedCharacter.base_job === -8) {
// Find the character associated with this bank
const characterPair = rawCharacters?.find(pair => pair.bank.id === selectedCharacter.id)
const associatedCharacter = characterPair?.character
return (
<div className="flex flex-col items-center gap-2 p-4 border border-gray-300 rounded bg-gray-50 h-[140px] justify-center">
<div className="text-sm font-bold">Bank for: {associatedCharacter?.name || 'Unknown'}</div>
<div className="flex items-center gap-4">
<img
className="h-24"
src="https://knowledge.lifeto.co/animations/npc/npc041_4.png"
alt="Bank NPC"
/>
{associatedCharacter && (
<img
className="h-24"
src={`https://knowledge.lifeto.co/animations/character/chr${(
associatedCharacter.current_type || associatedCharacter.base_job
)
.toString()
.padStart(3, '0')}_13.png`}
alt={`${associatedCharacter.name} walking`}
/>
)}
</div>
</div>
)
}
return (
<div className="flex flex-col items-center gap-2 p-4 border border-gray-300 rounded bg-gray-50 h-[140px] justify-center">
<div className="text-sm font-bold">Selected: {selectedCharacter.name}</div>
<img
className="h-24"
src={`https://knowledge.lifeto.co/animations/character/chr${(
selectedCharacter.current_type || selectedCharacter.base_job
)
.toString()
.padStart(3, '0')}_13.png`}
alt={`${selectedCharacter.name} walking`}
/>
</div>
)
}
const CharacterRow = ({ characterPair }: { characterPair: { bank: TricksterCharacter; character: TricksterCharacter } }) => {
const [selectedCharacter, setSelectedCharacter] = useAtom(selectedCharacterAtom)
return (
<div className="flex flex-row">
<button
type="button"
onClick={() => setSelectedCharacter(characterPair.bank)}
className={`flex items-center justify-center px-1 py-0.5 border border-black hover:bg-blue-100 ${
characterPair.bank.path === selectedCharacter?.path ? 'bg-blue-200' : ''
}`}
>
<img
src="https://beta.lifeto.co/item_img/gel.nri.003.000.png"
alt="Bank"
className="h-6"
/>
</button>
<button
type="button"
onClick={() => setSelectedCharacter(characterPair.character)}
className={`flex items-center justify-start px-2 py-1 border-l-0 border-t border-r border-b border-black hover:bg-blue-100 flex-1 ${
characterPair.character.path === selectedCharacter?.path ? 'bg-blue-200' : ''
}`}
>
<img
src={`https://beta.lifeto.co/img/job/${characterPair.character.current_type - 1}.png`}
alt="Class icon"
className="h-4 w-4 mr-2"
/>
{characterPair.character.name}
</button>
</div>
)
}
export const CharacterRoulette = () => { export const CharacterRoulette = () => {
const [{ data: rawCharacters }] = useAtom(charactersAtom) const [{ data: rawCharacters }] = useAtom(charactersAtom)
@ -137,25 +232,27 @@ export const CharacterRoulette = () => {
}) })
.map(x => { .map(x => {
return ( return (
<div className="flex flex-col" key={`${x.item.character.account_id}`}> <CharacterRow key={`${x.item.character.account_id}`} characterPair={x.item} />
<CharacterCard key={x.item.bank.id} character={x.item.bank} />
<CharacterCard key={x.item.character.id} character={x.item.character} noTopBorder={true} />
</div>
) )
}) })
return ( return (
<> <>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-4">
<SelectedCharacterDisplay />
<div className="flex flex-col gap-2">
<input <input
className="border border-black-1 bg-gray-100 placeholder-gray-600 p-1 max-w-[180px]" className="border border-black-1 bg-gray-100 placeholder-gray-600 p-1"
placeholder="search character..." placeholder="search character..."
value={search} value={search}
onChange={e => { onChange={e => {
setSearch(e.target.value) setSearch(e.target.value)
}} }}
></input> />
<div className="flex flex-row flex-wrap overflow-x-scroll gap-1 h-full min-h-36 max-w-48"> <div className="flex flex-col">
{searchResults ? searchResults : null} {searchResults.length > 0 ? searchResults : (
search ? <div className="text-sm text-gray-500 p-2">No characters matched search</div> : null
)}
</div>
</div> </div>
</div> </div>
</> </>