This commit is contained in:
Benjamin Sherriff
2023-10-05 09:07:53 -04:00
parent ac17be838a
commit 1b41849115
54 changed files with 6473 additions and 129 deletions

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

0
ui/src/app/bot/page.tsx Normal file
View File

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

40
ui/src/app/layout.tsx Normal file
View File

@@ -0,0 +1,40 @@
import React from 'react';
import RecoilRootWrapper from '@app/recoil-root-wrapper';
import Topbar from '@/components/Topbar';
import { Inter } from 'next/font/google';
import { Box, MantineProvider } from '@mantine/core';
import { ModalsProvider } from '@mantine/modals';
import { Notifications } from '@mantine/notifications';
import 'styles/globals.css';
import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
export const metadata = {
title: 'Siren',
description: ''
};
const inter = Inter({ subsets: ['latin'] });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang='en' className='h-full bg-white'>
<head>
<title>Siren</title>
</head>
<body className={`${inter.className} wrapper h-full`}>
<RecoilRootWrapper>
<MantineProvider>
<Notifications />
<ModalsProvider>
<Topbar />
<Box p='xl' pt='sm' className='h-full'>
{children}
</Box>
</ModalsProvider>
</MantineProvider>
</RecoilRootWrapper>
</body>
</html>
);
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

5
ui/src/app/page.tsx Normal file
View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
export default function Page() {
return <></>;
}

View File

@@ -0,0 +1,8 @@
'use client';
import { RecoilRoot } from 'recoil';
import React, { ReactNode } from 'react';
export default function RecoilRootWrapper({ children }: { children: ReactNode }) {
return <RecoilRoot>{children}</RecoilRoot>;
}

View File

@@ -0,0 +1,91 @@
'use client';
import { getSpells } from '@/api/spells';
import { Spell } from '@/api/spells.types';
import SpellModal from '@/components/SpellModal';
import React, { useEffect, useState } from 'react';
import './spells.css';
import { Box, TextInput } from '@mantine/core';
import { AiOutlineVerticalAlignTop } from 'react-icons/ai';
export default function Page() {
const [cantrips, setCantrips] = useState<Spell[]>([]);
const [level1, setLevel1] = useState<Spell[]>([]);
const [level2, setLevel2] = useState<Spell[]>([]);
const [level3, setLevel3] = useState<Spell[]>([]);
const [level4, setLevel4] = useState<Spell[]>([]);
const [level5, setLevel5] = useState<Spell[]>([]);
const [level6, setLevel6] = useState<Spell[]>([]);
const [level7, setLevel7] = useState<Spell[]>([]);
const [level8, setLevel8] = useState<Spell[]>([]);
const [level9, setLevel9] = useState<Spell[]>([]);
const [activeSpell, setActiveSpell] = useState<Spell | undefined>(undefined);
const [isOpen, setIsOpen] = useState(false);
const [searchName, setSearchName] = useState('');
useEffect(() => {
getSpells({ levels: [0] }).then((s) => setCantrips(s.data));
getSpells({ levels: [1] }).then((s) => setLevel1(s.data));
getSpells({ levels: [2] }).then((s) => setLevel2(s.data));
getSpells({ levels: [3] }).then((s) => setLevel3(s.data));
getSpells({ levels: [4] }).then((s) => setLevel4(s.data));
getSpells({ levels: [5] }).then((s) => setLevel5(s.data));
getSpells({ levels: [6] }).then((s) => setLevel6(s.data));
getSpells({ levels: [7] }).then((s) => setLevel7(s.data));
getSpells({ levels: [8] }).then((s) => setLevel8(s.data));
getSpells({ levels: [9] }).then((s) => setLevel9(s.data));
}, []);
return (
<Box style={{ width: '60%', margin: 'auto' }}>
<h1>Spells</h1>
<TextInput
label='Search by name'
placeholder='Acid Splash...'
onChange={(e) => setSearchName(e.target.value)}
style={{ width: '25%' }}
/>
<SpellSection
title='Cantrips'
spells={cantrips.filter((s) => s.name.toLowerCase().includes(searchName.toLowerCase()))}
onClick={(spell) => {
setActiveSpell(spell);
setIsOpen(true);
}}
/>
<hr />
{activeSpell && <SpellModal spell={activeSpell} isOpen={isOpen} onClose={() => setIsOpen(false)} />}
</Box>
);
}
function SpellSection({ title, spells, onClick }: { title: string; spells: Spell[]; onClick: (spell: Spell) => void }) {
const isBrowser = () => typeof window !== 'undefined'; //The approach recommended by Next.js
function scrollToTop() {
if (!isBrowser()) return;
window.scrollTo({ top: 0, behavior: 'smooth' });
}
return (
<Box>
<h2>{title}</h2>
<ul>
{spells.map((spell) => (
<li
key={spell.id}
className='link spell-item'
style={{ width: 'fit-content' }}
onClick={() => onClick(spell)}
>
{spell.name}
</li>
))}
</ul>
<div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', color: 'gray' }} onClick={scrollToTop}>
<span style={{ paddingRight: '0.2em' }}>Back to top</span>
<AiOutlineVerticalAlignTop />
</div>
</Box>
);
}

View File

@@ -0,0 +1,7 @@
.spell-item {
padding: 0.2rem;
}
.spell-item:hover {
text-decoration: underline;
}