From 7e540587a51f41f5247da9fe94ab2b6aa1b2c8ba Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 May 2021 17:21:52 +0200 Subject: [PATCH] Multiple changes to App related components. Created form to add new Apps, outsourced AppGrid component --- .../Apps/AppCard/AppCard.module.css | 5 +- .../src/components/Apps/AppCard/AppCard.tsx | 13 ++- .../Apps/AppForm/AppForm.module.css | 49 +++++++++ .../src/components/Apps/AppForm/AppForm.tsx | 99 +++++++++++++++++++ .../Apps/AppGrid/AppGrid.module.css | 28 ++++++ .../src/components/Apps/AppGrid/AppGrid.tsx | 25 +++++ client/src/components/Apps/Apps.module.css | 32 +----- client/src/components/Apps/Apps.tsx | 68 ++++++++++--- client/src/store/reducers/app.ts | 2 +- 9 files changed, 276 insertions(+), 45 deletions(-) create mode 100644 client/src/components/Apps/AppForm/AppForm.module.css create mode 100644 client/src/components/Apps/AppForm/AppForm.tsx create mode 100644 client/src/components/Apps/AppGrid/AppGrid.module.css create mode 100644 client/src/components/Apps/AppGrid/AppGrid.tsx diff --git a/client/src/components/Apps/AppCard/AppCard.module.css b/client/src/components/Apps/AppCard/AppCard.module.css index 05dce47..bba98be 100644 --- a/client/src/components/Apps/AppCard/AppCard.module.css +++ b/client/src/components/Apps/AppCard/AppCard.module.css @@ -9,7 +9,10 @@ } .AppCardIcon { + /* height: 64px; */ width: 40px; + height: 40px; + margin-right: 0.5em; } .AppCardDetails { @@ -23,7 +26,7 @@ font-size: 1em; font-weight: 500; color: var(--color-primary); - margin-bottom: -8px; + margin-bottom: -4px; } .AppCardDetails span { diff --git a/client/src/components/Apps/AppCard/AppCard.tsx b/client/src/components/Apps/AppCard/AppCard.tsx index 4821d89..b927c5c 100644 --- a/client/src/components/Apps/AppCard/AppCard.tsx +++ b/client/src/components/Apps/AppCard/AppCard.tsx @@ -1,3 +1,5 @@ +import { Link } from 'react-router-dom'; + import classes from './AppCard.module.css'; import Icon from '../../UI/Icon/Icon'; @@ -5,6 +7,7 @@ import { App } from '../../../interfaces'; interface ComponentProps { app: App; + pinHandler?: Function; } const AppCard = (props: ComponentProps): JSX.Element => { @@ -18,16 +21,20 @@ const AppCard = (props: ComponentProps): JSX.Element => { return parsedName; } + const redirectHandler = (url: string): void => { + window.open(url); + } + return ( -
+
{props.app.name}
-
{props.app.url} + {props.app.url}
-
+ ) } diff --git a/client/src/components/Apps/AppForm/AppForm.module.css b/client/src/components/Apps/AppForm/AppForm.module.css new file mode 100644 index 0000000..7a451ea --- /dev/null +++ b/client/src/components/Apps/AppForm/AppForm.module.css @@ -0,0 +1,49 @@ +.AppForm { + background-color: var(--color-background); + color: var(--color-primary); + border-radius: 6px; + width: 60%; + position: relative; + /* height: 50vh; */ + padding: 50px 50px; +} + +.AppFormIcon { + width: 40px; + position: absolute; + right: 5px; + top: 5px; +} + +.AppFormIcon:hover { + cursor: pointer; +} + +.InputGroup { + margin-bottom: 15px; +} + +.InputGroup label, +.InputGroup span, +.InputGroup input { + display: block; +} + +.InputGroup input { + margin: 8px 0; + width: 100%; + border: none; + border-radius: 4px; + padding: 10px; + background-color: var(--color-primary); + color: var(--color-background); +} + +.InputGroup span { + font-size: 12px; + color: var(--color-primary) +} + +.InputGroup span a { + color: var(--color-accent); +} \ No newline at end of file diff --git a/client/src/components/Apps/AppForm/AppForm.tsx b/client/src/components/Apps/AppForm/AppForm.tsx new file mode 100644 index 0000000..6e822f9 --- /dev/null +++ b/client/src/components/Apps/AppForm/AppForm.tsx @@ -0,0 +1,99 @@ +import { useState, ChangeEvent, SyntheticEvent } from 'react'; +import { connect } from 'react-redux'; +import { addApp } from '../../../store/actions'; +import { NewApp } from '../../../interfaces/App'; + +import classes from './AppForm.module.css'; +import Icon from '../../UI/Icon/Icon'; + +interface ComponentProps { + modalHandler: Function; + addApp: (formData: NewApp) => any; +} + +const AppForm = (props: ComponentProps): JSX.Element => { + const [formData, setFormData] = useState({ + name: '', + url: '', + icon: '' + }); + + const _modalHandler = () => { + props.modalHandler(); + } + + const inputChangeHandler = (e: ChangeEvent): void => { + setFormData({ + ...formData, + [e.target.name]: e.target.value + }) + } + + const formSubmitHandler = (e: SyntheticEvent): void => { + e.preventDefault(); + props.addApp(formData); + setFormData({ + name: '', + url: '', + icon: '' + }) + } + + return ( +
+
+ +
+
formSubmitHandler(e)}> +
+ + inputChangeHandler(e)} + /> +
+
+ + inputChangeHandler(e)} + /> + Use URL without protocol +
+
+ + inputChangeHandler(e)} + /> + + Use icon name from MDI. + + {' '}Click here for reference + + +
+ +
+
+ ) +} + +export default connect(null, { addApp })(AppForm); \ No newline at end of file diff --git a/client/src/components/Apps/AppGrid/AppGrid.module.css b/client/src/components/Apps/AppGrid/AppGrid.module.css new file mode 100644 index 0000000..e8ee751 --- /dev/null +++ b/client/src/components/Apps/AppGrid/AppGrid.module.css @@ -0,0 +1,28 @@ +.AppGrid { + display: grid; + grid-template-columns: repeat(1, 1fr); +} + +@media (min-width: 430px) { + .AppGrid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 670px) { + .AppGrid { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (min-width: 900px) { + .AppGrid { + grid-template-columns: repeat(4, 1fr); + } +} + +/* 320px — 480px: Mobile devices. +481px — 768px: iPads, Tablets. +769px — 1024px: Small screens, laptops. +1025px — 1200px: Desktops, large screens. +1201px and more — Extra large screens, TV. */ \ No newline at end of file diff --git a/client/src/components/Apps/AppGrid/AppGrid.tsx b/client/src/components/Apps/AppGrid/AppGrid.tsx new file mode 100644 index 0000000..f61e402 --- /dev/null +++ b/client/src/components/Apps/AppGrid/AppGrid.tsx @@ -0,0 +1,25 @@ +import classes from './AppGrid.module.css'; +import { App } from '../../../interfaces/App'; + +import AppCard from '../AppCard/AppCard'; + +interface ComponentProps { + apps: App[]; +} + +const AppGrid = (props: ComponentProps): JSX.Element => { + const apps = ( +
+ {props.apps.map((app: App): JSX.Element => { + return + })} +
+ ); + + return apps; +} + +export default AppGrid; \ No newline at end of file diff --git a/client/src/components/Apps/Apps.module.css b/client/src/components/Apps/Apps.module.css index 302fb5b..09810e1 100644 --- a/client/src/components/Apps/Apps.module.css +++ b/client/src/components/Apps/Apps.module.css @@ -1,28 +1,4 @@ -.Apps { - display: grid; - grid-template-columns: repeat(1, 1fr); -} - -@media (min-width: 430px) { - .Apps { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (min-width: 670px) { - .Apps { - grid-template-columns: repeat(3, 1fr); - } -} - -@media (min-width: 900px) { - .Apps { - grid-template-columns: repeat(4, 1fr); - } -} - -/* 320px — 480px: Mobile devices. -481px — 768px: iPads, Tablets. -769px — 1024px: Small screens, laptops. -1025px — 1200px: Desktops, large screens. -1201px and more — Extra large screens, TV. */ \ No newline at end of file +.ActionsContainer { + display: flex; + align-items: center; +} \ No newline at end of file diff --git a/client/src/components/Apps/Apps.tsx b/client/src/components/Apps/Apps.tsx index a2aa91f..0bb6d64 100644 --- a/client/src/components/Apps/Apps.tsx +++ b/client/src/components/Apps/Apps.tsx @@ -1,12 +1,12 @@ -import { Fragment, useEffect } from 'react'; +import { Fragment, useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; // Redux import { connect } from 'react-redux'; -import { getApps } from '../../store/actions'; +import { getApps, pinApp, addApp } from '../../store/actions'; // Typescript -import { App, GlobalState } from '../../interfaces'; +import { App, GlobalState, NewApp } from '../../interfaces'; // CSS import classes from './Apps.module.css'; @@ -15,34 +15,78 @@ import classes from './Apps.module.css'; import { Container } from '../UI/Layout/Layout'; import Headline from '../UI/Headlines/Headline/Headline'; import Spinner from '../UI/Spinner/Spinner'; +import ActionButton from '../UI/Buttons/ActionButton/ActionButton'; +import Modal from '../UI/Modal/Modal'; // Subcomponents -import AppCard from './AppCard/AppCard'; +import AppGrid from './AppGrid/AppGrid'; +import AppForm from './AppForm/AppForm'; +import AppTable from './AppTable/AppTable'; +import Test from '../Test'; interface ComponentProps { getApps: Function; + pinApp: (id: number, isPinned: boolean) => any; + addApp: (formData: NewApp) => any; apps: App[]; loading: boolean; } const Apps = (props: ComponentProps): JSX.Element => { + const [modalIsOpen, setModalIsOpen] = useState(false); + const [isInEdit, setIsInEdit] = useState(false); + useEffect(() => { - props.getApps() + props.getApps(); + // props.addApp({ + // name: 'Plex', + // url: '192.168.0.128', + // icon: 'cat' + // }) }, [props.getApps]); + const pinAppHandler = (id: number, state: boolean): void => { + props.pinApp(id, state); + } + + const toggleModal = (): void => { + setModalIsOpen(!modalIsOpen); + } + + const toggleEdit = (): void => { + setIsInEdit(!isInEdit); + } + return ( + + + + Go back} + title='All Apps' + subtitle={(Go back)} /> - + +
+ + +
+
{props.loading ? 'loading' - : props.apps.map((app: App): JSX.Element => { - return - }) + : (!isInEdit + ? + : ) }
@@ -56,4 +100,4 @@ const mapStateToProps = (state: GlobalState) => { } } -export default connect(mapStateToProps, { getApps })(Apps); \ No newline at end of file +export default connect(mapStateToProps, { getApps, pinApp, addApp })(Apps); \ No newline at end of file diff --git a/client/src/store/reducers/app.ts b/client/src/store/reducers/app.ts index f8d9d7b..06678ca 100644 --- a/client/src/store/reducers/app.ts +++ b/client/src/store/reducers/app.ts @@ -52,7 +52,7 @@ const pinApp = (state: State, action: Action): State => { } const addAppSuccess = (state: State, action: Action): State => { - const tmpApps = [...state.apps, ...action.payload]; + const tmpApps = [...state.apps, action.payload]; return { ...state,