Sorting settings. Sort apps on change/add/update
This commit is contained in:
parent
8974fb3b49
commit
754dc3a7b9
9 changed files with 124 additions and 27 deletions
|
@ -1 +1 @@
|
||||||
REACT_APP_VERSION=1.3.3
|
REACT_APP_VERSION=1.3.5
|
|
@ -2,7 +2,7 @@ import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
|
||||||
|
|
||||||
// Redux
|
// Redux
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createNotification, updateConfig } from '../../../store/actions';
|
import { createNotification, updateConfig, sortApps } from '../../../store/actions';
|
||||||
|
|
||||||
// Typescript
|
// Typescript
|
||||||
import { GlobalState, NewNotification, SettingsForm } from '../../../interfaces';
|
import { GlobalState, NewNotification, SettingsForm } from '../../../interfaces';
|
||||||
|
@ -17,6 +17,7 @@ import { searchConfig } from '../../../utility';
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
createNotification: (notification: NewNotification) => void;
|
createNotification: (notification: NewNotification) => void;
|
||||||
updateConfig: (formData: SettingsForm) => void;
|
updateConfig: (formData: SettingsForm) => void;
|
||||||
|
sortApps: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +27,8 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
customTitle: document.title,
|
customTitle: document.title,
|
||||||
pinAppsByDefault: 1,
|
pinAppsByDefault: 1,
|
||||||
pinCategoriesByDefault: 1,
|
pinCategoriesByDefault: 1,
|
||||||
hideHeader: 0
|
hideHeader: 0,
|
||||||
|
useOrdering: 'createdAt'
|
||||||
})
|
})
|
||||||
|
|
||||||
// Get config
|
// Get config
|
||||||
|
@ -35,7 +37,8 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
customTitle: searchConfig('customTitle', 'Flame'),
|
customTitle: searchConfig('customTitle', 'Flame'),
|
||||||
pinAppsByDefault: searchConfig('pinAppsByDefault', 1),
|
pinAppsByDefault: searchConfig('pinAppsByDefault', 1),
|
||||||
pinCategoriesByDefault: searchConfig('pinCategoriesByDefault', 1),
|
pinCategoriesByDefault: searchConfig('pinCategoriesByDefault', 1),
|
||||||
hideHeader: searchConfig('hideHeader', 0)
|
hideHeader: searchConfig('hideHeader', 0),
|
||||||
|
useOrdering: searchConfig('useOrdering', 'createdAt')
|
||||||
})
|
})
|
||||||
}, [props.loading]);
|
}, [props.loading]);
|
||||||
|
|
||||||
|
@ -46,8 +49,11 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
// Save settings
|
// Save settings
|
||||||
await props.updateConfig(formData);
|
await props.updateConfig(formData);
|
||||||
|
|
||||||
// update local page title
|
// Update local page title
|
||||||
document.title = formData.customTitle;
|
document.title = formData.customTitle;
|
||||||
|
|
||||||
|
// Get sorted apps
|
||||||
|
props.sortApps();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input handler
|
// Input handler
|
||||||
|
@ -113,6 +119,19 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
|
||||||
<option value={0}>False</option>
|
<option value={0}>False</option>
|
||||||
</select>
|
</select>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
<InputGroup>
|
||||||
|
<label htmlFor='useOrdering'>Sorting type</label>
|
||||||
|
<select
|
||||||
|
id='useOrdering'
|
||||||
|
name='useOrdering'
|
||||||
|
value={formData.useOrdering}
|
||||||
|
onChange={(e) => inputChangeHandler(e)}
|
||||||
|
>
|
||||||
|
<option value='createdAt'>By creation date</option>
|
||||||
|
<option value='name'>Alphabetical order</option>
|
||||||
|
<option value='orderId'>Custom order</option>
|
||||||
|
</select>
|
||||||
|
</InputGroup>
|
||||||
<Button>Save changes</Button>
|
<Button>Save changes</Button>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
@ -124,4 +143,4 @@ const mapStateToProps = (state: GlobalState) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, { createNotification, updateConfig })(OtherSettings);
|
export default connect(mapStateToProps, { createNotification, updateConfig, sortApps })(OtherSettings);
|
|
@ -3,6 +3,7 @@ import { Model, Bookmark } from '.';
|
||||||
export interface Category extends Model {
|
export interface Category extends Model {
|
||||||
name: string;
|
name: string;
|
||||||
isPinned: boolean;
|
isPinned: boolean;
|
||||||
|
orderId: number;
|
||||||
bookmarks: Bookmark[];
|
bookmarks: Bookmark[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ import {
|
||||||
AddAppAction,
|
AddAppAction,
|
||||||
DeleteAppAction,
|
DeleteAppAction,
|
||||||
UpdateAppAction,
|
UpdateAppAction,
|
||||||
ReorderAppAction,
|
ReorderAppsAction,
|
||||||
|
SortAppsAction,
|
||||||
// Categories
|
// Categories
|
||||||
GetCategoriesAction,
|
GetCategoriesAction,
|
||||||
AddCategoryAction,
|
AddCategoryAction,
|
||||||
|
@ -38,7 +39,8 @@ export enum ActionTypes {
|
||||||
addAppSuccess = 'ADD_APP_SUCCESS',
|
addAppSuccess = 'ADD_APP_SUCCESS',
|
||||||
deleteApp = 'DELETE_APP',
|
deleteApp = 'DELETE_APP',
|
||||||
updateApp = 'UPDATE_APP',
|
updateApp = 'UPDATE_APP',
|
||||||
reorderApp = 'REORDER_APP',
|
reorderApps = 'REORDER_APPS',
|
||||||
|
sortApps = 'SORT_APPS',
|
||||||
// Categories
|
// Categories
|
||||||
getCategories = 'GET_CATEGORIES',
|
getCategories = 'GET_CATEGORIES',
|
||||||
getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS',
|
getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS',
|
||||||
|
@ -68,7 +70,8 @@ export type Action =
|
||||||
AddAppAction |
|
AddAppAction |
|
||||||
DeleteAppAction |
|
DeleteAppAction |
|
||||||
UpdateAppAction |
|
UpdateAppAction |
|
||||||
ReorderAppAction |
|
ReorderAppsAction |
|
||||||
|
SortAppsAction |
|
||||||
// Categories
|
// Categories
|
||||||
GetCategoriesAction<any> |
|
GetCategoriesAction<any> |
|
||||||
AddCategoryAction |
|
AddCategoryAction |
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { ActionTypes } from './actionTypes';
|
import { ActionTypes } from './actionTypes';
|
||||||
import { App, ApiResponse, NewApp } from '../../interfaces';
|
import { App, ApiResponse, NewApp, Config } from '../../interfaces';
|
||||||
import { CreateNotificationAction } from './notification';
|
import { CreateNotificationAction } from './notification';
|
||||||
|
|
||||||
export interface GetAppsAction<T> {
|
export interface GetAppsAction<T> {
|
||||||
|
@ -73,10 +73,13 @@ export const addApp = (formData: NewApp) => async (dispatch: Dispatch) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
dispatch<AddAppAction>({
|
await dispatch<AddAppAction>({
|
||||||
type: ActionTypes.addAppSuccess,
|
type: ActionTypes.addAppSuccess,
|
||||||
payload: res.data.data
|
payload: res.data.data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Sort apps
|
||||||
|
dispatch<any>(sortApps())
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
@ -125,17 +128,20 @@ export const updateApp = (id: number, formData: NewApp) => async (dispatch: Disp
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
dispatch<UpdateAppAction>({
|
await dispatch<UpdateAppAction>({
|
||||||
type: ActionTypes.updateApp,
|
type: ActionTypes.updateApp,
|
||||||
payload: res.data.data
|
payload: res.data.data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Sort apps
|
||||||
|
dispatch<any>(sortApps())
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReorderAppAction {
|
export interface ReorderAppsAction {
|
||||||
type: ActionTypes.reorderApp;
|
type: ActionTypes.reorderApps;
|
||||||
payload: App[]
|
payload: App[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +152,7 @@ interface ReorderQuery {
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reorderApp = (apps: App[]) => async (dispatch: Dispatch) => {
|
export const reorderApps = (apps: App[]) => async (dispatch: Dispatch) => {
|
||||||
try {
|
try {
|
||||||
const updateQuery: ReorderQuery = { apps: [] }
|
const updateQuery: ReorderQuery = { apps: [] }
|
||||||
|
|
||||||
|
@ -157,11 +163,39 @@ export const reorderApp = (apps: App[]) => async (dispatch: Dispatch) => {
|
||||||
|
|
||||||
await axios.put<{}>('/api/apps/0/reorder', updateQuery);
|
await axios.put<{}>('/api/apps/0/reorder', updateQuery);
|
||||||
|
|
||||||
dispatch<ReorderAppAction>({
|
dispatch<CreateNotificationAction>({
|
||||||
type: ActionTypes.reorderApp,
|
type: ActionTypes.createNotification,
|
||||||
|
payload: {
|
||||||
|
title: 'Success',
|
||||||
|
message: 'New order saved'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch<ReorderAppsAction>({
|
||||||
|
type: ActionTypes.reorderApps,
|
||||||
payload: apps
|
payload: apps
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SortAppsAction {
|
||||||
|
type: ActionTypes.sortApps;
|
||||||
|
payload: {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sortApps = () => async (dispatch: Dispatch) => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get<ApiResponse<Config>>('/api/config/useOrdering');
|
||||||
|
|
||||||
|
console.log(res.data.data);
|
||||||
|
|
||||||
|
dispatch<SortAppsAction>({
|
||||||
|
type: ActionTypes.sortApps,
|
||||||
|
payload: res.data.data.value
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { ActionTypes, Action } from '../actions';
|
import { ActionTypes, Action } from '../actions';
|
||||||
import { App } from '../../interfaces/App';
|
import { App } from '../../interfaces/App';
|
||||||
|
import { sortData } from '../../utility';
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
@ -52,15 +53,9 @@ const pinApp = (state: State, action: Action): State => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const addAppSuccess = (state: State, action: Action): State => {
|
const addAppSuccess = (state: State, action: Action): State => {
|
||||||
const tmpApps: App[] = [...state.apps, action.payload].sort((a: App, b: App) => {
|
|
||||||
if (a.name.toLowerCase() < b.name.toLowerCase()) { return -1 }
|
|
||||||
if (a.name.toLowerCase() > b.name.toLowerCase()) { return 1 }
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
apps: tmpApps
|
apps: [...state.apps, action.payload]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,13 +84,22 @@ const updateApp = (state: State, action: Action): State => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reorderApp = (state: State, action: Action): State => {
|
const reorderApps = (state: State, action: Action): State => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
apps: action.payload
|
apps: action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sortApps = (state: State, action: Action): State => {
|
||||||
|
const sortedApps = sortData<App>(state.apps, action.payload);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
apps: sortedApps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const appReducer = (state = initialState, action: Action) => {
|
const appReducer = (state = initialState, action: Action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.getApps: return getApps(state, action);
|
case ActionTypes.getApps: return getApps(state, action);
|
||||||
|
@ -105,7 +109,8 @@ const appReducer = (state = initialState, action: Action) => {
|
||||||
case ActionTypes.addAppSuccess: return addAppSuccess(state, action);
|
case ActionTypes.addAppSuccess: return addAppSuccess(state, action);
|
||||||
case ActionTypes.deleteApp: return deleteApp(state, action);
|
case ActionTypes.deleteApp: return deleteApp(state, action);
|
||||||
case ActionTypes.updateApp: return updateApp(state, action);
|
case ActionTypes.updateApp: return updateApp(state, action);
|
||||||
case ActionTypes.reorderApp: return reorderApp(state, action);
|
case ActionTypes.reorderApps: return reorderApps(state, action);
|
||||||
|
case ActionTypes.sortApps: return sortApps(state, action);
|
||||||
default: return state;
|
default: return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './iconParser';
|
export * from './iconParser';
|
||||||
export * from './urlParser';
|
export * from './urlParser';
|
||||||
export * from './searchConfig';
|
export * from './searchConfig';
|
||||||
export * from './checkVersion';
|
export * from './checkVersion';
|
||||||
|
export * from './sortData';
|
29
client/src/utility/sortData.ts
Normal file
29
client/src/utility/sortData.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
interface Data {
|
||||||
|
name: string;
|
||||||
|
orderId: number;
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sortData = <T extends Data>(array: T[], field: string): T[] => {
|
||||||
|
const sortedData = array.slice();
|
||||||
|
|
||||||
|
if (field === 'name') {
|
||||||
|
sortedData.sort((a: T, b: T) => {
|
||||||
|
return a.name.localeCompare(b.name, 'en', { sensitivity: 'base' })
|
||||||
|
})
|
||||||
|
} else if (field === 'orderId') {
|
||||||
|
sortedData.sort((a: T, b: T) => {
|
||||||
|
if (a.orderId < b.orderId) { return -1 }
|
||||||
|
if (a.orderId > b.orderId) { return 1 }
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
sortedData.sort((a: T, b: T) => {
|
||||||
|
if (a.createdAt < b.createdAt) { return -1 }
|
||||||
|
if (a.createdAt > b.createdAt) { return 1 }
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortedData;
|
||||||
|
}
|
|
@ -9,6 +9,11 @@ const Category = sequelize.define('Category', {
|
||||||
isPinned: {
|
isPinned: {
|
||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.BOOLEAN,
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
|
},
|
||||||
|
orderId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: null
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'categories'
|
tableName: 'categories'
|
||||||
|
|
Loading…
Reference in a new issue