Skip to content
Snippets Groups Projects
Commit 98f51585 authored by borzechof99's avatar borzechof99 :whale2:
Browse files

Merge branch '10-fragenreihenfolge' into 'master'

Implemented Question Ordering

Closes #10

See merge request swp-unisport/team-warumkeinrust/admin-frontend!6
parents 1c3dbc04 eb788902
Branches
No related tags found
No related merge requests found
......@@ -7,11 +7,13 @@ import { SportList, SportEdit, SportCreate } from './sportList';
import { IncompleteList, IncompleteEdit } from './incompleteList';
import { QuestionList, QuestionEdit, QuestionCreate } from './questionList';
import { ScraperList } from './scraperList';
import { orderList } from './questionOrderList';
import QuestionAnswerIcon from '@material-ui/icons/QuestionAnswer';
import SportsFootballIcon from '@material-ui/icons/SportsFootball';
import ListAltIcon from '@material-ui/icons/ListAlt';
import YoutubeSearchedForIcon from '@material-ui/icons/YoutubeSearchedFor';
import FormatListNumberedIcon from '@material-ui/icons/FormatListNumbered';
const App = () => (
......@@ -52,6 +54,13 @@ const App = () => (
options={{ label: 'Sportarten auslesen' }}
list={ScraperList}
/>
<Resource
name='question-order'
icon={FormatListNumberedIcon}
options={{ label: 'Fragenreihenfolge' }}
list={orderList}
/>
</Admin>
);
......
import drfProvider from 'ra-data-django-rest-framework';
import sportIncompleteProvider from './sportIncompleteProvider.js';
import scraperDataProvider from './scraperDataProvider.js';
import questionOrderProvider from './questionOrderProvider.js';
import {
GET_LIST,
GET_ONE,
......@@ -28,6 +29,10 @@ const dataProviders = [
{
dataProvider: scraperDataProvider('http://localhost:8000/api/admin'),
resources: ['sport-scraper'],
},
{
dataProvider: questionOrderProvider('http://localhost:8000/api/admin'),
resources: ['question-order'],
}
];
......@@ -50,6 +55,10 @@ export default (type, resource, params) => {
[DELETE]: 'delete',
[DELETE_MANY]: 'deleteMany',
["post_scraper"]: 'post_scraper',
["add_entry"]: 'add_entry',
["move_entry"]: 'move_entry',
["save_order"]: 'save_order',
["empty_cache"]: 'empty_cache',
};
......
import { Pagination } from 'react-admin';
// Removes the pagination field at the bottom of the page
// Used whenever flipping pages on a view might crash things
export default (props => <Pagination rowsPerPageOptions={[]} {...props} />)
\ No newline at end of file
......@@ -24,15 +24,19 @@ import Card from '@material-ui/core/Card'
import CardContent from '@material-ui/core/CardContent'
const AsideIncompleteList = () => (
<div style={{ width: 400, margin: '1em' }}>
<div style={{
width: 400,
margin: '1em'
}}>
<Card>
<CardContent>
<Typography variant="h4" align="center">Tipps&Tricks</Typography>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>Klicke auf die ID von dem Sport, um den gesamten Sport zu bearbeiten.</li>
......@@ -63,19 +67,32 @@ const IncompleteEditToolbar = props => (
export const IncompleteList = props => (
<List {...props} aside={<AsideIncompleteList />} bulkActionButtons={false}>
<List
{...props}
aside={<AsideIncompleteList />}
bulkActionButtons={false}>
{/* rowClick expand means that no edit page is opened, but instead shown below the data row itself without reloading */}
<Datagrid rowClick="expand" expand={IncompleteEdit} style={{ tableLayout: "fixed", }} >
<Datagrid
rowClick="expand"
expand={IncompleteEdit}
style={{ tableLayout: "fixed", }} >
{/* Reference Field fetches every Sport object if a reference field to it exists.
This means that n GETs are made for one load of the incomplete-list.
Could probably be improved in the future.
*/}
<ReferenceField label="ID" source="id" reference="sport" >
<ReferenceField
label="ID"
source="id"
reference="sport"
sortable={false}>
<ChipField source="id" />
</ReferenceField>
<TextField source="name" />
<TextField
source="name"
sortable={false}
/>
</Datagrid>
</List>
);
......@@ -96,17 +113,32 @@ export const IncompleteEdit = props => {
// mutationMode pessimistic deactivates the optimistic rendering
// Now the request will be sent first and rendered after getting the return code
return (
<Edit {...props} mutationMode="pessimistic" onSuccess={onSuccess}>
<Edit
{...props}
mutationMode="pessimistic"
onSuccess={onSuccess}
>
<SimpleForm toolbar={<IncompleteEditToolbar />}>
<Typography variant="h5" fullWidth>Fehlende Kriterien ausfüllen</Typography>
<Typography
variant="h5"
fullWidth
>
Fehlende Kriterien ausfüllen
</Typography>
{/* Documentation for formatting https://marmelab.com/react-admin/CreateEdit.html#custom-row-container */}
<TextField source="id" />
<TextField source="name" />
<ArrayInput source="criteria" label="">
<SimpleFormIterator disableAdd disableRemove>
<ArrayInput
source="criteria"
label=""
>
<SimpleFormIterator
disableAdd
disableRemove
>
{/* Documentation is wrong, look here! https://github.com/marmelab/react-admin/issues/2500
getSource() needs to get called without arguments before the return,
......
import { Pagination } from 'react-admin';
const PostPagination = props => <Pagination rowsPerPageOptions={[100, 500, 1000]} {...props} />;
\ No newline at end of file
......@@ -18,7 +18,12 @@ const AsideQuestionList = () => (
<div style={{ width: 400, margin: '1em' }}>
<Card>
<CardContent>
<Typography variant="h4" align="center">Tipps&Tricks</Typography>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>Klicke auf eine Datenreihe, um sie zu bearbeiten.</li>
......@@ -37,7 +42,12 @@ const AsideQuestionEdit = () => (
<div style={{ width: 400, margin: '1em' }}>
<Card>
<CardContent>
<Typography variant="h4" align="center">Tipps&Tricks</Typography>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>Pflichtangaben sind:
......@@ -60,10 +70,18 @@ const AsideQuestionEdit = () => (
);
const AsideQuestionCreate = () => (
<div style={{ width: 400, margin: '1em' }}>
<div style={{
width: 400,
margin: '1em'
}}>
<Card>
<CardContent>
<Typography variant="h4" align="center">Tipps&Tricks</Typography>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>Pflichtangaben sind:
......@@ -86,11 +104,28 @@ const AsideQuestionCreate = () => (
export const QuestionList = props => (
<List {...props} aside={<AsideQuestionList />}>
<Datagrid rowClick="edit" style={{ tableLayout: "fixed", }}>
<TextField source="text_de" label="Deutscher Fragetext" />
<TextField source="text_en" label="Englischer Fragetext" />
<TextField source="criterion" label="Name des Kriteriums" />
<List
{...props}
aside={<AsideQuestionList />}
>
<Datagrid
rowClick="edit"
style={{ tableLayout: "fixed", }}>
<TextField
source="text_de"
label="Deutscher Fragetext"
sortable={false}
/>
<TextField
source="text_en"
label="Englischer Fragetext"
sortable={false}
/>
<TextField
source="criterion"
label="Name des Kriteriums"
sortable={false}
/>
</Datagrid>
</List>
);
......@@ -98,12 +133,34 @@ export const QuestionList = props => (
// mutationMode pessimistic deactivates the optimistic rendering
// Now the request will be sent first and rendered after getting the return code
export const QuestionEdit = props => (
<Edit {...props} mutationMode="pessimistic" aside={<AsideQuestionEdit />}>
<Edit
{...props}
mutationMode="pessimistic"
aside={<AsideQuestionEdit />}
>
<SimpleForm>
<Typography variant="h5" fullWidth >Frage bearbeiten</Typography>
<TextInput source="text_de" label="Deutscher Fragetext" fullWidth required />
<TextInput source="text_en" label="Englischer Fragetext" fullWidth />
<TextInput source="criterion" label="Name des Kriteriums" required />
<Typography
ariant="h5"
fullWidth
>
Frage bearbeiten
</Typography>
<TextInput
source="text_de"
label="Deutscher Fragetext"
fullWidth
required
/>
<TextInput
source="text_en"
label="Englischer Fragetext"
fullWidth
/>
<TextInput
source="criterion"
label="Name des Kriteriums"
required
/>
</SimpleForm>
</Edit>
);
......@@ -112,11 +169,29 @@ export const QuestionCreate = props => (
<Create {...props} aside={<AsideQuestionCreate />}>
<SimpleForm redirect="list">
<Typography variant="h5" fullWidth >Frage erstellen</Typography>
<TextInput source="text_de" label="Deutscher Fragetext" fullWidth required />
<TextInput source="text_en" label="Englischer Fragetext" fullWidth />
<TextInput source="criterion" label="Name des Kriteriums" required />
<Typography
variant="h5"
fullWidth
>
Frage erstellen
</Typography>
<TextInput
source="text_de"
label="Deutscher Fragetext"
fullWidth
required
/>
<TextInput
source="text_en"
label="Englischer Fragetext"
fullWidth
/>
<TextInput
source="criterion"
label="Name des Kriteriums"
required
/>
</SimpleForm>
</Create>
);
\ No newline at end of file
import {
List,
Datagrid,
TextField,
TopToolbar,
Button,
useNotify,
Edit,
SimpleForm,
ReferenceInput,
SelectInput,
Toolbar,
SaveButton,
ReferenceField,
useDataProvider,
RadioButtonGroupInput,
useRefresh,
useListContext,
Pagination
} from 'react-admin';
import Typography from '@material-ui/core/Typography';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
const PostShowActions = ({ basePath, data, resource }) => {
const notify = useNotify();
const refresh = useRefresh();
const dataProvider = useDataProvider();
const addOrderEntry = () => {
dataProvider.add_entry("question-order", {}).then(
response => {
notify("Neuen Entry Hinzugefügt!");
refresh();
}).catch(error => {
notify("Etwas ist schief gelaufen!", "warning");
});
}
const save_order = () => {
dataProvider.save_order("question-order", {}).then(
response => {
notify("Veränderungen gespeichert!");
refresh();
}).catch(error => {
notify("Etwas ist schief gelaufen!", "warning");
});
}
const empty_cache = () => {
dataProvider.empty_cache("question-order", {}).then(
response => {
notify("Veränderungen auf Serverzustand zurückgesetzt!");
refresh();
}).catch(error => {
notify("Etwas ist schief gelaufen!", "warning");
});
}
return (
<TopToolbar>
<Button
color="primary"
onClick={addOrderEntry}
label="Eintrag Hinzufügen"
/>
<Button
color="primary"
onClick={save_order}
label="Veränderungen Speichern"
/>
<Button
color="primary"
onClick={empty_cache}
label="Veränderungen Verwerfen"
/>
</TopToolbar>
)
};
const AsideQuestionOrder = () => (
<div style={{ width: 400, margin: '1em' }}>
<Card>
<CardContent>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>
Jeder Eintrag in dieser Liste ist eine Seite im Quiz, die entweder Fragen, Aktivitäten, oder Wissenssnacks anzeigt.
Wenn die Zeilen aufgeklappt werden, dann kann sowohl die Art des Eintrags, als auch die Frage selbst ausgewählt werden.
</li>
<br />
<li>
Um die einzelnen Änderungen an einem Eintrag zu speichern, muss "Save" in dem aufgeklappten Feld gedrückt werden.
Dort kann man ebenso den Eintrag löschen, wenn man möchte.
</li>
<br />
<li>
<p><b>Um die Reihenfolge zu speichern, muss der Knopf "Veränderungen Speichern" oben rechts gedrückt werden.</b></p>
<p>Mit dem Knopf "Veränderungen Verwerfen" werden die lokalen Änderungen auf den letzten Stand zurückgesetzt.</p>
<p>Mit dem Knopf "Eintrag Hinzufügen" kann ein neuer Eintrag am Ende der Liste angehangen werden.</p>
</li>
<br />
<li>
Mit den Knöpfen "Hoch" und "Runter" können die Einträge um jeweils eine Zeile verschoben werden.
</li>
<br />
<li>
In jedem Eintrag vom Typ Frage muss ein Fragentext ausgewählt sein, bevor die Liste gespeichert werden kann.
</li>
</ul>
</CardContent>
</Card>
</div>
);
const DirEditButton = props => {
const notify = useNotify();
const refresh = useRefresh();
const dataProvider = useDataProvider();
const listContext = useListContext();
if (props.record === undefined) {
return null;
};
if (props.record.id + props.direction > listContext.total || props.record.id + props.direction < 1) {
return null;
};
const id = props.record.id;
const dir = props.direction;
const action = (props) => {
dataProvider.move_entry('question-order', { id: id, move: dir }).then(
response => {
notify("Entry Verschoben!");
refresh();
}).catch(error => {
notify("Etwas ist schief gelaufen!", "warning");
});
};
return <Button label={props.custom_label} onClick={action} />
}
const QuestionOrderPagination = props => <Pagination rowsPerPageOptions={[]} {...props} />;
export const orderList = props => (
<List
{...props}
actions={<PostShowActions />}
aside={<AsideQuestionOrder />}
pagination={<QuestionOrderPagination />}
perPage={1000}
bulkActionButtons={false}
>
<Datagrid
rowClick="expand"
expand={orderEdit}
>
<TextField
label="Reihenfolge"
source="id"
sortable={false}
/>
<TextField
label="Eintragstyp"
source="type"
sortable={false}
/>
<TextField
label="Fragen-ID"
source="question_id"
sortable={false}
/>
<ReferenceField
label="Fragentext"
reference="question"
source="question_id"
sortable={false}
>
<TextField source="text_de" />
</ReferenceField>
<DirEditButton direction={-1} custom_label="Hoch" sortable={false} />
<DirEditButton direction={+1} custom_label="Runter" sortable={false} />
</Datagrid>
</List>
);
const OrderEditToolbar = props => {
const notify = useNotify();
const refresh = useRefresh();
const dataProvider = useDataProvider();
const deleteEntry = () => {
dataProvider.delete("question-order", { id: props.record.id }).then(
response => {
notify("Eintrag erfolgreich gelöscht!");
refresh();
}).catch(error => {
notify("Etwas ist schief gelaufen!", "warning");
});
};
return (
<Toolbar {...props} >
<SaveButton />
<Button
color="primary"
onClick={deleteEntry}
label="Eintrag Löschen"
/>
</Toolbar>
)
};
const CategorySensitiveQuestionField = (props) => {
if (props.record.type === "question") {
return (
<ReferenceInput
label="Frage"
reference="question"
source="question_id"
fullWidth
>
<SelectInput
optionText="text_de"
fullWidth
/>
</ReferenceInput>
)
} else {
return null
}
}
export const orderEdit = props => (
<Edit {...props} mutationMode={"optimistic"}>
<SimpleForm toolbar={<OrderEditToolbar />}>
<RadioButtonGroupInput
source="type"
label="Eintragsart"
choices={[
{ id: "snack", name: "Wissenssnack" },
{ id: "activity", name: "Aktivität" },
{ id: "question", name: "Frage" }]}
/>
<CategorySensitiveQuestionField />
</SimpleForm>
</Edit>
);
\ No newline at end of file
import { stringify } from 'query-string';
import {
fetchUtils,
} from 'ra-core';
/*
DEFAULT DATA PROVIDER TEMPLATE
Just for copy and pasting when other data providers are needed.
*/
// export {
// default as tokenAuthProvider,
// fetchJsonWithAuthToken,
// } from './tokenAuthProvider';
// export {
// default as jwtTokenAuthProvider,
// fetchJsonWithAuthJWTToken,
// } from './jwtTokenAuthProvider';
const getPaginationQuery = (pagination) => {
return {
page: pagination.page,
page_size: pagination.perPage,
};
};
const getFilterQuery = (filter) => {
const { q: search, ...otherSearchParams } = filter;
return {
...otherSearchParams,
search,
};
};
export const getOrderingQuery = (sort) => {
const { field, order } = sort;
return {
ordering: `${order === 'ASC' ? '' : '-'}${field}`,
};
};
let cached_order = undefined;
export default (
apiUrl,
httpClient = fetchUtils.fetchJson
) => {
const getOneJson = (resource, id) =>
httpClient(`${apiUrl}/${resource}/${id}/`).then(
(response) => response.json
);
return {
getList: async (resource, params) => {
const query = {
...getFilterQuery(params.filter),
...getPaginationQuery(params.pagination),
...getOrderingQuery(params.sort),
};
console.log("cached_order in getList is: ", cached_order)
if (cached_order == undefined) {
const url = `${apiUrl}/${resource}/?${stringify(query)}`;
const { json } = await httpClient(url);
cached_order = {
data: json.results,
total: json.count,
}
return {
data: json.results,
total: json.count,
};
} else {
// Cache Already exist, so continue working on that
return cached_order;
}
},
getOne: async (resource, params) => {
const data = cached_order.data[params.id - 1];
return {
data,
};
},
getMany: (resource, params) => {
return Promise.all(
params.ids.map(id => getOneJson(resource, id))
).then(data => ({ data }));
},
getManyReference: async (resource, params) => {
const query = {
...getFilterQuery(params.filter),
...getPaginationQuery(params.pagination),
...getOrderingQuery(params.sort),
[params.target]: params.id,
};
const url = `${apiUrl}/${resource}/?${stringify(query)}`;
const { json } = await httpClient(url);
return {
data: json.results,
total: json.count,
};
},
update: async (resource, params) => {
let data = cached_order.data[params.id - 1];
data.type = params.data.type;
if (data.type !== "question") {
data.question_id = null;
} else {
data.question_id = params.data.question_id;
}
cached_order.data[params.id - 1] = data;
return { data: data };
},
updateMany: (resource, params) =>
Promise.all(
params.ids.map(id =>
httpClient(`${apiUrl}/${resource}/${id}/`, {
method: 'PATCH',
body: JSON.stringify(params.data),
})
)
).then(responses => ({ data: responses.map(({ json }) => json.id) })),
create: async (resource, params) => {
const { json } = await httpClient(`${apiUrl}/${resource}/`, {
method: 'POST',
body: JSON.stringify(params.data),
});
return {
data: { ...json },
};
},
delete: async (resource, params) => {
cached_order.data.splice(params.id - 1, 1);
cached_order.total -= 1;
for (let index = 0; index < cached_order.data.length; index++) {
cached_order.data[index].id = index + 1;
}
console.log(cached_order);
return { data: {} };
},
deleteMany: (resource, params) =>
Promise.all(
params.ids.map(id =>
httpClient(`${apiUrl}/${resource}/${id}/`, {
method: 'DELETE',
})
)
).then(responses => ({ data: responses.map(({ json }) => json.id) })),
add_entry: async (resource, params) => {
cached_order.data.push({
id: cached_order.data.length + 1,
type: "snack",
question_id: null,
});
cached_order.total += 1;
console.log(cached_order);
return { data: cached_order.data[cached_order.data.length] };
},
move_entry: async (resource, params) => {
let old_id = params.id - 1;
let new_id = params.id - 1 + params.move;
let obj = cached_order.data[old_id];
let overwritten_obj = cached_order.data[new_id];
let tmp = obj.id;
obj.id = overwritten_obj.id;
overwritten_obj.id = tmp;
cached_order.data[new_id] = obj;
cached_order.data[old_id] = overwritten_obj;
console.log(cached_order)
return { data: {} }
},
empty_cache: async (resource, params) => {
// Call Fresh Data again to Overwrite Changes
const url = `${apiUrl}/${resource}/`;
const { json } = await httpClient(url);
cached_order = {
data: json.results,
total: json.count,
}
return { data: {} };
},
save_order: async (resource, params) => {
const { json } = await httpClient(`${apiUrl}/${resource}/`, {
method: 'POST',
body: JSON.stringify(cached_order.data),
});
return { data: json }
},
};
};
......@@ -4,6 +4,7 @@ import {
TextField,
NumberField,
DateField,
Pagination,
} from 'react-admin';
import Typography from '@material-ui/core/Typography';
......@@ -16,7 +17,12 @@ const AsideSportScrape = () => (
<div style={{ width: 400, margin: '1em' }}>
<Card>
<CardContent>
<Typography variant="h4" align="center">Tipps&Tricks</Typography>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>
......@@ -40,20 +46,51 @@ const AsideSportScrape = () => (
</div>
);
{/* bulkActionButtons should theoretically be able to take a whole Fragment worth of buttons, but React hates us and we return the feeling*/ }
const ScraperPagination = props => <Pagination rowsPerPageOptions={[]} {...props} />;
/* bulkActionButtons should theoretically be able to take a whole Fragment worth of buttons, but React hates us and we return the feeling*/
// perPage is set to 1000
// It is important that the page shows all entries, so that there are no issues while scrolling through the pages and sending the data afterwards
// So, all Diffs should be displayed on the same page
export const ScraperList = props => (
<List {...props} perPage={1000} bulkActionButtons={<PostScraperButton {...props} />} aside={<AsideSportScrape />}>
<List
{...props}
perPage={1000}
pagination={<ScraperPagination />}
bulkActionButtons={<PostScraperButton {...props} />}
aside={<AsideSportScrape />}
>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="kind_of_diff" label="Art der Differenz" />
<NumberField source="old_sport.id" label="Alte ID" />
<DateField source="old_sport.last_used" label="Zuletzt aktiviert" />
<TextField source="old_sport.name" label="Alter Sport" />
<TextField source="new_sport.name" label="Neuer Sport" />
<TextField
source="id"
sortable={false}
/>
<TextField
source="kind_of_diff"
label="Art der Differenz"
sortable={false}
/>
<NumberField
source="old_sport.id"
label="Alte ID"
sortable={false}
/>
<DateField
source="old_sport.last_used"
label="Zuletzt aktiviert"
sortable={false}
/>
<TextField
source="old_sport.name"
label="Alter Sport"
sortable={false}
/>
<TextField
source="new_sport.name"
label="Neuer Sport"
sortable={false}
/>
</Datagrid>
</List>
);
\ No newline at end of file
......@@ -20,13 +20,23 @@ import Typography from '@material-ui/core/Typography'
import Card from '@material-ui/core/Card'
import CardContent from '@material-ui/core/CardContent'
import { criterionEditValidator } from './criterionEditValidator.js';
const AsideSportList = () => (
<div style={{ width: 400, margin: '1em' }}>
<div style={{
width: 400,
margin: '1em'
}}>
<Card>
<CardContent>
<Typography variant="h4" align="center">Tipps&Tricks</Typography>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>Klicke auf eine Datenreihe, um den Eintrag zu bearbeiten.</li>
......@@ -42,10 +52,18 @@ const AsideSportList = () => (
);
const AsideSportEdit = () => (
<div style={{ width: 400, margin: '1em' }}>
<div style={{
width: 400,
margin: '1em'
}}>
<Card>
<CardContent>
<Typography variant="h4" align="center">Tipps&Tricks</Typography>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>Der Slider "Wird aktuell angeboten" entscheidet, ob der Sport beim Quiz als Ergebnis auftreten kann.</li>
......@@ -77,7 +95,12 @@ const AsideSportCreate = () => (
<div style={{ width: 400, margin: '1em' }}>
<Card>
<CardContent>
<Typography variant="h4" align="center">Tipps&Tricks</Typography>
<Typography
variant="h4"
align="center"
>
Tipps&Tricks
</Typography>
<br />
<ul>
<li>Die Kriterien können ausgefüllt werden, nachdem der Sport erstellt wurde.
......@@ -91,12 +114,19 @@ const AsideSportCreate = () => (
export const SportList = props => (
<List {...props} aside={<AsideSportList />}>
<Datagrid rowClick="edit" style={{ tableLayout: "fixed", }}>
<Datagrid
rowClick="edit"
style={{
tableLayout: "fixed",
}}>
<TextField source="id" />
<TextField source="name" />
<UrlField source="url" />
<BooleanField source="is_filled" label="Kriterien ausgefüllt?" />
<BooleanField
source="is_filled"
label="Kriterien ausgefüllt?"
/>
</Datagrid>
</List>
);
......@@ -107,18 +137,52 @@ export const SportEdit = props => (
<Edit {...props} aside={<AsideSportEdit />}>
<SimpleForm>
<Typography variant="h4" fullWidth>Sport bearbeiten</Typography>
<TextInput disabled source="id" label="ID" />
<TextInput source="name" fullWidth required />
<TextInput source="url" label="URL" fullWidth required />
<BooleanInput source="currently_active" label="Wird aktuell angeboten" />
<DateInput disabled source="last_used" label="Zuletzt genutzt" />
<Typography variant="h5" align="left">Kriterien</Typography>
<Typography
variant="h4"
fullWidth
>
Sport bearbeiten
</Typography>
<TextInput
disabled source="id"
label="ID"
/>
<TextInput
source="name"
fullWidth
required
/>
<TextInput
source="url"
label="URL"
fullWidth
required
/>
<BooleanInput
source="currently_active"
label="Wird aktuell angeboten"
/>
<DateInput
disabled
source="last_used"
label="Zuletzt genutzt"
/>
<ArrayInput source="criteria" label="">
<SimpleFormIterator disableAdd disableRemove> {/* Used because you cannot know how many objects will be sent */}
<Typography
variant="h5"
align="left"
>
Kriterien
</Typography>
<ArrayInput
source="criteria"
label=""
>
<SimpleFormIterator
disableAdd
disableRemove
> {/* Used because you cannot know how many objects will be sent */}
{/* Documentation is wrong, look here! https://github.com/marmelab/react-admin/issues/2500
getSource() needs to get called without arguments before the return,
......@@ -151,9 +215,22 @@ export const SportEdit = props => (
export const SportCreate = props => (
<Create {...props} aside={<AsideSportCreate />}>
<SimpleForm>
<Typography variant="h4" align="left">Sport erstellen</Typography>
<TextInput source="name" fullWidth required />
<TextInput source="url" fullWidth required />
<Typography
variant="h4"
align="left"
>
Sport erstellen
</Typography>
<TextInput
source="name"
fullWidth
required
/>
<TextInput
source="url"
fullWidth
required
/>
</SimpleForm>
</Create>
);
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment