From 96003c21ddebe11f483cef35cee6e992b53fe7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rich=C3=A1rd=20Kunkli?= Date: Sat, 24 Oct 2020 17:00:44 +0200 Subject: [PATCH] Added Navbar --- Birdmap/Birdmap.csproj | 8 ++ Birdmap/ClientApp/package-lock.json | 12 +-- Birdmap/ClientApp/src/App.tsx | 95 +++++++++++++++---- .../ClientApp/src/common/ServiceBase.js.map | 2 +- Birdmap/ClientApp/src/common/ServiceBase.ts | 14 +-- .../src/common/components/BirdmapTitle.tsx | 59 ++++++++++++ Birdmap/ClientApp/src/components/Counter.tsx | 35 ------- .../ClientApp/src/components/FetchData.tsx | 84 ---------------- Birdmap/ClientApp/src/components/Home.tsx | 23 ----- Birdmap/ClientApp/src/components/Layout.tsx | 12 --- Birdmap/ClientApp/src/components/NavMenu.css | 13 --- Birdmap/ClientApp/src/components/NavMenu.tsx | 42 -------- .../ClientApp/src/components/auth/Auth.tsx | 30 ++++-- .../src/components/auth/AuthService.js | 2 +- .../src/components/auth/AuthService.js.map | 2 +- .../src/components/auth/AuthService.ts | 4 +- Birdmap/ClientApp/src/custom.css | 14 --- Birdmap/ClientApp/src/store/Counter.ts | 48 ---------- .../ClientApp/src/store/WeatherForecasts.ts | 91 ------------------ Birdmap/ClientApp/src/store/configureStore.js | 39 ++++++++ .../ClientApp/src/store/configureStore.js.map | 1 + Birdmap/ClientApp/src/store/index.js | 8 ++ Birdmap/ClientApp/src/store/index.js.map | 1 + Birdmap/ClientApp/src/store/index.ts | 7 +- 24 files changed, 230 insertions(+), 416 deletions(-) create mode 100644 Birdmap/ClientApp/src/common/components/BirdmapTitle.tsx delete mode 100644 Birdmap/ClientApp/src/components/Counter.tsx delete mode 100644 Birdmap/ClientApp/src/components/FetchData.tsx delete mode 100644 Birdmap/ClientApp/src/components/Home.tsx delete mode 100644 Birdmap/ClientApp/src/components/Layout.tsx delete mode 100644 Birdmap/ClientApp/src/components/NavMenu.css delete mode 100644 Birdmap/ClientApp/src/components/NavMenu.tsx delete mode 100644 Birdmap/ClientApp/src/custom.css delete mode 100644 Birdmap/ClientApp/src/store/Counter.ts delete mode 100644 Birdmap/ClientApp/src/store/WeatherForecasts.ts create mode 100644 Birdmap/ClientApp/src/store/configureStore.js create mode 100644 Birdmap/ClientApp/src/store/configureStore.js.map create mode 100644 Birdmap/ClientApp/src/store/index.js create mode 100644 Birdmap/ClientApp/src/store/index.js.map diff --git a/Birdmap/Birdmap.csproj b/Birdmap/Birdmap.csproj index 6754175..fe937b2 100644 --- a/Birdmap/Birdmap.csproj +++ b/Birdmap/Birdmap.csproj @@ -26,6 +26,7 @@ + @@ -34,6 +35,13 @@ + + + + + + + diff --git a/Birdmap/ClientApp/package-lock.json b/Birdmap/ClientApp/package-lock.json index 727f47b..c1a4c06 100644 --- a/Birdmap/ClientApp/package-lock.json +++ b/Birdmap/ClientApp/package-lock.json @@ -1444,9 +1444,9 @@ }, "dependencies": { "csstype": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", - "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", + "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" }, "dom-helpers": { "version": "5.2.0", @@ -7933,9 +7933,9 @@ }, "dependencies": { "csstype": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", - "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", + "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" } } }, diff --git a/Birdmap/ClientApp/src/App.tsx b/Birdmap/ClientApp/src/App.tsx index 2d9441a..4222906 100644 --- a/Birdmap/ClientApp/src/App.tsx +++ b/Birdmap/ClientApp/src/App.tsx @@ -1,45 +1,63 @@ import { Box, Container } from '@material-ui/core'; import AppBar from '@material-ui/core/AppBar'; import blue from '@material-ui/core/colors/blue'; -import { createMuiTheme } from '@material-ui/core/styles'; +import orange from '@material-ui/core/colors/orange'; +import { createMuiTheme, createStyles, makeStyles, Theme } from '@material-ui/core/styles'; import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; import { ThemeProvider } from '@material-ui/styles'; import React, { useState } from 'react'; import { BrowserRouter, NavLink, Redirect, Route, Switch } from 'react-router-dom'; +import BirdmapTitle from './common/components/BirdmapTitle'; import Auth from './components/auth/Auth'; import AuthService from './components/auth/AuthService'; -import Home from './components/Home'; -import './custom.css'; const theme = createMuiTheme({ palette: { primary: { - main: blue[800] + main: blue[900], }, + secondary: { + main: orange[200], + } }, }); function App() { + const [authenticated, setAuthenticated] = useState(AuthService.isAuthenticated()); const onAuthenticated = () => { setAuthenticated(AuthService.isAuthenticated()); }; - const LoginComponent = () => { + const AuthComponent = () => { return ( ); } + const DashboardComponent = () => { + return Dashboard; + }; + + const DevicesComponent = () => { + return Devices; + + }; + const HeatmapComponent = () => { + return Heatmap; + }; + return ( - - + + + + @@ -51,7 +69,7 @@ export default App; const PublicRoute = ({ component: Component, ...rest }: { [x: string]: any, component: any}) => { return ( ( - + )} /> ); } @@ -60,28 +78,32 @@ const PrivateRoute = ({ component: Component, authenticated: Authenticated, ...r return ( ( Authenticated - ? + ? : )} /> ); }; -const NoLayout = ({ component: Component, ...rest }: { [x: string]: any, component: any }) => { - return ( - - ); -}; +const DefaultLayout = ({ component: Component, authenticated: Authenticated, ...rest }: { [x: string]: any, component: any, authenticated: any }) => { + const classes = useDefaultLayoutStyles(); + + const renderNavLinks = () => { + return Authenticated + ? + Dashboard + Devices + Heatmap + + : null; + }; -const DefaultLayout = ({ component: Component, ...rest }: { [x: string]: any, component: any }) => { return ( - - - Dashboard - Login - + + + {renderNavLinks()} @@ -90,4 +112,35 @@ const DefaultLayout = ({ component: Component, ...rest }: { [x: string]: any, co ); -}; \ No newline at end of file +}; + +const useDefaultLayoutStyles = makeStyles((theme: Theme) => + createStyles({ + typo: { + marginLeft: 'auto', + color: 'white', + }, + nav_menu: { + display: 'flex', + flexDirection: 'row', + }, + nav_menu_item: { + textDecoration: 'none', + fontWeight: 'normal', + color: 'inherit', + marginLeft: '24px', + '&:hover': { + color: 'inherit', + } + }, + nav_menu_item_active: { + textDecoration: 'underline', + fontWeight: 'bold', + color: 'inherit', + marginLeft: '24px', + '&:hover': { + color: 'inherit', + } + }, + }), +); \ No newline at end of file diff --git a/Birdmap/ClientApp/src/common/ServiceBase.js.map b/Birdmap/ClientApp/src/common/ServiceBase.js.map index 8ae2c0e..ea31fe9 100644 --- a/Birdmap/ClientApp/src/common/ServiceBase.js.map +++ b/Birdmap/ClientApp/src/common/ServiceBase.js.map @@ -1 +1 @@ -{"version":3,"file":"ServiceBase.js","sourceRoot":"","sources":["ServiceBase.ts"],"names":[],"mappings":";;AAAA,qDAAgD;AAEhD,SAAS,GAAG,CAAC,GAAG;IACZ,IAAI,OAAO,GAAG;QACV,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACL,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;SAClD;KACJ,CAAC;IAEF,OAAO,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,IAAI,CAAC,GAAG,EAAE,OAAO;IACtB,IAAI,OAAO,GAAG;QACV,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACL,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;SAClD;QACD,IAAI,EAAE,EAAE;KACX,CAAC;IAEF,IAAI,OAAO;QACP,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAE3C,OAAO,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,WAAW,CAAC,GAAG,EAAE,OAAO;IAC7B,OAAO,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC;SACrB,IAAI,CAAC,qBAAqB,CAAC;SAC3B,KAAK,CAAC,YAAY,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAQ;IACnC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACZ,OAAO,QAAQ,CAAC,IAAI,EAAE;aACjB,IAAI,CAAC,UAAA,IAAI,IAAI,OAAA,YAAY,CAAC,IAAI,CAAC,EAAlB,CAAkB,CAAC,CAAC;IAE1C,OAAO,QAAQ,CAAC,IAAI,EAAE;SACjB,IAAI,CAAC,UAAA,IAAI,IAAI,OAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAnC,CAAmC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,YAAY,CAAC,QAAQ;IAC1B,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEtB,IAAI,QAAQ,IAAI,QAAQ,CAAC,KAAK;QAC1B,yBAAe,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE/C,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,kBAAe;IACX,GAAG,KAAA;IACH,IAAI,MAAA;IACJ,WAAW,aAAA;CACd,CAAC"} \ No newline at end of file +{"version":3,"file":"ServiceBase.js","sourceRoot":"","sources":["ServiceBase.ts"],"names":[],"mappings":";;AAAA,qDAAgD;AAEhD,SAAS,GAAG,CAAC,GAAW;IACpB,IAAI,OAAO,GAAG;QACV,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACL,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;SAClD;KACJ,CAAC;IAEF,OAAO,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,IAAI,CAAC,GAAW,EAAE,OAAY;IACnC,IAAI,OAAO,GAAG;QACV,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACL,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;SAClD;QACD,IAAI,EAAE,EAAE;KACX,CAAC;IAEF,IAAI,OAAO;QACP,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAE3C,OAAO,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,WAAW,CAAC,GAAW,EAAE,OAAY;IAC1C,OAAO,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC;SACrB,IAAI,CAAC,qBAAqB,CAAC;SAC3B,KAAK,CAAC,YAAY,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAa;IACxC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACZ,OAAO,QAAQ,CAAC,IAAI,EAAE;aACjB,IAAI,CAAC,UAAC,IAAS,IAAK,OAAA,YAAY,CAAC,IAAI,CAAC,EAAlB,CAAkB,CAAC,CAAC;IAEjD,OAAO,QAAQ,CAAC,IAAI,EAAE;SACjB,IAAI,CAAC,UAAC,IAAS,IAAK,OAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAnC,CAAmC,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,YAAY,CAAC,QAAa;IAC/B,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEtB,IAAI,QAAQ,IAAI,QAAQ,CAAC,KAAK;QAC1B,yBAAe,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE/C,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,kBAAe;IACX,GAAG,KAAA;IACH,IAAI,MAAA;IACJ,WAAW,aAAA;CACd,CAAC"} \ No newline at end of file diff --git a/Birdmap/ClientApp/src/common/ServiceBase.ts b/Birdmap/ClientApp/src/common/ServiceBase.ts index df29612..7dddd1f 100644 --- a/Birdmap/ClientApp/src/common/ServiceBase.ts +++ b/Birdmap/ClientApp/src/common/ServiceBase.ts @@ -1,6 +1,6 @@ import ErrorDispatcher from './ErrorDispatcher'; -function get(url) { +function get(url: string) { let options = { method: 'GET', headers: { @@ -12,7 +12,7 @@ function get(url) { return makeRequest(url, options); } -function post(url, request) { +function post(url: string, request: any) { let options = { method: 'POST', headers: { @@ -28,22 +28,22 @@ function post(url, request) { return makeRequest(url, options); } -function makeRequest(url, options) { +function makeRequest(url: string, options: any) { return fetch(url, options) .then(ensureResponseSuccess) .catch(errorHandler); } -function ensureResponseSuccess(response) { +function ensureResponseSuccess(response: any) { if (!response.ok) return response.json() - .then(data => errorHandler(data)); + .then((data: any) => errorHandler(data)); return response.text() - .then(text => text.length ? JSON.parse(text) : {}); + .then((text: any) => text.length ? JSON.parse(text) : {}); } -function errorHandler(response) { +function errorHandler(response: any) { console.log(response); if (response && response.Error) diff --git a/Birdmap/ClientApp/src/common/components/BirdmapTitle.tsx b/Birdmap/ClientApp/src/common/components/BirdmapTitle.tsx new file mode 100644 index 0000000..f5259bd --- /dev/null +++ b/Birdmap/ClientApp/src/common/components/BirdmapTitle.tsx @@ -0,0 +1,59 @@ +import { Box, Typography } from '@material-ui/core'; +import { BrowserRouter, NavLink, Redirect, Route, Switch } from 'react-router-dom'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import React from 'react'; + +export default function BirdmapTitle(props: any) { + const classes = useStyles(); + + return ( + + + + Bird + + + + + map + + + + ); +}; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'inline', + }, + bird: { + textAlign: "left", + fontWeight: 1000, + fontSize: 30, + textShadow: '3px 3px 0px rgba(0,0,0,0.2)', + }, + map: { + textAlign: "left", + fontWeight: 100, + fontSize: 26, + textShadow: '2px 2px 0px rgba(0,0,0,0.2)', + }, + nav_menu_item: { + textDecoration: 'none', + color: 'white', + '&:hover': { + textDecoration: 'underline', + color: 'white', + } + }, + nav_menu_item_active: { + textDecoration: 'none', + color: 'white', + '&:hover': { + textDecoration: 'underline', + color: 'white', + } + }, + }), +); \ No newline at end of file diff --git a/Birdmap/ClientApp/src/components/Counter.tsx b/Birdmap/ClientApp/src/components/Counter.tsx deleted file mode 100644 index 82fde95..0000000 --- a/Birdmap/ClientApp/src/components/Counter.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import * as React from 'react'; -import { connect } from 'react-redux'; -import { RouteComponentProps } from 'react-router'; -import { ApplicationState } from '../store'; -import * as CounterStore from '../store/Counter'; - -type CounterProps = - CounterStore.CounterState & - typeof CounterStore.actionCreators & - RouteComponentProps<{}>; - -class Counter extends React.PureComponent { - public render() { - return ( - -

Counter

- -

This is a simple example of a React component.

- -

Current count: {this.props.count}

- - -
- ); - } -}; - -export default connect( - (state: ApplicationState) => state.counter, - CounterStore.actionCreators -)(Counter); diff --git a/Birdmap/ClientApp/src/components/FetchData.tsx b/Birdmap/ClientApp/src/components/FetchData.tsx deleted file mode 100644 index 9fed830..0000000 --- a/Birdmap/ClientApp/src/components/FetchData.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import * as React from 'react'; -import { connect } from 'react-redux'; -import { RouteComponentProps } from 'react-router'; -import { Link } from 'react-router-dom'; -import { ApplicationState } from '../store'; -import * as WeatherForecastsStore from '../store/WeatherForecasts'; - -// At runtime, Redux will merge together... -type WeatherForecastProps = - WeatherForecastsStore.WeatherForecastsState // ... state we've requested from the Redux store - & typeof WeatherForecastsStore.actionCreators // ... plus action creators we've requested - & RouteComponentProps<{ startDateIndex: string }>; // ... plus incoming routing parameters - - -class FetchData extends React.PureComponent { - // This method is called when the component is first added to the document - public componentDidMount() { - this.ensureDataFetched(); - } - - // This method is called when the route parameters change - public componentDidUpdate() { - this.ensureDataFetched(); - } - - public render() { - return ( - -

Weather forecast

-

This component demonstrates fetching data from the server and working with URL parameters.

- {this.renderForecastsTable()} - {this.renderPagination()} -
- ); - } - - private ensureDataFetched() { - const startDateIndex = parseInt(this.props.match.params.startDateIndex, 10) || 0; - this.props.requestWeatherForecasts(startDateIndex); - } - - private renderForecastsTable() { - return ( - - - - - - - - - - - {this.props.forecasts.map((forecast: WeatherForecastsStore.WeatherForecast) => - - - - - - - )} - -
DateTemp. (C)Temp. (F)Summary
{forecast.date}{forecast.temperatureC}{forecast.temperatureF}{forecast.summary}
- ); - } - - private renderPagination() { - const prevStartDateIndex = (this.props.startDateIndex || 0) - 5; - const nextStartDateIndex = (this.props.startDateIndex || 0) + 5; - - return ( -
- Previous - {this.props.isLoading && Loading...} - Next -
- ); - } -} - -export default connect( - (state: ApplicationState) => state.weatherForecasts, // Selects which state properties are merged into the component's props - WeatherForecastsStore.actionCreators // Selects which action creators are merged into the component's props -)(FetchData as any); diff --git a/Birdmap/ClientApp/src/components/Home.tsx b/Birdmap/ClientApp/src/components/Home.tsx deleted file mode 100644 index 5f7d5ff..0000000 --- a/Birdmap/ClientApp/src/components/Home.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from 'react'; -import { connect } from 'react-redux'; - -const Home = () => ( -
-

Hello, world!

-

Welcome to your new single-page application, built with:

- -

To help you get started, we've also set up:

-
    -
  • Client-side navigation. For example, click Counter then Back to return here.
  • -
  • Development server integration. In development mode, the development server from create-react-app runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
  • -
  • Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration produces minified, efficiently bundled JavaScript files.
  • -
-

The ClientApp subdirectory is a standard React application based on the create-react-app template. If you open a command prompt in that directory, you can run npm commands such as npm test or npm install.

-
-); - -export default connect()(Home); diff --git a/Birdmap/ClientApp/src/components/Layout.tsx b/Birdmap/ClientApp/src/components/Layout.tsx deleted file mode 100644 index 80ddb46..0000000 --- a/Birdmap/ClientApp/src/components/Layout.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import * as React from 'react'; -import { Container } from 'reactstrap'; -import NavMenu from './NavMenu'; - -export default (props: { children?: React.ReactNode }) => ( - - - - {props.children} - - -); diff --git a/Birdmap/ClientApp/src/components/NavMenu.css b/Birdmap/ClientApp/src/components/NavMenu.css deleted file mode 100644 index 4abcb33..0000000 --- a/Birdmap/ClientApp/src/components/NavMenu.css +++ /dev/null @@ -1,13 +0,0 @@ -a.navbar-brand { - white-space: normal; - text-align: center; - word-break: break-all; -} - -html { font-size: 14px; } - -@media (min-width: 768px) { - html { font-size: 16px; } -} - -.box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); } diff --git a/Birdmap/ClientApp/src/components/NavMenu.tsx b/Birdmap/ClientApp/src/components/NavMenu.tsx deleted file mode 100644 index 6d27452..0000000 --- a/Birdmap/ClientApp/src/components/NavMenu.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import * as React from 'react'; -import { Collapse, Container, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap'; -import { Link } from 'react-router-dom'; -import './NavMenu.css'; - -export default class NavMenu extends React.PureComponent<{}, { isOpen: boolean }> { - public state = { - isOpen: false - }; - - public render() { - return ( -
- - - Birdmap - - -
    - - Home - - - Counter - - - Fetch data - -
-
-
-
-
- ); - } - - private toggle = () => { - this.setState({ - isOpen: !this.state.isOpen - }); - } -} diff --git a/Birdmap/ClientApp/src/components/auth/Auth.tsx b/Birdmap/ClientApp/src/components/auth/Auth.tsx index 2136a1b..b73418f 100644 --- a/Birdmap/ClientApp/src/components/auth/Auth.tsx +++ b/Birdmap/ClientApp/src/components/auth/Auth.tsx @@ -1,18 +1,19 @@ import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { Box, Grid, TextField, Button, Typography, Paper } from '@material-ui/core'; +import { Box, Grid, TextField, Button, Typography, Paper, CircularProgress } from '@material-ui/core'; import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; import AuthService from './AuthService'; -export default function MenuItem(props: any) { +export default function Auth(props: any) { const history = useHistory(); const classes = useStyles(); - const [username, setUsername] = useState(null); - const [password, setPassword] = useState(null); + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); const [showError, setShowError] = useState(false); const [errorMessage, setErrorMessage] = useState(''); + const [isLoggingIn, setIsLoggingIn] = useState(false); const onUsernameChanged = (event: any) => { setUsername(event.target.value); @@ -35,6 +36,8 @@ export default function MenuItem(props: any) { }; const onLoginClicked = () => { + setIsLoggingIn(true); + if (!username) { setShowError(true); setErrorMessage('Username required'); @@ -49,13 +52,15 @@ export default function MenuItem(props: any) { return; } - AuthService.login(username, password).then(() => { + AuthService.login(username, password) + .then(() => { props.onAuthenticated(); history.push('/'); - }).catch(() => { setShowError(true); setErrorMessage('Invalid credentials'); + }).finally(() => { + setIsLoggingIn(false); }); }; @@ -65,9 +70,15 @@ export default function MenuItem(props: any) { : ; }; + const renderLoginButton = () => { + return isLoggingIn + ? + : + }; + return ( - + @@ -84,7 +95,7 @@ export default function MenuItem(props: any) { {renderErrorLabel()} - + {renderLoginButton()} @@ -108,9 +119,10 @@ const useStyles = makeStyles((theme: Theme) => alignItems: "center", }, paper: { + borderRadius: 15, }, button: { - width: '100%', + justifyContent: "center", }, error: { color: "red", diff --git a/Birdmap/ClientApp/src/components/auth/AuthService.js b/Birdmap/ClientApp/src/components/auth/AuthService.js index 386b927..713ba8a 100644 --- a/Birdmap/ClientApp/src/components/auth/AuthService.js +++ b/Birdmap/ClientApp/src/components/auth/AuthService.js @@ -1,7 +1,7 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var ServiceBase_1 = require("../../common/ServiceBase"); -var login_url = '/auth/authenticate'; +var login_url = 'api/auth/authenticate'; exports.default = { isAuthenticated: function () { return sessionStorage.getItem('user') !== null; diff --git a/Birdmap/ClientApp/src/components/auth/AuthService.js.map b/Birdmap/ClientApp/src/components/auth/AuthService.js.map index f9e2c3f..634c337 100644 --- a/Birdmap/ClientApp/src/components/auth/AuthService.js.map +++ b/Birdmap/ClientApp/src/components/auth/AuthService.js.map @@ -1 +1 @@ -{"version":3,"file":"AuthService.js","sourceRoot":"","sources":["AuthService.ts"],"names":[],"mappings":";;AAAA,wDAAmD;AAEnD,IAAM,SAAS,GAAG,oBAAoB,CAAC;AAEvC,kBAAe;IACX,eAAe;QACX,OAAO,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACnD,CAAC;IAED,KAAK,YAAC,QAAQ,EAAE,QAAQ;QACpB,IAAI,IAAI,GAAG;YACP,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,QAAQ;SACrB,CAAC;QACF,IAAI,OAAO,GAAG;YACV,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;aACrC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC7B,CAAC;QAEF,OAAO,qBAAW,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC;aAC7C,IAAI,CAAC,UAAA,QAAQ;YACV,cAAc,CAAC,OAAO,CAAC,MAAM,EAAK,QAAQ,CAAC,UAAU,SAAI,QAAQ,CAAC,YAAc,CAAC,CAAC;YAClF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;IACX,CAAC;CACJ,CAAA"} \ No newline at end of file +{"version":3,"file":"AuthService.js","sourceRoot":"","sources":["AuthService.ts"],"names":[],"mappings":";;AAAA,wDAAmD;AAEnD,IAAM,SAAS,GAAG,uBAAuB,CAAC;AAE1C,kBAAe;IACX,eAAe;QACX,OAAO,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACnD,CAAC;IAED,KAAK,EAAL,UAAM,QAAgB,EAAE,QAAgB;QACpC,IAAI,IAAI,GAAG;YACP,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,QAAQ;SACrB,CAAC;QACF,IAAI,OAAO,GAAG;YACV,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;aACrC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC7B,CAAC;QAEF,OAAO,qBAAW,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC;aAC7C,IAAI,CAAC,UAAA,QAAQ;YACV,cAAc,CAAC,OAAO,CAAC,MAAM,EAAK,QAAQ,CAAC,UAAU,SAAI,QAAQ,CAAC,YAAc,CAAC,CAAC;YAClF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;IACX,CAAC;CACJ,CAAA"} \ No newline at end of file diff --git a/Birdmap/ClientApp/src/components/auth/AuthService.ts b/Birdmap/ClientApp/src/components/auth/AuthService.ts index f8e5cbe..8470427 100644 --- a/Birdmap/ClientApp/src/components/auth/AuthService.ts +++ b/Birdmap/ClientApp/src/components/auth/AuthService.ts @@ -1,13 +1,13 @@ import ServiceBase from '../../common/ServiceBase'; -const login_url = '/auth/authenticate'; +const login_url = 'api/auth/authenticate'; export default { isAuthenticated() { return sessionStorage.getItem('user') !== null; }, - login(username, password) { + login(username: string, password: string) { let body = { username: username, password: password diff --git a/Birdmap/ClientApp/src/custom.css b/Birdmap/ClientApp/src/custom.css deleted file mode 100644 index 5fdfd06..0000000 --- a/Birdmap/ClientApp/src/custom.css +++ /dev/null @@ -1,14 +0,0 @@ -/* Provide sufficient contrast against white background */ -a { - color: #0366d6; -} - -code { - color: #E01A76; -} - -.btn-primary { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; -} diff --git a/Birdmap/ClientApp/src/store/Counter.ts b/Birdmap/ClientApp/src/store/Counter.ts deleted file mode 100644 index 8a529b0..0000000 --- a/Birdmap/ClientApp/src/store/Counter.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Action, Reducer } from 'redux'; - -// ----------------- -// STATE - This defines the type of data maintained in the Redux store. - -export interface CounterState { - count: number; -} - -// ----------------- -// ACTIONS - These are serializable (hence replayable) descriptions of state transitions. -// They do not themselves have any side-effects; they just describe something that is going to happen. -// Use @typeName and isActionType for type detection that works even after serialization/deserialization. - -export interface IncrementCountAction { type: 'INCREMENT_COUNT' } -export interface DecrementCountAction { type: 'DECREMENT_COUNT' } - -// Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the -// declared type strings (and not any other arbitrary string). -export type KnownAction = IncrementCountAction | DecrementCountAction; - -// ---------------- -// ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition. -// They don't directly mutate state, but they can have external side-effects (such as loading data). - -export const actionCreators = { - increment: () => ({ type: 'INCREMENT_COUNT' } as IncrementCountAction), - decrement: () => ({ type: 'DECREMENT_COUNT' } as DecrementCountAction) -}; - -// ---------------- -// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state. - -export const reducer: Reducer = (state: CounterState | undefined, incomingAction: Action): CounterState => { - if (state === undefined) { - return { count: 0 }; - } - - const action = incomingAction as KnownAction; - switch (action.type) { - case 'INCREMENT_COUNT': - return { count: state.count + 1 }; - case 'DECREMENT_COUNT': - return { count: state.count - 1 }; - default: - return state; - } -}; diff --git a/Birdmap/ClientApp/src/store/WeatherForecasts.ts b/Birdmap/ClientApp/src/store/WeatherForecasts.ts deleted file mode 100644 index 7648f68..0000000 --- a/Birdmap/ClientApp/src/store/WeatherForecasts.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Action, Reducer } from 'redux'; -import { AppThunkAction } from './'; - -// ----------------- -// STATE - This defines the type of data maintained in the Redux store. - -export interface WeatherForecastsState { - isLoading: boolean; - startDateIndex?: number; - forecasts: WeatherForecast[]; -} - -export interface WeatherForecast { - date: string; - temperatureC: number; - temperatureF: number; - summary: string; -} - -// ----------------- -// ACTIONS - These are serializable (hence replayable) descriptions of state transitions. -// They do not themselves have any side-effects; they just describe something that is going to happen. - -interface RequestWeatherForecastsAction { - type: 'REQUEST_WEATHER_FORECASTS'; - startDateIndex: number; -} - -interface ReceiveWeatherForecastsAction { - type: 'RECEIVE_WEATHER_FORECASTS'; - startDateIndex: number; - forecasts: WeatherForecast[]; -} - -// Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the -// declared type strings (and not any other arbitrary string). -type KnownAction = RequestWeatherForecastsAction | ReceiveWeatherForecastsAction; - -// ---------------- -// ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition. -// They don't directly mutate state, but they can have external side-effects (such as loading data). - -export const actionCreators = { - requestWeatherForecasts: (startDateIndex: number): AppThunkAction => (dispatch, getState) => { - // Only load data if it's something we don't already have (and are not already loading) - const appState = getState(); - if (appState && appState.weatherForecasts && startDateIndex !== appState.weatherForecasts.startDateIndex) { - fetch(`weatherforecast`) - .then(response => response.json() as Promise) - .then(data => { - dispatch({ type: 'RECEIVE_WEATHER_FORECASTS', startDateIndex: startDateIndex, forecasts: data }); - }); - - dispatch({ type: 'REQUEST_WEATHER_FORECASTS', startDateIndex: startDateIndex }); - } - } -}; - -// ---------------- -// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state. - -const unloadedState: WeatherForecastsState = { forecasts: [], isLoading: false }; - -export const reducer: Reducer = (state: WeatherForecastsState | undefined, incomingAction: Action): WeatherForecastsState => { - if (state === undefined) { - return unloadedState; - } - - const action = incomingAction as KnownAction; - switch (action.type) { - case 'REQUEST_WEATHER_FORECASTS': - return { - startDateIndex: action.startDateIndex, - forecasts: state.forecasts, - isLoading: true - }; - case 'RECEIVE_WEATHER_FORECASTS': - // Only accept the incoming data if it matches the most recent request. This ensures we correctly - // handle out-of-order responses. - if (action.startDateIndex === state.startDateIndex) { - return { - startDateIndex: action.startDateIndex, - forecasts: action.forecasts, - isLoading: false - }; - } - break; - } - - return state; -}; diff --git a/Birdmap/ClientApp/src/store/configureStore.js b/Birdmap/ClientApp/src/store/configureStore.js new file mode 100644 index 0000000..de7050c --- /dev/null +++ b/Birdmap/ClientApp/src/store/configureStore.js @@ -0,0 +1,39 @@ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __spreadArrays = (this && this.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var redux_1 = require("redux"); +var redux_thunk_1 = require("redux-thunk"); +var connected_react_router_1 = require("connected-react-router"); +var _1 = require("./"); +function configureStore(history, initialState) { + var middleware = [ + redux_thunk_1.default, + connected_react_router_1.routerMiddleware(history) + ]; + var rootReducer = redux_1.combineReducers(__assign(__assign({}, _1.reducers), { router: connected_react_router_1.connectRouter(history) })); + var enhancers = []; + var windowIfDefined = typeof window === 'undefined' ? null : window; + if (windowIfDefined && windowIfDefined.__REDUX_DEVTOOLS_EXTENSION__) { + enhancers.push(windowIfDefined.__REDUX_DEVTOOLS_EXTENSION__()); + } + return redux_1.createStore(rootReducer, initialState, redux_1.compose.apply(void 0, __spreadArrays([redux_1.applyMiddleware.apply(void 0, middleware)], enhancers))); +} +exports.default = configureStore; +//# sourceMappingURL=configureStore.js.map \ No newline at end of file diff --git a/Birdmap/ClientApp/src/store/configureStore.js.map b/Birdmap/ClientApp/src/store/configureStore.js.map new file mode 100644 index 0000000..bcdeb42 --- /dev/null +++ b/Birdmap/ClientApp/src/store/configureStore.js.map @@ -0,0 +1 @@ +{"version":3,"file":"configureStore.js","sourceRoot":"","sources":["configureStore.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,+BAA+E;AAC/E,2CAAgC;AAChC,iEAAyE;AAEzE,uBAAgD;AAEhD,SAAwB,cAAc,CAAC,OAAgB,EAAE,YAA+B;IACpF,IAAM,UAAU,GAAG;QACf,qBAAK;QACL,yCAAgB,CAAC,OAAO,CAAC;KAC5B,CAAC;IAEF,IAAM,WAAW,GAAG,uBAAe,uBAC5B,WAAQ,KACX,MAAM,EAAE,sCAAa,CAAC,OAAO,CAAC,IAChC,CAAC;IAEH,IAAM,SAAS,GAAG,EAAE,CAAC;IACrB,IAAM,eAAe,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAa,CAAC;IAC7E,IAAI,eAAe,IAAI,eAAe,CAAC,4BAA4B,EAAE;QACjE,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,4BAA4B,EAAE,CAAC,CAAC;KAClE;IAED,OAAO,mBAAW,CACd,WAAW,EACX,YAAY,EACZ,eAAO,+BAAC,uBAAe,eAAI,UAAU,IAAM,SAAS,GACvD,CAAC;AACN,CAAC;AAtBD,iCAsBC"} \ No newline at end of file diff --git a/Birdmap/ClientApp/src/store/index.js b/Birdmap/ClientApp/src/store/index.js new file mode 100644 index 0000000..26b7aad --- /dev/null +++ b/Birdmap/ClientApp/src/store/index.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.reducers = void 0; +// Whenever an action is dispatched, Redux will update each top-level application state property using +// the reducer with the matching name. It's important that the names match exactly, and that the reducer +// acts on the corresponding ApplicationState property type. +exports.reducers = {}; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/Birdmap/ClientApp/src/store/index.js.map b/Birdmap/ClientApp/src/store/index.js.map new file mode 100644 index 0000000..d58ff64 --- /dev/null +++ b/Birdmap/ClientApp/src/store/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;AAMA,sGAAsG;AACtG,wGAAwG;AACxG,4DAA4D;AAC/C,QAAA,QAAQ,GAAG,EACvB,CAAC"} \ No newline at end of file diff --git a/Birdmap/ClientApp/src/store/index.ts b/Birdmap/ClientApp/src/store/index.ts index a5f7c05..12bcf5e 100644 --- a/Birdmap/ClientApp/src/store/index.ts +++ b/Birdmap/ClientApp/src/store/index.ts @@ -1,18 +1,13 @@ -import * as WeatherForecasts from './WeatherForecasts'; -import * as Counter from './Counter'; + // The top-level state object export interface ApplicationState { - counter: Counter.CounterState | undefined; - weatherForecasts: WeatherForecasts.WeatherForecastsState | undefined; } // Whenever an action is dispatched, Redux will update each top-level application state property using // the reducer with the matching name. It's important that the names match exactly, and that the reducer // acts on the corresponding ApplicationState property type. export const reducers = { - counter: Counter.reducer, - weatherForecasts: WeatherForecasts.reducer }; // This type can be used as a hint on action creators so that its 'dispatch' and 'getState' params are