Merge pull request #2 from internet4refugees/feature/search-frontend
next project scaffold + table for filtering and search
This commit is contained in:
commit
c4955c067d
|
@ -9,7 +9,7 @@
|
|||
:place_city "",
|
||||
:contact_phone nil,
|
||||
:place_zip "",
|
||||
:time_from_str "",
|
||||
:time_from_str "10/04/2022",
|
||||
:place_country "",
|
||||
:animals_present false,
|
||||
:languages (),
|
||||
|
@ -26,7 +26,7 @@
|
|||
:place_city "",
|
||||
:contact_phone nil,
|
||||
:place_zip "",
|
||||
:time_from_str "",
|
||||
:time_from_str "03/04/2022",
|
||||
:place_country nil,
|
||||
:animals_present true,
|
||||
:languages (),
|
||||
|
@ -43,7 +43,7 @@
|
|||
:place_city nil,
|
||||
:contact_phone "Q",
|
||||
:place_zip "RD",
|
||||
:time_from_str "q",
|
||||
:time_from_str "03/04/2022",
|
||||
:place_country nil,
|
||||
:animals_present true,
|
||||
:languages (),
|
||||
|
@ -60,7 +60,7 @@
|
|||
:place_city "1bh",
|
||||
:contact_phone nil,
|
||||
:place_zip "PG4",
|
||||
:time_from_str "8az",
|
||||
:time_from_str "03/04/2022",
|
||||
:place_country "I7",
|
||||
:animals_present false,
|
||||
:languages ("x" "ni"),
|
||||
|
@ -77,7 +77,7 @@
|
|||
:place_city "x",
|
||||
:contact_phone "w8l",
|
||||
:place_zip "1E",
|
||||
:time_from_str "6",
|
||||
:time_from_str "01/01/2022",
|
||||
:place_country "8",
|
||||
:animals_present nil,
|
||||
:languages (),
|
||||
|
@ -94,7 +94,7 @@
|
|||
:place_city "6",
|
||||
:contact_phone "OI",
|
||||
:place_zip "1",
|
||||
:time_from_str "hS192",
|
||||
:time_from_str "03/04/2022",
|
||||
:place_country "",
|
||||
:animals_present false,
|
||||
:languages ("DX" "E" "G3wuv"),
|
||||
|
@ -111,7 +111,7 @@
|
|||
:place_city "cj1A2",
|
||||
:contact_phone "r",
|
||||
:place_zip "cJ9z7",
|
||||
:time_from_str "",
|
||||
:time_from_str "03/04/2022",
|
||||
:place_country "5",
|
||||
:animals_present true,
|
||||
:languages nil,
|
||||
|
@ -128,7 +128,7 @@
|
|||
:place_city "4ML4yG",
|
||||
:contact_phone "7uQ3Px",
|
||||
:place_zip "kp5IS",
|
||||
:time_from_str "Y5lSe",
|
||||
:time_from_str "03/04/2022",
|
||||
:place_country nil,
|
||||
:animals_present true,
|
||||
:languages (),
|
||||
|
@ -145,7 +145,7 @@
|
|||
:place_city "u6k41v",
|
||||
:contact_phone "354VW",
|
||||
:place_zip "DI19",
|
||||
:time_from_str "Z0QGzI8",
|
||||
:time_from_str "09/04/2022",
|
||||
:place_country nil,
|
||||
:animals_present true,
|
||||
:languages nil,
|
||||
|
@ -162,7 +162,7 @@
|
|||
:place_city "f",
|
||||
:contact_phone "sQdB",
|
||||
:place_zip "cid1",
|
||||
:time_from_str "KSAFC",
|
||||
:time_from_str "03/05/2022",
|
||||
:place_country "toTz5B",
|
||||
:animals_present false,
|
||||
:languages ("vMR0zyECr"),
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
|
@ -1 +1,38 @@
|
|||
node_modules
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
TODO: A frontend for authorized NGO members, to search the database
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
* [The submisison frontend](../submission/README.md) should be written first (since it is more simple / closer to the hello-world of jsonforms)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from 'react';
|
||||
import {QueryClient, QueryClientProvider} from 'react-query'
|
||||
import {ReactQueryDevtools} from 'react-query/devtools'
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
export default function MyQueryClientProvider({children}: {children: React.Component}) {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{children}
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
import React from 'react'
|
||||
import '@inovua/reactdatagrid-community/index.css'
|
||||
|
||||
import DataGrid from '@inovua/reactdatagrid-community'
|
||||
import DateFilter from '@inovua/reactdatagrid-community/DateFilter'
|
||||
import StringFilter from '@inovua/reactdatagrid-community/StringFilter'
|
||||
import BoolFilter from '@inovua/reactdatagrid-community/BoolFilter'
|
||||
import {GetOffersQuery} from "../../codegen/generates";
|
||||
import {TypeColumn, TypeFilterValue, TypeSingleFilterValue} from "@inovua/reactdatagrid-community/types";
|
||||
import NumberFilter from "@inovua/reactdatagrid-community/NumberFilter";
|
||||
import moment from "moment";
|
||||
|
||||
global.moment = moment
|
||||
|
||||
type HostOfferLookupTableProps = {
|
||||
data: GetOffersQuery
|
||||
}
|
||||
|
||||
const columnsRaw: { name: string; header: string; type: string }[] = [
|
||||
{
|
||||
"name": "beds",
|
||||
"header": "beds",
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"name": "place_street",
|
||||
"header": "place street",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "contact_email",
|
||||
"header": "contact email",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"name": "contact_name_full",
|
||||
"header": "contact name full",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "time_duration_str",
|
||||
"header": "time duration str",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "note",
|
||||
"header": "note",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "place_street_number",
|
||||
"header": "place street number",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "place_city",
|
||||
"header": "place city",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "contact_phone",
|
||||
"header": "contact phone",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"name": "place_zip",
|
||||
"header": "place zip",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "time_from_str",
|
||||
"header": "time from str",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"name": "place_country",
|
||||
"header": "place country",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "animals_present",
|
||||
"header": "animals present",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "languages",
|
||||
"header": "languages",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"name": "accessible",
|
||||
"header": "accessible",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "animals_allowed",
|
||||
"header": "animals allowed",
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
|
||||
const filterMappings = {
|
||||
string: StringFilter,
|
||||
boolean: BoolFilter,
|
||||
number: NumberFilter,
|
||||
date: DateFilter
|
||||
}
|
||||
const operatorsForType = {
|
||||
number: 'gte',
|
||||
string: 'contains',
|
||||
date: 'inrange'}
|
||||
|
||||
|
||||
const columns: TypeColumn[] = columnsRaw
|
||||
.map(c => ({
|
||||
...c,
|
||||
filterEditor: filterMappings[c.type as 'string' | 'number' | 'boolean' | 'date']
|
||||
}))
|
||||
|
||||
const defaultFilterValue: TypeFilterValue = columns
|
||||
.filter(( {type} ) => type && ['string', 'number', 'date'].includes( type ))
|
||||
.map(( {name, type} ) => {
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
value: '',
|
||||
operator: operatorsForType[type as 'string' | 'number' | 'date']
|
||||
} as unknown as TypeSingleFilterValue
|
||||
} )
|
||||
|
||||
const makeColumnDefinition = (data: any ) => Object.keys(data)
|
||||
.map(k => ({
|
||||
name: k,
|
||||
header: k.replace(/_/g, ' '),
|
||||
type: typeof data[k]}))
|
||||
|
||||
const HostOfferLookupTable = ({ data }: HostOfferLookupTableProps) => {
|
||||
const dataSource = data.get_offers || []
|
||||
return <>
|
||||
<DataGrid
|
||||
idProperty="id"
|
||||
filterable
|
||||
showColumnMenuFilterOptions={true}
|
||||
showFilteringMenuItems={true}
|
||||
defaultFilterValue={defaultFilterValue}
|
||||
rowIndexColumn
|
||||
enableSelection
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
style={{minHeight: '1000px'}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
export default HostOfferLookupTable
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react'
|
||||
import {Auth, useGetOffersQuery} from "../../codegen/generates";
|
||||
import testAuth from '../util/testAuth.json'
|
||||
import HostOfferLookupTable from "./HostOfferLookupTable";
|
||||
|
||||
type HostLookupWrapperProps = Record<string, never>
|
||||
|
||||
const HostOfferLookupWrapper = ({}: HostLookupWrapperProps) => {
|
||||
const {data, isFetching} = useGetOffersQuery({auth: testAuth as Auth}, {staleTime: 60 * 1000})
|
||||
if (isFetching) {
|
||||
return "loading…"
|
||||
} else if (data?.get_offers) {
|
||||
return (<HostOfferLookupTable data={data}/>)
|
||||
}
|
||||
}
|
||||
|
||||
export default HostOfferLookupWrapper
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"mail": "crewing@example-ngo.com",
|
||||
"password": "Vr(+cFtUG=rsj2:/]*uR"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import * as default_config from './public/config.json'
|
||||
|
||||
export let config = default_config /** to infer the types **/
|
||||
let config_fetched = false
|
||||
|
||||
export async function fetch_config () {
|
||||
if(!config_fetched) {
|
||||
await fetch("/config.json").then(async res => {config = await res.json()
|
||||
config_fetched = true})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import i18next from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import en from './en.json'
|
||||
import de from './de.json'
|
||||
|
||||
export const resources = {
|
||||
en: { translation: en },
|
||||
de: { translation: de }
|
||||
} as const
|
||||
|
||||
i18next
|
||||
.use(initReactI18next)
|
||||
.use(LanguageDetector)
|
||||
.init({
|
||||
resources,
|
||||
|
||||
//lng: "en", /* we use LanguageDetector instead */
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
|
||||
}
|
||||
})
|
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +1,45 @@
|
|||
{
|
||||
"name": "beherbergung-search-frontend",
|
||||
"version": "0.0.1",
|
||||
"name": "beherbergung",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"generate": "cd codegen && graphql-codegen generate",
|
||||
"dev": "next dev",
|
||||
"build": "next build --no-lint",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"export": "next export"
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"graphql-request": "^3.7.0",
|
||||
"react-query": "^3.34.0"
|
||||
"@emotion/react": "^11.8.1",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@inovua/reactdatagrid-community": "^4.16.2",
|
||||
"@jsonforms/core": "3.0.0-alpha.3",
|
||||
"@jsonforms/material-renderers": "3.0.0-alpha.3",
|
||||
"@jsonforms/react": "3.0.0-alpha.3",
|
||||
"@mui/icons-material": "^5.2.0",
|
||||
"@mui/lab": "^5.0.0-alpha.71",
|
||||
"@mui/material": "^5.2.2",
|
||||
"@mui/styles": "^5.2.3",
|
||||
"graphql-request": "^4.0.0",
|
||||
"i18next": "^21.6.13",
|
||||
"i18next-browser-languagedetector": "^6.1.3",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "12.1.0",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-i18next": "^11.15.5",
|
||||
"react-query": "^3.34.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "^2.3.0",
|
||||
"@graphql-codegen/typescript": "^2.4.1",
|
||||
"@graphql-codegen/typescript-operations": "^2.2.1",
|
||||
"@graphql-codegen/typescript-react-query": "^3.2.2",
|
||||
"typescript": "4.5.2"
|
||||
"@types/lodash": "^4.14.179",
|
||||
"@types/node": "17.0.21",
|
||||
"@types/react": "17.0.39",
|
||||
"eslint": "8.10.0",
|
||||
"eslint-config-next": "12.1.0",
|
||||
"typescript": "4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import '../styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
import MyQueryClientProvider from "../components/QueryClientProvider";
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <MyQueryClientProvider>
|
||||
<Component {...pageProps} />
|
||||
</MyQueryClientProvider>
|
||||
}
|
||||
|
||||
export default MyApp
|
|
@ -0,0 +1,13 @@
|
|||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
type Data = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import HostOfferLookupWrapper from '../components/ngo/HostOfferLookupWrapper'
|
||||
import styles from '../styles/Home.module.css'
|
||||
|
||||
const Home: NextPage = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
</Head>
|
||||
|
||||
<main className={styles.main}>
|
||||
<HostOfferLookupWrapper />
|
||||
</main>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
|
@ -0,0 +1,2 @@
|
|||
{"base_url": "http://localhost:3000",
|
||||
"backend_base_url": "http://localhost:4000"}
|
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -0,0 +1,8 @@
|
|||
.container {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: 100vh;
|
||||
padding: 4rem 4rem;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue