diff --git a/api.js b/api.js index 1c2d863..9eb9b9f 100644 --- a/api.js +++ b/api.js @@ -20,6 +20,7 @@ api.use('/api/config', require('./routes/config')); api.use('/api/weather', require('./routes/weather')); api.use('/api/categories', require('./routes/category')); api.use('/api/bookmarks', require('./routes/bookmark')); +api.use('/api/queries', require('./routes/queries')); // Custom error handler api.use(errorHandler); diff --git a/client/src/components/Settings/OtherSettings/OtherSettings.tsx b/client/src/components/Settings/OtherSettings/OtherSettings.tsx index 1ca0d83..0672d64 100644 --- a/client/src/components/Settings/OtherSettings/OtherSettings.tsx +++ b/client/src/components/Settings/OtherSettings/OtherSettings.tsx @@ -19,9 +19,7 @@ import { // UI import InputGroup from '../../UI/Forms/InputGroup/InputGroup'; import Button from '../../UI/Buttons/Button/Button'; - -// CSS -import classes from './OtherSettings.module.css'; +import SettingsHeadline from '../../UI/Headlines/SettingsHeadline/SettingsHeadline'; // Utils import { searchConfig } from '../../../utility'; @@ -104,7 +102,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => { return (
formSubmitHandler(e)}> {/* OTHER OPTIONS */} -

Miscellaneous

+ { {/* BEAHVIOR OPTIONS */} -

App Behavior

+ {/* MODULES OPTIONS */} -

Modules

+ { {/* KUBERNETES SETTINGS */} -

Kubernetes

+ inputChangeHandler(e)} + /> + + + + inputChangeHandler(e)} + /> + + + + inputChangeHandler(e)} + /> + + + + ); +}; + +export default QueriesForm; diff --git a/client/src/components/Settings/SearchSettings/SearchSettings.tsx b/client/src/components/Settings/SearchSettings/SearchSettings.tsx index a3fec29..5b40f71 100644 --- a/client/src/components/Settings/SearchSettings/SearchSettings.tsx +++ b/client/src/components/Settings/SearchSettings/SearchSettings.tsx @@ -1,5 +1,5 @@ // React -import { useState, useEffect, FormEvent, ChangeEvent } from 'react'; +import { useState, useEffect, FormEvent, ChangeEvent, Fragment } from 'react'; import { connect } from 'react-redux'; // State @@ -13,15 +13,19 @@ import { SearchForm, } from '../../../interfaces'; -// Utils -import { searchConfig } from '../../../utility'; -import InputGroup from '../../UI/Forms/InputGroup/InputGroup'; - -// Data -import { queries } from '../../../utility/searchQueries.json'; +// Components +import CustomQueries from './CustomQueries/CustomQueries'; // UI import Button from '../../UI/Buttons/Button/Button'; +import SettingsHeadline from '../../UI/Headlines/SettingsHeadline/SettingsHeadline'; +import InputGroup from '../../UI/Forms/InputGroup/InputGroup'; + +// Utils +import { searchConfig } from '../../../utility'; + +// Data +import { queries } from '../../../utility/searchQueries.json'; interface Props { createNotification: (notification: NewNotification) => void; @@ -73,50 +77,65 @@ const SearchSettings = (props: Props): JSX.Element => { }; return ( - formSubmitHandler(e)}> - - - - - - - - - - - - - - + + {/* GENERAL SETTINGS */} +
formSubmitHandler(e)} + style={{ marginBottom: '30px' }} + > + + + + + + + + + + + + + + + + + {/* CUSTOM QUERIES */} + + +
); }; diff --git a/client/src/components/Settings/OtherSettings/OtherSettings.module.css b/client/src/components/UI/Headlines/SettingsHeadline/SettingsHeadline.module.css similarity index 89% rename from client/src/components/Settings/OtherSettings/OtherSettings.module.css rename to client/src/components/UI/Headlines/SettingsHeadline/SettingsHeadline.module.css index 36e4deb..137667c 100644 --- a/client/src/components/Settings/OtherSettings/OtherSettings.module.css +++ b/client/src/components/UI/Headlines/SettingsHeadline/SettingsHeadline.module.css @@ -1,4 +1,4 @@ -.SettingsSection { +.SettingsHeadline { color: var(--color-primary); padding-bottom: 3px; margin-bottom: 10px; @@ -6,4 +6,4 @@ font-weight: 500; border-bottom: 2px solid var(--color-accent); display: inline-block; -} \ No newline at end of file +} diff --git a/client/src/components/UI/Headlines/SettingsHeadline/SettingsHeadline.tsx b/client/src/components/UI/Headlines/SettingsHeadline/SettingsHeadline.tsx new file mode 100644 index 0000000..5d14949 --- /dev/null +++ b/client/src/components/UI/Headlines/SettingsHeadline/SettingsHeadline.tsx @@ -0,0 +1,11 @@ +const classes = require('./SettingsHeadline.module.css'); + +interface Props { + text: string; +} + +const SettingsHeadline = (props: Props): JSX.Element => { + return

{props.text}

; +}; + +export default SettingsHeadline; diff --git a/client/src/store/actions/actionTypes.ts b/client/src/store/actions/actionTypes.ts index 0c1cc87..f040016 100644 --- a/client/src/store/actions/actionTypes.ts +++ b/client/src/store/actions/actionTypes.ts @@ -28,7 +28,11 @@ import { GetConfigAction, UpdateConfigAction, } from './'; -import { FetchQueriesAction } from './config'; +import { + AddQueryAction, + DeleteQueryAction, + FetchQueriesAction, +} from './config'; export enum ActionTypes { // Theme @@ -65,6 +69,8 @@ export enum ActionTypes { getConfig = 'GET_CONFIG', updateConfig = 'UPDATE_CONFIG', fetchQueries = 'FETCH_QUERIES', + addQuery = 'ADD_QUERY', + deleteQuery = 'DELETE_QUERY', } export type Action = @@ -96,4 +102,6 @@ export type Action = // Config | GetConfigAction | UpdateConfigAction - | FetchQueriesAction; + | FetchQueriesAction + | AddQueryAction + | DeleteQueryAction; diff --git a/client/src/store/actions/config.ts b/client/src/store/actions/config.ts index baddbe5..d0c8a42 100644 --- a/client/src/store/actions/config.ts +++ b/client/src/store/actions/config.ts @@ -60,9 +60,7 @@ export interface FetchQueriesAction { export const fetchQueries = () => async (dispatch: Dispatch) => { try { - const res = await axios.get>( - '/api/config/0/queries' - ); + const res = await axios.get>('/api/queries'); dispatch({ type: ActionTypes.fetchQueries, @@ -72,3 +70,43 @@ export const fetchQueries = console.log(err); } }; + +export interface AddQueryAction { + type: ActionTypes.addQuery; + payload: Query; +} + +export const addQuery = + (query: Query) => async (dispatch: Dispatch) => { + try { + const res = await axios.post>('/api/queries', query); + + dispatch({ + type: ActionTypes.addQuery, + payload: res.data.data, + }); + } catch (err) { + console.log(err); + } + }; + +export interface DeleteQueryAction { + type: ActionTypes.deleteQuery; + payload: Query[]; +} + +export const deleteQuery = + (prefix: string) => async (dispatch: Dispatch) => { + try { + const res = await axios.delete>( + `/api/queries/${prefix}` + ); + + dispatch({ + type: ActionTypes.deleteQuery, + payload: res.data.data, + }); + } catch (err) { + console.log(err); + } + }; diff --git a/client/src/store/reducers/config.ts b/client/src/store/reducers/config.ts index 93150e2..08b68e6 100644 --- a/client/src/store/reducers/config.ts +++ b/client/src/store/reducers/config.ts @@ -34,6 +34,20 @@ const fetchQueries = (state: State, action: Action): State => { }; }; +const addQuery = (state: State, action: Action): State => { + return { + ...state, + customQueries: [...state.customQueries, action.payload], + }; +}; + +const deleteQuery = (state: State, action: Action): State => { + return { + ...state, + customQueries: action.payload, + }; +}; + const configReducer = (state: State = initialState, action: Action) => { switch (action.type) { case ActionTypes.getConfig: @@ -42,6 +56,10 @@ const configReducer = (state: State = initialState, action: Action) => { return updateConfig(state, action); case ActionTypes.fetchQueries: return fetchQueries(state, action); + case ActionTypes.addQuery: + return addQuery(state, action); + case ActionTypes.deleteQuery: + return deleteQuery(state, action); default: return state; } diff --git a/controllers/config.js b/controllers/config.js index 85b209a..e5290aa 100644 --- a/controllers/config.js +++ b/controllers/config.js @@ -175,16 +175,3 @@ exports.updateCss = asyncWrapper(async (req, res, next) => { data: {}, }); }); - -// @desc Get custom queries file -// @route GET /api/config/0/queries -// @access Public -exports.getQueries = asyncWrapper(async (req, res, next) => { - const file = new File(join(__dirname, '../data/customQueries.json')); - const content = JSON.parse(file.read()); - - res.status(200).json({ - success: true, - data: content.queries, - }); -}); diff --git a/controllers/queries/index.js b/controllers/queries/index.js new file mode 100644 index 0000000..b68f145 --- /dev/null +++ b/controllers/queries/index.js @@ -0,0 +1,53 @@ +const asyncWrapper = require('../../middleware/asyncWrapper'); +const File = require('../../utils/File'); +const { join } = require('path'); + +const QUERIES_PATH = join(__dirname, '../../data/customQueries.json'); + +// @desc Add custom search query +// @route POST /api/queries +// @access Public +exports.addQuery = asyncWrapper(async (req, res, next) => { + const file = new File(QUERIES_PATH); + let content = JSON.parse(file.read()); + + // Add new query + content.queries.push(req.body); + file.write(content, true); + + res.status(201).json({ + success: true, + data: req.body, + }); +}); + +// @desc Get custom queries file +// @route GET /api/queries +// @access Public +exports.getQueries = asyncWrapper(async (req, res, next) => { + const file = new File(QUERIES_PATH); + const content = JSON.parse(file.read()); + + res.status(200).json({ + success: true, + data: content.queries, + }); +}); + +// @desc Delete query +// @route DELETE /api/queries/:prefix +// @access Public +exports.deleteQuery = asyncWrapper(async (req, res, next) => { + const file = new File(QUERIES_PATH); + let content = JSON.parse(file.read()); + + content.queries = content.queries.filter( + (q) => q.prefix != req.params.prefix + ); + file.write(content, true); + + res.status(200).json({ + success: true, + data: content.queries, + }); +}); diff --git a/routes/config.js b/routes/config.js index 6abea15..8c9ac15 100644 --- a/routes/config.js +++ b/routes/config.js @@ -10,7 +10,6 @@ const { deletePair, updateCss, getCss, - getQueries, } = require('../controllers/config'); router.route('/').post(createPair).get(getAllPairs).put(updateValues); @@ -19,6 +18,4 @@ router.route('/:key').get(getSinglePair).put(updateValue).delete(deletePair); router.route('/0/css').get(getCss).put(updateCss); -router.route('/0/queries').get(getQueries); - module.exports = router; diff --git a/routes/queries.js b/routes/queries.js new file mode 100644 index 0000000..0e17bc0 --- /dev/null +++ b/routes/queries.js @@ -0,0 +1,13 @@ +const express = require('express'); +const router = express.Router(); + +const { + getQueries, + addQuery, + deleteQuery, +} = require('../controllers/queries/'); + +router.route('/').post(addQuery).get(getQueries); +router.route('/:prefix').delete(deleteQuery); + +module.exports = router;