1
0
Fork 0
mirror of https://github.com/pawelmalak/flame.git synced 2025-07-23 05:19:37 +02:00

Apps reordering with drag-and-drop functionality

This commit is contained in:
unknown 2021-06-18 12:09:59 +02:00
parent 754dc3a7b9
commit 5b900872af
4 changed files with 134 additions and 90 deletions

View file

@ -1 +1 @@
REACT_APP_VERSION=1.3.5 REACT_APP_VERSION=1.3.6

View file

@ -20,10 +20,10 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
.Message span { .Message a {
color: var(--color-accent); color: var(--color-accent);
} }
.Message span:hover { .Message a:hover {
cursor: pointer; cursor: pointer;
} }

View file

@ -1,22 +1,52 @@
import { KeyboardEvent } from 'react'; import { Fragment, KeyboardEvent, useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { App, GlobalState } from '../../../interfaces';
import { pinApp, deleteApp, reorderApp } from '../../../store/actions';
import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd'; import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd';
import { Link } from 'react-router-dom';
// Redux
import { connect } from 'react-redux';
import { pinApp, deleteApp, reorderApps, updateConfig, createNotification } from '../../../store/actions';
// Typescript
import { App, GlobalState, NewNotification } from '../../../interfaces';
// CSS
import classes from './AppTable.module.css'; import classes from './AppTable.module.css';
// UI
import Icon from '../../UI/Icons/Icon/Icon'; import Icon from '../../UI/Icons/Icon/Icon';
import Table from '../../UI/Table/Table'; import Table from '../../UI/Table/Table';
// Utils
import { searchConfig } from '../../../utility';
interface ComponentProps { interface ComponentProps {
apps: App[]; apps: App[];
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; reorderApps: (apps: App[]) => void;
updateConfig: (formData: any) => void;
createNotification: (notification: NewNotification) => void;
} }
const AppTable = (props: ComponentProps): JSX.Element => { const AppTable = (props: ComponentProps): JSX.Element => {
const [localApps, setLocalApps] = useState<App[]>([]);
const [isCustomOrder, setIsCustomOrder] = useState<boolean>(false);
// Copy apps array
useEffect(() => {
setLocalApps([...props.apps]);
}, [props.apps])
// Check ordering
useEffect(() => {
const order = searchConfig('useOrdering', '');
if (order === 'orderId') {
setIsCustomOrder(true);
}
}, [])
const deleteAppHandler = (app: App): void => { const deleteAppHandler = (app: App): void => {
const proceed = window.confirm(`Are you sure you want to delete ${app.name} at ${app.url} ?`); const proceed = window.confirm(`Are you sure you want to delete ${app.name} at ${app.url} ?`);
@ -25,6 +55,7 @@ const AppTable = (props: ComponentProps): JSX.Element => {
} }
} }
// Support keyboard navigation for actions
const keyboardActionHandler = (e: KeyboardEvent, app: App, handler: Function) => { const keyboardActionHandler = (e: KeyboardEvent, app: App, handler: Function) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
handler(app); handler(app);
@ -32,20 +63,34 @@ const AppTable = (props: ComponentProps): JSX.Element => {
} }
const dragEndHanlder = (result: DropResult): void => { const dragEndHanlder = (result: DropResult): void => {
console.log(result); if (!isCustomOrder) {
props.createNotification({
title: 'Error',
message: 'Custom order is disabled'
})
return;
}
if (!result.destination) { if (!result.destination) {
return; return;
} }
const tmpApps = [...props.apps]; const tmpApps = [...localApps];
const [movedApp] = tmpApps.splice(result.source.index, 1); const [movedApp] = tmpApps.splice(result.source.index, 1);
tmpApps.splice(result.destination.index, 0, movedApp); tmpApps.splice(result.destination.index, 0, movedApp);
props.reorderApp(tmpApps); setLocalApps(tmpApps);
props.reorderApps(tmpApps);
} }
return ( return (
<Fragment>
<div className={classes.Message}>
{isCustomOrder
? <p>You can drag and drop single rows to reorder application</p>
: <p>Custom order is disabled. You can change it in <Link to='/settings/other'>settings</Link></p>
}
</div>
<DragDropContext onDragEnd={dragEndHanlder}> <DragDropContext onDragEnd={dragEndHanlder}>
<Droppable droppableId='apps'> <Droppable droppableId='apps'>
{(provided) => ( {(provided) => (
@ -56,7 +101,7 @@ const AppTable = (props: ComponentProps): JSX.Element => {
'Actions' 'Actions'
]} ]}
innerRef={provided.innerRef}> innerRef={provided.innerRef}>
{props.apps.map((app: App, index): JSX.Element => { {localApps.map((app: App, index): JSX.Element => {
return ( return (
<Draggable key={app.id} draggableId={app.id.toString()} index={index}> <Draggable key={app.id} draggableId={app.id.toString()} index={index}>
{(provided, snapshot) => { {(provided, snapshot) => {
@ -73,9 +118,9 @@ const AppTable = (props: ComponentProps): JSX.Element => {
ref={provided.innerRef} ref={provided.innerRef}
style={style} style={style}
> >
<td style={{width:'200px'}}>{app.name}</td> <td style={{ width:'200px' }}>{app.name}</td>
<td style={{width:'200px'}}>{app.url}</td> <td style={{ width:'200px' }}>{app.url}</td>
<td style={{width:'200px'}}>{app.icon}</td> <td style={{ width:'200px' }}>{app.icon}</td>
{!snapshot.isDragging && ( {!snapshot.isDragging && (
<td className={classes.TableActions}> <td className={classes.TableActions}>
<div <div
@ -114,6 +159,7 @@ const AppTable = (props: ComponentProps): JSX.Element => {
)} )}
</Droppable> </Droppable>
</DragDropContext> </DragDropContext>
</Fragment>
) )
} }
@ -123,4 +169,12 @@ const mapStateToProps = (state: GlobalState) => {
} }
} }
export default connect(mapStateToProps, { pinApp, deleteApp, reorderApp })(AppTable); const actions = {
pinApp,
deleteApp,
reorderApps,
updateConfig,
createNotification
}
export default connect(mapStateToProps, actions)(AppTable);

View file

@ -161,15 +161,7 @@ export const reorderApps = (apps: App[]) => async (dispatch: Dispatch) => {
orderId: index + 1 orderId: index + 1
})) }))
await axios.put<{}>('/api/apps/0/reorder', updateQuery); await axios.put<ApiResponse<{}>>('/api/apps/0/reorder', updateQuery);
dispatch<CreateNotificationAction>({
type: ActionTypes.createNotification,
payload: {
title: 'Success',
message: 'New order saved'
}
})
dispatch<ReorderAppsAction>({ dispatch<ReorderAppsAction>({
type: ActionTypes.reorderApps, type: ActionTypes.reorderApps,
@ -189,8 +181,6 @@ export const sortApps = () => async (dispatch: Dispatch) => {
try { try {
const res = await axios.get<ApiResponse<Config>>('/api/config/useOrdering'); const res = await axios.get<ApiResponse<Config>>('/api/config/useOrdering');
console.log(res.data.data);
dispatch<SortAppsAction>({ dispatch<SortAppsAction>({
type: ActionTypes.sortApps, type: ActionTypes.sortApps,
payload: res.data.data.value payload: res.data.data.value