commit 376aab437d3df209b90ff989d3ec73ce1420a964
parent ce42583414a689837101ec26c0f67b415d56cfb2
Author: finwo <finwo@pm.me>
Date: Sun, 12 Dec 2021 02:36:16 +0100
Showing drawn numbers on web page through rest call now
Diffstat:
8 files changed, 232 insertions(+), 10 deletions(-)
diff --git a/solutions/day-04/api/src/bingo/bingo.controller.ts b/solutions/day-04/api/src/bingo/bingo.controller.ts
@@ -0,0 +1,63 @@
+import { Service, Inject } from 'typedi';
+import { Request, Response } from 'express';
+
+import { Controller, Get } from '../interface/rest';
+import { BingoService } from './bingo.service';
+
+@Controller('bingo')
+@Service()
+export class BingoController {
+
+ constructor(
+ private bingoService: BingoService
+ ) {}
+
+ // List game uuids
+ @Get('games/:uuid/boards')
+ async getGameBoards({ res, uuid }: { res: Response, uuid: string }) {
+ const found = await this.bingoService.getGameBoards(uuid);
+ if (!found) return res.end(JSON.stringify({
+ ok : false,
+ error : 'not-found',
+ }));
+ res.end(JSON.stringify({
+ ok : true,
+ data : found.map(board => ({
+ uuid : board.uuid,
+ values : board.values,
+ }))
+ }, null, 2));
+ }
+
+ // Fetch a single game
+ @Get('games/:uuid')
+ async getGame({ res, uuid }: { res: Response, uuid: string }) {
+ const found = await this.bingoService.get(uuid);
+ if (!found) return res.end(JSON.stringify({
+ ok : false,
+ error : 'not-found',
+ }));
+ res.end(JSON.stringify({
+ ok : true,
+ data : {
+ uuid : found.uuid,
+ name : found.name,
+ drawn : found.drawn,
+ },
+ }, null, 2));
+ }
+
+ // List game uuids
+ @Get('games')
+ async listGames({ res }: { res: Response }) {
+ const found = await this.bingoService.all();
+ res.end(JSON.stringify({
+ ok : true,
+ data : found.map(game => ({
+ uuid : game.uuid,
+ name : game.name,
+ })),
+ }));
+ }
+
+}
diff --git a/solutions/day-04/api/src/bingo/bingo.module.ts b/solutions/day-04/api/src/bingo/bingo.module.ts
@@ -1,13 +1,17 @@
import { Service, Container } from 'typedi';
import { lineByLine } from '@common/line-by-line';
+import { BingoController } from './bingo.controller';
+
import { Board } from './model/board';
import { Game } from './model/game';
@Service()
export class BingoModule {
- constructor() {
+ constructor(
+ private bingoController: BingoController
+ ) {
this.initializeDb();
}
@@ -24,7 +28,11 @@ export class BingoModule {
// Task defined first line as already-drawn numbers
if (!game) {
- game = new Game({ drawn: line.split(',').map(v => parseInt(v)), board: [] });
+ game = new Game({
+ name : 'Base game',
+ drawn : line.split(',').map(v => parseInt(v)),
+ board : []
+ });
db.game.push(game);
return;
}
diff --git a/solutions/day-04/api/src/bingo/bingo.service.ts b/solutions/day-04/api/src/bingo/bingo.service.ts
@@ -0,0 +1,31 @@
+import { Service, Inject } from 'typedi';
+import { Board } from './model/board';
+import { Game } from './model/game';
+
+@Service()
+export class BingoService {
+
+ constructor(
+ @Inject('db') private db
+ ) {}
+
+ async all(): Promise<Game[]> {
+ return this.db.game;
+ }
+
+ async get(game: string|Partial<Game>): Promise<Game> {
+ if ('string' === typeof game) return this.db.game.find(g => g.uuid === game);
+ if ('uuid' in game) return this.db.game.find(g => g.uuid === game.uuid);
+ throw new Error("Could not fetch game: invalid identifier");
+ }
+
+ async getGameBoards(game: string|Partial<Game>): Promise<Board[]> {
+ const found = await this.get(game);
+ if (!found) return [];
+ return found.board.map(board => {
+ if (board instanceof Board) return board;
+ return this.db.board.find(g => g.uuid === game);
+ });;
+ }
+
+}
diff --git a/solutions/day-04/api/src/interface/rest/index.ts b/solutions/day-04/api/src/interface/rest/index.ts
@@ -19,7 +19,7 @@ export function Controller(prefix?: string): ClassDecorator {
const path = ['',prefix,route.path].join('/').replace(/\/+/g,'/');
router[route.method](path, async (req: Request, res: Response, next: Function) => {
const controller = Container.get(constructor);
- await controller[route.name]({ req, res });
+ await controller[route.name]({ req, res, ...req.params });
if (!res.writableEnded) next();
});
}
diff --git a/solutions/day-04/web/public/global.less b/solutions/day-04/web/public/global.less
@@ -8,6 +8,7 @@
* {
box-sizing: border-box;
+ line-height: 1.5em;
}
html, body {
@@ -15,3 +16,5 @@ html, body {
margin : 0;
padding : 0;
}
+
+h1, h2, h3 { font-weight: 500; }
diff --git a/solutions/day-04/web/src/main.js b/solutions/day-04/web/src/main.js
@@ -6,15 +6,17 @@ import VueNotificationList from '@dafcoe/vue-notification'
import '@dafcoe/vue-notification/dist/vue-notification.css'
import PageHome from './page/home.vue';
+import PageGameList from './page/game-list.vue';
import PageGame from './page/game.vue';
import PageNotFound from './page/not-found.vue';
const router = createRouter({
history: createWebHashHistory(),
routes: [
- { path: '/' , component: PageHome , name: 'Home' , meta: { nav: true , icon: 'home' } },
- { path: '/game' , component: PageGame , name: 'Game' , meta: { nav: true , icon: 'videogame_asset' } },
- { path: '/:catchAll(.*)', component: PageNotFound, name: 'Not Found', meta: { nav: false, icon: 'error' } },
+ { path: '/' , component: PageHome , name: 'Home' , meta: { nav: true , icon: 'home' } },
+ { path: '/game/:uuid(.*)', component: PageGame , name: 'Game' , meta: { nav: false, icon: 'videogame_asset' } },
+ { path: '/game' , component: PageGameList, name: 'Games' , meta: { nav: true , icon: 'videogame_asset' } },
+ { path: '/:catchAll(.*)' , component: PageNotFound, name: 'Not Found', meta: { nav: false, icon: 'error' } },
]
});
@@ -22,3 +24,5 @@ const app = createApp(root);
app.use(router);
app.use(VueNotificationList);
app.mount(document.body);
+
+console.log(router.getRoutes());
diff --git a/solutions/day-04/web/src/page/game-list.vue b/solutions/day-04/web/src/page/game-list.vue
@@ -0,0 +1,50 @@
+<template>
+ <layout>
+ <div id="gamelist">
+ <div v-for="g in games">
+ <router-link :to="'/game/' + g.uuid">{{ g.name }}</router-link>
+ </div>
+ </div>
+ </layout>
+</template>
+
+<style>
+#gamelist {
+ display: flex;
+ flex-wrap: wrap;
+ /* margin: -0.5rem; */
+ gap: 1rem;
+}
+#gamelist > * {
+ /* flex: 1; */
+ background: var(--col-primary);
+ color: #fff;
+ padding: 1rem;
+ /* margin: 0.5rem; */
+}
+#gamelist a {
+ background: var(--col-primary);
+ color: #fff;
+}
+</style>
+
+<script lang="ts">
+import { ref, onMounted } from 'vue';
+import Layout from '../layout/default.vue';
+
+export default {
+ components: {Layout},
+ setup() {
+ const games = ref([]);
+
+ onMounted(async () => {
+ const gameResponse = await (await fetch('http://api.docker/bingo/games', { method: 'GET' })).json();
+ if (gameResponse.ok) games.value = gameResponse.data;
+ });
+
+ return {
+ games,
+ };
+ }
+}
+</script>
diff --git a/solutions/day-04/web/src/page/game.vue b/solutions/day-04/web/src/page/game.vue
@@ -1,21 +1,84 @@
<template>
<layout>
- <div id="drawn">
- DRAWN
+ <div>
+ <h3>Drawn numbers</h3>
+ <div id="numberlist">
+ <div v-for="n in game.drawn">{{ n }}</div>
+ </div>
</div>
- <div id="boards">
- BOARDS
+ <div>
+ <h3>Boards</h3>
+ <div id="boardlist">
+ <div v-for="board in boards">
+
+ </div>
+ </div>
</div>
+ Single game life
</layout>
</template>
<style>
+#numberlist {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+#numberlist > * {
+ background: var(--col-primary);
+ color: #fff;
+ padding: 1rem;
+ height: 3.5rem; /* 1.5 line height + 2 edges of padding */
+ width: 3.5rem; /* making the block square */
+ text-align: center;
+}
+
+#boardlist {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+#boardlist > * {
+ background: var(--col-primary);
+ color: #fff;
+ padding: 1rem;
+ font-size: 1rem;
+ height: 3.5rem;
+ width: 3.5rem;
+ text-align: center;
+}
+
</style>
<script lang="ts">
+import { ref, onMounted, getCurrentInstance } from 'vue';
import Layout from '../layout/default.vue';
+
export default {
components: {Layout},
+ setup() {
+ const instance = getCurrentInstance();
+ const route = instance.proxy.$root.$route;
+ const { uuid } = route.params;
+
+ console.log({uuid});
+
+ const selected = ref([]);
+ const game = ref({});
+ const boards = ref([]);
+
+ onMounted(async () => {
+ const gameResponse = await (await fetch(`http://api.docker/bingo/games/${uuid}` , { method: 'GET' })).json();
+ const boardResponse = await (await fetch(`http://api.docker/bingo/games/${uuid}/boards`, { method: 'GET' })).json();
+ if (gameResponse.ok ) game.value = gameResponse.data;
+ if (boardResponse.ok) boards.value = boardResponse.data;
+ });
+
+ return {
+ game,
+ boards
+ };
+ }
}
</script>