Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New page to draw random cards #135

Merged
merged 9 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# @eop/client

## 1.1.0

### Minor Changes

- Draw a random card.

### Patch Changes

- Updated dependencies
- @eop/[email protected]
- @eop/[email protected]

## 1.0.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion apps/client/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@eop/client",
"type": "module",
"version": "1.0.1",
"version": "1.1.0",
"main": "src/client/index.tsx",
"scripts": {
"build": "tsc -b tsconfig.app.json && vite build",
Expand Down
Binary file modified apps/client/public/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 7 additions & 4 deletions apps/client/src/components/dealtcard/dealtcard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ import type { Card } from '@eop/shared';
interface DealtCardProps {
gameMode: GameMode;
card: Card;
isAlignedRight?: boolean;
}

const DealtCard: React.FC<DealtCardProps> = ({ gameMode, card }) => {
const DealtCard: React.FC<DealtCardProps> = ({
gameMode,
card,
isAlignedRight = false,
}) => {
const roundedClass =
gameMode === GameMode.CUMULUS ? `card-rounded-cumulus` : `card-rounded`;
const translationClass =
gameMode === GameMode.EOMLSEC ? `card-translate-left` : ``;
return (
<div
className={`playing-card ${getCardCssClass(
gameMode,
card,
)} active ${roundedClass} scaled-big ${translationClass}`}
)} active ${roundedClass} scaled-big ${isAlignedRight ? `aligned-right` : ``} `}
/>
);
};
Expand Down
12 changes: 7 additions & 5 deletions apps/client/src/components/footer/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ type FooterProps = {
};

const Footer: FC<FooterProps> = ({ short = false }) => (
<small className="text-white-50">
<small className="text-black-50">
v{packageJson.version}
{!short && (
<>
<span>
{' '}
- made with{' '}
<FontAwesomeIcon icon={faHeart} style={{ color: '#00cc00' }} /> at
Careem and{' '}
<a href="https://www.tngtech.com/en/">TNG Technology Consulting</a> -
Elevation of Privilege was originally invented at Microsoft,
Cornucopia was developed at OWASP, Cumulus was started at{' '}
&nbsp;
<a href="https://www.tngtech.com/en/">
TNG Technology Consulting
</a>{' '}
and Careem - Elevation of Privilege was originally invented at
Microsoft, Cornucopia was developed at OWASP, Cumulus was started at{' '}
<a href="https://www.tngtech.com/en/">TNG Technology Consulting</a>,
Elevation of MLsec was developed at{' '}
<a href="https://www.kantega.no/">Kantega AS</a>.
Expand Down
3 changes: 3 additions & 0 deletions apps/client/src/components/logo/logo.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.logo {
cursor: pointer;
}
2 changes: 2 additions & 0 deletions apps/client/src/components/logo/logo.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type React from 'react';
import { useNavigate } from 'react-router-dom';
import classes from './logo.module.css';

const Logo: React.FC = () => {
const navigate = useNavigate();

return (
<img
className={classes['logo']}
src="logo.png"
alt="logo"
height="120px"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.card-deck-selector-container {
display: flex;
flex-wrap: wrap;
justify-content: start;
}
.card-deck-selection {
width: 15rem;
cursor: pointer;
}

.card-container {
display: flex;
position: inherit;
justify-content: center;
height: 600px;
}
64 changes: 64 additions & 0 deletions apps/client/src/components/randomcarddisplay/randomCardDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { CARD_DECKS, GameMode, SUITS } from '@eop/shared';
import { FC, useState } from 'react';
import DealtCard from '../dealtcard/dealtcard';
import classes from './randomCardDisplay.module.css';
import { Button, FormGroup, Input, Label } from 'reactstrap';

const allDecks = Object.keys(CARD_DECKS) as GameMode[];
const RandomCardDisplay: FC = () => {
const [selectedDecks, setSelectedDecks] = useState<GameMode[]>(allDecks);
const selectableCards = selectedDecks.flatMap((deck) =>
SUITS.flatMap((suit) =>
CARD_DECKS[deck][suit].cards.map((card) => ({
card,
gameMode: deck,
})),
),
);
const getRandomSelectableCard = () =>
selectableCards[Math.floor(Math.random() * selectableCards.length)];
const [selectedCard, setSelectedCard] = useState(getRandomSelectableCard());
const selectCard = () => setSelectedCard(getRandomSelectableCard());
const toggleCardDeck = (deck: GameMode) =>
setSelectedDecks((selectedDecks) =>
selectedDecks.includes(deck)
? selectedDecks.filter((selectedDeck) => selectedDeck !== deck)
: [...selectedDecks, deck],
);

return (
<>
<FormGroup check className={classes['card-deck-selector-container']}>
{allDecks.map((deck) => (
<Label
check
className={classes['card-deck-selection']}
key={`card-deck-selector-${deck}`}
>
<Input
id="radio-button-default-model"
type="checkbox"
onChange={() => toggleCardDeck(deck)}
checked={selectedDecks.includes(deck)}
/>
{deck}
</Label>
))}
</FormGroup>
{selectedCard && (
<div className={classes['card-container']}>
<DealtCard
ChristophNiehoff marked this conversation as resolved.
Show resolved Hide resolved
gameMode={selectedCard.gameMode}
card={selectedCard.card}
/>
</div>
)}
<Button block size="lg" color="primary" onClick={selectCard}>
New Card
</Button>
</>
);
};

export default RandomCardDisplay;
9 changes: 9 additions & 0 deletions apps/client/src/components/sidebar/sidebar.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,12 @@
margin-top: 3%;
margin-bottom: 1%;
}

.dealt-card-container {
position: relative;
}

.aligned-right {
position: absolute;
right: 0;
}
13 changes: 9 additions & 4 deletions apps/client/src/components/sidebar/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const Sidebar: FC<SidebarProps> = ({
secret={secret}
block
size="lg"
color="success"
color="secondary"
apiEndpoint="download"
>
Download Model
Expand All @@ -66,7 +66,7 @@ const Sidebar: FC<SidebarProps> = ({
secret={secret}
block
size="lg"
color="warning"
color="secondary"
apiEndpoint="download/text"
>
Download Threats
Expand Down Expand Up @@ -96,8 +96,13 @@ const Sidebar: FC<SidebarProps> = ({
Pass
</Button>
)}

<DealtCard card={dealtCard} gameMode={G.gameMode} />
<div className="dealt-card-container">
<DealtCard
card={dealtCard}
gameMode={G.gameMode}
isAlignedRight={true}
/>
</div>
</div>
);
};
Expand Down
5 changes: 4 additions & 1 deletion apps/client/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import About from './pages/about';
import App from './pages/app';
import Create from './pages/create';
import About from './pages/about';
import RandomCard from './pages/random-card';

import './styles/index.css';
import 'bootstrap/dist/css/bootstrap.min.css';

const container = document.getElementById('root');
Expand All @@ -14,6 +16,7 @@ const router = createBrowserRouter([
{ path: '/:matchID/:playerID/:credentials', element: <App /> },
{ path: '/', element: <Create /> },
{ path: '/about', element: <About /> },
{ path: '/random-card', element: <RandomCard /> },
]);

root.render(<RouterProvider router={router} />);
2 changes: 0 additions & 2 deletions apps/client/src/pages/about.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import '../styles/about.css';

import type React from 'react';
import type { FC } from 'react';
import { Card, CardBody, CardHeader, Col, Container, Row } from 'reactstrap';
Expand Down
40 changes: 16 additions & 24 deletions apps/client/src/pages/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
import type { PlayerID } from 'boardgame.io';
import _ from 'lodash';
import React, { ChangeEvent } from 'react';
import { Link } from 'react-router-dom';
import {
Button,
Card,
Expand Down Expand Up @@ -256,7 +255,7 @@ class Create extends React.Component<CreateProps, CreateState> {

formatAllLinks(): string {
return (
'You have been invited to a game of Elevation of Privilege:\n\n' +
'You have been invited to a threat modeling game:\n\n' +
Array(this.state.players)
.fill(0)
.map((_, i) => {
Expand All @@ -270,19 +269,6 @@ class Create extends React.Component<CreateProps, CreateState> {
const cardBody = !this.state.created ? (
<div>
<Banner />
<p>
Elevation of Privilege (EoP) is the easy way to get started and learn
threat modeling. It is a card game that developers, architects or
security experts can play.
</p>
<p>
To learn more about the game, navigate to the{' '}
<Link to="/about">about page</Link>.
</p>
<small className="text-secondary">
To start playing, select the number of players and enter their names.
</small>
<hr />
<Form>
<FormGroup row>
<Label for="players" sm={2}>
Expand Down Expand Up @@ -488,18 +474,26 @@ class Create extends React.Component<CreateProps, CreateState> {
<Button
block
size="lg"
color="warning"
color="primary"
disabled={this.state.creating || !this.isFormValid()}
onClick={this.createGame.bind(this)}
>
Proceed
</Button>
</Form>
<hr />
<small className="text-secondary">
Players will be able to join the game with the links that are
generated after you proceed.
</small>
<p className="centered">
Alternatively, if you do not want to play a full game you can just
select a few random cards.
</p>
<Button
block
size="lg"
color="secondary"
onClick={() => (window.location.href = `/random-card`)}
>
Draw a random card
</Button>
</div>
) : (
<div>
Expand Down Expand Up @@ -551,7 +545,7 @@ class Create extends React.Component<CreateProps, CreateState> {
<hr />
<CopyButton
text={this.formatAllLinks()}
color="warning"
color="secondary"
block
size="lg"
>
Expand All @@ -576,9 +570,7 @@ class Create extends React.Component<CreateProps, CreateState> {
<Logo />
</div>
<Card className="create-card">
<CardHeader className="text-center">
Elevation of Privilege
</CardHeader>
<CardHeader className="text-center">Threat Modeling</CardHeader>
<CardBody>{cardBody}</CardBody>
</Card>
</Col>
Expand Down
38 changes: 38 additions & 0 deletions apps/client/src/pages/random-card.tsx
ghost91- marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { FC } from 'react';
import { Card, CardBody, CardHeader, Col, Container, Row } from 'reactstrap';
import Banner from '../components/banner/banner';

import Footer from '../components/footer/footer';
import Logo from '../components/logo/logo';
import RandomCardDisplay from '../components/randomcarddisplay/randomCardDisplay';

const RandomCard: FC = () => {
return (
<div>
<Banner />
<Container className="about" fluid>
<Row style={{ paddingTop: '20px' }}>
<Col sm="12" md={{ size: 6, offset: 3 }}>
<div className="text-center">
<Logo />
</div>
<Card>
<CardHeader className="text-center">Threat Modeling</CardHeader>
<CardBody>
<h1>Random Card</h1>
<RandomCardDisplay />
</CardBody>
</Card>
</Col>
</Row>
<Row>
<Col sm="12" md={{ size: 6, offset: 3 }} className="text-center">
<Footer />
</Col>
</Row>
</Container>
</div>
);
};

export default RandomCard;
3 changes: 0 additions & 3 deletions apps/client/src/styles/about.css

This file was deleted.

4 changes: 0 additions & 4 deletions apps/client/src/styles/cards.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
border-radius: 25px;
}

.card-translate-left {
margin-left: -5rem;
}

.playingCardsContainer {
position: fixed;
bottom: 0;
Expand Down
Loading
Loading