Skip to content

Commit

Permalink
Admin login, games page, etc; custom pocketbase-react
Browse files Browse the repository at this point in the history
  • Loading branch information
mariansam committed Jun 3, 2023
1 parent d145e8e commit 1127462
Show file tree
Hide file tree
Showing 17 changed files with 3,502 additions and 17,583 deletions.
20,773 changes: 3,227 additions & 17,546 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"bootstrap": "^5.2.3",
"pocketbase-react": "^0.1.28",
"pocketbase-react": "radeksoft/pocketbase-react#radeksoft",
"react": "^18.2.0",
"react-bootstrap": "^2.7.4",
"react-dom": "^18.2.0",
Expand Down
37 changes: 27 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,50 @@
import { useState } from 'react';

import { Pocketbase as PocketbaseProvider } from 'pocketbase-react';
import React from 'react';
import { Pocketbase as PocketbaseProvider } from 'pocketbase-react/src';
import { Router } from './pages/_router';
import { AppHeader } from './components/app-header';
import './style.css';

import { useAppContent } from 'pocketbase-react/src';
import { Game, GameState, Goal, Player, Team } from './types';

const serverURL = "http://127.0.0.1:8090";
// const collections = ['games', 'goals', 'players', 'teams', 'misc'];
const collections: string[] = [];
// these get automatically prefetched & subscribed to updates
const collections = ['games', 'goals', 'players', 'teams', 'misc'];
const webRedirectURL = "mariansam.eu/webred";
const mobileRedirectURL = "mariansam.eu/mobred" // for example

const App: React.FC = () => {
export const App: React.FC = () => {
return (
<PocketbaseProvider
serverURL={serverURL}
webRedirectUrl={webRedirectURL}
mobileRedirectUrl={mobileRedirectURL}
initialCollections={collections}
openURL={async (url) => {
// for example expo WebBrowser
}}
>
<div className="d-flex flex-column align-items-center justify-content-start">
<AppHeader />
<div style={{maxWidth: 600, width: '100%'}}>
<Router />
</div>
<PocketbaseDataLoader>
<div style={{maxWidth: 600, width: '100%'}}>
<Router />
</div>
</PocketbaseDataLoader>
</div>
</PocketbaseProvider>
);
};

export default App;
const PocketbaseDataLoader: React.FC<React.PropsWithChildren> = ({ children }) => {
const gameMisc = useAppContent<GameState>('misc', true);
const players = useAppContent<Player>('players', true);
const games = useAppContent<Game>('games', true);
const teams = useAppContent<Team>('teams', true);
const goals = useAppContent<Goal>('goals', true);

if (!gameMisc.records || !players.records || !games.records || !teams.records || !goals.records)
return null;

return children;
}
2 changes: 1 addition & 1 deletion src/components/app-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const AppHeader: React.FC = () => {
<Nav className="me-auto">
<Link href='/'><Nav.Link>Hlavní stránka</Nav.Link></Link>
<Link href='/teams'><Nav.Link>Týmy a hráči</Nav.Link></Link>
<Link href='/matches'><Nav.Link>Všechny zápasy</Nav.Link></Link>
<Link href='/games'><Nav.Link>Všechny zápasy</Nav.Link></Link>
<Link href='/stats'><Nav.Link>Velká tabulka</Nav.Link></Link>
<Link href='/bufet'><Nav.Link>Bufet</Nav.Link></Link>
<Link href='/vote'><Nav.Link>Hlasování</Nav.Link></Link>
Expand Down
84 changes: 84 additions & 0 deletions src/components/finished-matches.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useMemo } from 'react';
import Card from 'react-bootstrap/Card';
import Table from 'react-bootstrap/Table';
import { useGameLogic } from '../logic';
import { Game } from '../types';
import { GoalRow } from './goal-row';

export const FinishedMatches: React.FC = () => {
const {
games,
gameState,
} = useGameLogic();

const finishedGames = useMemo(() => {
const filtered = games.filter(g => g.no < gameState.currentGameNo)
return filtered;
}, [games, gameState]);

return (
<div>
<h3 className="text-center mb-3">Dohrané zápasy</h3>
{finishedGames.map(game => (
<GameView game={game} key={game.id} />
))}
</div>
);
};

type MatchProps = {
game: Game,
};

const vsInlineStyle: React.CSSProperties = {
marginTop: 'auto',
marginBottom: 'auto',
fontSize: 20,
};


const GameView: React.FC<MatchProps> = props => {
const {
game,
} = props;

const { teams } = useGameLogic();

const team1 = useMemo(() => {
return teams.find(t => t.id === game.team1);
}, [teams, game]);

const team2 = useMemo(() => {
return teams.find(t => t.id === game.team2);
}, [teams, game]);

if (!team1 || !team2)
return null;

return (
<Card className='text-center' style={{marginBottom: 32}}>
<Card.Body className='d-flex flex-row justify-content-around' style={{marginBottom: -10}}>
<div className='d-flex flex-column flex-grow-1'>
<Card.Text style={{fontSize: 25}}>{team1.name}</Card.Text>
<Card.Text style={{fontSize: 50, fontWeight: 600, marginTop: -25}} >{game.goals1}</Card.Text>
</div>

<Card.Text style={vsInlineStyle}>vs</Card.Text>

<div className='d-flex flex-column flex-grow-1'>
<Card.Text style={{fontSize: 25}}>{team2.name}</Card.Text>
<Card.Text style={{fontSize: 50, fontWeight: 600, marginTop: -25}} >{game.goals2}</Card.Text>
</div>
</Card.Body>
{!!game.goals.length && (
<Table bordered style={{tableLayout: 'fixed', marginBottom: 0, marginRight: 0}}>
<tbody>
{game.goals.map(goalId => (
<GoalRow goalId={goalId} small={true} key={goalId} />
))}
</tbody>
</Table>
)}
</Card>
);
};
58 changes: 58 additions & 0 deletions src/components/goal-row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useMemo } from 'react';
import { Goal, ReferenceTo } from '../types';
import { useGameLogic } from '../logic';

type GoalRowProps = {
goalId: ReferenceTo<Goal>,
small?: true,
};

export const GoalRow: React.FC<GoalRowProps> = props => {
const {
goalId,
small,
} = props;

const { goals, players } = useGameLogic();

const goal = useMemo(() => {
return goals.find(g => g.id === goalId);
}, [goals, goalId])

const player = useMemo(() => {
if (!goal)
return;
return players.find(p => p.id === goal.player);
}, [players, goal]);

if (!goal || !player)
return null;

if (goal.side === 1) {
return (
<tr>
<td className="table-active" style={small && {fontSize: 13, padding: 4}}>
<strong>{player.name}</strong>
<br />
{goal.minute}. minuta
</td>
<td className="table-light">
</td>
</tr>
);
} else if (goal.side === 2) {
return (
<tr className="goal-row">
<td className="table-light">
</td>
<td className="table-active" style={small && {fontSize: 13, padding: 4, paddingRight: -1}}>
<strong>{player.name}</strong>
<br />
{goal.minute}. minuta
</td>
</tr>
);
}

return null;
};
15 changes: 11 additions & 4 deletions src/components/team-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,28 @@ export const TeamView: React.FC<TeamProps> = props => {
} = props;

const members = useMemo(() => {
const array = players.filter(player => player.team === team.id).sort((a, b) => a.goals.length - b.goals.length);
return new Set(array);
return players.filter(player => player.team === team.id).sort((a, b) => a.goals.length - b.goals.length);
}, [players, team.id]);

const totalGoals = useMemo(() => {
return members.map(m => m.goals.length).reduce((prev, curr) => prev + curr);
}, [members]);

return (
<Card className='my-3'>
<Card.Header as="h5">{team.name}</Card.Header>
<Card.Body className='pb-0'>
<Table>
<tbody>
{[...members.values()].map((member, idx) => (
<PlayerRow player={member} last={idx === members.size-1} key={member.id}/>
{members.map((member, idx) => (
<PlayerRow player={member} last={idx === members.length-1} key={member.id}/>
))}
</tbody>
<tfoot>
<tr style={teamPointsInlineStyle}>
<td><b>Góly týmu:</b></td>
<td><b>{totalGoals}</b></td>
</tr>
<tr style={teamPointsInlineStyle}>
<td><b>Body týmu:</b></td>
<td><b>{team.points}</b></td>
Expand Down
21 changes: 7 additions & 14 deletions src/logic.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { useEffect, useMemo } from 'react';
import { useAppContent } from 'pocketbase-react';
import { Game, GameState, Player, Team } from './types';
import { useAppContent } from 'pocketbase-react/src';
import { Game, GameState, Goal, Player, Team } from './types';

export const useGameLogic = () => {
// add new ones to `subs` in the `useEffect` below!
const gameMisc = useAppContent<GameState>('misc', true);
const players = useAppContent<Player>('players', true);
const games = useAppContent<Game>('games', true);
const teams = useAppContent<Team>('teams', true);
const goals = useAppContent<Goal>('goals', true);

console.log({games, players, teams});
// console.log({games, players, teams});

// there's only one (we hope)
console.log({gameMisc});
const gameState = gameMisc.records[0];
console.log({gameMisc, gameState});
// console.log({gameMisc, gameState});

const currentGame = useMemo(() => {
const game = games.records?.find(g => g.no === gameState.currentGameNo);
Expand All @@ -27,23 +29,14 @@ export const useGameLogic = () => {
};
}, [games.records, gameState.currentGameNo, teams.records]);

useEffect(() => {
const subs = [gameMisc, players, games, teams, gameMisc];

subs.forEach(sub => sub.actions.subscribe());

return () => {
subs.forEach(sub => sub.actions.unsubscribe());
};
}, []);

return {
gameState,
currentGame,

players: players.records,
games: games.records,
teams: teams.records,
goals: goals.records,

addPlayer: players.actions.create,
};
Expand Down
4 changes: 1 addition & 3 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import { App } from './App.tsx'
import 'bootstrap/dist/css/bootstrap.min.css';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
6 changes: 5 additions & 1 deletion src/pages/_router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import { HomePage } from './home';
import { BufetPage } from './bufet';
import { TeamsPage } from './teams';
import { AdminHomePage } from './admin/home';
import { GamesPage } from './games';
import { AdminRouter } from './admin/_router';

export const Router: React.FC = () => {
return (
<Switch>
<Route path="/" component={HomePage} />
<Route path="/bufet" component={BufetPage} />
<Route path="/teams" component={TeamsPage} />
<Route path="/games" component={GamesPage} />

<Route path="/admin" component={AdminHomePage} />
<Route path="/admin" component={AdminRouter} />
<Route path="/admin/:rest" component={AdminRouter} />
</Switch>
);
};
26 changes: 26 additions & 0 deletions src/pages/admin/_login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { useState } from 'react';
import { useAuth } from 'pocketbase-react/src';

import FormControl from 'react-bootstrap/FormControl';
import Button from 'react-bootstrap/Button';

const ADMIN_EMAIL = '[email protected]';

export const AdminLoginPage: React.FC = () => {
const { user, isSignedIn, actions: { signInWithEmailAdmin } } = useAuth();

const [password, setPassword] = useState("");

const login = () => {
signInWithEmailAdmin(ADMIN_EMAIL, password);
};

return (
<div className='p-4 mx-auto'>
<h3>Admin panel</h3>
<img src='https://kagi.com/proxy/dancing-hotdog.gif?c=LwXhtTInURWSS6isT8YHWKe8FwnGJQHVUI8ripCyb5_bo5Nz6m4FFRepUDLKKGEiwUgilfKdVgJGOj_FkTWB3oVDNKsQqxkcy779n6bix2g%3D' className='mw-100'></img>
<FormControl type="password" className='my-2 py-2' placeholder='heslo zmrde' onChange={(e) => setPassword(e.target.value)} />
<Button className='mx-auto' onClick={() => login()} style={{display: "block"}}>Login</Button>
</div>
);
};
26 changes: 26 additions & 0 deletions src/pages/admin/_router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { Route, Switch, Redirect } from "wouter";
import { useAuth } from 'pocketbase-react/src';
import { AdminHomePage } from './home';
import { AdminLoginPage } from './_login';

export const AdminRouter: React.FC = () => {
const { user, isSignedIn, actions } = useAuth();
console.log({user, isSignedIn, actions});

if (!isSignedIn) {
return (
<Switch>
<Route path="/admin/login" component={AdminLoginPage} />
<Route><Redirect href="/" /></Route>
</Switch>
);
}

return (
<Switch>
<Route path="/admin/login"><Redirect href="/admin" /></Route>
<Route path="/admin" component={AdminHomePage} />
</Switch>
);
};
Empty file added src/pages/admin/login.tsx
Empty file.
Loading

0 comments on commit 1127462

Please sign in to comment.