mirror of
https://github.com/pawelmalak/flame.git
synced 2025-07-19 03:29:37 +02:00
Apps reordering. Sorting apps while adding them
This commit is contained in:
parent
9a1ec76ffd
commit
ce173f2c42
12 changed files with 219 additions and 53 deletions
|
@ -1 +1 @@
|
||||||
REACT_APP_VERSION=1.3.2
|
REACT_APP_VERSION=1.3.3
|
45
client/package-lock.json
generated
45
client/package-lock.json
generated
|
@ -2397,6 +2397,14 @@
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-beautiful-dnd": {
|
||||||
|
"version": "13.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz",
|
||||||
|
"integrity": "sha512-by80tJ8aTTDXT256Gl+RfLRtFjYbUWOnZuEigJgNsJrSEGxvFe5eY6k3g4VIvf0M/6+xoLgfYWoWonlOo6Wqdg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react-dom": {
|
"@types/react-dom": {
|
||||||
"version": "17.0.3",
|
"version": "17.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
|
||||||
|
@ -4614,6 +4622,14 @@
|
||||||
"postcss": "^7.0.5"
|
"postcss": "^7.0.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"css-box-model": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
|
||||||
|
"requires": {
|
||||||
|
"tiny-invariant": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"css-color-names": {
|
"css-color-names": {
|
||||||
"version": "0.0.4",
|
"version": "0.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
||||||
|
@ -9932,6 +9948,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||||
},
|
},
|
||||||
|
"memoize-one": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
||||||
|
},
|
||||||
"memory-fs": {
|
"memory-fs": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
||||||
|
@ -12300,6 +12321,11 @@
|
||||||
"performance-now": "^2.1.0"
|
"performance-now": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"raf-schd": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
|
||||||
|
},
|
||||||
"randombytes": {
|
"randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
|
@ -12362,6 +12388,20 @@
|
||||||
"whatwg-fetch": "^3.4.1"
|
"whatwg-fetch": "^3.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-beautiful-dnd": {
|
||||||
|
"version": "13.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz",
|
||||||
|
"integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.9.2",
|
||||||
|
"css-box-model": "^1.2.0",
|
||||||
|
"memoize-one": "^5.1.1",
|
||||||
|
"raf-schd": "^4.0.2",
|
||||||
|
"react-redux": "^7.2.0",
|
||||||
|
"redux": "^4.0.4",
|
||||||
|
"use-memo-one": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-dev-utils": {
|
"react-dev-utils": {
|
||||||
"version": "11.0.4",
|
"version": "11.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
||||||
|
@ -15077,6 +15117,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
||||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
|
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
|
||||||
},
|
},
|
||||||
|
"use-memo-one": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ=="
|
||||||
|
},
|
||||||
"util": {
|
"util": {
|
||||||
"version": "0.11.1",
|
"version": "0.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||||
|
|
|
@ -11,12 +11,14 @@
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^26.0.23",
|
||||||
"@types/node": "^12.20.12",
|
"@types/node": "^12.20.12",
|
||||||
"@types/react": "^17.0.5",
|
"@types/react": "^17.0.5",
|
||||||
|
"@types/react-beautiful-dnd": "^13.0.0",
|
||||||
"@types/react-dom": "^17.0.3",
|
"@types/react-dom": "^17.0.3",
|
||||||
"@types/react-redux": "^7.1.16",
|
"@types/react-redux": "^7.1.16",
|
||||||
"@types/react-router-dom": "^5.1.7",
|
"@types/react-router-dom": "^5.1.7",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"http-proxy-middleware": "^2.0.0",
|
"http-proxy-middleware": "^2.0.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-redux": "^7.2.4",
|
"react-redux": "^7.2.4",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { KeyboardEvent } from 'react';
|
import { KeyboardEvent } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { App, GlobalState } from '../../../interfaces';
|
import { App, GlobalState } from '../../../interfaces';
|
||||||
import { pinApp, deleteApp } from '../../../store/actions';
|
import { pinApp, deleteApp, reorderApp } from '../../../store/actions';
|
||||||
|
import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd';
|
||||||
|
|
||||||
import classes from './AppTable.module.css';
|
import classes from './AppTable.module.css';
|
||||||
import Icon from '../../UI/Icons/Icon/Icon';
|
import Icon from '../../UI/Icons/Icon/Icon';
|
||||||
|
@ -12,6 +13,7 @@ interface ComponentProps {
|
||||||
pinApp: (app: App) => void;
|
pinApp: (app: App) => void;
|
||||||
deleteApp: (id: number) => void;
|
deleteApp: (id: number) => void;
|
||||||
updateAppHandler: (app: App) => void;
|
updateAppHandler: (app: App) => void;
|
||||||
|
reorderApp: (apps: App[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppTable = (props: ComponentProps): JSX.Element => {
|
const AppTable = (props: ComponentProps): JSX.Element => {
|
||||||
|
@ -29,49 +31,89 @@ const AppTable = (props: ComponentProps): JSX.Element => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dragEndHanlder = (result: DropResult): void => {
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
|
if (!result.destination) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmpApps = [...props.apps];
|
||||||
|
const [movedApp] = tmpApps.splice(result.source.index, 1);
|
||||||
|
tmpApps.splice(result.destination.index, 0, movedApp);
|
||||||
|
|
||||||
|
props.reorderApp(tmpApps);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table headers={[
|
<DragDropContext onDragEnd={dragEndHanlder}>
|
||||||
'Name',
|
<Droppable droppableId='apps'>
|
||||||
'URL',
|
{(provided) => (
|
||||||
'Icon',
|
<Table headers={[
|
||||||
'Actions'
|
'Name',
|
||||||
]}>
|
'URL',
|
||||||
{props.apps.map((app: App): JSX.Element => {
|
'Icon',
|
||||||
return (
|
'Actions'
|
||||||
<tr key={app.id}>
|
]}
|
||||||
<td>{app.name}</td>
|
innerRef={provided.innerRef}>
|
||||||
<td>{app.url}</td>
|
{props.apps.map((app: App, index): JSX.Element => {
|
||||||
<td>{app.icon}</td>
|
return (
|
||||||
<td className={classes.TableActions}>
|
<Draggable key={app.id} draggableId={app.id.toString()} index={index}>
|
||||||
<div
|
{(provided, snapshot) => {
|
||||||
className={classes.TableAction}
|
const style = {
|
||||||
onClick={() => deleteAppHandler(app)}
|
border: snapshot.isDragging ? '1px solid var(--color-accent)' : 'none',
|
||||||
onKeyDown={(e) => keyboardActionHandler(e, app, deleteAppHandler)}
|
borderRadius: '4px',
|
||||||
tabIndex={0}>
|
...provided.draggableProps.style,
|
||||||
<Icon icon='mdiDelete' />
|
};
|
||||||
</div>
|
|
||||||
<div
|
return (
|
||||||
className={classes.TableAction}
|
<tr
|
||||||
onClick={() => props.updateAppHandler(app)}
|
{...provided.draggableProps}
|
||||||
onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
|
{...provided.dragHandleProps}
|
||||||
tabIndex={0}>
|
ref={provided.innerRef}
|
||||||
<Icon icon='mdiPencil' />
|
style={style}
|
||||||
</div>
|
>
|
||||||
<div
|
<td style={{width:'200px'}}>{app.name}</td>
|
||||||
className={classes.TableAction}
|
<td style={{width:'200px'}}>{app.url}</td>
|
||||||
onClick={() => props.pinApp(app)}
|
<td style={{width:'200px'}}>{app.icon}</td>
|
||||||
onKeyDown={(e) => keyboardActionHandler(e, app, props.pinApp)}
|
{!snapshot.isDragging && (
|
||||||
tabIndex={0}>
|
<td className={classes.TableActions}>
|
||||||
{app.isPinned
|
<div
|
||||||
? <Icon icon='mdiPinOff' color='var(--color-accent)' />
|
className={classes.TableAction}
|
||||||
: <Icon icon='mdiPin' />
|
onClick={() => deleteAppHandler(app)}
|
||||||
}
|
onKeyDown={(e) => keyboardActionHandler(e, app, deleteAppHandler)}
|
||||||
</div>
|
tabIndex={0}>
|
||||||
</td>
|
<Icon icon='mdiDelete' />
|
||||||
</tr>
|
</div>
|
||||||
)
|
<div
|
||||||
})}
|
className={classes.TableAction}
|
||||||
</Table>
|
onClick={() => props.updateAppHandler(app)}
|
||||||
|
onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
|
||||||
|
tabIndex={0}>
|
||||||
|
<Icon icon='mdiPencil' />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={classes.TableAction}
|
||||||
|
onClick={() => props.pinApp(app)}
|
||||||
|
onKeyDown={(e) => keyboardActionHandler(e, app, props.pinApp)}
|
||||||
|
tabIndex={0}>
|
||||||
|
{app.isPinned
|
||||||
|
? <Icon icon='mdiPinOff' color='var(--color-accent)' />
|
||||||
|
: <Icon icon='mdiPin' />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Draggable>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</DragDropContext>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,4 +123,4 @@ const mapStateToProps = (state: GlobalState) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, { pinApp, deleteApp })(AppTable);
|
export default connect(mapStateToProps, { pinApp, deleteApp, reorderApp })(AppTable);
|
|
@ -8,15 +8,17 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Table th,
|
.Table th,
|
||||||
.Table td {
|
.Table td {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Head */
|
/* Head */
|
||||||
|
|
||||||
.Table th {
|
.Table th {
|
||||||
--header-radius: 4px;
|
--header-radius: 4px;
|
||||||
background-color: var(--color-primary);
|
background-color: var(--color-primary);
|
||||||
|
@ -34,8 +36,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Body */
|
/* Body */
|
||||||
|
|
||||||
.Table td {
|
.Table td {
|
||||||
/* opacity: 0.5; */
|
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
|
@ -3,11 +3,12 @@ import classes from './Table.module.css';
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
children: JSX.Element | JSX.Element[];
|
children: JSX.Element | JSX.Element[];
|
||||||
headers: string[];
|
headers: string[];
|
||||||
|
innerRef?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Table = (props: ComponentProps): JSX.Element => {
|
const Table = (props: ComponentProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className={classes.TableContainer}>
|
<div className={classes.TableContainer} ref={props.innerRef}>
|
||||||
<table className={classes.Table}>
|
<table className={classes.Table}>
|
||||||
<thead className={classes.TableHead}>
|
<thead className={classes.TableHead}>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
AddAppAction,
|
AddAppAction,
|
||||||
DeleteAppAction,
|
DeleteAppAction,
|
||||||
UpdateAppAction,
|
UpdateAppAction,
|
||||||
|
ReorderAppAction,
|
||||||
// Categories
|
// Categories
|
||||||
GetCategoriesAction,
|
GetCategoriesAction,
|
||||||
AddCategoryAction,
|
AddCategoryAction,
|
||||||
|
@ -37,6 +38,7 @@ 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',
|
||||||
// Categories
|
// Categories
|
||||||
getCategories = 'GET_CATEGORIES',
|
getCategories = 'GET_CATEGORIES',
|
||||||
getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS',
|
getCategoriesSuccess = 'GET_CATEGORIES_SUCCESS',
|
||||||
|
@ -66,6 +68,7 @@ export type Action =
|
||||||
AddAppAction |
|
AddAppAction |
|
||||||
DeleteAppAction |
|
DeleteAppAction |
|
||||||
UpdateAppAction |
|
UpdateAppAction |
|
||||||
|
ReorderAppAction |
|
||||||
// Categories
|
// Categories
|
||||||
GetCategoriesAction<any> |
|
GetCategoriesAction<any> |
|
||||||
AddCategoryAction |
|
AddCategoryAction |
|
||||||
|
|
|
@ -132,4 +132,36 @@ export const updateApp = (id: number, formData: NewApp) => async (dispatch: Disp
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReorderAppAction {
|
||||||
|
type: ActionTypes.reorderApp;
|
||||||
|
payload: App[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReorderQuery {
|
||||||
|
apps: {
|
||||||
|
id: number;
|
||||||
|
orderId: number;
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reorderApp = (apps: App[]) => async (dispatch: Dispatch) => {
|
||||||
|
try {
|
||||||
|
const updateQuery: ReorderQuery = { apps: [] }
|
||||||
|
|
||||||
|
apps.forEach((app, index) => updateQuery.apps.push({
|
||||||
|
id: app.id,
|
||||||
|
orderId: index + 1
|
||||||
|
}))
|
||||||
|
|
||||||
|
await axios.put<{}>('/api/apps/0/reorder', updateQuery);
|
||||||
|
|
||||||
|
dispatch<ReorderAppAction>({
|
||||||
|
type: ActionTypes.reorderApp,
|
||||||
|
payload: apps
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -52,8 +52,12 @@ const pinApp = (state: State, action: Action): State => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const addAppSuccess = (state: State, action: Action): State => {
|
const addAppSuccess = (state: State, action: Action): State => {
|
||||||
const tmpApps = [...state.apps, action.payload];
|
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: tmpApps
|
||||||
|
@ -85,6 +89,13 @@ const updateApp = (state: State, action: Action): State => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reorderApp = (state: State, action: Action): State => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
apps: action.payload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@ -94,6 +105,7 @@ 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);
|
||||||
default: return state;
|
default: return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,11 @@ exports.createApp = asyncWrapper(async (req, res, next) => {
|
||||||
// @route GET /api/apps
|
// @route GET /api/apps
|
||||||
// @access Public
|
// @access Public
|
||||||
exports.getApps = asyncWrapper(async (req, res, next) => {
|
exports.getApps = asyncWrapper(async (req, res, next) => {
|
||||||
|
// const apps = await App.findAll({
|
||||||
|
// order: [[ Sequelize.fn('lower', Sequelize.col('name')), 'ASC' ]]
|
||||||
|
// });
|
||||||
const apps = await App.findAll({
|
const apps = await App.findAll({
|
||||||
order: [[ Sequelize.fn('lower', Sequelize.col('name')), 'ASC' ]]
|
order: [[ 'orderId', 'ASC' ]]
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
|
@ -92,6 +95,22 @@ exports.deleteApp = asyncWrapper(async (req, res, next) => {
|
||||||
where: { id: req.params.id }
|
where: { id: req.params.id }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// @desc Reorder apps
|
||||||
|
// @route PUT /api/apps/0/reorder
|
||||||
|
// @access Public
|
||||||
|
exports.reorderApps = asyncWrapper(async (req, res, next) => {
|
||||||
|
req.body.apps.forEach(async ({ id, orderId }) => {
|
||||||
|
await App.update({ orderId }, {
|
||||||
|
where: { id }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: {}
|
data: {}
|
||||||
|
|
|
@ -18,6 +18,11 @@ const App = sequelize.define('App', {
|
||||||
isPinned: {
|
isPinned: {
|
||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.BOOLEAN,
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
|
},
|
||||||
|
orderId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: null
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: 'apps'
|
tableName: 'apps'
|
||||||
|
|
|
@ -6,7 +6,8 @@ const {
|
||||||
getApps,
|
getApps,
|
||||||
getApp,
|
getApp,
|
||||||
updateApp,
|
updateApp,
|
||||||
deleteApp
|
deleteApp,
|
||||||
|
reorderApps
|
||||||
} = require('../controllers/apps');
|
} = require('../controllers/apps');
|
||||||
|
|
||||||
router
|
router
|
||||||
|
@ -20,4 +21,8 @@ router
|
||||||
.put(updateApp)
|
.put(updateApp)
|
||||||
.delete(deleteApp);
|
.delete(deleteApp);
|
||||||
|
|
||||||
|
router
|
||||||
|
.route('/0/reorder')
|
||||||
|
.put(reorderApps);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
Loading…
Add table
Add a link
Reference in a new issue