Skip to content

Commit

Permalink
feat(app-create-game): add create game form
Browse files Browse the repository at this point in the history
  • Loading branch information
rams23 committed Jan 14, 2021
1 parent 83cef7c commit de21b18
Show file tree
Hide file tree
Showing 38 changed files with 491 additions and 21 deletions.
12 changes: 6 additions & 6 deletions fixtures/firestore-data/cards.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"51xgCF43n5iaMNLkxGs9": {
"type": "Scenario",
"type": "scenario",
"tags": null,
"title": "Social Media Online Application",
"subtitle": null,
Expand All @@ -9,7 +9,7 @@
"deckId": "7p5qqvE8kCV9WWysVc2n"
},
"1Zs45g2y6GSE5s4XS9BA": {
"type": "Scenario",
"type": "scenario",
"tags": null,
"title": "Online Payments for Solar Power",
"subtitle": null,
Expand All @@ -18,7 +18,7 @@
"deckId": "7p5qqvE8kCV9WWysVc2n"
},
"G5JfGVoM7SZ6jOsdjWWp": {
"type": "Scenario",
"type": "scenario",
"tags": null,
"title": "Fitness & Running Mobile App",
"subtitle": null,
Expand All @@ -27,7 +27,7 @@
"deckId": "7p5qqvE8kCV9WWysVc2n"
},
"UUmhPl9KzWZ3v7Vt7nrT": {
"type": "Scenario",
"type": "scenario",
"tags": null,
"title": "Photo Editing Software",
"subtitle": null,
Expand All @@ -36,7 +36,7 @@
"deckId": "7p5qqvE8kCV9WWysVc2n"
},
"k4ourZRR6MBxmfXJiDd5": {
"type": "Scenario",
"type": "scenario",
"tags": null,
"title": "Classified Ads for Second-Hand Items",
"subtitle": null,
Expand Down Expand Up @@ -330,4 +330,4 @@
"number": 39,
"deckId": "7p5qqvE8kCV9WWysVc2n"
}
}
}
2 changes: 2 additions & 0 deletions packages/common/src/card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ export interface Card {
number: number,
deckId: string
}

export type CardEntity = Card & {id:string};
3 changes: 2 additions & 1 deletion packages/common/src/firebaseCollections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export enum FirebaseCollections {
Users = 'users',
DynamicData = 'dynamicData',
Games = 'games',
Decks = 'decks'
Decks = 'decks',
Cards = 'cards',
}
3 changes: 2 additions & 1 deletion packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {FirebaseCollections} from "./firebaseCollections";
import {FirebaseDocs} from "./firebaseDocs";
import {DevOpsMaturitiesDoc} from "./devOpsMaturitiesDoc";
import {GameRolesDoc} from "./gameRolesDoc";
import {Card, CardTypes, CardTags} from "./card";
import {Card, CardTypes, CardTags, CardEntity} from "./card";
import {Game} from "./game";
import {ShortUser} from "./user";

Expand All @@ -16,6 +16,7 @@ export {
export type {
DevOpsMaturitiesDoc,
GameRolesDoc,
CardEntity,
Card,
Game,
ShortUser
Expand Down
2 changes: 2 additions & 0 deletions packages/game-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const VerifyEmail = React.lazy(() => import('./signup/components/VerifyEmail'));
const Dashboard = React.lazy(() => import('./dashboard/components/Dashboard'));
const Login = React.lazy(() => import('./login/components/Login'));
const GameView = React.lazy(() => import('./gameView/components/GameView'));
const CreateGameView = React.lazy(() => import('./createGame/components/CreateGameView'));

/**
* Returns route and default redirect according to auth condition:
Expand Down Expand Up @@ -57,6 +58,7 @@ function App() {
<Switch>
<PrivateRoute path={RoutingPath.Dashboard} component={Dashboard} />
<PrivateRoute path={RoutingPath.Game} component={GameView} />
<PrivateRoute path={RoutingPath.CreateGame} component={CreateGameView} />
{renderAuthRoutes(user)}
</Switch>
</Suspense>
Expand Down
1 change: 1 addition & 0 deletions packages/game-app/src/_shared/auth/saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ function* executeEmailVerification(action: ReturnType<typeof actions.verifyEmail
// need to refresh user to get token with email verified set to true
if (currentUser && firebaseUser) {
yield call(() => firebaseUser.reload());
yield call(() => firebaseUser.getIdToken(true));
const newUser = firebase.auth().currentUser!;
yield put(
actions.setLoggedUser({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import styled from 'styled-components';

type Props = {
name: string;
label: string;
label?: string;
value: string;
onChange: React.ChangeEventHandler<HTMLInputElement>;
errorMessage?: string | null;
Expand Down
30 changes: 30 additions & 0 deletions packages/game-app/src/_shared/components/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import styled from 'styled-components';

type Props = {
name: string;
label?: string;
value: string;
onChange: React.ChangeEventHandler<HTMLInputElement>;
errorMessage?: string | null;
disabled?: boolean;
};

const StyledTextArea = styled.textarea`
border: 0;
border-radius: 10px;
`;

const TextArea: React.FC<Props> = ({ name, value, errorMessage, label, onChange, disabled }) => {
return (
<div className="column">
{label ? <label htmlFor={name}>{label}</label> : null}
<StyledTextArea rows={4} disabled={disabled} value={value} name={name} id={name} onChange={onChange as any} />
{errorMessage ? <span className="error-message">{errorMessage}</span> : null}
</div>
);
};

TextArea.displayName = 'TextArea';

export default TextArea;
3 changes: 3 additions & 0 deletions packages/game-app/src/_shared/components/TextArea/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import TextArea from './TextArea';

export default TextArea;
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import React from 'react';

type Props = {
name: string;
label: string;
label?: string;
value: string;
onChange: React.ChangeEventHandler<HTMLInputElement>;
errorMessage?: string | null;
type?: string;
disabled?: boolean;
};

const TextInput: React.FC<Props> = ({ name, value, errorMessage, label, onChange, type = 'text' }) => {
const TextInput: React.FC<Props> = ({ name, value, errorMessage, label, onChange, type = 'text', disabled }) => {
return (
<div className="column">
<label htmlFor={name}>{label}</label>
<input type={type} value={value} name={name} id={name} onChange={onChange} />
{label ? <label htmlFor={name}>{label}</label> : null}
<input disabled={disabled} type={type} value={value} name={name} id={name} onChange={onChange} />
{errorMessage ? <span className="error-message">{errorMessage}</span> : null}
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/game-app/src/_shared/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import SelectInput from './SelectInput';
import TextInput from './TextInput';
import PasswordInput from './PasswordInput';
import TextArea from './TextArea';

export { TextInput, SelectInput, PasswordInput };
export { TextInput, SelectInput, PasswordInput, TextArea };
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ type Props = {
/**
* Label to show over the input
*/
label: string;
label?: string;
type?: string;
CustomInput?: React.ComponentType<React.ComponentProps<typeof TextInput>>;
disabled?: boolean;
};

/**
* Input directly connected to the parent form that includes error message
* visualization. Its value can be found under the {name} key in the form
*/
const FormTextField: React.FC<Props> = ({ name, label, type, CustomInput }) => {
const FormTextField: React.FC<Props> = ({ name, label, type, CustomInput, disabled }) => {
const data = useFormContext();

const error = data.errors[name];
Expand All @@ -46,10 +47,11 @@ const FormTextField: React.FC<Props> = ({ name, label, type, CustomInput }) => {
onChange={props.onChange}
errorMessage={translatedError}
type={type}
disabled={disabled}
/>
);
},
[translatedError, label, type, CustomInput],
[CustomInput, label, translatedError, type, disabled],
);

return (
Expand Down
2 changes: 2 additions & 0 deletions packages/game-app/src/_shared/i18n/useTranslate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ export function translateFactory(language: string) {
key: Path<typeof enTranslations>,
options?: {
default?: string;
data?: object;
},
) {
return I18n.t(key, {
locale: language,
defaultValue: options?.default,
...(options?.data || {}),
});
};
}
2 changes: 2 additions & 0 deletions packages/game-app/src/_shared/requests-status/requestsKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export interface RequestsKeys {
'auth.emailVerification': null;
'auth.login': null;
'auth.logout': null;
'game.loadCards': null;
createGame: null;
}
1 change: 1 addition & 0 deletions packages/game-app/src/_shared/routing/routingPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export enum RoutingPath {
EmailVerificationRequired = '/email-verification-required',
VerifyEmail = '/verify-email',
Game = '/game',
CreateGame = '/create-game',
}
2 changes: 2 additions & 0 deletions packages/game-app/src/_shared/store/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { name as i18nName, reducer as i18nReducer } from '@pipeline/i18n';
import { name as authName, reducer as authReducer } from '@pipeline/auth';
import { name as requestsStatusName, reducer as requestsStatusReducer } from '@pipeline/requests-status';
import { name as dynamicDataName, reducer as dynamicDataReducer } from '@pipeline/dynamicData';
import { name as gameName, reducer as gameReducer } from '../../gameView/slice';

const reducers = {
[i18nName]: i18nReducer,
[authName]: authReducer,
[requestsStatusName]: requestsStatusReducer,
[dynamicDataName]: dynamicDataReducer,
[gameName]: gameReducer,
};

export default reducers;
11 changes: 10 additions & 1 deletion packages/game-app/src/_shared/store/rootSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ import { Saga } from 'redux-saga';
import { all, call, spawn } from 'redux-saga/effects';
import { saga as authSaga } from '@pipeline/auth';
import signupSaga from '../../signup/sagas';
import gameSaga from '../../gameView/sagas';
import createGameSaga from '../../createGame/sagas';
import {
runRetrieveDevOpsMaturities as retrieveDevOpsMaturitiesSaga,
runRetrieveGameRoles as retrieveGameRolesMaturitiesSaga,
} from '@pipeline/dynamicData';

export default function* rootSaga() {
const sagas: Saga[] = [authSaga, signupSaga, retrieveGameRolesMaturitiesSaga, retrieveDevOpsMaturitiesSaga];
const sagas: Saga[] = [
authSaga,
signupSaga,
retrieveGameRolesMaturitiesSaga,
retrieveDevOpsMaturitiesSaga,
gameSaga,
createGameSaga,
];
yield all(
sagas.map(saga =>
spawn(function* () {
Expand Down
8 changes: 8 additions & 0 deletions packages/game-app/src/assets/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ const translations = {
},
logout: 'Sign out',
},
createGame: {
title: 'Choose a scenario',
subtitle: 'Premade {{cardsCount}}',
writeYours: 'Make your own',
createButtonText: 'Start Game',
},
dashboard: {
title: 'Pipeline',
subtitle: 'The Game that Delivers',
newGameLabel: 'Create game',
message:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
},
Expand Down Expand Up @@ -53,6 +60,7 @@ const translations = {
},
},
general: {
cancel: 'Cancel',
errors: {
required: 'This field is required',
},
Expand Down
4 changes: 4 additions & 0 deletions packages/game-app/src/createGame/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createAction } from '@reduxjs/toolkit';
import { GameCreationData } from '../types/gameCreationData';

export const createGame = createAction<GameCreationData>('createGame/start');
22 changes: 22 additions & 0 deletions packages/game-app/src/createGame/apis/callCreateGame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GameCreationData } from '../types/gameCreationData';
import firebase from 'firebase/app';
import 'firebase/firestore';
import { FirebaseCollections, Game } from '@pipeline/common';

const DEFAULT_DECK_ID = '7p5qqvE8kCV9WWysVc2n';

export default function callCreateGame(data: GameCreationData, userId: string) {
return firebase
.firestore()
.collection(FirebaseCollections.Games)
.add({
scenarioContent: data.scenarioContent,
scenarioTitle: data.scenarioTitle,
scenarioCardId: data.scenarioCardId || null,
facilitator: {
id: userId,
},
deckId: DEFAULT_DECK_ID,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
} as Game);
}
Loading

0 comments on commit de21b18

Please sign in to comment.