Edit visibility restriction (#19499)
https://github.com/user-attachments/assets/98496f15-2e58-46a8-b233-9cf46b7b9600
This commit is contained in:
committed by
GitHub
parent
8a10071253
commit
e3077691d1
@@ -896,6 +896,7 @@ type PageLayoutWidget {
|
||||
position: PageLayoutWidgetPosition
|
||||
configuration: WidgetConfiguration!
|
||||
conditionalDisplay: JSON
|
||||
conditionalAvailabilityExpression: String
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
deletedAt: DateTime
|
||||
@@ -4012,6 +4013,7 @@ input UpdatePageLayoutWidgetWithIdInput {
|
||||
position: JSON
|
||||
configuration: JSON
|
||||
conditionalDisplay: JSON
|
||||
conditionalAvailabilityExpression: String
|
||||
}
|
||||
|
||||
input GridPositionInput {
|
||||
@@ -4040,6 +4042,7 @@ input UpdatePageLayoutWidgetInput {
|
||||
position: JSON
|
||||
configuration: JSON
|
||||
conditionalDisplay: JSON
|
||||
conditionalAvailabilityExpression: String
|
||||
}
|
||||
|
||||
input CreateLogicFunctionFromSourceInput {
|
||||
|
||||
@@ -671,6 +671,7 @@ export interface PageLayoutWidget {
|
||||
position?: PageLayoutWidgetPosition
|
||||
configuration: WidgetConfiguration
|
||||
conditionalDisplay?: Scalars['JSON']
|
||||
conditionalAvailabilityExpression?: Scalars['String']
|
||||
createdAt: Scalars['DateTime']
|
||||
updatedAt: Scalars['DateTime']
|
||||
deletedAt?: Scalars['DateTime']
|
||||
@@ -3811,6 +3812,7 @@ export interface PageLayoutWidgetGenqlSelection{
|
||||
position?: PageLayoutWidgetPositionGenqlSelection
|
||||
configuration?: WidgetConfigurationGenqlSelection
|
||||
conditionalDisplay?: boolean | number
|
||||
conditionalAvailabilityExpression?: boolean | number
|
||||
createdAt?: boolean | number
|
||||
updatedAt?: boolean | number
|
||||
deletedAt?: boolean | number
|
||||
@@ -6553,13 +6555,13 @@ export interface UpdatePageLayoutWithTabsInput {name: Scalars['String'],type: Pa
|
||||
|
||||
export interface UpdatePageLayoutTabWithWidgetsInput {id: Scalars['UUID'],title: Scalars['String'],position: Scalars['Float'],icon?: (Scalars['String'] | null),layoutMode?: (PageLayoutTabLayoutMode | null),widgets: UpdatePageLayoutWidgetWithIdInput[]}
|
||||
|
||||
export interface UpdatePageLayoutWidgetWithIdInput {id: Scalars['UUID'],pageLayoutTabId: Scalars['UUID'],title: Scalars['String'],type: WidgetType,objectMetadataId?: (Scalars['UUID'] | null),gridPosition: GridPositionInput,position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null)}
|
||||
export interface UpdatePageLayoutWidgetWithIdInput {id: Scalars['UUID'],pageLayoutTabId: Scalars['UUID'],title: Scalars['String'],type: WidgetType,objectMetadataId?: (Scalars['UUID'] | null),gridPosition: GridPositionInput,position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null)}
|
||||
|
||||
export interface GridPositionInput {row: Scalars['Float'],column: Scalars['Float'],rowSpan: Scalars['Float'],columnSpan: Scalars['Float']}
|
||||
|
||||
export interface CreatePageLayoutWidgetInput {pageLayoutTabId: Scalars['UUID'],title: Scalars['String'],type: WidgetType,objectMetadataId?: (Scalars['UUID'] | null),gridPosition: GridPositionInput,position?: (Scalars['JSON'] | null),configuration: Scalars['JSON']}
|
||||
|
||||
export interface UpdatePageLayoutWidgetInput {pageLayoutTabId?: (Scalars['UUID'] | null),title?: (Scalars['String'] | null),type?: (WidgetType | null),objectMetadataId?: (Scalars['UUID'] | null),gridPosition?: (GridPositionInput | null),position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null)}
|
||||
export interface UpdatePageLayoutWidgetInput {pageLayoutTabId?: (Scalars['UUID'] | null),title?: (Scalars['String'] | null),type?: (WidgetType | null),objectMetadataId?: (Scalars['UUID'] | null),gridPosition?: (GridPositionInput | null),position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null)}
|
||||
|
||||
export interface CreateLogicFunctionFromSourceInput {id?: (Scalars['UUID'] | null),universalIdentifier?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),timeoutSeconds?: (Scalars['Float'] | null),toolInputSchema?: (Scalars['JSON'] | null),isTool?: (Scalars['Boolean'] | null),source?: (Scalars['JSON'] | null),cronTriggerSettings?: (Scalars['JSON'] | null),databaseEventTriggerSettings?: (Scalars['JSON'] | null),httpRouteTriggerSettings?: (Scalars['JSON'] | null)}
|
||||
|
||||
|
||||
@@ -1893,6 +1893,9 @@ export default {
|
||||
"conditionalDisplay": [
|
||||
15
|
||||
],
|
||||
"conditionalAvailabilityExpression": [
|
||||
1
|
||||
],
|
||||
"createdAt": [
|
||||
4
|
||||
],
|
||||
@@ -10194,6 +10197,9 @@ export default {
|
||||
"conditionalDisplay": [
|
||||
15
|
||||
],
|
||||
"conditionalAvailabilityExpression": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
@@ -10266,6 +10272,9 @@ export default {
|
||||
"conditionalDisplay": [
|
||||
15
|
||||
],
|
||||
"conditionalAvailabilityExpression": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
@@ -10,6 +10,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql`
|
||||
updatedAt
|
||||
deletedAt
|
||||
conditionalDisplay
|
||||
conditionalAvailabilityExpression
|
||||
gridPosition {
|
||||
column
|
||||
columnSpan
|
||||
|
||||
@@ -12,4 +12,5 @@ export type PageLayoutWidget = Omit<
|
||||
> & {
|
||||
objectMetadataId?: Nullable<string>;
|
||||
configuration: WidgetConfiguration | FieldsConfiguration | FieldConfiguration;
|
||||
conditionalAvailabilityExpression?: Nullable<string>;
|
||||
};
|
||||
|
||||
+110
@@ -5,6 +5,7 @@ import { evaluateWidgetVisibility } from '@/page-layout/utils/evaluateWidgetVisi
|
||||
describe('evaluateWidgetVisibility', () => {
|
||||
it('should return true (visible) when no conditionalDisplay is provided', () => {
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay: undefined,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
@@ -24,6 +25,7 @@ describe('evaluateWidgetVisibility', () => {
|
||||
};
|
||||
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'MOBILE',
|
||||
@@ -43,6 +45,7 @@ describe('evaluateWidgetVisibility', () => {
|
||||
};
|
||||
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
@@ -62,6 +65,7 @@ describe('evaluateWidgetVisibility', () => {
|
||||
};
|
||||
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
@@ -81,6 +85,7 @@ describe('evaluateWidgetVisibility', () => {
|
||||
};
|
||||
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'MOBILE',
|
||||
@@ -103,6 +108,7 @@ describe('evaluateWidgetVisibility', () => {
|
||||
};
|
||||
|
||||
const resultMobile = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'MOBILE',
|
||||
@@ -110,6 +116,7 @@ describe('evaluateWidgetVisibility', () => {
|
||||
});
|
||||
|
||||
const resultDesktop = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
@@ -127,6 +134,7 @@ describe('evaluateWidgetVisibility', () => {
|
||||
|
||||
expect(() => {
|
||||
evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay: invalidConditionalDisplay,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
@@ -134,4 +142,106 @@ describe('evaluateWidgetVisibility', () => {
|
||||
});
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
describe('conditionalAvailabilityExpression', () => {
|
||||
it('should return true when expression matches MOBILE device', () => {
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: 'device == "MOBILE"',
|
||||
conditionalDisplay: undefined,
|
||||
context: {
|
||||
device: 'MOBILE',
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when expression does not match MOBILE device', () => {
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: 'device == "MOBILE"',
|
||||
conditionalDisplay: undefined,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when expression matches DESKTOP device', () => {
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: 'device == "DESKTOP"',
|
||||
conditionalDisplay: undefined,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should take priority over conditionalDisplay when both are set', () => {
|
||||
const conditionalDisplay: RulesLogic = {
|
||||
and: [
|
||||
{
|
||||
'===': [{ var: 'device' }, 'DESKTOP'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: 'device == "MOBILE"',
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
},
|
||||
});
|
||||
|
||||
// Expression says MOBILE only, so DESKTOP should be hidden
|
||||
// even though conditionalDisplay says DESKTOP is visible
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should fall through to conditionalDisplay when expression is null', () => {
|
||||
const conditionalDisplay: RulesLogic = {
|
||||
and: [
|
||||
{
|
||||
'===': [{ var: 'device' }, 'MOBILE'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: null,
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
},
|
||||
});
|
||||
|
||||
// Expression is null, so conditionalDisplay takes over
|
||||
// conditionalDisplay says MOBILE only → DESKTOP hidden
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should fall through to conditionalDisplay when expression is undefined', () => {
|
||||
const conditionalDisplay: RulesLogic = {
|
||||
and: [
|
||||
{
|
||||
'===': [{ var: 'device' }, 'DESKTOP'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression: undefined,
|
||||
conditionalDisplay,
|
||||
context: {
|
||||
device: 'DESKTOP',
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+2
@@ -76,6 +76,8 @@ export const convertPageLayoutDraftToUpdateInput = (
|
||||
tab.layoutMode ?? PageLayoutTabLayoutMode.GRID,
|
||||
),
|
||||
configuration: widget.configuration ?? null,
|
||||
conditionalAvailabilityExpression:
|
||||
widget.conditionalAvailabilityExpression ?? null,
|
||||
})),
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import jsonLogic, { type RulesLogic } from 'json-logic-js';
|
||||
import {
|
||||
evaluateConditionalAvailabilityExpression,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type WidgetVisibilityContext } from '@/page-layout/types/WidgetVisibilityContext';
|
||||
|
||||
type EvaluateWidgetVisibilityParams = {
|
||||
conditionalAvailabilityExpression: string | null | undefined;
|
||||
conditionalDisplay: RulesLogic | undefined;
|
||||
context: WidgetVisibilityContext;
|
||||
};
|
||||
|
||||
export const evaluateWidgetVisibility = ({
|
||||
conditionalAvailabilityExpression,
|
||||
conditionalDisplay,
|
||||
context,
|
||||
}: EvaluateWidgetVisibilityParams): boolean => {
|
||||
if (isDefined(conditionalAvailabilityExpression)) {
|
||||
return evaluateConditionalAvailabilityExpression(
|
||||
conditionalAvailabilityExpression,
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isDefined(conditionalDisplay)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ export const filterVisibleWidgets = ({
|
||||
}: FilterVisibleWidgetsParams): PageLayoutTab['widgets'] => {
|
||||
return widgets.filter((widget) => {
|
||||
return evaluateWidgetVisibility({
|
||||
conditionalAvailabilityExpression:
|
||||
widget.conditionalAvailabilityExpression,
|
||||
conditionalDisplay: widget.conditionalDisplay,
|
||||
context,
|
||||
});
|
||||
|
||||
+37
@@ -1,10 +1,15 @@
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown';
|
||||
import { useDeletePageLayoutWidget } from '@/page-layout/hooks/useDeletePageLayoutWidget';
|
||||
import { useResetPageLayoutWidgetToDefault } from '@/page-layout/hooks/useResetPageLayoutWidgetToDefault';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { WidgetVisibilityDropdownContent } from '@/side-panel/pages/page-layout/components/dropdown-content/WidgetVisibilityDropdownContent';
|
||||
import { WIDGET_SETTINGS_SELECTABLE_ITEM_IDS } from '@/side-panel/pages/page-layout/constants/settings/WidgetSettingsSelectableItemIds';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { getVisibilityLabel } from '@/side-panel/pages/page-layout/utils/getVisibilityLabel';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
@@ -13,6 +18,7 @@ import { useLingui } from '@lingui/react/macro';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconEyeX,
|
||||
IconRefreshDot,
|
||||
IconSwitchHorizontal,
|
||||
IconTrash,
|
||||
@@ -29,6 +35,8 @@ export const WidgetSettingsManageSection = ({
|
||||
}: WidgetSettingsManageSectionProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const pageLayoutEditingWidgetId = useAtomComponentStateValue(
|
||||
pageLayoutEditingWidgetIdComponentState,
|
||||
pageLayoutId,
|
||||
@@ -47,6 +55,15 @@ export const WidgetSettingsManageSection = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const visibilityLabel = getVisibilityLabel(
|
||||
widgetInEditMode?.conditionalAvailabilityExpression,
|
||||
{
|
||||
anyDevice: t`Any device`,
|
||||
mobile: t`Mobile`,
|
||||
desktop: t`Desktop`,
|
||||
},
|
||||
);
|
||||
|
||||
const handleResetToDefault = () => {
|
||||
openModal(RESET_WIDGET_TO_DEFAULT_MODAL_ID);
|
||||
};
|
||||
@@ -68,6 +85,26 @@ export const WidgetSettingsManageSection = ({
|
||||
return (
|
||||
<>
|
||||
<SidePanelGroup heading={t`Manage`}>
|
||||
<SelectableListItem
|
||||
itemId={WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.VISIBILITY_RESTRICTION}
|
||||
>
|
||||
<CommandMenuItemDropdown
|
||||
id={WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.VISIBILITY_RESTRICTION}
|
||||
label={t`Visibility Restriction`}
|
||||
Icon={IconEyeX}
|
||||
dropdownId={
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.VISIBILITY_RESTRICTION
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<WidgetVisibilityDropdownContent />
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownPlacement="bottom-end"
|
||||
description={visibilityLabel}
|
||||
contextualTextPosition="right"
|
||||
/>
|
||||
</SelectableListItem>
|
||||
<SelectableListItem
|
||||
itemId={WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT}
|
||||
onEnter={handleResetToDefault}
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
import { EXPRESSION_DEVICE_DESKTOP } from '@/side-panel/pages/page-layout/constants/ExpressionDeviceDesktop';
|
||||
import { EXPRESSION_DEVICE_MOBILE } from '@/side-panel/pages/page-layout/constants/ExpressionDeviceMobile';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
|
||||
const VISIBILITY_ANY_DEVICE = 'any-device';
|
||||
const VISIBILITY_MOBILE = 'mobile';
|
||||
const VISIBILITY_DESKTOP = 'desktop';
|
||||
|
||||
const VISIBILITY_OPTIONS = [
|
||||
{ id: VISIBILITY_ANY_DEVICE },
|
||||
{ id: VISIBILITY_MOBILE },
|
||||
{ id: VISIBILITY_DESKTOP },
|
||||
] as const;
|
||||
|
||||
const expressionToOptionId = (
|
||||
expression: string | null | undefined,
|
||||
): string => {
|
||||
if (!expression) {
|
||||
return VISIBILITY_ANY_DEVICE;
|
||||
}
|
||||
|
||||
if (expression === EXPRESSION_DEVICE_MOBILE) {
|
||||
return VISIBILITY_MOBILE;
|
||||
}
|
||||
|
||||
if (expression === EXPRESSION_DEVICE_DESKTOP) {
|
||||
return VISIBILITY_DESKTOP;
|
||||
}
|
||||
|
||||
return VISIBILITY_ANY_DEVICE;
|
||||
};
|
||||
|
||||
const optionIdToExpression = (optionId: string): string | null => {
|
||||
switch (optionId) {
|
||||
case VISIBILITY_MOBILE:
|
||||
return EXPRESSION_DEVICE_MOBILE;
|
||||
case VISIBILITY_DESKTOP:
|
||||
return EXPRESSION_DEVICE_DESKTOP;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const WidgetVisibilityDropdownContent = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const currentOptionId = expressionToOptionId(
|
||||
widgetInEditMode?.conditionalAvailabilityExpression,
|
||||
);
|
||||
|
||||
const dropdownId = useAvailableComponentInstanceIdOrThrow(
|
||||
DropdownComponentInstanceContext,
|
||||
);
|
||||
|
||||
const selectedItemId = useAtomComponentStateValue(
|
||||
selectedItemIdComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const handleSelectVisibility = (optionId: string) => {
|
||||
updateCurrentWidgetConfig({
|
||||
conditionalAvailabilityExpression: optionIdToExpression(optionId),
|
||||
});
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
const visibilityLabels: Record<string, string> = {
|
||||
[VISIBILITY_ANY_DEVICE]: t`Any device`,
|
||||
[VISIBILITY_MOBILE]: t`Mobile`,
|
||||
[VISIBILITY_DESKTOP]: t`Desktop`,
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
selectableListInstanceId={dropdownId}
|
||||
focusId={dropdownId}
|
||||
selectableItemIdArray={VISIBILITY_OPTIONS.map((option) => option.id)}
|
||||
>
|
||||
{VISIBILITY_OPTIONS.map((option) => (
|
||||
<SelectableListItem
|
||||
key={option.id}
|
||||
itemId={option.id}
|
||||
onEnter={() => {
|
||||
handleSelectVisibility(option.id);
|
||||
}}
|
||||
>
|
||||
<MenuItemSelect
|
||||
text={visibilityLabels[option.id]}
|
||||
selected={currentOptionId === option.id}
|
||||
focused={selectedItemId === option.id}
|
||||
onClick={() => {
|
||||
handleSelectVisibility(option.id);
|
||||
}}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
);
|
||||
};
|
||||
+1
@@ -72,6 +72,7 @@ export const SidePanelRecordPageFieldSettings = () => {
|
||||
const selectableItemIds = [
|
||||
'field',
|
||||
'layout',
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.VISIBILITY_RESTRICTION,
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT,
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.REPLACE_WIDGET,
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.DELETE_WIDGET,
|
||||
|
||||
+1
@@ -86,6 +86,7 @@ export const SidePanelRecordPageFieldsSettings = () => {
|
||||
'new-field-default-visibility',
|
||||
'display-more-fields-button',
|
||||
'action-button',
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.VISIBILITY_RESTRICTION,
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT,
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.REPLACE_WIDGET,
|
||||
WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.DELETE_WIDGET,
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const EXPRESSION_DEVICE_DESKTOP = 'device == "DESKTOP"';
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const EXPRESSION_DEVICE_MOBILE = 'device == "MOBILE"';
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
export const WIDGET_SETTINGS_SELECTABLE_ITEM_IDS = {
|
||||
VISIBILITY_RESTRICTION: 'visibility-restriction',
|
||||
MOVE_DOWN: 'widget-move-down',
|
||||
MOVE_UP: 'widget-move-up',
|
||||
MOVE_TO_TAB: 'widget-move-to-tab',
|
||||
|
||||
+5
@@ -23,9 +23,11 @@ export const useUpdateCurrentWidgetConfig = (pageLayoutIdFromProps: string) => {
|
||||
({
|
||||
objectMetadataId,
|
||||
configToUpdate,
|
||||
conditionalAvailabilityExpression,
|
||||
}: {
|
||||
objectMetadataId?: string | null;
|
||||
configToUpdate?: Partial<PageLayoutWidget['configuration']>;
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
}) => {
|
||||
const prev = store.get(pageLayoutDraft);
|
||||
store.set(pageLayoutDraft, {
|
||||
@@ -41,6 +43,9 @@ export const useUpdateCurrentWidgetConfig = (pageLayoutIdFromProps: string) => {
|
||||
...(widget.configuration ?? {}),
|
||||
...configToUpdate,
|
||||
} as PageLayoutWidget['configuration'],
|
||||
...(conditionalAvailabilityExpression !== undefined
|
||||
? { conditionalAvailabilityExpression }
|
||||
: {}),
|
||||
}
|
||||
: widget,
|
||||
),
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { EXPRESSION_DEVICE_DESKTOP } from '@/side-panel/pages/page-layout/constants/ExpressionDeviceDesktop';
|
||||
import { EXPRESSION_DEVICE_MOBILE } from '@/side-panel/pages/page-layout/constants/ExpressionDeviceMobile';
|
||||
|
||||
export const getVisibilityLabel = (
|
||||
expression: string | null | undefined,
|
||||
labels: { anyDevice: string; mobile: string; desktop: string },
|
||||
): string => {
|
||||
if (!isDefined(expression)) {
|
||||
return labels.anyDevice;
|
||||
}
|
||||
|
||||
if (expression === EXPRESSION_DEVICE_MOBILE) {
|
||||
return labels.mobile;
|
||||
}
|
||||
|
||||
if (expression === EXPRESSION_DEVICE_DESKTOP) {
|
||||
return labels.desktop;
|
||||
}
|
||||
|
||||
return labels.anyDevice;
|
||||
};
|
||||
+1
@@ -778,6 +778,7 @@ export class BackfillPageLayoutsAndFieldsWidgetViewFieldsCommand extends ActiveO
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
};
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddConditionalAvailabilityExpressionToPageLayoutWidget1775654781000
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddConditionalAvailabilityExpressionToPageLayoutWidget1775654781000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."pageLayoutWidget" ADD "conditionalAvailabilityExpression" varchar`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."pageLayoutWidget" DROP COLUMN "conditionalAvailabilityExpression"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
@@ -36,6 +36,7 @@ export const fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget = ({
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
universalOverrides: null,
|
||||
};
|
||||
};
|
||||
|
||||
+1
@@ -194,6 +194,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
"universalConfiguration",
|
||||
"deletedAt",
|
||||
"conditionalDisplay",
|
||||
"conditionalAvailabilityExpression",
|
||||
"isActive",
|
||||
"universalOverrides",
|
||||
],
|
||||
|
||||
+6
@@ -929,6 +929,12 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
conditionalAvailabilityExpression: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
isOverridable: true,
|
||||
},
|
||||
isActive: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
|
||||
+1
@@ -8,5 +8,6 @@ export const FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES = [
|
||||
'position',
|
||||
'configuration',
|
||||
'conditionalDisplay',
|
||||
'conditionalAvailabilityExpression',
|
||||
'pageLayoutTabId',
|
||||
] as const satisfies MetadataEntityPropertyName<'pageLayoutWidget'>[];
|
||||
|
||||
+1
@@ -74,6 +74,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
|
||||
applicationId: flatApplication.id,
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
universalConfiguration:
|
||||
|
||||
+5
@@ -68,4 +68,9 @@ export class UpdatePageLayoutWidgetWithIdInput {
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
}
|
||||
|
||||
+5
@@ -62,4 +62,9 @@ export class UpdatePageLayoutWidgetInput {
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
}
|
||||
|
||||
+4
-1
@@ -13,9 +13,9 @@ import {
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { type PageLayoutWidgetOverrides } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { PageLayoutWidgetPositionUnion } from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget-position.union';
|
||||
import { WidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface';
|
||||
import { type PageLayoutWidgetOverrides } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
|
||||
import { AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type';
|
||||
|
||||
@@ -65,6 +65,9 @@ export class PageLayoutWidgetDTO {
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
+4
@@ -31,6 +31,7 @@ export type PageLayoutWidgetOverrides = {
|
||||
title?: string;
|
||||
position?: PageLayoutWidgetPosition | null;
|
||||
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
pageLayoutTabId?: SerializedRelation;
|
||||
};
|
||||
|
||||
@@ -85,6 +86,9 @@ export class PageLayoutWidgetEntity<
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
conditionalDisplay: JsonbProperty<PageLayoutWidgetConditionalDisplay | null>;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
conditionalAvailabilityExpression: string | null;
|
||||
|
||||
@Column({ type: 'jsonb', nullable: false })
|
||||
gridPosition: JsonbProperty<GridPosition>;
|
||||
|
||||
|
||||
+8
-1
@@ -598,7 +598,9 @@ export class PageLayoutUpdateService {
|
||||
applicationId: workspaceCustomApplicationId,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
conditionalDisplay: null,
|
||||
conditionalDisplay: widgetInput.conditionalDisplay ?? null,
|
||||
conditionalAvailabilityExpression:
|
||||
widgetInput.conditionalAvailabilityExpression ?? null,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
isActive: true,
|
||||
@@ -737,6 +739,11 @@ export class PageLayoutUpdateService {
|
||||
widgetInput.conditionalDisplay ?? null;
|
||||
}
|
||||
|
||||
if (widgetInput.conditionalAvailabilityExpression !== undefined) {
|
||||
editableProperties.conditionalAvailabilityExpression =
|
||||
widgetInput.conditionalAvailabilityExpression ?? null;
|
||||
}
|
||||
|
||||
const { overrides, updatedEditableProperties } =
|
||||
sanitizeOverridableEntityInput({
|
||||
metadataName: 'pageLayoutWidget',
|
||||
|
||||
+6
@@ -17,6 +17,12 @@ export const CONDITIONAL_DISPLAY_DEVICE_DESKTOP = {
|
||||
and: [{ '===': [{ var: 'device' }, 'DESKTOP'] }],
|
||||
} as const satisfies PageLayoutWidgetConditionalDisplay;
|
||||
|
||||
export const CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_MOBILE =
|
||||
'device == "MOBILE"';
|
||||
|
||||
export const CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_DESKTOP =
|
||||
'device == "DESKTOP"';
|
||||
|
||||
export const GRID_POSITIONS = {
|
||||
FULL_WIDTH: {
|
||||
row: 0,
|
||||
|
||||
+6
@@ -2,6 +2,8 @@ import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
|
||||
import {
|
||||
CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_DESKTOP,
|
||||
CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_MOBILE,
|
||||
CONDITIONAL_DISPLAY_DEVICE_DESKTOP,
|
||||
CONDITIONAL_DISPLAY_DEVICE_MOBILE,
|
||||
TAB_PROPS,
|
||||
@@ -28,6 +30,8 @@ const NOTE_PAGE_TABS = {
|
||||
gridPosition: WIDGET_PROPS.noteRichText.gridPosition,
|
||||
position: { layoutMode: TAB_PROPS.home.layoutMode, index: 1 },
|
||||
conditionalDisplay: CONDITIONAL_DISPLAY_DEVICE_MOBILE,
|
||||
conditionalAvailabilityExpression:
|
||||
CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_MOBILE,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -39,6 +43,8 @@ const NOTE_PAGE_TABS = {
|
||||
universalIdentifier: '20202020-ac04-4004-8004-a0be5a11a421',
|
||||
...WIDGET_PROPS.noteRichText,
|
||||
conditionalDisplay: CONDITIONAL_DISPLAY_DEVICE_DESKTOP,
|
||||
conditionalAvailabilityExpression:
|
||||
CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_DESKTOP,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+2
@@ -15,6 +15,7 @@ export type StandardPageLayoutWidgetConfig = {
|
||||
gridPosition?: GridPosition;
|
||||
position?: PageLayoutWidgetPosition;
|
||||
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
fieldUniversalIdentifier?: string;
|
||||
};
|
||||
|
||||
@@ -43,6 +44,7 @@ export type StandardRecordPageWidgetConfig = {
|
||||
gridPosition: GridPosition;
|
||||
position?: PageLayoutWidgetPosition;
|
||||
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
fieldUniversalIdentifier?: string;
|
||||
};
|
||||
|
||||
|
||||
+6
@@ -2,6 +2,8 @@ import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
|
||||
import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
|
||||
import {
|
||||
CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_DESKTOP,
|
||||
CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_MOBILE,
|
||||
CONDITIONAL_DISPLAY_DEVICE_DESKTOP,
|
||||
CONDITIONAL_DISPLAY_DEVICE_MOBILE,
|
||||
TAB_PROPS,
|
||||
@@ -28,6 +30,8 @@ const TASK_PAGE_TABS = {
|
||||
gridPosition: WIDGET_PROPS.taskRichText.gridPosition,
|
||||
position: { layoutMode: TAB_PROPS.home.layoutMode, index: 1 },
|
||||
conditionalDisplay: CONDITIONAL_DISPLAY_DEVICE_MOBILE,
|
||||
conditionalAvailabilityExpression:
|
||||
CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_MOBILE,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -39,6 +43,8 @@ const TASK_PAGE_TABS = {
|
||||
universalIdentifier: '20202020-ac05-4005-8005-ba5ca11a5521',
|
||||
...WIDGET_PROPS.taskRichText,
|
||||
conditionalDisplay: CONDITIONAL_DISPLAY_DEVICE_DESKTOP,
|
||||
conditionalAvailabilityExpression:
|
||||
CONDITIONAL_AVAILABILITY_EXPRESSION_DEVICE_DESKTOP,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+2
@@ -321,6 +321,8 @@ const computeRecordPageWidgets = ({
|
||||
universalConfiguration,
|
||||
objectMetadataId,
|
||||
conditionalDisplay: widget.conditionalDisplay ?? null,
|
||||
conditionalAvailabilityExpression:
|
||||
widget.conditionalAvailabilityExpression ?? null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
+8
@@ -242,6 +242,7 @@ const createWelcomeRichText = ({
|
||||
universalConfiguration: configuration,
|
||||
objectMetadataId: null,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -306,6 +307,7 @@ const createDealsByCompany = ({
|
||||
},
|
||||
objectMetadataId: opportunityObjectId,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -383,6 +385,7 @@ const createPipelineValueByStage = ({
|
||||
},
|
||||
objectMetadataId: opportunityObjectId,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -449,6 +452,7 @@ const createRevenueTimeline = ({
|
||||
},
|
||||
objectMetadataId: opportunityObjectId,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -530,6 +534,7 @@ const createOpportunitiesByOwner = ({
|
||||
},
|
||||
objectMetadataId: opportunityObjectId,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -565,6 +570,7 @@ const createStockMarketIframe = ({
|
||||
universalConfiguration: configuration,
|
||||
objectMetadataId: null,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -633,6 +639,7 @@ const createDealsCreatedThisMonth = ({
|
||||
},
|
||||
objectMetadataId: opportunityObjectId,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -701,6 +708,7 @@ const createDealValueCreatedThisMonth = ({
|
||||
},
|
||||
objectMetadataId: opportunityObjectId,
|
||||
conditionalDisplay: null,
|
||||
conditionalAvailabilityExpression: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
+4
@@ -30,6 +30,7 @@ export type CreateStandardPageLayoutWidgetContext = {
|
||||
universalConfiguration: MetadataUniversalFlatEntity<'pageLayoutWidget'>['universalConfiguration'];
|
||||
objectMetadataId: string | null;
|
||||
conditionalDisplay: PageLayoutWidgetConditionalDisplay | null;
|
||||
conditionalAvailabilityExpression: string | null;
|
||||
};
|
||||
|
||||
export type CreateStandardPageLayoutWidgetArgs = {
|
||||
@@ -54,6 +55,7 @@ export const createStandardPageLayoutWidgetFlatMetadata = ({
|
||||
universalConfiguration,
|
||||
objectMetadataId,
|
||||
conditionalDisplay,
|
||||
conditionalAvailabilityExpression,
|
||||
},
|
||||
workspaceId,
|
||||
twentyStandardApplicationId,
|
||||
@@ -109,6 +111,8 @@ export const createStandardPageLayoutWidgetFlatMetadata = ({
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
conditionalDisplay: conditionalDisplay ?? null,
|
||||
conditionalAvailabilityExpression:
|
||||
conditionalAvailabilityExpression ?? null,
|
||||
overrides: null,
|
||||
universalOverrides: null,
|
||||
};
|
||||
|
||||
@@ -171,6 +171,7 @@ export {
|
||||
IconEye,
|
||||
IconEyeOff,
|
||||
IconEyeShare,
|
||||
IconEyeX,
|
||||
IconFile,
|
||||
IconFileCheck,
|
||||
IconFileExport,
|
||||
|
||||
@@ -249,6 +249,7 @@ export {
|
||||
IconEye,
|
||||
IconEyeOff,
|
||||
IconEyeShare,
|
||||
IconEyeX,
|
||||
IconFile,
|
||||
IconFileCheck,
|
||||
IconFileExport,
|
||||
|
||||
Reference in New Issue
Block a user