Init DB and Tests with Components
- add Artikel, Kauf, Kategorie Model - generate Components - playing with custom components - add Tailwindcss
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')),
|
||||
require('autoprefixer'),
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['src/**/*.{js,jsx,ts,tsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
+6
-1
@@ -14,6 +14,7 @@
|
||||
"@redwoodjs/forms": "6.3.2",
|
||||
"@redwoodjs/router": "6.3.2",
|
||||
"@redwoodjs/web": "6.3.2",
|
||||
"humanize-string": "2.1.0",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
@@ -21,6 +22,10 @@
|
||||
"devDependencies": {
|
||||
"@redwoodjs/vite": "6.3.2",
|
||||
"@types/react": "18.2.14",
|
||||
"@types/react-dom": "18.2.6"
|
||||
"@types/react-dom": "18.2.6",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"tailwindcss": "^3.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'
|
||||
import FatalErrorPage from 'src/pages/FatalErrorPage'
|
||||
import Routes from 'src/Routes'
|
||||
|
||||
import './scaffold.css'
|
||||
import './index.css'
|
||||
|
||||
const App = () => (
|
||||
|
||||
+25
-2
@@ -7,12 +7,35 @@
|
||||
// 'src/pages/HomePage/HomePage.js' -> HomePage
|
||||
// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage
|
||||
|
||||
import { Router, Route } from '@redwoodjs/router'
|
||||
import { Set, Router, Route } from '@redwoodjs/router'
|
||||
|
||||
import ScaffoldLayout from 'src/layouts/ScaffoldLayout'
|
||||
import PosLayout from 'src/layouts/PosLayout'
|
||||
|
||||
const Routes = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Route path="/pos" page={PosPage} name="pos" />
|
||||
<Set wrap={ScaffoldLayout} title="Kategories" titleTo="kategories" buttonLabel="New Kategorie" buttonTo="newKategorie">
|
||||
<Route path="/kategories/new" page={KategorieNewKategoriePage} name="newKategorie" />
|
||||
<Route path="/kategories/{id:Int}/edit" page={KategorieEditKategoriePage} name="editKategorie" />
|
||||
<Route path="/kategories/{id:Int}" page={KategorieKategoriePage} name="kategorie" />
|
||||
<Route path="/kategories" page={KategorieKategoriesPage} name="kategories" />
|
||||
</Set>
|
||||
<Set wrap={ScaffoldLayout} title="Kaufs" titleTo="kaufs" buttonLabel="New Kauf" buttonTo="newKauf">
|
||||
<Route path="/kaufs/new" page={KaufNewKaufPage} name="newKauf" />
|
||||
<Route path="/kaufs/{id:Int}/edit" page={KaufEditKaufPage} name="editKauf" />
|
||||
<Route path="/kaufs/{id:Int}" page={KaufKaufPage} name="kauf" />
|
||||
<Route path="/kaufs" page={KaufKaufsPage} name="kaufs" />
|
||||
</Set>
|
||||
<Set wrap={ScaffoldLayout} title="Artikels" titleTo="artikels" buttonLabel="New Artikel" buttonTo="newArtikel">
|
||||
<Route path="/artikels/new" page={ArtikelNewArtikelPage} name="newArtikel" />
|
||||
<Route path="/artikels/{id:Int}/edit" page={ArtikelEditArtikelPage} name="editArtikel" />
|
||||
<Route path="/artikels/{id:Int}" page={ArtikelArtikelPage} name="artikel" />
|
||||
<Route path="/artikels" page={ArtikelArtikelsPage} name="artikels" />
|
||||
</Set>
|
||||
<Set wrap={PosLayout} title="WaffelKass" titleTo="pos">
|
||||
<Route path="/pos" page={PosPage} name="pos" />
|
||||
</Set>
|
||||
<Route notfound page={NotFoundPage} />
|
||||
</Router>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Link, routes, navigate } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import 'src/lib/formatters'
|
||||
|
||||
const DELETE_ARTIKEL_MUTATION = gql`
|
||||
mutation DeleteArtikelMutation($id: Int!) {
|
||||
deleteArtikel(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Artikel = ({ artikel }) => {
|
||||
const [deleteArtikel] = useMutation(DELETE_ARTIKEL_MUTATION, {
|
||||
onCompleted: () => {
|
||||
toast.success('Artikel deleted')
|
||||
navigate(routes.artikels())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (confirm('Are you sure you want to delete artikel ' + id + '?')) {
|
||||
deleteArtikel({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Artikel {artikel.id} Detail
|
||||
</h2>
|
||||
</header>
|
||||
<table className="rw-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<td>{artikel.id}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<td>{artikel.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Preis</th>
|
||||
<td>{artikel.preis}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Kategorie</th>
|
||||
<td>{artikel.kategorie.name}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav className="rw-button-group">
|
||||
<Link
|
||||
to={routes.editArtikel({ id: artikel.id })}
|
||||
className="rw-button rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="rw-button rw-button-red"
|
||||
onClick={() => onDeleteClick(artikel.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Artikel
|
||||
@@ -0,0 +1,24 @@
|
||||
import Artikel from 'src/components/Artikel/Artikel'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindArtikelById($id: Int!) {
|
||||
artikel: artikel(id: $id) {
|
||||
id
|
||||
name
|
||||
preis
|
||||
kategorie{name}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => <div>Artikel not found</div>
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ artikel }) => {
|
||||
return <Artikel artikel={artikel} />
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import {
|
||||
Form,
|
||||
FormError,
|
||||
FieldError,
|
||||
Label,
|
||||
TextField,
|
||||
NumberField,
|
||||
SelectField,
|
||||
Submit,
|
||||
} from '@redwoodjs/forms'
|
||||
|
||||
const GET_KATEGORIES = gql`
|
||||
query FindKategories {
|
||||
kategories {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ArtikelForm = (props) => {
|
||||
const onSubmit = (data) => {
|
||||
data.kategorie_id = parseInt(data.kategorie_id)
|
||||
props.onSave(data, props?.artikel?.id)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-form-wrapper">
|
||||
<Form onSubmit={onSubmit} error={props.error}>
|
||||
<FormError
|
||||
error={props.error}
|
||||
wrapperClassName="rw-form-error-wrapper"
|
||||
titleClassName="rw-form-error-title"
|
||||
listClassName="rw-form-error-list"
|
||||
/>
|
||||
|
||||
<Label
|
||||
name="name"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Name
|
||||
</Label>
|
||||
|
||||
<TextField
|
||||
name="name"
|
||||
defaultValue={props.artikel?.name}
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
validation={{ required: true }}
|
||||
/>
|
||||
|
||||
<FieldError name="name" className="rw-field-error" />
|
||||
|
||||
<Label
|
||||
name="preis"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Preis
|
||||
</Label>
|
||||
|
||||
<TextField
|
||||
name="preis"
|
||||
defaultValue={props.artikel?.preis}
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
validation={{ valueAsNumber: true }}
|
||||
/>
|
||||
|
||||
<FieldError name="preis" className="rw-field-error" />
|
||||
|
||||
<Label
|
||||
name="kategorie_id"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Kategorie id
|
||||
</Label>
|
||||
|
||||
<SelectField name="kategorie_id"
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
defaultValue={props.artikel?.kategorie_id} >
|
||||
{props.kategories.map((kategorie) => (
|
||||
<option key={kategorie.id} value={kategorie.id} >{kategorie.name}</option>
|
||||
))}
|
||||
</SelectField>
|
||||
|
||||
<FieldError name="kategorie_id" className="rw-field-error" />
|
||||
|
||||
<div className="rw-button-group">
|
||||
<Submit disabled={props.loading} className="rw-button rw-button-blue">
|
||||
Save
|
||||
</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArtikelForm
|
||||
@@ -0,0 +1,90 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { QUERY } from 'src/components/Artikel/ArtikelsCell'
|
||||
import { truncate } from 'src/lib/formatters'
|
||||
|
||||
const DELETE_ARTIKEL_MUTATION = gql`
|
||||
mutation DeleteArtikelMutation($id: Int!) {
|
||||
deleteArtikel(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const ArtikelsList = ({ artikels }) => {
|
||||
const [deleteArtikel] = useMutation(DELETE_ARTIKEL_MUTATION, {
|
||||
onCompleted: () => {
|
||||
toast.success('Artikel deleted')
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
// This refetches the query on the list page. Read more about other ways to
|
||||
// update the cache over here:
|
||||
// https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
|
||||
refetchQueries: [{ query: QUERY }],
|
||||
awaitRefetchQueries: true,
|
||||
})
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (confirm('Are you sure you want to delete artikel ' + id + '?')) {
|
||||
deleteArtikel({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment rw-table-wrapper-responsive">
|
||||
<table className="rw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Name</th>
|
||||
<th>Preis</th>
|
||||
<th>Kategorie</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{artikels.map((artikel) => (
|
||||
<tr key={artikel.id}>
|
||||
<td>{truncate(artikel.id)}</td>
|
||||
<td>{truncate(artikel.name)}</td>
|
||||
<td>{truncate(artikel.preis)}</td>
|
||||
<td>{truncate(artikel.kategorie.name)}</td>
|
||||
<td>
|
||||
<nav className="rw-table-actions">
|
||||
<Link
|
||||
to={routes.artikel({ id: artikel.id })}
|
||||
title={'Show artikel ' + artikel.id + ' detail'}
|
||||
className="rw-button rw-button-small"
|
||||
>
|
||||
Show
|
||||
</Link>
|
||||
<Link
|
||||
to={routes.editArtikel({ id: artikel.id })}
|
||||
title={'Edit artikel ' + artikel.id}
|
||||
className="rw-button rw-button-small rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
title={'Delete artikel ' + artikel.id}
|
||||
className="rw-button rw-button-small rw-button-red"
|
||||
onClick={() => onDeleteClick(artikel.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</nav>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArtikelsList
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import Artikels from 'src/components/Artikel/Artikels'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindArtikels {
|
||||
artikels {
|
||||
id
|
||||
name
|
||||
preis
|
||||
kategorie {name}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => {
|
||||
return (
|
||||
<div className="rw-text-center">
|
||||
{'No artikels yet. '}
|
||||
<Link to={routes.newArtikel()} className="rw-link">
|
||||
{'Create one?'}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({artikels }) => {
|
||||
return <Artikels artikels={artikels} />
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { QUERY } from 'src/components/Artikel/ArtikelsCell'
|
||||
import { truncate } from 'src/lib/formatters'
|
||||
|
||||
const ArtikelsList = ({ artikels }) => {
|
||||
console.log(artikels)
|
||||
let listKategoryArtikels = {}
|
||||
artikels.forEach((artikel) => {
|
||||
if(!(artikel.kategorie.id in listKategoryArtikels)){
|
||||
listKategoryArtikels[artikel.kategorie.id] = {}
|
||||
listKategoryArtikels[artikel.kategorie.id]["name"] = artikel.kategorie.name
|
||||
listKategoryArtikels[artikel.kategorie.id]["artikel"] = []
|
||||
}
|
||||
listKategoryArtikels[artikel.kategorie.id]["artikel"].append(artikel)
|
||||
})
|
||||
|
||||
|
||||
|
||||
const [deleteArtikel] = useMutation(DELETE_ARTIKEL_MUTATION, {
|
||||
onCompleted: () => {
|
||||
toast.success('Artikel deleted')
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
// This refetches the query on the list page. Read more about other ways to
|
||||
// update the cache over here:
|
||||
// https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
|
||||
refetchQueries: [{ query: QUERY }],
|
||||
awaitRefetchQueries: true,
|
||||
})
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (confirm('Are you sure you want to delete artikel ' + id + '?')) {
|
||||
deleteArtikel({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment rw-table-wrapper-responsive">
|
||||
<table className="rw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Name</th>
|
||||
<th>Preis</th>
|
||||
<th>Kategorie</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{listKategoryArtikels.map((kategorieartikel) => (<>
|
||||
<div>
|
||||
{kategorieartikel.name}
|
||||
</div>
|
||||
<div>
|
||||
{kategorieartikel["artikel"].map((artikel) => (
|
||||
<tr key={artikel.id}>
|
||||
<td>{truncate(artikel.id)}</td>
|
||||
<td>{truncate(artikel.name)}</td>
|
||||
<td>{truncate(artikel.preis)}</td>
|
||||
<td>{truncate(artikel.kategorie.name)}</td>
|
||||
<td>
|
||||
<nav className="rw-table-actions">
|
||||
<Link
|
||||
to={routes.artikel({ id: artikel.id })}
|
||||
title={'Show artikel ' + artikel.id + ' detail'}
|
||||
className="rw-button rw-button-small"
|
||||
>
|
||||
Show
|
||||
</Link>
|
||||
<Link
|
||||
to={routes.editArtikel({ id: artikel.id })}
|
||||
title={'Edit artikel ' + artikel.id}
|
||||
className="rw-button rw-button-small rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
title={'Delete artikel ' + artikel.id}
|
||||
className="rw-button rw-button-small rw-button-red"
|
||||
onClick={() => onDeleteClick(artikel.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</nav>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArtikelsList
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import Artikels from 'src/components/Artikel/Artikels'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindArtikels {
|
||||
artikels {
|
||||
id
|
||||
name
|
||||
preis
|
||||
kategorie {id, name}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => {
|
||||
return (
|
||||
<div className="rw-text-center">
|
||||
{'No artikels yet. '}
|
||||
<Link to={routes.newArtikel()} className="rw-link">
|
||||
{'Create one?'}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({artikels }) => {
|
||||
return <Artikels artikels={artikels} />
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import ArtikelForm from 'src/components/Artikel/ArtikelForm'
|
||||
|
||||
export const QUERY = gql`
|
||||
query EditArtikelById($id: Int!) {
|
||||
artikel: artikel(id: $id) {
|
||||
id
|
||||
name
|
||||
preis
|
||||
kategorie_id
|
||||
},
|
||||
kategories: kategories {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
const UPDATE_ARTIKEL_MUTATION = gql`
|
||||
mutation UpdateArtikelMutation($id: Int!, $input: UpdateArtikelInput!) {
|
||||
updateArtikel(id: $id, input: $input) {
|
||||
id
|
||||
name
|
||||
preis
|
||||
kategorie_id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ artikel, kategories }) => {
|
||||
const [updateArtikel, { loading, error }] = useMutation(
|
||||
UPDATE_ARTIKEL_MUTATION,
|
||||
{
|
||||
onCompleted: () => {
|
||||
toast.success('Artikel updated')
|
||||
navigate(routes.artikels())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const onSave = (input, id) => {
|
||||
updateArtikel({ variables: { id, input } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Edit Artikel {artikel?.id}
|
||||
</h2>
|
||||
</header>
|
||||
<div className="rw-segment-main">
|
||||
<ArtikelForm
|
||||
artikel={artikel}
|
||||
kategories={kategories}
|
||||
onSave={onSave}
|
||||
error={error}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import ArtikelForm from 'src/components/Artikel/ArtikelForm'
|
||||
|
||||
const CREATE_ARTIKEL_MUTATION = gql`
|
||||
mutation CreateArtikelMutation($input: CreateArtikelInput!) {
|
||||
createArtikel(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const NewArtikel = ({kategories}) => {
|
||||
const [createArtikel, { loading, error }] = useMutation(
|
||||
CREATE_ARTIKEL_MUTATION,
|
||||
{
|
||||
onCompleted: () => {
|
||||
toast.success('Artikel created')
|
||||
navigate(routes.artikels())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const onSave = (input) => {
|
||||
createArtikel({ variables: { input } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">New Artikel</h2>
|
||||
</header>
|
||||
<div className="rw-segment-main">
|
||||
<ArtikelForm kategories={kategories} onSave={onSave} loading={loading} error={error} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewArtikel
|
||||
@@ -0,0 +1,66 @@
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import KategorieForm from 'src/components/Kategorie/KategorieForm'
|
||||
|
||||
export const QUERY = gql`
|
||||
query EditKategorieById($id: Int!) {
|
||||
kategorie: kategorie(id: $id) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
const UPDATE_KATEGORIE_MUTATION = gql`
|
||||
mutation UpdateKategorieMutation($id: Int!, $input: UpdateKategorieInput!) {
|
||||
updateKategorie(id: $id, input: $input) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ kategorie }) => {
|
||||
const [updateKategorie, { loading, error }] = useMutation(
|
||||
UPDATE_KATEGORIE_MUTATION,
|
||||
{
|
||||
onCompleted: () => {
|
||||
toast.success('Kategorie updated')
|
||||
navigate(routes.kategories())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const onSave = (input, id) => {
|
||||
updateKategorie({ variables: { id, input } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Edit Kategorie {kategorie?.id}
|
||||
</h2>
|
||||
</header>
|
||||
<div className="rw-segment-main">
|
||||
<KategorieForm
|
||||
kategorie={kategorie}
|
||||
onSave={onSave}
|
||||
error={error}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Link, routes, navigate } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import 'src/lib/formatters'
|
||||
|
||||
const DELETE_KATEGORIE_MUTATION = gql`
|
||||
mutation DeleteKategorieMutation($id: Int!) {
|
||||
deleteKategorie(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Kategorie = ({ kategorie }) => {
|
||||
const [deleteKategorie] = useMutation(DELETE_KATEGORIE_MUTATION, {
|
||||
onCompleted: () => {
|
||||
toast.success('Kategorie deleted')
|
||||
navigate(routes.kategories())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (confirm('Are you sure you want to delete kategorie ' + id + '?')) {
|
||||
deleteKategorie({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Kategorie {kategorie.id} Detail
|
||||
</h2>
|
||||
</header>
|
||||
<table className="rw-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<td>{kategorie.id}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<td>{kategorie.name}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav className="rw-button-group">
|
||||
<Link
|
||||
to={routes.editKategorie({ id: kategorie.id })}
|
||||
className="rw-button rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="rw-button rw-button-red"
|
||||
onClick={() => onDeleteClick(kategorie.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Kategorie
|
||||
@@ -0,0 +1,22 @@
|
||||
import Kategorie from 'src/components/Kategorie/Kategorie'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindKategorieById($id: Int!) {
|
||||
kategorie: kategorie(id: $id) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => <div>Kategorie not found</div>
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ kategorie }) => {
|
||||
return <Kategorie kategorie={kategorie} />
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
Form,
|
||||
FormError,
|
||||
FieldError,
|
||||
Label,
|
||||
TextField,
|
||||
Submit,
|
||||
} from '@redwoodjs/forms'
|
||||
|
||||
const KategorieForm = (props) => {
|
||||
const onSubmit = (data) => {
|
||||
props.onSave(data, props?.kategorie?.id)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-form-wrapper">
|
||||
<Form onSubmit={onSubmit} error={props.error}>
|
||||
<FormError
|
||||
error={props.error}
|
||||
wrapperClassName="rw-form-error-wrapper"
|
||||
titleClassName="rw-form-error-title"
|
||||
listClassName="rw-form-error-list"
|
||||
/>
|
||||
|
||||
<Label
|
||||
name="name"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Name
|
||||
</Label>
|
||||
|
||||
<TextField
|
||||
name="name"
|
||||
defaultValue={props.kategorie?.name}
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
validation={{ required: true }}
|
||||
/>
|
||||
|
||||
<FieldError name="name" className="rw-field-error" />
|
||||
|
||||
<div className="rw-button-group">
|
||||
<Submit disabled={props.loading} className="rw-button rw-button-blue">
|
||||
Save
|
||||
</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default KategorieForm
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindKategories {
|
||||
kategories {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => {
|
||||
return (
|
||||
<div className="rw-text-center">
|
||||
{'No kategories yet. '}
|
||||
<Link to={routes.newKategorie()} className="rw-link">
|
||||
{'Create one?'}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ kategories }) => {
|
||||
return <Kategories kategories={kategories} />
|
||||
}
|
||||
|
||||
const KategoriesSelect = ({ kategories, name }) => {
|
||||
|
||||
return (
|
||||
<select name="{truncate(kategorie.id)}" >
|
||||
{kategories.map((kategorie) => (
|
||||
<option value="{truncate(kategorie.id)}" >{truncate(kategorie.name)}</option>
|
||||
))}
|
||||
</select>
|
||||
)
|
||||
}
|
||||
|
||||
export default KategoriesSelect
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { QUERY } from 'src/components/Kategorie/KategoriesCell'
|
||||
import { truncate } from 'src/lib/formatters'
|
||||
|
||||
const DELETE_KATEGORIE_MUTATION = gql`
|
||||
mutation DeleteKategorieMutation($id: Int!) {
|
||||
deleteKategorie(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const KategoriesList = ({ kategories }) => {
|
||||
const [deleteKategorie] = useMutation(DELETE_KATEGORIE_MUTATION, {
|
||||
onCompleted: () => {
|
||||
toast.success('Kategorie deleted')
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
// This refetches the query on the list page. Read more about other ways to
|
||||
// update the cache over here:
|
||||
// https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
|
||||
refetchQueries: [{ query: QUERY }],
|
||||
awaitRefetchQueries: true,
|
||||
})
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (confirm('Are you sure you want to delete kategorie ' + id + '?')) {
|
||||
deleteKategorie({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment rw-table-wrapper-responsive">
|
||||
<table className="rw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Name</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{kategories.map((kategorie) => (
|
||||
<tr key={kategorie.id}>
|
||||
<td>{truncate(kategorie.id)}</td>
|
||||
<td>{truncate(kategorie.name)}</td>
|
||||
<td>
|
||||
<nav className="rw-table-actions">
|
||||
<Link
|
||||
to={routes.kategorie({ id: kategorie.id })}
|
||||
title={'Show kategorie ' + kategorie.id + ' detail'}
|
||||
className="rw-button rw-button-small"
|
||||
>
|
||||
Show
|
||||
</Link>
|
||||
<Link
|
||||
to={routes.editKategorie({ id: kategorie.id })}
|
||||
title={'Edit kategorie ' + kategorie.id}
|
||||
className="rw-button rw-button-small rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
title={'Delete kategorie ' + kategorie.id}
|
||||
className="rw-button rw-button-small rw-button-red"
|
||||
onClick={() => onDeleteClick(kategorie.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</nav>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default KategoriesList
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import Kategories from 'src/components/Kategorie/Kategories'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindKategories {
|
||||
kategories {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => {
|
||||
return (
|
||||
<div className="rw-text-center">
|
||||
{'No kategories yet. '}
|
||||
<Link to={routes.newKategorie()} className="rw-link">
|
||||
{'Create one?'}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ kategories }) => {
|
||||
return <Kategories kategories={kategories} />
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import KategorieForm from 'src/components/Kategorie/KategorieForm'
|
||||
|
||||
const CREATE_KATEGORIE_MUTATION = gql`
|
||||
mutation CreateKategorieMutation($input: CreateKategorieInput!) {
|
||||
createKategorie(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const NewKategorie = () => {
|
||||
const [createKategorie, { loading, error }] = useMutation(
|
||||
CREATE_KATEGORIE_MUTATION,
|
||||
{
|
||||
onCompleted: () => {
|
||||
toast.success('Kategorie created')
|
||||
navigate(routes.kategories())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const onSave = (input) => {
|
||||
createKategorie({ variables: { input } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">New Kategorie</h2>
|
||||
</header>
|
||||
<div className="rw-segment-main">
|
||||
<KategorieForm onSave={onSave} loading={loading} error={error} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewKategorie
|
||||
@@ -0,0 +1,60 @@
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import KaufForm from 'src/components/Kauf/KaufForm'
|
||||
|
||||
export const QUERY = gql`
|
||||
query EditKaufById($id: Int!) {
|
||||
kauf: kauf(id: $id) {
|
||||
id
|
||||
timestamp
|
||||
preis_ges
|
||||
}
|
||||
}
|
||||
`
|
||||
const UPDATE_KAUF_MUTATION = gql`
|
||||
mutation UpdateKaufMutation($id: Int!, $input: UpdateKaufInput!) {
|
||||
updateKauf(id: $id, input: $input) {
|
||||
id
|
||||
timestamp
|
||||
preis_ges
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ kauf }) => {
|
||||
const [updateKauf, { loading, error }] = useMutation(UPDATE_KAUF_MUTATION, {
|
||||
onCompleted: () => {
|
||||
toast.success('Kauf updated')
|
||||
navigate(routes.kaufs())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
const onSave = (input, id) => {
|
||||
updateKauf({ variables: { id, input } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Edit Kauf {kauf?.id}
|
||||
</h2>
|
||||
</header>
|
||||
<div className="rw-segment-main">
|
||||
<KaufForm kauf={kauf} onSave={onSave} error={error} loading={loading} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Link, routes, navigate } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { timeTag } from 'src/lib/formatters'
|
||||
|
||||
const DELETE_KAUF_MUTATION = gql`
|
||||
mutation DeleteKaufMutation($id: Int!) {
|
||||
deleteKauf(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Kauf = ({ kauf }) => {
|
||||
const [deleteKauf] = useMutation(DELETE_KAUF_MUTATION, {
|
||||
onCompleted: () => {
|
||||
toast.success('Kauf deleted')
|
||||
navigate(routes.kaufs())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (confirm('Are you sure you want to delete kauf ' + id + '?')) {
|
||||
deleteKauf({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Kauf {kauf.id} Detail
|
||||
</h2>
|
||||
</header>
|
||||
<table className="rw-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<td>{kauf.id}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<td>{timeTag(kauf.timestamp)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Preis ges</th>
|
||||
<td>{kauf.preis_ges}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav className="rw-button-group">
|
||||
<Link
|
||||
to={routes.editKauf({ id: kauf.id })}
|
||||
className="rw-button rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="rw-button rw-button-red"
|
||||
onClick={() => onDeleteClick(kauf.id)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Kauf
|
||||
@@ -0,0 +1,23 @@
|
||||
import Kauf from 'src/components/Kauf/Kauf'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindKaufById($id: Int!) {
|
||||
kauf: kauf(id: $id) {
|
||||
id
|
||||
timestamp
|
||||
preis_ges
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => <div>Kauf not found</div>
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ kauf }) => {
|
||||
return <Kauf kauf={kauf} />
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
Form,
|
||||
FormError,
|
||||
FieldError,
|
||||
Label,
|
||||
TextField,
|
||||
Submit,
|
||||
} from '@redwoodjs/forms'
|
||||
|
||||
const KaufForm = (props) => {
|
||||
const onSubmit = (data) => {
|
||||
props.onSave(data, props?.kauf?.id)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-form-wrapper">
|
||||
<Form onSubmit={onSubmit} error={props.error}>
|
||||
<FormError
|
||||
error={props.error}
|
||||
wrapperClassName="rw-form-error-wrapper"
|
||||
titleClassName="rw-form-error-title"
|
||||
listClassName="rw-form-error-list"
|
||||
/>
|
||||
|
||||
<Label
|
||||
name="preis_ges"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Preis ges
|
||||
</Label>
|
||||
|
||||
<TextField
|
||||
name="preis_ges"
|
||||
defaultValue={props.kauf?.preis_ges}
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
validation={{ valueAsNumber: true }}
|
||||
/>
|
||||
|
||||
<FieldError name="preis_ges" className="rw-field-error" />
|
||||
|
||||
<div className="rw-button-group">
|
||||
<Submit disabled={props.loading} className="rw-button rw-button-blue">
|
||||
Save
|
||||
</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default KaufForm
|
||||
@@ -0,0 +1,27 @@
|
||||
import { timeTag, truncate } from 'src/lib/formatters'
|
||||
|
||||
const KaufsList = ({ kaufs }) => {
|
||||
|
||||
return (
|
||||
<div className="rw-segment rw-table-wrapper-responsive">
|
||||
<table className="rw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Uhrzeit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{kaufs.map((kauf) => (
|
||||
<tr key={kauf.id}>
|
||||
<td>{truncate(kauf.id+100)}</td>
|
||||
<td>{truncate(kauf.timestamp)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default KaufsList
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import Kaufs from 'src/components/Kauf/Kaufs'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindKaufs {
|
||||
kaufs {
|
||||
id
|
||||
timestamp
|
||||
preis_ges
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => {
|
||||
return (
|
||||
<div className="rw-text-center">
|
||||
{'No kaufs yet. '}
|
||||
<Link to={routes.newKauf()} className="rw-link">
|
||||
{'Create one?'}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ kaufs }) => {
|
||||
return <Kaufs kaufs={kaufs} />
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FindKaufs {
|
||||
kaufs {
|
||||
id
|
||||
timestamp
|
||||
preis_ges
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => {
|
||||
return (
|
||||
<div className="rw-text-center">
|
||||
{'No kaufs yet. '}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Failure = ({ error }) => (
|
||||
<div className="rw-cell-error">{error?.message}</div>
|
||||
)
|
||||
|
||||
export const Success = ({ kaufs }) => {
|
||||
return (<div className="rw-segment rw-table-wrapper-responsive">
|
||||
<table className="rw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Timestamp</th>
|
||||
<th>Preis ges</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{kaufs.map((kauf) => (
|
||||
<tr key={kauf.id}>
|
||||
<td>{truncate(kauf.id+100)}</td>
|
||||
<td>{timeTag(kauf.timestamp)}</td>
|
||||
<td>{truncate(kauf.preis_ges)}</td>
|
||||
<td>
|
||||
<nav className="rw-table-actions">
|
||||
<Link
|
||||
to={routes.kauf({ id: kauf.id })}
|
||||
title={'Show kauf ' + kauf.id + ' detail'}
|
||||
className="rw-button rw-button-small"
|
||||
>
|
||||
Show
|
||||
</Link>
|
||||
</nav>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { useMutation } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import KaufForm from 'src/components/Kauf/KaufForm'
|
||||
|
||||
const CREATE_KAUF_MUTATION = gql`
|
||||
mutation CreateKaufMutation($input: CreateKaufInput!) {
|
||||
createKauf(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const NewKauf = () => {
|
||||
const [createKauf, { loading, error }] = useMutation(CREATE_KAUF_MUTATION, {
|
||||
onCompleted: () => {
|
||||
toast.success('Kauf created')
|
||||
navigate(routes.kaufs())
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
const onSave = (input) => {
|
||||
createKauf({ variables: { input } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">New Kauf</h2>
|
||||
</header>
|
||||
<div className="rw-segment-main">
|
||||
<KaufForm onSave={onSave} loading={loading} error={error} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewKauf
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* START --- SETUP TAILWINDCSS EDIT
|
||||
*
|
||||
* `yarn rw setup ui tailwindcss` placed these directives here
|
||||
* to inject Tailwind's styles into your CSS.
|
||||
* For more information, see: https://tailwindcss.com/docs/installation
|
||||
*/
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/**
|
||||
* END --- SETUP TAILWINDCSS EDIT
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import { Toaster } from '@redwoodjs/web/toast'
|
||||
|
||||
const ScaffoldLayout = ({
|
||||
title,
|
||||
titleTo,
|
||||
buttonLabel,
|
||||
buttonTo,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className="rw-scaffold">
|
||||
<Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
|
||||
<header className="rw-header">
|
||||
<h1 className="rw-heading rw-heading-primary">
|
||||
<Link to={routes[titleTo]()} className="rw-link">
|
||||
{title}
|
||||
</Link>
|
||||
</h1>
|
||||
</header>
|
||||
<main className="rw-main">{children}</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ScaffoldLayout
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import { Toaster } from '@redwoodjs/web/toast'
|
||||
|
||||
const ScaffoldLayout = ({
|
||||
title,
|
||||
titleTo,
|
||||
buttonLabel,
|
||||
buttonTo,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className="rw-scaffold">
|
||||
<Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
|
||||
<header className="rw-header">
|
||||
<h1 className="rw-heading rw-heading-primary">
|
||||
<Link to={routes[titleTo]()} className="rw-link">
|
||||
{title}
|
||||
</Link>
|
||||
</h1>
|
||||
</header>
|
||||
<main className="rw-main">{children}</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ScaffoldLayout
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import { Toaster } from '@redwoodjs/web/toast'
|
||||
|
||||
const ScaffoldLayout = ({
|
||||
title,
|
||||
titleTo,
|
||||
buttonLabel,
|
||||
buttonTo,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className="rw-scaffold">
|
||||
<Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
|
||||
<header className="rw-header">
|
||||
<h1 className="rw-heading rw-heading-primary">
|
||||
<Link to={routes[titleTo]()} className="rw-link">
|
||||
{title}
|
||||
</Link>
|
||||
</h1>
|
||||
<Link to={routes[buttonTo]()} className="rw-button rw-button-green">
|
||||
<div className="rw-button-icon">+</div> {buttonLabel}
|
||||
</Link>
|
||||
</header>
|
||||
<main className="rw-main">{children}</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ScaffoldLayout
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react'
|
||||
|
||||
import humanize from 'humanize-string'
|
||||
|
||||
const MAX_STRING_LENGTH = 150
|
||||
|
||||
export const formatEnum = (values) => {
|
||||
let output = ''
|
||||
|
||||
if (Array.isArray(values)) {
|
||||
const humanizedValues = values.map((value) => humanize(value))
|
||||
output = humanizedValues.join(', ')
|
||||
} else if (typeof values === 'string') {
|
||||
output = humanize(values)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
export const jsonDisplay = (obj) => {
|
||||
return (
|
||||
<pre>
|
||||
<code>{JSON.stringify(obj, null, 2)}</code>
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
export const truncate = (value) => {
|
||||
let output = value?.toString() ?? ''
|
||||
|
||||
if (output.length > MAX_STRING_LENGTH) {
|
||||
output = output.substring(0, MAX_STRING_LENGTH) + '...'
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
export const jsonTruncate = (obj) => {
|
||||
return truncate(JSON.stringify(obj, null, 2))
|
||||
}
|
||||
|
||||
export const timeTag = (dateTime) => {
|
||||
let output = ''
|
||||
|
||||
if (dateTime) {
|
||||
output = (
|
||||
<time dateTime={dateTime} title={dateTime}>
|
||||
{new Date(dateTime).toUTCString()}
|
||||
</time>
|
||||
)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
export const checkboxInputTag = (checked) => {
|
||||
return <input type="checkbox" checked={checked} disabled />
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import { render, waitFor, screen } from '@redwoodjs/testing/web'
|
||||
|
||||
import {
|
||||
formatEnum,
|
||||
jsonTruncate,
|
||||
truncate,
|
||||
timeTag,
|
||||
jsonDisplay,
|
||||
checkboxInputTag,
|
||||
} from './formatters'
|
||||
|
||||
describe('formatEnum', () => {
|
||||
it('handles nullish values', () => {
|
||||
expect(formatEnum(null)).toEqual('')
|
||||
expect(formatEnum('')).toEqual('')
|
||||
expect(formatEnum(undefined)).toEqual('')
|
||||
})
|
||||
|
||||
it('formats a list of values', () => {
|
||||
expect(
|
||||
formatEnum(['RED', 'ORANGE', 'YELLOW', 'GREEN', 'BLUE', 'VIOLET'])
|
||||
).toEqual('Red, Orange, Yellow, Green, Blue, Violet')
|
||||
})
|
||||
|
||||
it('formats a single value', () => {
|
||||
expect(formatEnum('DARK_BLUE')).toEqual('Dark blue')
|
||||
})
|
||||
|
||||
it('returns an empty string for values of the wrong type (for JS projects)', () => {
|
||||
// @ts-expect-error - Testing JS scenario
|
||||
expect(formatEnum(5)).toEqual('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('truncate', () => {
|
||||
it('truncates really long strings', () => {
|
||||
expect(truncate('na '.repeat(1000) + 'batman').length).toBeLessThan(1000)
|
||||
expect(truncate('na '.repeat(1000) + 'batman')).not.toMatch(/batman/)
|
||||
})
|
||||
|
||||
it('does not modify short strings', () => {
|
||||
expect(truncate('Short strinG')).toEqual('Short strinG')
|
||||
})
|
||||
|
||||
it('adds ... to the end of truncated strings', () => {
|
||||
expect(truncate('repeat'.repeat(1000))).toMatch(/\w\.\.\.$/)
|
||||
})
|
||||
|
||||
it('accepts numbers', () => {
|
||||
expect(truncate(123)).toEqual('123')
|
||||
expect(truncate(0)).toEqual('0')
|
||||
expect(truncate(0o000)).toEqual('0')
|
||||
})
|
||||
|
||||
it('handles arguments of invalid type', () => {
|
||||
// @ts-expect-error - Testing JS scenario
|
||||
expect(truncate(false)).toEqual('false')
|
||||
|
||||
expect(truncate(undefined)).toEqual('')
|
||||
expect(truncate(null)).toEqual('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('jsonTruncate', () => {
|
||||
it('truncates large json structures', () => {
|
||||
expect(
|
||||
jsonTruncate({
|
||||
foo: 'foo',
|
||||
bar: 'bar',
|
||||
baz: 'baz',
|
||||
kittens: 'kittens meow',
|
||||
bazinga: 'Sheldon',
|
||||
nested: {
|
||||
foobar: 'I have no imagination',
|
||||
two: 'Second nested item',
|
||||
},
|
||||
five: 5,
|
||||
bool: false,
|
||||
})
|
||||
).toMatch(/.+\n.+\w\.\.\.$/s)
|
||||
})
|
||||
})
|
||||
|
||||
describe('timeTag', () => {
|
||||
it('renders a date', async () => {
|
||||
render(<div>{timeTag(new Date('1970-08-20').toUTCString())}</div>)
|
||||
|
||||
await waitFor(() => screen.getByText(/1970.*00:00:00/))
|
||||
})
|
||||
|
||||
it('can take an empty input string', async () => {
|
||||
expect(timeTag('')).toEqual('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('jsonDisplay', () => {
|
||||
it('produces the correct output', () => {
|
||||
expect(
|
||||
jsonDisplay({
|
||||
title: 'TOML Example (but in JSON)',
|
||||
database: {
|
||||
data: [['delta', 'phi'], [3.14]],
|
||||
enabled: true,
|
||||
ports: [8000, 8001, 8002],
|
||||
temp_targets: {
|
||||
case: 72.0,
|
||||
cpu: 79.5,
|
||||
},
|
||||
},
|
||||
owner: {
|
||||
dob: '1979-05-27T07:32:00-08:00',
|
||||
name: 'Tom Preston-Werner',
|
||||
},
|
||||
servers: {
|
||||
alpha: {
|
||||
ip: '10.0.0.1',
|
||||
role: 'frontend',
|
||||
},
|
||||
beta: {
|
||||
ip: '10.0.0.2',
|
||||
role: 'backend',
|
||||
},
|
||||
},
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
<pre>
|
||||
<code>
|
||||
{
|
||||
"title": "TOML Example (but in JSON)",
|
||||
"database": {
|
||||
"data": [
|
||||
[
|
||||
"delta",
|
||||
"phi"
|
||||
],
|
||||
[
|
||||
3.14
|
||||
]
|
||||
],
|
||||
"enabled": true,
|
||||
"ports": [
|
||||
8000,
|
||||
8001,
|
||||
8002
|
||||
],
|
||||
"temp_targets": {
|
||||
"case": 72,
|
||||
"cpu": 79.5
|
||||
}
|
||||
},
|
||||
"owner": {
|
||||
"dob": "1979-05-27T07:32:00-08:00",
|
||||
"name": "Tom Preston-Werner"
|
||||
},
|
||||
"servers": {
|
||||
"alpha": {
|
||||
"ip": "10.0.0.1",
|
||||
"role": "frontend"
|
||||
},
|
||||
"beta": {
|
||||
"ip": "10.0.0.2",
|
||||
"role": "backend"
|
||||
}
|
||||
}
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('checkboxInputTag', () => {
|
||||
it('can be checked', () => {
|
||||
render(checkboxInputTag(true))
|
||||
expect(screen.getByRole('checkbox')).toBeChecked()
|
||||
})
|
||||
|
||||
it('can be unchecked', () => {
|
||||
render(checkboxInputTag(false))
|
||||
expect(screen.getByRole('checkbox')).not.toBeChecked()
|
||||
})
|
||||
|
||||
it('is disabled when checked', () => {
|
||||
render(checkboxInputTag(true))
|
||||
expect(screen.getByRole('checkbox')).toBeDisabled()
|
||||
})
|
||||
|
||||
it('is disabled when unchecked', () => {
|
||||
render(checkboxInputTag(false))
|
||||
expect(screen.getByRole('checkbox')).toBeDisabled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
import ArtikelCell from 'src/components/Artikel/ArtikelCell'
|
||||
|
||||
const ArtikelPage = ({ id }) => {
|
||||
return <ArtikelCell id={id} />
|
||||
}
|
||||
|
||||
export default ArtikelPage
|
||||
@@ -0,0 +1,7 @@
|
||||
import ArtikelsCell from 'src/components/Artikel/ArtikelsCell'
|
||||
|
||||
const ArtikelsPage = () => {
|
||||
return <ArtikelsCell />
|
||||
}
|
||||
|
||||
export default ArtikelsPage
|
||||
@@ -0,0 +1,7 @@
|
||||
import EditArtikelCell from 'src/components/Artikel/EditArtikelCell'
|
||||
|
||||
const EditArtikelPage = ({ id }) => {
|
||||
return <EditArtikelCell id={id} />
|
||||
}
|
||||
|
||||
export default EditArtikelPage
|
||||
@@ -0,0 +1,7 @@
|
||||
import NewArtikel from 'src/components/Artikel/NewArtikelCell'
|
||||
|
||||
const NewArtikelPage = () => {
|
||||
return <NewArtikel />
|
||||
}
|
||||
|
||||
export default NewArtikelPage
|
||||
@@ -0,0 +1,7 @@
|
||||
import EditKategorieCell from 'src/components/Kategorie/EditKategorieCell'
|
||||
|
||||
const EditKategoriePage = ({ id }) => {
|
||||
return <EditKategorieCell id={id} />
|
||||
}
|
||||
|
||||
export default EditKategoriePage
|
||||
@@ -0,0 +1,7 @@
|
||||
import KategorieCell from 'src/components/Kategorie/KategorieCell'
|
||||
|
||||
const KategoriePage = ({ id }) => {
|
||||
return <KategorieCell id={id} />
|
||||
}
|
||||
|
||||
export default KategoriePage
|
||||
@@ -0,0 +1,7 @@
|
||||
import KategoriesCell from 'src/components/Kategorie/KategoriesCell'
|
||||
|
||||
const KategoriesPage = () => {
|
||||
return <KategoriesCell />
|
||||
}
|
||||
|
||||
export default KategoriesPage
|
||||
@@ -0,0 +1,7 @@
|
||||
import NewKategorie from 'src/components/Kategorie/NewKategorie'
|
||||
|
||||
const NewKategoriePage = () => {
|
||||
return <NewKategorie />
|
||||
}
|
||||
|
||||
export default NewKategoriePage
|
||||
@@ -0,0 +1,7 @@
|
||||
import EditKaufCell from 'src/components/Kauf/EditKaufCell'
|
||||
|
||||
const EditKaufPage = ({ id }) => {
|
||||
return <EditKaufCell id={id} />
|
||||
}
|
||||
|
||||
export default EditKaufPage
|
||||
@@ -0,0 +1,7 @@
|
||||
import KaufCell from 'src/components/Kauf/KaufCell'
|
||||
|
||||
const KaufPage = ({ id }) => {
|
||||
return <KaufCell id={id} />
|
||||
}
|
||||
|
||||
export default KaufPage
|
||||
@@ -0,0 +1,7 @@
|
||||
import KaufsCell from 'src/components/Kauf/KaufsCell'
|
||||
|
||||
const KaufsPage = () => {
|
||||
return <KaufsCell />
|
||||
}
|
||||
|
||||
export default KaufsPage
|
||||
@@ -0,0 +1,7 @@
|
||||
import NewKauf from 'src/components/Kauf/NewKauf'
|
||||
|
||||
const NewKaufPage = () => {
|
||||
return <NewKauf />
|
||||
}
|
||||
|
||||
export default NewKaufPage
|
||||
@@ -1,19 +1,22 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import { MetaTags } from '@redwoodjs/web'
|
||||
|
||||
import KaufsPosCell from 'src/components/Kauf/KaufsPosCell'
|
||||
import ArtikelsPos from 'src/components/Artikel/ArtikelsPos'
|
||||
const PosPage = () => {
|
||||
return (
|
||||
<>
|
||||
<MetaTags title="Pos" description="Pos page" />
|
||||
<div class="container mx-auto">
|
||||
<div class="columns-2">
|
||||
<div>
|
||||
<ArtikelsPos></ArtikelsPos>
|
||||
</div>
|
||||
<div >
|
||||
<KaufsPosCell></KaufsPosCell>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>PosPage</h1>
|
||||
<p>
|
||||
Find me in <code>./web/src/pages/PosPage/PosPage.jsx</code>
|
||||
</p>
|
||||
<p>
|
||||
My default route is named <code>pos</code>, link to me with `
|
||||
<Link to={routes.pos()}>Pos</Link>`
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css
|
||||
*/
|
||||
|
||||
.rw-scaffold *,
|
||||
.rw-scaffold ::after,
|
||||
.rw-scaffold ::before {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
.rw-scaffold main {
|
||||
color: #4a5568;
|
||||
display: block;
|
||||
}
|
||||
.rw-scaffold h1,
|
||||
.rw-scaffold h2 {
|
||||
margin: 0;
|
||||
}
|
||||
.rw-scaffold a {
|
||||
background-color: transparent;
|
||||
}
|
||||
.rw-scaffold ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.rw-scaffold input {
|
||||
font-family: inherit;
|
||||
font-size: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.rw-scaffold input:-ms-input-placeholder {
|
||||
color: #a0aec0;
|
||||
}
|
||||
.rw-scaffold input::-ms-input-placeholder {
|
||||
color: #a0aec0;
|
||||
}
|
||||
.rw-scaffold input::placeholder {
|
||||
color: #a0aec0;
|
||||
}
|
||||
.rw-scaffold table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/*
|
||||
Style
|
||||
*/
|
||||
|
||||
.rw-scaffold,
|
||||
.rw-toast {
|
||||
background-color: #fff;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
.rw-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 2rem 1rem 2rem;
|
||||
}
|
||||
.rw-main {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.rw-segment {
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-color: #e5e7eb;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
scrollbar-color: #a1a1aa transparent;
|
||||
}
|
||||
.rw-segment::-webkit-scrollbar {
|
||||
height: initial;
|
||||
}
|
||||
.rw-segment::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
border-color: #e2e8f0;
|
||||
border-style: solid;
|
||||
border-radius: 0 0 10px 10px;
|
||||
border-width: 1px 0 0 0;
|
||||
padding: 2px;
|
||||
}
|
||||
.rw-segment::-webkit-scrollbar-thumb {
|
||||
background-color: #a1a1aa;
|
||||
background-clip: content-box;
|
||||
border: 3px solid transparent;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.rw-segment-header {
|
||||
background-color: #e2e8f0;
|
||||
color: #4a5568;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
.rw-segment-main {
|
||||
background-color: #f7fafc;
|
||||
padding: 1rem;
|
||||
}
|
||||
.rw-link {
|
||||
color: #4299e1;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.rw-link:hover {
|
||||
color: #2b6cb0;
|
||||
}
|
||||
.rw-forgot-link {
|
||||
font-size: 0.75rem;
|
||||
color: #a0aec0;
|
||||
text-align: right;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
.rw-forgot-link:hover {
|
||||
font-size: 0.75rem;
|
||||
color: #4299e1;
|
||||
}
|
||||
.rw-heading {
|
||||
font-weight: 600;
|
||||
}
|
||||
.rw-heading.rw-heading-primary {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.rw-heading.rw-heading-secondary {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.rw-heading .rw-link {
|
||||
color: #4a5568;
|
||||
text-decoration: none;
|
||||
}
|
||||
.rw-heading .rw-link:hover {
|
||||
color: #1a202c;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.rw-cell-error {
|
||||
font-size: 90%;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rw-form-wrapper {
|
||||
box-sizing: border-box;
|
||||
font-size: 0.875rem;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
.rw-cell-error,
|
||||
.rw-form-error-wrapper {
|
||||
padding: 1rem;
|
||||
background-color: #fff5f5;
|
||||
color: #c53030;
|
||||
border-width: 1px;
|
||||
border-color: #feb2b2;
|
||||
border-radius: 0.25rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.rw-form-error-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
.rw-form-error-list {
|
||||
margin-top: 0.5rem;
|
||||
list-style-type: disc;
|
||||
list-style-position: inside;
|
||||
}
|
||||
.rw-button {
|
||||
border: none;
|
||||
color: #718096;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 0.25rem 1rem;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
letter-spacing: 0.025em;
|
||||
border-radius: 0.25rem;
|
||||
line-height: 2;
|
||||
border: 0;
|
||||
}
|
||||
.rw-button:hover {
|
||||
background-color: #718096;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-small {
|
||||
font-size: 0.75rem;
|
||||
border-radius: 0.125rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
line-height: inherit;
|
||||
}
|
||||
.rw-button.rw-button-green {
|
||||
background-color: #48bb78;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-green:hover {
|
||||
background-color: #38a169;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-blue {
|
||||
background-color: #3182ce;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-blue:hover {
|
||||
background-color: #2b6cb0;
|
||||
}
|
||||
.rw-button.rw-button-red {
|
||||
background-color: #e53e3e;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-button.rw-button-red:hover {
|
||||
background-color: #c53030;
|
||||
}
|
||||
.rw-button-icon {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
.rw-button-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0.75rem 0.5rem;
|
||||
}
|
||||
.rw-button-group .rw-button {
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
.rw-form-wrapper .rw-button-group {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.rw-label {
|
||||
display: block;
|
||||
margin-top: 1.5rem;
|
||||
color: #4a5568;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
.rw-label.rw-label-error {
|
||||
color: #c53030;
|
||||
}
|
||||
.rw-input {
|
||||
display: block;
|
||||
margin-top: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #e2e8f0;
|
||||
color: #4a5568;
|
||||
border-radius: 0.25rem;
|
||||
outline: none;
|
||||
}
|
||||
.rw-check-radio-item-none {
|
||||
color: #4a5568;
|
||||
}
|
||||
.rw-check-radio-items {
|
||||
display: flex;
|
||||
justify-items: center;
|
||||
}
|
||||
.rw-input[type='checkbox'] {
|
||||
display: inline;
|
||||
width: 1rem;
|
||||
margin-left: 0;
|
||||
margin-right: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.rw-input[type='radio'] {
|
||||
display: inline;
|
||||
width: 1rem;
|
||||
margin-left: 0;
|
||||
margin-right: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.rw-input:focus {
|
||||
border-color: #a0aec0;
|
||||
}
|
||||
.rw-input-error {
|
||||
border-color: #c53030;
|
||||
color: #c53030;
|
||||
}
|
||||
|
||||
.rw-input-error:focus {
|
||||
outline: none;
|
||||
border-color: #c53030;
|
||||
box-shadow: 0 0 5px #c53030;
|
||||
}
|
||||
|
||||
.rw-field-error {
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
color: #c53030;
|
||||
}
|
||||
.rw-table-wrapper-responsive {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.rw-table-wrapper-responsive .rw-table {
|
||||
min-width: 48rem;
|
||||
}
|
||||
.rw-table {
|
||||
table-layout: auto;
|
||||
width: 100%;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.rw-table th,
|
||||
.rw-table td {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
.rw-table td {
|
||||
background-color: #ffffff;
|
||||
color: #1a202c;
|
||||
}
|
||||
.rw-table tr:nth-child(odd) td,
|
||||
.rw-table tr:nth-child(odd) th {
|
||||
background-color: #f7fafc;
|
||||
}
|
||||
.rw-table thead tr {
|
||||
color: #4a5568;
|
||||
}
|
||||
.rw-table th {
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
.rw-table thead th {
|
||||
background-color: #e2e8f0;
|
||||
text-align: left;
|
||||
}
|
||||
.rw-table tbody th {
|
||||
text-align: right;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.rw-table tbody th {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
.rw-table tbody tr {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
.rw-table input {
|
||||
margin-left: 0;
|
||||
}
|
||||
.rw-table-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
height: 17px;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
.rw-table-actions .rw-button {
|
||||
background-color: transparent;
|
||||
}
|
||||
.rw-table-actions .rw-button:hover {
|
||||
background-color: #718096;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-table-actions .rw-button-blue {
|
||||
color: #3182ce;
|
||||
}
|
||||
.rw-table-actions .rw-button-blue:hover {
|
||||
background-color: #3182ce;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-table-actions .rw-button-red {
|
||||
color: #e53e3e;
|
||||
}
|
||||
.rw-table-actions .rw-button-red:hover {
|
||||
background-color: #e53e3e;
|
||||
color: #fff;
|
||||
}
|
||||
.rw-text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.rw-login-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24rem;
|
||||
margin: 4rem auto;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.rw-login-container .rw-form-wrapper {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.rw-login-link {
|
||||
margin-top: 1rem;
|
||||
color: #4a5568;
|
||||
font-size: 90%;
|
||||
text-align: center;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
.rw-webauthn-wrapper {
|
||||
margin: 1.5rem 1rem 1rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.rw-webauthn-wrapper h2 {
|
||||
font-size: 150%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
Reference in New Issue
Block a user