Compare commits
10 Commits
3640116dd5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
00b2f025fd
|
|||
|
018974266e
|
|||
|
0ddf35bc26
|
|||
|
bbab72187a
|
|||
|
86fb93f665
|
|||
|
9733b9116f
|
|||
|
b9d1a748d8
|
|||
|
abbc2de298
|
|||
|
82c98435f6
|
|||
|
3ce3fabc81
|
@@ -35,6 +35,9 @@ type Repo interface {
|
||||
// Get all players
|
||||
GetPlayers() ([]*model.Player, error)
|
||||
|
||||
// Search players by a query
|
||||
SearchPlayer(string) ([]*model.Player, error)
|
||||
|
||||
// Get all games
|
||||
GetGames() ([]*model.Game, error)
|
||||
}
|
||||
|
||||
@@ -277,3 +277,30 @@ func (s *SQLRepo) GetGames() ([]*model.Game, error) {
|
||||
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
func (s *SQLRepo) SearchPlayer(query string) ([]*model.Player, error) {
|
||||
rows, err := s.db.Query(`
|
||||
SELECT id, name, elo
|
||||
FROM Players
|
||||
WHERE name LIKE ?
|
||||
`, query+"%")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtn := []*model.Player{}
|
||||
|
||||
for rows.Next() {
|
||||
var player model.Player
|
||||
|
||||
err = rows.Scan(&player.ID, &player.Name, &player.Elo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtn = append(rtn, &player)
|
||||
}
|
||||
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
@@ -329,15 +329,36 @@ func createShema(repo repo.Repo) graphql.Schema {
|
||||
},
|
||||
},
|
||||
"players": &graphql.Field{
|
||||
Type: graphql.NewNonNull(graphql.NewList(player)),
|
||||
Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(player))),
|
||||
Description: "Get all players",
|
||||
Args: graphql.FieldConfigArgument{
|
||||
"query": &graphql.ArgumentConfig{
|
||||
Type: graphql.String,
|
||||
DefaultValue: "",
|
||||
},
|
||||
},
|
||||
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||
players, err := repo.GetPlayers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
query, ok := p.Args["query"].(string)
|
||||
if !ok {
|
||||
log.Printf("Failed to parse query at players: %v", p.Args["query"])
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return players, nil
|
||||
if query == "" {
|
||||
players, err := repo.GetPlayers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return players, nil
|
||||
} else {
|
||||
players, err := repo.SearchPlayer(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return players, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
"games": &graphql.Field{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Front from "./pages/Front.svelte";
|
||||
import Game from "./pages/Game.svelte";
|
||||
import Player from "./pages/Player.svelte";
|
||||
import { RouterView } from "@dvcol/svelte-simple-router/components";
|
||||
@@ -8,6 +9,11 @@
|
||||
} from "@dvcol/svelte-simple-router/models";
|
||||
|
||||
const routes: Readonly<Route[]> = [
|
||||
{
|
||||
name: "front",
|
||||
path: "/",
|
||||
component: Front,
|
||||
},
|
||||
{
|
||||
name: "game",
|
||||
path: "/game/:{string}:id",
|
||||
|
||||
87
web/src/gql/fragment-masking.ts
Normal file
87
web/src/gql/fragment-masking.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/* eslint-disable */
|
||||
import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
|
||||
import type { FragmentDefinitionNode } from 'graphql';
|
||||
import type { Incremental } from './graphql';
|
||||
|
||||
|
||||
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
|
||||
infer TType,
|
||||
any
|
||||
>
|
||||
? [TType] extends [{ ' $fragmentName'?: infer TKey }]
|
||||
? TKey extends string
|
||||
? { ' $fragmentRefs'?: { [key in TKey]: TType } }
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
// return non-nullable if `fragmentType` is non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
|
||||
): TType;
|
||||
// return nullable if `fragmentType` is undefined
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined
|
||||
): TType | undefined;
|
||||
// return nullable if `fragmentType` is nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null
|
||||
): TType | null;
|
||||
// return nullable if `fragmentType` is nullable or undefined
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
|
||||
): TType | null | undefined;
|
||||
// return array of non-nullable if `fragmentType` is array of non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
): Array<TType>;
|
||||
// return array of nullable if `fragmentType` is array of nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
|
||||
): Array<TType> | null | undefined;
|
||||
// return readonly array of non-nullable if `fragmentType` is array of non-nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
|
||||
): ReadonlyArray<TType>;
|
||||
// return readonly array of nullable if `fragmentType` is array of nullable
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
|
||||
): ReadonlyArray<TType> | null | undefined;
|
||||
export function useFragment<TType>(
|
||||
_documentNode: DocumentTypeDecoration<TType, any>,
|
||||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | Array<FragmentType<DocumentTypeDecoration<TType, any>>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
|
||||
): TType | Array<TType> | ReadonlyArray<TType> | null | undefined {
|
||||
return fragmentType as any;
|
||||
}
|
||||
|
||||
|
||||
export function makeFragmentData<
|
||||
F extends DocumentTypeDecoration<any, any>,
|
||||
FT extends ResultOf<F>
|
||||
>(data: FT, _fragment: F): FragmentType<F> {
|
||||
return data as FragmentType<F>;
|
||||
}
|
||||
export function isFragmentReady<TQuery, TFrag>(
|
||||
queryNode: DocumentTypeDecoration<TQuery, any>,
|
||||
fragmentNode: TypedDocumentNode<TFrag>,
|
||||
data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined
|
||||
): data is FragmentType<typeof fragmentNode> {
|
||||
const deferredFields = (queryNode as { __meta__?: { deferredFields: Record<string, (keyof TFrag)[]> } }).__meta__
|
||||
?.deferredFields;
|
||||
|
||||
if (!deferredFields) return true;
|
||||
|
||||
const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined;
|
||||
const fragName = fragDef?.name?.value;
|
||||
|
||||
const fields = (fragName && deferredFields[fragName]) || [];
|
||||
return fields.length > 0 && fields.every(field => data && field in data);
|
||||
}
|
||||
53
web/src/gql/gql.ts
Normal file
53
web/src/gql/gql.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/* eslint-disable */
|
||||
import * as types from './graphql';
|
||||
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||
|
||||
/**
|
||||
* Map of all GraphQL operations in the project.
|
||||
*
|
||||
* This map has several performance disadvantages:
|
||||
* 1. It is not tree-shakeable, so it will include all operations in the project.
|
||||
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
|
||||
* 3. It does not support dead code elimination, so it will add unused operations.
|
||||
*
|
||||
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
||||
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
|
||||
*/
|
||||
const documents = {
|
||||
"\n query searchPlayer($query: String!) {\n players(query: $query) {\n ID\n name\n }\n }\n ": types.SearchPlayerDocument,
|
||||
"\n query getGame($gameID: ID!) {\n game(id: $gameID) {\n added\n overtime\n score\n author {\n ID\n name\n }\n team0player0 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team0player1 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team1player0 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team1player1 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n }\n }\n ": types.GetGameDocument,
|
||||
"\n query getPlayer($playerID: ID!) {\n player(id: $playerID) {\n ID\n name\n elo\n history {\n delta\n endElo\n game {\n id\n }\n }\n }\n }\n ": types.GetPlayerDocument,
|
||||
};
|
||||
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
|
||||
* ```
|
||||
*
|
||||
* The query argument is unknown!
|
||||
* Please regenerate the types.
|
||||
*/
|
||||
export function graphql(source: string): unknown;
|
||||
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query searchPlayer($query: String!) {\n players(query: $query) {\n ID\n name\n }\n }\n "): (typeof documents)["\n query searchPlayer($query: String!) {\n players(query: $query) {\n ID\n name\n }\n }\n "];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query getGame($gameID: ID!) {\n game(id: $gameID) {\n added\n overtime\n score\n author {\n ID\n name\n }\n team0player0 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team0player1 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team1player0 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team1player1 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n }\n }\n "): (typeof documents)["\n query getGame($gameID: ID!) {\n game(id: $gameID) {\n added\n overtime\n score\n author {\n ID\n name\n }\n team0player0 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team0player1 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team1player0 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n team1player1 {\n ID\n name\n history(game: $gameID) {\n startElo\n delta\n }\n }\n }\n }\n "];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query getPlayer($playerID: ID!) {\n player(id: $playerID) {\n ID\n name\n elo\n history {\n delta\n endElo\n game {\n id\n }\n }\n }\n }\n "): (typeof documents)["\n query getPlayer($playerID: ID!) {\n player(id: $playerID) {\n ID\n name\n elo\n history {\n delta\n endElo\n game {\n id\n }\n }\n }\n }\n "];
|
||||
|
||||
export function graphql(source: string) {
|
||||
return (documents as any)[source] ?? {};
|
||||
}
|
||||
|
||||
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;
|
||||
120
web/src/gql/graphql.ts
Normal file
120
web/src/gql/graphql.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/* eslint-disable */
|
||||
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||
export type Maybe<T> = T | null;
|
||||
export type InputMaybe<T> = Maybe<T>;
|
||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
|
||||
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
ID: { input: string; output: string; }
|
||||
String: { input: string; output: string; }
|
||||
Boolean: { input: boolean; output: boolean; }
|
||||
Int: { input: number; output: number; }
|
||||
Float: { input: number; output: number; }
|
||||
/** The `DateTime` scalar type represents a DateTime. The DateTime is serialized as an RFC 3339 quoted string */
|
||||
DateTime: { input: any; output: any; }
|
||||
};
|
||||
|
||||
/** A game played */
|
||||
export type Game = {
|
||||
__typename?: 'Game';
|
||||
added: Scalars['DateTime']['output'];
|
||||
author: Player;
|
||||
id: Scalars['ID']['output'];
|
||||
overtime: Scalars['Boolean']['output'];
|
||||
score: Scalars['Int']['output'];
|
||||
team0player0: Player;
|
||||
team0player1: Player;
|
||||
team1player0: Player;
|
||||
team1player1: Player;
|
||||
};
|
||||
|
||||
/** The ELO change for a player in a game */
|
||||
export type GameResult = {
|
||||
__typename?: 'GameResult';
|
||||
delta: Scalars['Int']['output'];
|
||||
endElo: Scalars['Int']['output'];
|
||||
game: Game;
|
||||
id: Scalars['ID']['output'];
|
||||
player?: Maybe<Player>;
|
||||
startElo: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
/** A player. Can also be authors of games */
|
||||
export type Player = {
|
||||
__typename?: 'Player';
|
||||
ID: Scalars['ID']['output'];
|
||||
elo: Scalars['Int']['output'];
|
||||
games: Array<Maybe<Game>>;
|
||||
history: Array<Maybe<GameResult>>;
|
||||
name: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
|
||||
/** A player. Can also be authors of games */
|
||||
export type PlayerHistoryArgs = {
|
||||
game?: InputMaybe<Scalars['ID']['input']>;
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
/** Get game by ID */
|
||||
game?: Maybe<Game>;
|
||||
/** Result of a game on a players ELO */
|
||||
gameResult?: Maybe<GameResult>;
|
||||
/** Get all games */
|
||||
games: Array<Maybe<Game>>;
|
||||
/** Get player by ID */
|
||||
player?: Maybe<Player>;
|
||||
/** Get all players */
|
||||
players: Array<Player>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryGameArgs = {
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGameResultArgs = {
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryPlayerArgs = {
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryPlayersArgs = {
|
||||
query?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export type SearchPlayerQueryVariables = Exact<{
|
||||
query: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type SearchPlayerQuery = { __typename?: 'Query', players: Array<{ __typename?: 'Player', ID: string, name: string }> };
|
||||
|
||||
export type GetGameQueryVariables = Exact<{
|
||||
gameID: Scalars['ID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetGameQuery = { __typename?: 'Query', game?: { __typename?: 'Game', added: any, overtime: boolean, score: number, author: { __typename?: 'Player', ID: string, name: string }, team0player0: { __typename?: 'Player', ID: string, name: string, history: Array<{ __typename?: 'GameResult', startElo: number, delta: number } | null> }, team0player1: { __typename?: 'Player', ID: string, name: string, history: Array<{ __typename?: 'GameResult', startElo: number, delta: number } | null> }, team1player0: { __typename?: 'Player', ID: string, name: string, history: Array<{ __typename?: 'GameResult', startElo: number, delta: number } | null> }, team1player1: { __typename?: 'Player', ID: string, name: string, history: Array<{ __typename?: 'GameResult', startElo: number, delta: number } | null> } } | null };
|
||||
|
||||
export type GetPlayerQueryVariables = Exact<{
|
||||
playerID: Scalars['ID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetPlayerQuery = { __typename?: 'Query', player?: { __typename?: 'Player', ID: string, name: string, elo: number, history: Array<{ __typename?: 'GameResult', delta: number, endElo: number, game: { __typename?: 'Game', id: string } } | null> } | null };
|
||||
|
||||
|
||||
export const SearchPlayerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"searchPlayer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"players"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<SearchPlayerQuery, SearchPlayerQueryVariables>;
|
||||
export const GetGameDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getGame"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"gameID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"game"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"gameID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"added"}},{"kind":"Field","name":{"kind":"Name","value":"overtime"}},{"kind":"Field","name":{"kind":"Name","value":"score"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"team0player0"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"game"},"value":{"kind":"Variable","name":{"kind":"Name","value":"gameID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startElo"}},{"kind":"Field","name":{"kind":"Name","value":"delta"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"team0player1"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"game"},"value":{"kind":"Variable","name":{"kind":"Name","value":"gameID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startElo"}},{"kind":"Field","name":{"kind":"Name","value":"delta"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"team1player0"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"game"},"value":{"kind":"Variable","name":{"kind":"Name","value":"gameID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startElo"}},{"kind":"Field","name":{"kind":"Name","value":"delta"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"team1player1"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"game"},"value":{"kind":"Variable","name":{"kind":"Name","value":"gameID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startElo"}},{"kind":"Field","name":{"kind":"Name","value":"delta"}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetGameQuery, GetGameQueryVariables>;
|
||||
export const GetPlayerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getPlayer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"playerID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"player"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"playerID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"elo"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"delta"}},{"kind":"Field","name":{"kind":"Name","value":"endElo"}},{"kind":"Field","name":{"kind":"Name","value":"game"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetPlayerQuery, GetPlayerQueryVariables>;
|
||||
2
web/src/gql/index.ts
Normal file
2
web/src/gql/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./fragment-masking";
|
||||
export * from "./gql";
|
||||
43
web/src/lib/LastGameCard.svelte
Normal file
43
web/src/lib/LastGameCard.svelte
Normal file
@@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { link } from "@dvcol/svelte-simple-router";
|
||||
|
||||
let {
|
||||
id,
|
||||
score,
|
||||
t0p0,
|
||||
t0p1,
|
||||
t1p0,
|
||||
t1p1,
|
||||
}: {
|
||||
id: string;
|
||||
score: number;
|
||||
t0p0: string;
|
||||
t0p1: string;
|
||||
t1p0: string;
|
||||
t1p1: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<a use:link href="/game/{id}">
|
||||
<div class="flex justify-between border rounded-xl my-3">
|
||||
<div class="team">
|
||||
<div>{t0p0}</div>
|
||||
<div>{t0p1}</div>
|
||||
</div>
|
||||
<div class="text-xl flex items-center">
|
||||
<div>
|
||||
<span class="mr-2">{score > 0 ? score : 0}</span> - <span class="ml-2">{score < 0 ? -score : 0}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="team">
|
||||
<div>{t1p0}</div>
|
||||
<div>{t1p1}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.team {
|
||||
@apply my-1 mx-2;
|
||||
}
|
||||
</style>
|
||||
26
web/src/lib/PlayerSearch.svelte
Normal file
26
web/src/lib/PlayerSearch.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { graphql } from "../gql";
|
||||
import Search from "./Search.svelte";
|
||||
import { request } from "graphql-request";
|
||||
|
||||
const doc = graphql(`
|
||||
query searchPlayer($query: String!) {
|
||||
players(query: $query) {
|
||||
ID
|
||||
name
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
let suggestions: string[] = $state([]);
|
||||
|
||||
function findSuggestions(input: string) {
|
||||
request(window.location.origin + "/graphql", doc, {
|
||||
query: input,
|
||||
}).then((result) => {
|
||||
suggestions = result.players.map((e) => e.name);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<Search placeholder="Search player" onSuggest={findSuggestions} {suggestions} />
|
||||
45
web/src/lib/Search.svelte
Normal file
45
web/src/lib/Search.svelte
Normal file
@@ -0,0 +1,45 @@
|
||||
<script lang="ts">
|
||||
let {
|
||||
placeholder = "Search",
|
||||
onSearch = () => {},
|
||||
onSuggest = () => {},
|
||||
suggestions = [],
|
||||
}: {
|
||||
placeholder?: string;
|
||||
onSearch?: (input: string) => void;
|
||||
onSuggest?: (input: string) => void;
|
||||
suggestions?: string[];
|
||||
} = $props();
|
||||
|
||||
let inputText = $state("");
|
||||
let showSuggestions = $derived(inputText != "");
|
||||
</script>
|
||||
|
||||
<div class="relative w-full">
|
||||
<input
|
||||
type="search"
|
||||
{placeholder}
|
||||
bind:value={inputText}
|
||||
class="bg-slate-500 text-white p-2 rounded-xl w-full"
|
||||
onsubmit={() => {
|
||||
onSearch(inputText);
|
||||
}}
|
||||
oninput={() => {
|
||||
if (inputText != ""){
|
||||
onSuggest(inputText);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if showSuggestions}
|
||||
<div
|
||||
class="absolute z-10 bg-white border border-gray-300 rounded-lg w-full shadow-lg"
|
||||
>
|
||||
{#each suggestions as suggestion}
|
||||
<div class="px-4 py-2 hover:bg-gray-100 cursor-pointer">
|
||||
{suggestion}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
27
web/src/lib/TopPlayerCard.svelte
Normal file
27
web/src/lib/TopPlayerCard.svelte
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { link } from "@dvcol/svelte-simple-router/router";
|
||||
|
||||
let {
|
||||
place,
|
||||
name,
|
||||
elo,
|
||||
playerID,
|
||||
}: { place: number; name: string; elo: number; playerID: string } = $props();
|
||||
|
||||
function getPlaceClass(place: number) {
|
||||
if (place == 1) return "bg-yellow-500";
|
||||
if (place == 2) return "bg-slate-500";
|
||||
if (place == 3) return "bg-amber-800";
|
||||
return "bg-slate-800";
|
||||
}
|
||||
|
||||
let placeClass = $derived(getPlaceClass(place));
|
||||
</script>
|
||||
|
||||
<a use:link href="/player/{playerID}">
|
||||
<div class="flex justify-between w-full border rounded-xl my-3 min-h-10">
|
||||
<div class="{placeClass} px-3 py-1 rounded-l-xl flex items-center">{place}.</div>
|
||||
<div class=" py-1 flex items-center">{name}</div>
|
||||
<div class="rounded-r-xl px-3 py-1 bg-blue-400 flex items-center">{elo}</div>
|
||||
</div>
|
||||
</a>
|
||||
56
web/src/pages/Front.svelte
Normal file
56
web/src/pages/Front.svelte
Normal file
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import LastGameCard from "../lib/LastGameCard.svelte";
|
||||
import PlayerSearch from "../lib/PlayerSearch.svelte";
|
||||
import TopPlayerCard from "../lib/TopPlayerCard.svelte";
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<div class="flex flex-col w-3/4">
|
||||
<div class="mb-7 mt-3">
|
||||
<PlayerSearch />
|
||||
</div>
|
||||
<div class="mb-7">
|
||||
<h1 class="heading">Top players</h1>
|
||||
<div>
|
||||
<TopPlayerCard place={1} name="Player 1" elo={1337} playerID="1" />
|
||||
<TopPlayerCard place={2} name="Player 2" elo={1234} playerID="2" />
|
||||
<TopPlayerCard place={3} name="Player 3" elo={1001} playerID="3" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-7">
|
||||
<h1 class="heading">Last games</h1>
|
||||
<div>
|
||||
<LastGameCard
|
||||
id="1"
|
||||
score={2}
|
||||
t0p0="Player 1"
|
||||
t0p1="Player 2"
|
||||
t1p0="Player 3"
|
||||
t1p1="Player 4"
|
||||
/>
|
||||
<LastGameCard
|
||||
id="2"
|
||||
score={-2}
|
||||
t0p0="Player 1"
|
||||
t0p1="Player 2"
|
||||
t1p0="Player 3"
|
||||
t1p1="Player 4"
|
||||
/>
|
||||
<LastGameCard
|
||||
id="3"
|
||||
score={2}
|
||||
t0p0="Player 1"
|
||||
t0p1="Player 2"
|
||||
t1p0="Player 3"
|
||||
t1p1="Player 4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
@apply text-2xl;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
import { graphql } from "./../gql";
|
||||
import { onMount } from "svelte";
|
||||
import type { GetGameQuery } from "../gql/graphql";
|
||||
import { useRoute, link } from "@dvcol/svelte-simple-router/router";
|
||||
import { useRoute, link } from "@dvcol/svelte-simple-router";
|
||||
|
||||
const doc = graphql(`
|
||||
query getGame($gameID: ID!) {
|
||||
|
||||
8
web/src/types.ts
Normal file
8
web/src/types.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
interface Player {
|
||||
id: string
|
||||
}
|
||||
|
||||
interface Game {
|
||||
id: string,
|
||||
score: number
|
||||
}
|
||||
Reference in New Issue
Block a user