Skip to content
This repository was archived by the owner on May 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 76 additions & 3 deletions src/pages/Dashboards/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Button, Divider, FileInput, Modal, Stack, Text } from '@mantine/core';
import { Box, Button, Divider, FileInput, Modal, Stack, Text, TextInput } from '@mantine/core';
import Toolbar from './Toolbar';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
Expand All @@ -20,10 +20,10 @@ import { useDashboardsQuery } from '@/hooks/useDashboards';
import Tile from './Tile';
import { Layout } from 'react-grid-layout';
import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider';
import { ImportDashboardType } from '@/@types/parseable/api/dashboards';
import { EditTileType, ImportDashboardType, Tile as TileType } from '@/@types/parseable/api/dashboards';
import { templates } from './assets/templates';

const { toggleCreateDashboardModal, toggleCreateTileModal, toggleDeleteTileModal, handlePaging, toggleImportDashboardModal } =
const { toggleCreateDashboardModal, toggleCreateTileModal, toggleDuplicateTileModal, toggleDeleteTileModal, handlePaging, toggleImportDashboardModal } =
dashboardsStoreReducers;

const TilesView = (props: { onLayoutChange: (layout: Layout[]) => void }) => {
Expand Down Expand Up @@ -319,6 +319,78 @@ const NoTilesView = () => {
);
};

const findTileByTileId = (tiles: TileType[], tileId: string | null) => {
return _.find(tiles, tile => tile.tile_id === tileId)
}

const DuplicateTileModal = () => {
const [duplicateTileModalOpen, setDashboardsStore] = useDashboardsStore(store => store.duplicateTileModalOpen)
const [editTileId] = useDashboardsStore(store => store.editTileId);
const [activeDashboard] = useDashboardsStore(store => store.activeDashboard)
const [inputValue, setInputValue] = useState<string>('');
const onClose = useCallback(() => {
setDashboardsStore((store) => toggleDuplicateTileModal(store, false, null));
}, []);

const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
}, []);
const { updateDashboard, isUpdatingDashboard } = useDashboardsQuery({});

const handleSubmit = useCallback(() => {
const currentTile = findTileByTileId(activeDashboard?.tiles || [], editTileId);
if (currentTile && activeDashboard) {
const currentOrder = currentTile.order;
const tempTiles = [...activeDashboard.tiles] as EditTileType[];
const duplicatedTile = _.omit({ ...currentTile, name: inputValue }, 'tile_id');
tempTiles.splice(currentOrder, 0, duplicatedTile);
const updatedTilesWithOrder = assignOrderToTiles(tempTiles);
return updateDashboard({
dashboard: { ...activeDashboard, tiles: updatedTilesWithOrder },
onSuccess: () => {
onClose();
},
});
}
}, [inputValue, editTileId, activeDashboard]);

useEffect(() => {
const currentTile = findTileByTileId(activeDashboard?.tiles || [], editTileId);
if (currentTile) {
setInputValue(currentTile?.name);
}
}, [editTileId]);

return (
<Modal
opened={duplicateTileModalOpen}
onClose={onClose}
size="auto"
centered
styles={{
body: { padding: '0 1rem 1rem 1rem', width: 400 },
header: { padding: '1rem', paddingBottom: '0.4rem' },
}}
title={<Text style={{ fontSize: '0.9rem', fontWeight: 600 }}>Duplicate Tile</Text>}>
<Stack>
<Stack gap={12}>
<TextInput value={inputValue} onChange={handleInputChange} />
</Stack>
<Stack style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end' }}>
<Box>
<Button onClick={onClose} variant="outline">
Cancel
</Button>
</Box>
<Box>
<Button onClick={handleSubmit} loading={isUpdatingDashboard} disabled={_.isEmpty(inputValue)}>Done</Button>
</Box>
</Stack>
</Stack>
</Modal>
);
};

const Dashboard = () => {
const [dashboards] = useDashboardsStore((store) => store.dashboards);
const layoutRef = useRef<Layout[]>([]);
Expand All @@ -333,6 +405,7 @@ const Dashboard = () => {
return (
<Stack style={{ flex: 1 }} gap={0}>
<DeleteTileModal />
<DuplicateTileModal/>
<Toolbar layoutRef={layoutRef} />
<ImportDashboardModal/>
<TilesView onLayoutChange={onLayoutChange} />
Expand Down
13 changes: 12 additions & 1 deletion src/pages/Dashboards/Tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import classes from './styles/tile.module.css';
import {
IconAlertTriangle,
IconBraces,
IconCopyPlus,
IconDotsVertical,
IconGripVertical,
IconPencil,
Expand Down Expand Up @@ -71,7 +72,7 @@ const ParseableLogo = () => (
</div>
);

const { toggleCreateTileModal, toggleDeleteTileModal } = dashboardsStoreReducers;
const { toggleCreateTileModal, toggleDeleteTileModal, toggleDuplicateTileModal } = dashboardsStoreReducers;

const NoDataView = () => {
return (
Expand Down Expand Up @@ -171,6 +172,10 @@ function TileControls(props: { tile: TileType; data: TileQueryResponse }) {
setDashboardsStore((store) => toggleCreateTileModal(store, true, tile_id));
}, []);

const openDuplicateTileModal = useCallback(() => {
setDashboardsStore((store) => toggleDuplicateTileModal(store, true, tile_id));
}, []);

const openDeleteModal = useCallback(() => {
setDashboardsStore((store) => toggleDeleteTileModal(store, true, tile_id));
}, []);
Expand All @@ -197,6 +202,12 @@ function TileControls(props: { tile: TileType; data: TileQueryResponse }) {
leftSection={<IconPencil className={classes.tileCtrlItemIcon} size="1rem" stroke={1.2} />}>
<Text className={classes.tileCtrlItemText}>Edit</Text>
</Menu.Item>
<Menu.Item
className={classes.tileCtrlItem}
onClick={openDuplicateTileModal}
leftSection={<IconCopyPlus className={classes.tileCtrlItemIcon} size="1rem" stroke={1.2} />}>
<Text className={classes.tileCtrlItemText}>Duplicate</Text>
</Menu.Item>
<Menu.Item
className={classes.tileCtrlItem}
onClick={exportTileConfig}
Expand Down
17 changes: 14 additions & 3 deletions src/pages/Dashboards/providers/DashboardsProvider.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Dashboard, Tile, TileQueryResponse, tileSizeWidthMap } from '@/@types/parseable/api/dashboards';
import { Dashboard, EditTileType, Tile, TileQueryResponse, tileSizeWidthMap } from '@/@types/parseable/api/dashboards';
import initContext from '@/utils/initContext';
import _ from 'lodash';
import { Layout } from 'react-grid-layout';

export const TILES_PER_PAGE = 5;
export const TILES_PER_PAGE = 10;

export const sortTilesByOrder = (tiles: Tile[], idsByOrder: string[]): Tile[] => {
return _.chain(idsByOrder)
Expand All @@ -15,7 +15,7 @@ export const sortTilesByOrder = (tiles: Tile[], idsByOrder: string[]): Tile[] =>
.value();
};

export const assignOrderToTiles = (tiles: Tile[]) => {
export const assignOrderToTiles = (tiles: Tile[] | EditTileType[]) => {
return _.map(tiles, (tile, index) => {
return { ...tile, order: index + 1 };
});
Expand Down Expand Up @@ -77,6 +77,7 @@ type DashboardsStore = {
editDashboardModalOpen: boolean;
deleteDashboardModalOpen: boolean;
createTileFormOpen: boolean;
duplicateTileModalOpen: boolean;
vizEditorModalOpen: boolean;
allowDrag: boolean;
editTileId: string | null;
Expand All @@ -99,6 +100,7 @@ const initialState: DashboardsStore = {
deleteDashboardModalOpen: false,
createTileFormOpen: false,
vizEditorModalOpen: false,
duplicateTileModalOpen: false,
allowDrag: false,
editTileId: null,
tilesData: {},
Expand All @@ -118,6 +120,7 @@ type DashboardsStoreReducers = {
toggleEditDashboardModal: (store: DashboardsStore, val: boolean) => ReducerOutput;
selectDashboard: (store: DashboardsStore, dashboardId?: string | null, dashboard?: Dashboard) => ReducerOutput;
toggleCreateTileModal: (store: DashboardsStore, val: boolean, tileId?: string | null) => ReducerOutput;
toggleDuplicateTileModal: (store: DashboardsStore, val: boolean, tileId?: string | null) => ReducerOutput;
toggleVizEditorModal: (store: DashboardsStore, val: boolean) => ReducerOutput;
toggleAllowDrag: (store: DashboardsStore) => ReducerOutput;
toggleDeleteDashboardModal: (store: DashboardsStore, val: boolean) => ReducerOutput;
Expand Down Expand Up @@ -148,6 +151,13 @@ const toggleCreateTileModal = (_store: DashboardsStore, val: boolean, tileId: st
};
};

const toggleDuplicateTileModal = (_store: DashboardsStore, val: boolean, tileId: string | null = null) => {
return {
duplicateTileModalOpen: val,
editTileId: tileId,
};
};

const toggleVizEditorModal = (_store: DashboardsStore, val: boolean) => {
return {
vizEditorModalOpen: val,
Expand Down Expand Up @@ -275,6 +285,7 @@ const dashboardsStoreReducers: DashboardsStoreReducers = {
toggleImportTileModal,
toggleImportDashboardModal,
handlePaging,
toggleDuplicateTileModal
};

export { DashbaordsProvider, useDashboardsStore, dashboardsStoreReducers };
Loading