Compare commits

...

8 Commits

6 changed files with 213 additions and 0 deletions

View File

@ -49,3 +49,19 @@ create policy select_answer
for select
to candymat_anonymous, candymat_person -- maybe change to candymat_person only in the future
using (true);
drop table if exists candymat_data.user_app_info;
create table candymat_data.user_app_info
(
row_id character varying(50) primary key,
title character varying(300) NOT NULL,
content character varying(15000)
);
grant select on table candymat_data.user_app_info to candymat_anonymous, candymat_person;
grant insert, update, delete on table candymat_data.user_app_info to candymat_editor;
delete from candymat_data.user_app_info where row_id = 'about_page';
delete from candymat_data.user_app_info where row_id = 'legal_page';
insert into candymat_data.user_app_info (row_id, title, content) values
('about_page', 'About Candymat', '<h1>Wer steckt eigentlich hinter dem Kandimat?</h1><p>Der Kandimat wurde von den ehrenamtlichen Mitgliedern des Netzbegrünung e.V. entwickelt. Eure Geschäftsstelle und die Kandidat*innen haben die redaktionelle Arbeit für die Inhalte des Kandimats geleistet.</p>');
insert into candymat_data.user_app_info (row_id, title, content) values
('legal_page', 'Legal Candymat', '<h1>Impressum</h1><p>Impressum Infos</p>');

View File

@ -0,0 +1,25 @@
import { gql } from "@apollo/client";
import { GetAllPageInfoResponse } from "../queries/page_info";
export const EDIT_INFOS = gql`
mutation UpdateInfos(
$id: ID!
$title: String
$content: String
$rowId: String
) {
updateUserAppInfo(
input: {
id: $id
userAppInfoPatch: { content: $content, rowId: $rowId, title: $title }
clientMutationId: ""
}
) {
clientMutationId
}
}
`;
export interface EditInfosResponse {
updateUserAppInfo: GetAllPageInfoResponse;
}

View File

@ -0,0 +1,28 @@
import { gql } from "@apollo/client";
export const GET_ALL_PAGE_INFO = gql`
query AllInfos {
allUserAppInfos {
nodes {
id
rowId
title
content
}
}
}
`;
export interface PageInfo {
id: string;
rowId: string;
title: string;
content: string;
}
export interface GetAllPageInfoResponse {
allUserAppInfos: {
nodes: Array<PageInfo>;
__typename: "UserAppInfosConnection";
};
}

View File

@ -0,0 +1,59 @@
import React from "react";
import { Paper, Typography } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import { useQuery } from "@apollo/client";
import {
GET_ALL_PAGE_INFO,
GetAllPageInfoResponse,
PageInfo,
} from "../backend/queries/page_info";
import { EditInformationField } from "./EditInformationField";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
padding: theme.spacing(1),
marginBottom: theme.spacing(3),
},
textArea: {
width: "85%",
height: "150px",
padding: theme.spacing(1),
marginBottom: theme.spacing(3),
marginTop: theme.spacing(1),
resize: "none",
overflow: "auto",
},
}));
interface EditInformationProps {
loggedInPersonRowId: number;
}
export function EditInformation(
props: EditInformationProps
): React.ReactElement {
const infos =
useQuery<GetAllPageInfoResponse, null>(GET_ALL_PAGE_INFO).data
?.allUserAppInfos.nodes || [];
const classes = useStyles();
return (
<React.Fragment>
<Typography component={"h2"} variant="h6" gutterBottom>
Bearbeite hier die Webseiten Info-Texte für deinen Candymat:
</Typography>
<Paper className={classes.root}>
{infos.map((info: PageInfo) => {
return (
<EditInformationField
loggedInPersonRowId={props.loggedInPersonRowId}
key={info.id}
info={info}
/>
);
})}
</Paper>
</React.Fragment>
);
}

View File

@ -0,0 +1,74 @@
import React, { useState } from "react";
import { IconButton, Typography } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import { useMutation } from "@apollo/client";
import { Save } from "@material-ui/icons";
import { EDIT_INFOS, EditInfosResponse } from "../backend/mutations/page_info";
import { PageInfo } from "../backend/queries/page_info";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
padding: theme.spacing(1),
marginBottom: theme.spacing(3),
},
textArea: {
width: "85%",
height: "150px",
padding: theme.spacing(1),
marginBottom: theme.spacing(3),
marginTop: theme.spacing(1),
resize: "none",
overflow: "auto",
},
}));
interface EditInformationFieldProps {
info: PageInfo;
loggedInPersonRowId: number;
}
export function EditInformationField(
props: EditInformationFieldProps
): React.ReactElement {
const [info, setInfo] = useState(props.info);
const [edit, { loading, error }] = useMutation<EditInfosResponse>(EDIT_INFOS);
const classes = useStyles();
if (loading) console.log("Loading");
if (error) return <p>An error occurred</p>;
function changeInfo(e: React.ChangeEvent<HTMLTextAreaElement>) {
const changeInfoText = e.target.value;
setInfo({ ...info, content: changeInfoText });
}
return (
<div>
<Typography component={"h2"} variant="h6" color="primary" gutterBottom>
{info.title}
</Typography>
<form>
<textarea
className={classes.textArea}
onChange={changeInfo}
value={info.content}
>
{/*Hier kommt der Inhalt aus der Datenbank*/}
</textarea>
<IconButton
onClick={() =>
edit({
variables: {
id: info.id,
content: info.content,
},
})
}
>
<Save />
</IconButton>
</form>
</div>
);
}

View File

@ -10,6 +10,8 @@ import PeopleIcon from "@material-ui/icons/People";
import { MenuOption } from "./MainMenu";
import { PersonRoutes } from "./Main";
import { UserManagement } from "./UserManagement";
import { InfoRounded } from "@material-ui/icons";
import { EditInformation } from "./EditInformation";
const useStyles = makeStyles((theme) => ({
container: {
@ -22,6 +24,7 @@ const useStyles = makeStyles((theme) => ({
interface EditorRoutes extends PersonRoutes {
question: MenuOption;
userManagement: MenuOption;
editInformation: MenuOption;
}
export const editorRoutes: EditorRoutes = {
@ -35,6 +38,11 @@ export const editorRoutes: EditorRoutes = {
path: "/benutzer",
icon: <PeopleIcon />,
},
editInformation: {
title: "Infos bearbeiten",
path: "/edit",
icon: <InfoRounded />,
},
};
interface HomePageEditorProps {
@ -54,6 +62,9 @@ export function HomePageEditor(props: HomePageEditorProps): React.ReactElement {
<Route path={editorRoutes.userManagement.path}>
<UserManagement loggedInPersonRowId={props.loggedInUserRowId} />
</Route>
<Route path={editorRoutes.editInformation.path}>
<EditInformation loggedInPersonRowId={props.loggedInUserRowId} />
</Route>
</Switch>
<Copyright />
</Container>