From ce2d814506087c8c142e3eb4a6d6ca32567e1ac3 Mon Sep 17 00:00:00 2001 From: Sander Vocke Date: Mon, 14 Sep 2020 15:50:31 +0200 Subject: [PATCH] Got basic queries in result table working. --- client/src/components/Window.tsx | 103 ++++++++++++++++-- .../components/querybuilder/QBLeafElem.tsx | 18 ++- client/src/components/tables/ResultsTable.tsx | 49 +++++++++ client/src/lib/query/Query.tsx | 34 ++++++ 4 files changed, 186 insertions(+), 18 deletions(-) create mode 100644 client/src/components/tables/ResultsTable.tsx diff --git a/client/src/components/Window.tsx b/client/src/components/Window.tsx index 0df4391..e6d3013 100644 --- a/client/src/components/Window.tsx +++ b/client/src/components/Window.tsx @@ -1,9 +1,10 @@ -import React, { useState } from 'react'; -import { ThemeProvider, CssBaseline, createMuiTheme } from '@material-ui/core'; -import { QueryElem } from '../lib/query/Query'; +import React, { useState, useEffect } from 'react'; +import { ThemeProvider, CssBaseline, createMuiTheme, AppBar, Box } from '@material-ui/core'; +import { QueryElem, toApiQuery } from '../lib/query/Query'; import QueryBuilder from './querybuilder/QueryBuilder'; import * as serverApi from '../api'; -import { queryAllByRole } from '@testing-library/react'; +import { SongTable } from './tables/ResultsTable'; +var _ = require('lodash'); const darkTheme = createMuiTheme({ palette: { @@ -82,17 +83,95 @@ export async function getSongTitles(filter: string) { } export default function Window(props: any) { + interface ResultsFor { + for: QueryElem, + results: any[], + }; + const [query, setQuery] = useState(null); + const [resultsFor, setResultsFor] = useState(null); + + const loading = query && (!resultsFor || !_.isEqual(resultsFor.for, query)); + const showResults = (query && resultsFor && query == resultsFor.for) ? resultsFor.results : []; + + const songGetters = { + getTitle: (song: any) => song.title, + getArtist: (song: any) => "Artist", + getAlbum: (song: any) => "Album", + } + + const doQuery = async (_query: QueryElem) => { + var q: serverApi.QueryRequest = { + query: toApiQuery(_query), + offsetsLimits: { + songOffset: 0, + songLimit: 100, + }, + ordering: { + orderBy: { + type: serverApi.OrderByType.Name, + }, + ascending: true, + }, + }; + + const requestOpts = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(q), + }; + + return (async () => { + const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts) + let json: any = await response.json(); + if(_.isEqual(query, _query)) { + setResultsFor({ + for: _query, + results: json.songs, + }) + } + })(); + } + + useEffect(() => { + if (query) { + doQuery(query); + } else { + setResultsFor(null); + } + }, [query]); return - + + + error + + + + + + + + + + + } \ No newline at end of file diff --git a/client/src/components/querybuilder/QBLeafElem.tsx b/client/src/components/querybuilder/QBLeafElem.tsx index 59a5a94..f4904ea 100644 --- a/client/src/components/querybuilder/QBLeafElem.tsx +++ b/client/src/components/querybuilder/QBLeafElem.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { QueryLeafElem, QueryLeafBy, QueryLeafOp, QueryElem } from '../../lib/query/Query'; import { Chip, Typography, IconButton, Box } from '@material-ui/core'; import { QBPlaceholder } from './QBPlaceholder'; -import CloseIcon from '@material-ui/icons/Close'; +import DeleteIcon from '@material-ui/icons/Delete'; import { Requests } from './QueryBuilder'; export interface ElemChipProps { @@ -56,8 +56,12 @@ export interface DeleteButtonProps { } export function QBQueryElemDeleteButton(props: DeleteButtonProps) { - return - + return + } @@ -72,9 +76,11 @@ export function QBLeafElem(props: IProps) { let e = props.elem; const extraElements = props.editingQuery ? - props.onReplace(null)} - /> + + props.onReplace(null)} + /> + : undefined; if (e.a == QueryLeafBy.ArtistName && diff --git a/client/src/components/tables/ResultsTable.tsx b/client/src/components/tables/ResultsTable.tsx new file mode 100644 index 0000000..b701222 --- /dev/null +++ b/client/src/components/tables/ResultsTable.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { TableContainer, Table, TableHead, TableRow, TableCell, Paper, makeStyles, TableBody } from '@material-ui/core'; + +export interface SongGetters { + getTitle: (song: any) => string, + getArtist: (song: any) => string, + getAlbum: (song: any) => string, +} + +export interface IProps { + songs: any[], + songGetters: SongGetters, +} + +export function SongTable(props: IProps) { + const useTableStyles = makeStyles({ + table: { + minWidth: 650, + }, + }); + const classes = useTableStyles(); + + return ( + + + + + Title + Artist + Album + + + + {props.songs.map((song:any) => { + const title = props.songGetters.getTitle(song); + const artist = props.songGetters.getArtist(song); + const album = props.songGetters.getAlbum(song); + + return + {title} + {artist} + {album} + + })} + +
+
+ ); +} \ No newline at end of file diff --git a/client/src/lib/query/Query.tsx b/client/src/lib/query/Query.tsx index 09197ca..ad617b3 100644 --- a/client/src/lib/query/Query.tsx +++ b/client/src/lib/query/Query.tsx @@ -1,3 +1,5 @@ +import * as serverApi from '../../api'; + export enum QueryLeafBy { ArtistName = 0, AlbumName, @@ -149,4 +151,36 @@ export function simplify(q: QueryElem | null): QueryElem | null { } return q; +} + +export function toApiQuery(q: QueryElem) : serverApi.Query { + const propsMapping: any = { + [QueryLeafBy.SongTitle]: serverApi.QueryElemProperty.songTitle, + [QueryLeafBy.ArtistName]: serverApi.QueryElemProperty.artistName, + } + const leafOpsMapping: any = { + [QueryLeafOp.Equals]: serverApi.QueryFilterOp.Eq, + [QueryLeafOp.Like]: serverApi.QueryFilterOp.Like, + } + const nodeOpsMapping: any = { + [QueryNodeOp.And]: serverApi.QueryElemOp.And, + [QueryNodeOp.Or]: serverApi.QueryElemOp.Or, + } + + if(isLeafElem(q)) { + const r: serverApi.QueryElem = { + prop: propsMapping[q.a], + propOperator: leafOpsMapping[q.leafOp], + propOperand: q.b, + } + return r; + } else if(isNodeElem(q)) { + const r = { + children: q.operands.map((op: any) => toApiQuery(op)), + childrenOperator: nodeOpsMapping[q.nodeOp] + } + return r; + } + + return {}; } \ No newline at end of file