...
 
Commits (3)
......@@ -2,8 +2,6 @@
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
......@@ -12,35 +10,4 @@
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
\ No newline at end of file
# Subscriber App
> This web application by fortiss GmbH is a reference client for the Application Subscriber API of the FIT-Connect PoC. It has been developed as a minimal viable product (MVP) to showcase the main idea of the FIT-Connect PoC from the point of view of a government agency portal.
>
> The Subscriber App is complemented by the Sender App, which demonstrates the FIT-Connect PoC from the point of view of a citizen portal:
> https://git.fortiss.org/fit-connect/sender-app
Diese Web-Anwendung von fortiss GmbH ist ein Referenz-Client für die Application Subscriber API des FIT-Connect PoC. Sie wurde als Minimal Viable Product (MVP) entwickelt, um die Grundidee des FIT-Connect PoC aus Sicht eines Behördenportals zu demonstrieren.
Die Subscriber App wird durch die Sender App ergänzt, welche den FIT-Connect PoC aus Sicht eines BürgerInnen-Portals demonstriert:
https://git.fortiss.org/fit-connect/sender-app
**_ ENGLISH VERSION _**
This web application by fortiss GmbH is a reference client for the Application Subscriber API of the FIT-Connect PoC. It has been developed as a minimal viable product (MVP) to showcase the main idea of the FIT-Connect PoC from the point of view of a government agency portal.
The Subscriber App is complemented by the Sender App, which demonstrates the FIT-Connect PoC from the point of view of a citizen portal:
https://git.fortiss.org/fit-connect/sender-app
## FIT-Connect PoC
FIT-Connect wird als PoC von [FITKO](https://www.fitko.de/) bereitgestellt. Das Ziel von FIT-Connect ist eine einfache, schnelle Integration von Anwendungen zu OZG-Verfahren in medienbruchfreie Antragsprozesse.
......@@ -75,4 +77,24 @@ Diese Anwendung wurde von Micha Lutz bei fortiss GmbH entwickelt. Kontakt: Peter
## Lizenz
??
This project is licensed under the terms of the MIT license.
Copyright (c) 2020 fortiss GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
......@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@material-ui/core": "^4.10.2",
"@material-ui/icons": "^4.9.1",
"axios": "^0.19.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
......
import React, { useState, useEffect } from "react";
import {
AppBar,
Button,
Card,
CardContent,
......@@ -9,7 +10,13 @@ import {
Dialog,
DialogTitle,
Grid,
IconButton,
Menu,
MenuItem,
Toolbar,
Typography,
} from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu";
import {
getAccessToken,
getDestinations,
......@@ -18,49 +25,64 @@ import {
getApplicationData,
acknowledgeApplication,
} from "./requests";
import { useStyles } from "./styles";
const App = () => {
// load styles
const classes = useStyles();
// state variables and hook functions
const [status, setStatus] = useState("init");
const [accessToken, setAccessToken] = useState("");
const [destination, setDestination] = useState({ organizationName: "", destinationId: "" });
const [applications, setApplications] = useState([]);
const [dialogContent, setDialogContent] = useState({ open: false, title: "", content: "" });
const [menuAnchorEl, setMenuAnchorEl] = useState(null);
// call initalAPICalls() after page has rendered initially
// execute this callback once page has rendered initially
useEffect(() => {
initalAPICalls();
}, []);
// function to request the initially required data from Subscriber API
const initalAPICalls = async () => {
// get the access token
setStatus("getAccessToken");
const newAccessToken = await getAccessToken();
if (newAccessToken === "error") {
setStatus("error");
return;
}
// update access token state var
setAccessToken(newAccessToken);
getAccessToken().then((newAccessToken) => {
if (newAccessToken === "error") {
setStatus("error");
return;
}
// update access token state var
setAccessToken(newAccessToken);
});
}, []);
// get a destination
setStatus("getDestinations");
const newDestination = await getDestinations(newAccessToken);
if (newDestination === "error") {
setStatus("error");
return;
// execute this callback once accessToken is set
useEffect(() => {
if (accessToken) {
// get a destination
setStatus("getDestinations");
getDestinations(accessToken).then((newDestination) => {
if (newDestination === "error") {
setStatus("error");
return;
}
if (newDestination) {
// if a destination was found, udpate access token state var with this destination
setDestination(newDestination);
} else {
// else, create new destination and update access token state var with new destination
createNewDestination(accessToken).then((newDestination) => {
setDestination(newDestination);
});
}
setStatus("ready");
});
}
if (newDestination) {
// if a destination was found, udpate access token state var with this destination
setDestination(newDestination);
} else {
// else, create new destination and update access token state var with new destination
const newDestination = await createNewDestination(newAccessToken);
setDestination(newDestination);
}, [accessToken]);
// execute this callback once destination is set
useEffect(() => {
if (destination.destinationId) {
handleGetApplications();
}
setStatus("ready");
};
}, [destination]);
// handle function to request all applications from SubscriberAPI and save them in state variable
const handleGetApplications = async () => {
......@@ -108,8 +130,8 @@ const App = () => {
.join("; ");
// TODO: allow displaying of all docs, not just the first one
return (
<Card>
<CardContent>
<Card className={classes.card}>
<CardContent align="left">
Service: {application.publicServiceType.name}
<br />
LeikaId: {application.publicServiceType.leikaId}
......@@ -130,59 +152,92 @@ const App = () => {
// finally return our JSX
return (
<Container maxWidth="sm" style={{ fontFamily: "Roboto, Helvetica, Arial, sans-serif" }}>
<Grid container style={{ marginBottom: "30px" }}>
{destination.destinationId && (
<header style={{ width: "100%" }}>
<h1>{destination.organizationName}</h1>
<Grid container alignItems="center" style={{ color: "gray", fontSize: "0.8em" }}>
<Grid item xs={3}>
My Destination ID:
</Grid>
<Grid item xs={9}>
{destination.destinationId}
</Grid>
</Grid>
</header>
)}
</Grid>
<div className={classes.root}>
<AppBar position="static" color="primary">
<Toolbar variant="dense">
<IconButton
edge="start"
onClick={(event) => setMenuAnchorEl(event.currentTarget.parentNode)}
className={classes.menuButton}
color="inherit"
aria-label="menu"
>
<MenuIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={menuAnchorEl}
getContentAnchorEl={null}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
transformOrigin={{ vertical: "top", horizontal: "right" }}
keepMounted
open={Boolean(menuAnchorEl)}
onClose={() => setMenuAnchorEl(null)}
className={classes.menu}
>
<MenuItem onClick={() => (window.location.href = "/sender-app")}>Sender App</MenuItem>
<MenuItem onClick={() => (window.location.href = "/subscriber-app")}>Subscriber App</MenuItem>
</Menu>
<Typography variant="h6" className={classes.title}>
Subscriber App
</Typography>
</Toolbar>
</AppBar>
{status !== "ready" && (
<Grid container align="center">
<Grid item xs={12}>
<CircularProgress />
</Grid>
<Grid item xs={12}>
{status === "init" && <span>loading page</span>}
{status === "getAccessToken" && <span>requesting access token</span>}
{status === "getDestinations" && <span>requesting destination</span>}
{status === "createNewDestination" && <span>no destination found, creating new destination</span>}
{status === "getApplications" && <span>requesting applications</span>}
{status === "error" && <span>an error occured, see console for details</span>}
</Grid>
<Container maxWidth="md">
<Grid container className={classes.formRow}>
{destination.destinationId && (
<header className={classes.formInput}>
<h1>{destination.organizationName}</h1>
<Grid container alignItems="center">
<Grid item xs={3}>
<Typography className={classes.destinationLabel}>My Destination ID:</Typography>
</Grid>
<Grid item xs={9}>
<Typography className={classes.destinationLabel}>{destination.destinationId}</Typography>
</Grid>
</Grid>
</header>
)}
</Grid>
)}
<Grid container align="center">
{status === "ready" && (
<Grid item xs={12}>
<Button onClick={handleGetApplications} style={{ marginBottom: "20px" }}>
Update Applications
</Button>
{status !== "ready" && (
<Grid container align="center">
<Grid item xs={12}>
<CircularProgress />
</Grid>
<Grid item xs={12}>
{status === "init" && <span>loading page</span>}
{status === "getAccessToken" && <span>requesting access token</span>}
{status === "getDestinations" && <span>requesting destination</span>}
{status === "createNewDestination" && <span>no destination found, creating new destination</span>}
{status === "getApplications" && <span>requesting applications</span>}
{status === "error" && <span>an error occured, see console for details</span>}
</Grid>
</Grid>
)}
{status === "ready" &&
applications.length > 0 &&
applications.map((application) => (
<ApplicationCard application={application} key={application.applicationId} />
))}
</Grid>
<Dialog open={dialogContent.open} onClose={() => setDialogContent({ open: false, title: "", content: "" })}>
<DialogTitle>{dialogContent.title}</DialogTitle>
<div style={{ margin: "10px" }}>{dialogContent.content}</div>
</Dialog>
</Container>
<Grid container align="center" justify="flex-start" alignItems="flex-start">
{status === "ready" && (
<Grid item xs={12}>
<Button onClick={handleGetApplications} className={classes.formRow}>
Update Applications
</Button>
</Grid>
)}
{status === "ready" &&
applications.length > 0 &&
applications.map((application) => (
<ApplicationCard application={application} key={application.applicationId} />
))}
</Grid>
<Dialog open={dialogContent.open} onClose={() => setDialogContent({ open: false, title: "", content: "" })}>
<DialogTitle>{dialogContent.title}</DialogTitle>
<div className={classes.dialogContent}>{dialogContent.content}</div>
</Dialog>
</Container>
</div>
);
};
......
import { makeStyles } from "@material-ui/core/styles";
// define styles
export const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
fontFamily: "Roboto, Helvetica, Arial, sans-serif",
},
menu: {
marginLeft: -8,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
formRow: {
width: "100%",
marginBottom: 20,
},
formInput: {
width: "100%",
marginBottom: 10,
},
destinationLabel: {
color: "gray",
fontSize: "0.8em",
},
card: {
margin: 10,
},
dialogContent: {
margin: 10,
},
}));