A lot of improvements

- Removed "refresh" buttons, please just refresh the entire site (less cluttered appearance)
- Refined README on installation
- Added more themes
- Improved Dockerfile, now everything runs with production settings
This commit is contained in:
Bastian Meissner 2020-05-24 15:06:47 +02:00
parent 19bbc337bf
commit 22ee2ca08d
12 changed files with 242 additions and 91 deletions

2
.gitignore vendored
View file

@ -8,7 +8,7 @@
# testing
/coverage
# production
# building
/build
# misc

View file

@ -1,6 +1,18 @@
# Get current image of node.js
FROM node:current-slim
# Set /app as directory where the app should be at
WORKDIR /app
# Copy all of the relevant files over to the container
COPY . .
# Download dependencies, build container
RUN yarn
EXPOSE 3000
CMD [ "yarn", "start" ]
RUN [ "yarn", "build" ]
# Expose the two relevant ports
EXPOSE 3000 8080
# Serve the app
CMD [ "yarn", "serve:production" ]

View file

@ -18,41 +18,27 @@ Getting Dashboard to run is fairly simple and can be accomplished with two techn
1. Locally
**Prerequisites: node, npm, yarn**
To get Dashboard to run, just clone the repository, download the dependencies using yarn, then start using `yarn start`.
```
git clone https://github.com/phntxx/dashboard.git
cd dashboard
yarn
yarn build
yarn serve:production
$ git clone https://github.com/phntxx/dashboard.git
$ cd dashboard/
$ yarn
$ yarn build
$ yarn serve:production
```
alternatively, if you want to work using static files (requires a rebuild for
every change in the JSON-files), just replace `yarn start` with `yarn build`.
Then you can copy the files inside the `build` directory onto the webroot of
your webserver of choice.
2. Using Docker
Using Docker requires building the container manually. Fortunately, this can be accomplished fairly easily:
```
git clone https://github.com/phntxx/dashboard.git
cd dashboard
docker build -t dashboard:1.0
docker run -d \
-t \
$ git clone https://github.com/phntxx/dashboard.git
$ cd dashboard/
$ docker build -t dashboard:1.0 .
$ docker run -d \
-v $(pwd)/data:/app/data
-p 3000:3000 \
-v ./src/components/data:/app/src/components/data \
--name dashboard \
dashboard:1.0
```
**NOTE: The `-t` flag is very important, as the Dockerfile requires standard TTY.**
## Customization
Dashboard is designed to be customizable. Everything is handled using four .json-files, which can be found at /src/components/data

View file

@ -1,7 +1,7 @@
{
"apps": [
{
"name": "my pi hole",
"name": "Pihole",
"displayURL": "example.com",
"URL": "https://example.com",
"icon": "vpn_lock"

View file

@ -20,6 +20,90 @@
"mainColor": "",
"accentColor": "",
"backgroundColor": "#ffffff"
},
{
"label": "Blackboard",
"value": 3,
"mainColor": "#fffdea",
"accentColor": "5c5c5c",
"backgroundColor": "#1a1a1a"
},
{
"label": "Gazette",
"value": 4,
"mainColor": "#000000",
"accentColor": "#5c5c5c",
"backgroundColor": "#F2F7FF"
},
{
"label": "Espresso",
"value": 5,
"mainColor": "#d1b59a",
"accentColor": "#4e4e4e",
"backgroundColor": "#21211f"
},
{
"label": "Cab",
"value": 6,
"mainColor": "#1f1f1f",
"accentColor": "#424242",
"backgroundColor": "#f6d305"
},
{
"label": "Cloud",
"value": 7,
"mainColor": "#35342f",
"accentColor": "#37bbe4",
"backgroundColor": "#f1f2f0"
},
{
"label": "Lime",
"value": 8,
"mainColor": "#aabbc3",
"accentColor": "#aeea00",
"backgroundColor": "#263238"
},
{
"label": "White",
"value": 9,
"mainColor": "#222222",
"accentColor": "#dddddd",
"backgroundColor": "#ffffff"
},
{
"label": "Tron",
"value": 10,
"mainColor": "#effbff",
"accentColor": "#6ee2ff",
"backgroundColor": "#242b33"
},
{
"label": "Blues",
"value": 11,
"mainColor": "#eff1fc",
"accentColor": "#6677eb",
"backgroundColor": "#2b2c56"
},
{
"label": "Passion",
"value": 12,
"mainColor": "#12005e",
"accentColor": "#8e24aa",
"backgroundColor": "#f5f5f5"
},
{
"label": "Chalk",
"value": 13,
"mainColor": "#aabbc3",
"accentColor": "#ff869a",
"backgroundColor": "#263238"
},
{
"label": "Paper",
"value": 14,
"mainColor": "#4c432e",
"accentColor": "#aa9a73",
"backgroundColor": "#f8f6f1"
}
]
}

View file

@ -5,11 +5,11 @@ import styled from 'styled-components';
import selectedTheme from './themeManager';
import {
handleResponse,
Headline,
ListContainer,
ItemList,
Item,
RefreshButton,
ErrorMessage
} from './elements';
@ -48,13 +48,6 @@ const App = styled.div`
padding: 1rem;
`;
const handleResponse = response => {
if (response.ok) {
return response.json();
}
throw new Error('Failed to load app data.');
};
const useAppData = () => {
const [appData, setAppData] = useState({ apps: [], error: false });
const fetchAppData = useCallback(() => {
@ -84,7 +77,6 @@ const AppList = () => {
return (
<ListContainer>
<Headline>Applications</Headline>
<RefreshButton onClick={fetchAppData}>refresh</RefreshButton>
<ItemList>
{error && <ErrorMessage>{error}</ErrorMessage>}
{apps.map((app, idx) => {

View file

@ -3,11 +3,11 @@ import styled from 'styled-components';
import selectedTheme from './themeManager';
import {
handleResponse,
Headline,
ListContainer,
ItemList,
Item,
RefreshButton,
ErrorMessage
} from './elements';
@ -35,18 +35,12 @@ const Bookmark = styled.a`
font-size: 14px;
`;
const handleResponse = response => {
if (response.ok) {
return response.json();
}
throw new Error('Failed to load app data.');
};
const useBookmarkData = () => {
const [bookmarkData, setBookmarkData] = useState({
groups: [],
error: false
});
const fetchBookmarkData = useCallback(() => {
(process.env.NODE_ENV === 'production'
? fetch('/bookmarks.json').then(handleResponse)
@ -74,7 +68,6 @@ const BookmarkList = () => {
return (
<ListContainer>
<Headline>Bookmarks</Headline>
<RefreshButton onClick={fetchBookmarkData}>refresh</RefreshButton>
<ItemList>
{error && <ErrorMessage>{error}</ErrorMessage>}
{groups.map((group, idx) => {

View file

@ -20,6 +20,90 @@
"mainColor": "",
"accentColor": "",
"backgroundColor": "#ffffff"
},
{
"label": "Blackboard",
"value": 3,
"mainColor": "#fffdea",
"accentColor": "5c5c5c",
"backgroundColor": "#1a1a1a"
},
{
"label": "Gazette",
"value": 4,
"mainColor": "#000000",
"accentColor": "#5c5c5c",
"backgroundColor": "#F2F7FF"
},
{
"label": "Espresso",
"value": 5,
"mainColor": "#d1b59a",
"accentColor": "#4e4e4e",
"backgroundColor": "#21211f"
},
{
"label": "Cab",
"value": 6,
"mainColor": "#1f1f1f",
"accentColor": "#424242",
"backgroundColor": "#f6d305"
},
{
"label": "Cloud",
"value": 7,
"mainColor": "#35342f",
"accentColor": "#37bbe4",
"backgroundColor": "#f1f2f0"
},
{
"label": "Lime",
"value": 8,
"mainColor": "#aabbc3",
"accentColor": "#aeea00",
"backgroundColor": "#263238"
},
{
"label": "White",
"value": 9,
"mainColor": "#222222",
"accentColor": "#dddddd",
"backgroundColor": "#ffffff"
},
{
"label": "Tron",
"value": 10,
"mainColor": "#effbff",
"accentColor": "#6ee2ff",
"backgroundColor": "#242b33"
},
{
"label": "Blues",
"value": 11,
"mainColor": "#eff1fc",
"accentColor": "#6677eb",
"backgroundColor": "#2b2c56"
},
{
"label": "Passion",
"value": 12,
"mainColor": "#12005e",
"accentColor": "#8e24aa",
"backgroundColor": "#f5f5f5"
},
{
"label": "Chalk",
"value": 13,
"mainColor": "#aabbc3",
"accentColor": "#ff869a",
"backgroundColor": "#263238"
},
{
"label": "Paper",
"value": 14,
"mainColor": "#4c432e",
"accentColor": "#aa9a73",
"backgroundColor": "#f8f6f1"
}
]
}

View file

@ -5,6 +5,13 @@ import MaterialIcon from 'material-icons-react';
// File for elements that are/can be reused across the entire site.
export const handleResponse = response => {
if (response.ok) {
return response.json();
}
throw new Error('Failed to load data.');
};
export const ListContainer = styled.div`
padding: 2rem 0 2rem 0;
`;
@ -40,8 +47,8 @@ export const Button = styled.button`
border: 1px solid ${selectedTheme.mainColor};
color: ${selectedTheme.mainColor};
background: none;
margin-left: 1rem;
min-height: 3em;
height: 100%;
`;
const StyledButton = styled.button`

View file

@ -1,7 +1,7 @@
import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import { ErrorMessage } from './elements';
import { handleResponse, ErrorMessage } from './elements';
import selectedTheme from './themeManager';
@ -15,18 +15,12 @@ const SearchInput = styled.input`
color: ${selectedTheme.mainColor};
`;
const handleResponse = response => {
if (response.ok) {
return response.json();
}
throw new Error('Failed to load app data.');
};
const useSearchProviders = () => {
const [searchProviders, setSearchProviders] = useState({
providers: [],
error: false
});
const fetchSearchProviders = useCallback(() => {
(process.env.NODE_ENV === 'production'
? fetch('/search.json').then(handleResponse)
@ -43,11 +37,14 @@ const useSearchProviders = () => {
useEffect(() => {
fetchSearchProviders();
}, [fetchSearchProviders]);
return searchProviders;
return { searchProviders, fetchSearchProviders };
};
const SearchBar = () => {
const searchProviders = useSearchProviders();
const {
searchProviders: { providers, error },
fetchSearchProviders
} = useSearchProviders();
let [input, setInput] = useState();
@ -70,24 +67,17 @@ const SearchBar = () => {
let searchQuery = queryArray.join(' ');
var foundProvider = false;
searchProviders.providers.forEach(provider => {
if (provider.prefix === prefix) {
foundProvider = true;
providers.forEach(provider => {
if (provider.prefix === prefix)
window.location = provider.url + searchQuery;
}
});
if (!foundProvider) {
window.location = 'https://google.com/search?q=' + query;
}
};
return (
<form onSubmit={e => handleSearchQuery(e)}>
{searchProviders.error && (
<ErrorMessage>{searchProviders.error}</ErrorMessage>
)}
{error && <ErrorMessage>{error}</ErrorMessage>}
<SearchInput
type="text"
onChange={e => setInput(e.target.value)}

View file

@ -6,7 +6,17 @@ import Select from 'react-select';
import searchData from './data/search.json';
import selectedTheme, { setTheme } from './themeManager';
import { Button, IconButton, ErrorMessage } from './elements';
import {
handleResponse,
Button,
IconButton,
ErrorMessage,
Headline as hl
} from './elements';
const Headline = styled(hl)`
padding: 0.5rem 0 0.5rem 0;
`;
const Modal = styled.div`
position: absolute;
@ -24,16 +34,8 @@ const SelectContainer = styled.div`
`;
const FormContainer = styled.div`
display: flex;
flex-direction: row;
flex-wrap: nowrap;
`;
const Headline = styled.h3`
font-family: Roboto, sans-serif;
font-weight: 900;
color: ${selectedTheme.mainColor};
margin-top: 0;
display: grid;
grid-template-columns: auto auto auto;
`;
const Table = styled.table`
@ -57,6 +59,11 @@ const HeadCell = styled.th`
background: none;
`;
const InfoText = styled.p`
font-weight: 400;
padding: 0.5rem 0 0.5rem 0;
`;
const SelectorStyle = {
control: provided => ({
...provided,
@ -102,13 +109,6 @@ const SelectorStyle = {
}
};
const handleResponse = response => {
if (response.ok) {
return response.json();
}
throw new Error('Failed to load app data.');
};
const useThemeData = () => {
const [themeData, setThemeData] = useState({ themes: [], error: false });
const fetchThemeData = useCallback(() => {
@ -162,12 +162,15 @@ const SettingsModal = () => {
}}
styles={SelectorStyle}
/>
<Button onClick={fetchThemeData}>Refresh</Button>
<Button
onClick={() => setTheme(JSON.stringify(newTheme))}
>
Apply
</Button>
<Button onClick={() => window.location.reload()}>
Refresh
</Button>
</FormContainer>
</SelectContainer>
<Table>

View file

@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import App from './app';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(