Page Layout - Addition of widgets (#14184)
https://github.com/user-attachments/assets/130f12d7-5cef-4b83-912f-a9d42241f69f ~~This could be considered a POC 😅~~ addressing - https://github.com/twentyhq/core-team-issues/issues/1400 https://github.com/twentyhq/core-team-issues/issues/1403
This commit is contained in:
@@ -6,9 +6,13 @@ import { COMMAND_MENU_PAGES_CONFIG } from '@/command-menu/constants/CommandMenuP
|
||||
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
|
||||
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
|
||||
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -19,8 +23,9 @@ const StyledCommandMenuContent = styled.div`
|
||||
|
||||
export const CommandMenuRouter = () => {
|
||||
const commandMenuPage = useRecoilValue(commandMenuPageState);
|
||||
|
||||
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
|
||||
const location = useLocation();
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const commandMenuPageComponent = isDefined(commandMenuPage) ? (
|
||||
COMMAND_MENU_PAGES_CONFIG.get(commandMenuPage)
|
||||
@@ -30,6 +35,13 @@ export const CommandMenuRouter = () => {
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const isSettingsPage = location.pathname.includes(SettingsPath.PageLayout);
|
||||
const objectMetadataItemOverride = isSettingsPage
|
||||
? objectMetadataItems.find(
|
||||
(item) => item.nameSingular === CoreObjectNameSingular.Dashboard,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<CommandMenuContainer>
|
||||
<CommandMenuContextChipRecordSetterEffect />
|
||||
@@ -52,6 +64,7 @@ export const CommandMenuRouter = () => {
|
||||
isInRightDrawer={true}
|
||||
displayType="listItem"
|
||||
actionMenuType="command-menu"
|
||||
objectMetadataItemOverride={objectMetadataItemOverride}
|
||||
>
|
||||
{commandMenuPageComponent}
|
||||
</ActionMenuContextProvider>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { CommandMenuAIChatThreadsPage } from '@/command-menu/pages/AIChatThreads
|
||||
import { CommandMenuAskAIPage } from '@/command-menu/pages/ask-ai/components/CommandMenuAskAIPage';
|
||||
import { CommandMenuCalendarEventPage } from '@/command-menu/pages/calendar-event/components/CommandMenuCalendarEventPage';
|
||||
import { CommandMenuMessageThreadPage } from '@/command-menu/pages/message-thread/components/CommandMenuMessageThreadPage';
|
||||
import { CommandMenuPageLayoutGraphTypeSelect } from '@/command-menu/pages/page-layout/components/CommandMenuPageLayoutGraphTypeSelect';
|
||||
import { CommandMenuPageLayoutWidgetTypeSelect } from '@/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect';
|
||||
import { CommandMenuMergeRecordPage } from '@/command-menu/pages/record-page/components/CommandMenuMergeRecordPage';
|
||||
import { CommandMenuRecordPage } from '@/command-menu/pages/record-page/components/CommandMenuRecordPage';
|
||||
import { CommandMenuEditRichTextPage } from '@/command-menu/pages/rich-text-page/components/CommandMenuEditRichTextPage';
|
||||
@@ -38,4 +40,12 @@ export const COMMAND_MENU_PAGES_CONFIG = new Map<
|
||||
[CommandMenuPages.SearchRecords, <CommandMenuSearchRecordsPage />],
|
||||
[CommandMenuPages.AskAI, <CommandMenuAskAIPage />],
|
||||
[CommandMenuPages.ViewPreviousAIChats, <CommandMenuAIChatThreadsPage />],
|
||||
[
|
||||
CommandMenuPages.PageLayoutWidgetTypeSelect,
|
||||
<CommandMenuPageLayoutWidgetTypeSelect />,
|
||||
],
|
||||
[
|
||||
CommandMenuPages.PageLayoutGraphTypeSelect,
|
||||
<CommandMenuPageLayoutGraphTypeSelect />,
|
||||
],
|
||||
]);
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { usePageLayoutWidgetCreate } from '@/settings/page-layout/hooks/usePageLayoutWidgetCreate';
|
||||
import {
|
||||
GraphSubType,
|
||||
WidgetType,
|
||||
} from '@/settings/page-layout/mocks/mockWidgets';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
IconChartBar,
|
||||
IconChartPie,
|
||||
IconGauge,
|
||||
IconNumber,
|
||||
} from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledSectionTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const graphTypeOptions = [
|
||||
{
|
||||
type: GraphSubType.BAR,
|
||||
icon: IconChartBar,
|
||||
title: 'Bar Chart',
|
||||
},
|
||||
{
|
||||
type: GraphSubType.PIE,
|
||||
icon: IconChartPie,
|
||||
title: 'Pie Chart',
|
||||
},
|
||||
{
|
||||
type: GraphSubType.GAUGE,
|
||||
icon: IconGauge,
|
||||
title: 'Gauge',
|
||||
},
|
||||
{
|
||||
type: GraphSubType.NUMBER,
|
||||
icon: IconNumber,
|
||||
title: 'Number',
|
||||
},
|
||||
];
|
||||
|
||||
export const CommandMenuPageLayoutGraphTypeSelect = () => {
|
||||
const { closeCommandMenu } = useCommandMenu();
|
||||
const { handleCreateWidget } = usePageLayoutWidgetCreate();
|
||||
|
||||
const handleSelectGraphType = (graphType: GraphSubType) => {
|
||||
handleCreateWidget(WidgetType.GRAPH, graphType);
|
||||
closeCommandMenu();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledSectionTitle>Graph type</StyledSectionTitle>
|
||||
|
||||
{graphTypeOptions.map((option) => (
|
||||
<MenuItemCommand
|
||||
key={option.type}
|
||||
LeftIcon={option.icon}
|
||||
text={option.title}
|
||||
onClick={() => handleSelectGraphType(option.type)}
|
||||
/>
|
||||
))}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
|
||||
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import { WidgetType } from '@/settings/page-layout/mocks/mockWidgets';
|
||||
import { pageLayoutDraggedAreaState } from '@/settings/page-layout/states/pageLayoutDraggedAreaState';
|
||||
import styled from '@emotion/styled';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { IconChartPie, IconFrame, IconList } from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledSectionTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledDisabledMenuItem = styled.div`
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
const widgetTypeOptions = [
|
||||
{
|
||||
type: WidgetType.GRAPH,
|
||||
icon: IconChartPie,
|
||||
title: 'Add a graph',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
type: WidgetType.VIEW,
|
||||
icon: IconList,
|
||||
title: 'Add a view',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
type: WidgetType.IFRAME,
|
||||
icon: IconFrame,
|
||||
title: 'Add an iframe',
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const CommandMenuPageLayoutWidgetTypeSelect = () => {
|
||||
const { navigateCommandMenu } = useNavigateCommandMenu();
|
||||
const setPageLayoutDraggedArea = useSetRecoilState(
|
||||
pageLayoutDraggedAreaState,
|
||||
);
|
||||
|
||||
const handleSelectWidget = (widgetType: WidgetType) => {
|
||||
if (widgetType === WidgetType.GRAPH) {
|
||||
navigateCommandMenu({
|
||||
page: CommandMenuPages.PageLayoutGraphTypeSelect,
|
||||
pageTitle: 'Select Graph Type',
|
||||
pageIcon: IconChartPie,
|
||||
});
|
||||
} else {
|
||||
setPageLayoutDraggedArea(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledSectionTitle>Widget type</StyledSectionTitle>
|
||||
{widgetTypeOptions.map((option) => {
|
||||
const MenuItem = (
|
||||
<MenuItemCommand
|
||||
key={option.type}
|
||||
LeftIcon={option.icon}
|
||||
text={option.title}
|
||||
onClick={() => handleSelectWidget(option.type)}
|
||||
/>
|
||||
);
|
||||
|
||||
return option.disabled ? (
|
||||
<StyledDisabledMenuItem key={option.type}>
|
||||
{MenuItem}
|
||||
</StyledDisabledMenuItem>
|
||||
) : (
|
||||
MenuItem
|
||||
);
|
||||
})}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@@ -14,4 +14,6 @@ export enum CommandMenuPages {
|
||||
SearchRecords = 'search-records',
|
||||
AskAI = 'ask-ai',
|
||||
ViewPreviousAIChats = 'view-previous-ai-chats',
|
||||
PageLayoutWidgetTypeSelect = 'page-layout-widget-type-select',
|
||||
PageLayoutGraphTypeSelect = 'page-layout-graph-type-select',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user