Compare commits

...
Author SHA1 Message Date
Abdul Rahman d0bcb4d3b0 Merge branch 'main' into fix/custom-object-nav-item-color-and-icon 2026-03-17 02:14:12 +05:30
Abdul Rahman 4952332ff3 Revert "refactor: update color dimensions to use spread operator for MAIN_COLOR_NAMES"
This reverts commit 8a3a4bf928.
2026-03-17 02:08:00 +05:30
Abdul Rahman 4a3030d83f Revert "refactor: improve type safety in theme color utilities"
This reverts commit 91da7f852d.
2026-03-17 02:07:59 +05:30
Abdul Rahman 7cd46fe5c0 refactor: enhance type definitions for color utilities
- Updated `getDefaultColorForCustomObject` to use the refined `ThemeColorName` type, improving type safety.
- Introduced `ThemeColorName` type in `MainColorNames.ts` to standardize color references and ensure consistency across the application.
- Adjusted the `MAIN_COLOR_NAMES` constant to align with the new type definition, enhancing maintainability.
2026-03-17 02:07:59 +05:30
Abdul RahmanandGitHub a4025893ba Merge branch 'main' into fix/custom-object-nav-item-color-and-icon 2026-03-16 17:02:32 +05:30
Abdul Rahman 8a3a4bf928 refactor: update color dimensions to use spread operator for MAIN_COLOR_NAMES
- Modified color dimensions in Tag, Status, and MenuItemSelectColor stories to use the spread operator for MAIN_COLOR_NAMES, ensuring immutability.
- Changed the parameter type in getNextThemeColor to ReadonlyArray for improved type safety.
2026-03-16 08:42:34 +05:30
Abdul Rahman 91da7f852d refactor: improve type safety in theme color utilities
- Updated `parseThemeColor` to use readonly string array for `MAIN_COLOR_NAMES`, enhancing type safety.
- Modified `themeColorSchema` to spread `MAIN_COLOR_NAMES` into a tuple type, ensuring better type inference for theme colors.
2026-03-16 08:37:23 +05:30
Abdul Rahman 082582026d fix: update SidePanelNavigationMenuItemEditPage to conditionally render color option
- Modified the rendering logic to display the color customization option only when the selected item is not of type RECORD, enhancing the component's behavior and preventing unnecessary rendering.
2026-03-16 08:20:06 +05:30
Abdul Rahman 1c87eced8b refactor: enhance color utility and type definitions
- Updated `getDefaultColorForCustomObject` to return `ThemeColorName` for better type safety.
- Introduced `ThemeColorName` type in `MainColorNames.ts` to standardize color references.
- Adjusted imports and exports across relevant modules to incorporate the new type, improving consistency and maintainability.
2026-03-16 08:12:15 +05:30
Abdul Rahman bc4c1fd498 refactor: simplify getStandardObjectIconColor function
- Updated the `getStandardObjectIconColor` function to improve readability by removing unnecessary line breaks.
- Maintained functionality by ensuring the same logic for retrieving standard object icon colors is preserved.
2026-03-16 07:59:16 +05:30
Abdul Rahman 97ec8e5752 fix: update object metadata service to utilize custom object color utility
- Integrated `getDefaultColorForCustomObject` to set default colors based on object names.
- Refactored icon handling to use `objectMetadata.icon` when creating flat navigation menu items.
- Enhanced the `computeFlatNavigationMenuItemToCreate` method to accept `objectMetadata` as a parameter for improved clarity and functionality.
2026-03-16 07:55:34 +05:30
Abdul Rahman cd36eba13c feat: implement custom object color utility and refactor icon color logic
- Added `getDefaultColorForCustomObject` utility to generate colors for custom objects based on their names.
- Refactored `getStandardObjectIconColor` to utilize the new utility for custom object colors, improving maintainability.
- Introduced `MAIN_COLOR_NAMES` constant to centralize color definitions, excluding 'gray'.
- Updated relevant imports and exports across modules to accommodate these changes.
2026-03-16 07:54:42 +05:30
9 changed files with 100 additions and 31 deletions
@@ -1,4 +1,5 @@
import { type ThemeColor, MAIN_COLOR_NAMES } from 'twenty-ui/theme';
import { getDefaultColorForCustomObject } from 'twenty-shared/application';
import { type ThemeColor } from 'twenty-ui/theme';
import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultIconColor.constant';
import { CoreObjectNameSingular } from 'twenty-shared/types';
@@ -19,20 +20,6 @@ const STANDARD_OBJECT_ICON_COLOR: Partial<
[CoreObjectNameSingular.WorkflowVersion]: DEFAULT_NAV_ITEM_ICON_COLOR,
};
const CUSTOM_OBJECT_ICON_COLORS: ThemeColor[] = MAIN_COLOR_NAMES.filter(
(color) => color !== 'gray',
);
const getColorForCustomObject = (seed: string): ThemeColor => {
let hash = 0;
for (let index = 0; index < seed.length; index += 1) {
hash = (hash << 5) - hash + seed.charCodeAt(index);
hash |= 0;
}
const index = Math.abs(hash) % CUSTOM_OBJECT_ICON_COLORS.length;
return CUSTOM_OBJECT_ICON_COLORS[index];
};
export const getStandardObjectIconColor = (nameSingular: string): ThemeColor =>
STANDARD_OBJECT_ICON_COLOR[nameSingular as CoreObjectNameSingular] ??
getColorForCustomObject(nameSingular);
getDefaultColorForCustomObject(nameSingular);
@@ -187,14 +187,15 @@ export const SidePanelNavigationMenuItemEditPage = () => {
commandGroups={[]}
selectableItemIds={getOrganizeActionsSelectableItemIds(true)}
>
{selectedItem && (
<SidePanelGroup heading={t`Customize`}>
<SidePanelEditColorOption
navigationMenuItemId={selectedItem.id}
color={parseThemeColor(selectedItem.color)}
/>
</SidePanelGroup>
)}
{selectedItem &&
selectedItemType !== NavigationMenuItemType.RECORD && (
<SidePanelGroup heading={t`Customize`}>
<SidePanelEditColorOption
navigationMenuItemId={selectedItem.id}
color={parseThemeColor(selectedItem.color)}
/>
</SidePanelGroup>
)}
<SidePanelEditOrganizeActions
canMoveUp={canMoveUp}
canMoveDown={canMoveDown}
@@ -4,9 +4,9 @@ import { InjectRepository } from '@nestjs/typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import {
FeatureFlagKey,
ViewKey,
ViewOpenRecordIn,
ViewType,
ViewKey,
ViewVisibility,
} from 'twenty-shared/types';
import { fromArrayToUniqueKeyRecord, isDefined } from 'twenty-shared/utils';
@@ -31,6 +31,7 @@ import { fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities } from
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
import { getDefaultColorForCustomObject } from 'twenty-shared/application';
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
@@ -470,6 +471,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
const flatNavigationMenuItemToCreate =
await this.computeFlatNavigationMenuItemToCreate({
view: flatDefaultViewToCreate,
objectMetadata: flatObjectMetadataToCreate,
workspaceId,
workspaceCustomApplicationId: workspaceCustomFlatApplication.id,
workspaceCustomApplicationUniversalIdentifier:
@@ -684,11 +686,13 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
private async computeFlatNavigationMenuItemToCreate({
view,
objectMetadata,
workspaceId,
workspaceCustomApplicationId,
workspaceCustomApplicationUniversalIdentifier,
}: {
view: UniversalFlatView & { id: string };
objectMetadata: { nameSingular: string; icon: string | null };
workspaceId: string;
workspaceCustomApplicationId: string;
workspaceCustomApplicationUniversalIdentifier: string;
@@ -725,8 +729,8 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
folderUniversalIdentifier: null,
name: null,
link: null,
icon: null,
color: null,
icon: objectMetadata.icon ?? null,
color: getDefaultColorForCustomObject(objectMetadata.nameSingular),
position: nextPosition,
workspaceId,
applicationId: workspaceCustomApplicationId,
@@ -53,6 +53,7 @@ export type {
export type { ServerVariables } from './server-variables.type';
export type { SkillManifest } from './skillManifestType';
export type { SyncableEntityOptions } from './syncableEntityOptionsType';
export { getDefaultColorForCustomObject } from './utils/getDefaultColorForCustomObject.util';
export type {
ViewManifestFilterValue,
ViewFieldManifest,
@@ -0,0 +1,19 @@
import {
MAIN_COLOR_NAMES,
type ThemeColorName,
} from '../../constants/MainColorNames';
export const getDefaultColorForCustomObject = (
nameSingular: string,
): ThemeColorName => {
const customObjectColors = MAIN_COLOR_NAMES.filter(
(color): color is Exclude<ThemeColorName, 'gray'> => color !== 'gray',
);
let hash = 0;
for (let index = 0; index < nameSingular.length; index += 1) {
hash = (hash << 5) - hash + nameSingular.charCodeAt(index);
hash |= 0;
}
const index = Math.abs(hash) % customObjectColors.length;
return customObjectColors[index];
};
@@ -0,0 +1,54 @@
export type ThemeColorName =
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'orange'
| 'amber'
| 'yellow'
| 'lime'
| 'grass'
| 'green'
| 'jade'
| 'mint'
| 'turquoise'
| 'cyan'
| 'sky'
| 'blue'
| 'iris'
| 'violet'
| 'purple'
| 'plum'
| 'pink'
| 'bronze'
| 'gold'
| 'brown'
| 'gray';
export const MAIN_COLOR_NAMES: ThemeColorName[] = [
'red',
'ruby',
'crimson',
'tomato',
'orange',
'amber',
'yellow',
'lime',
'grass',
'green',
'jade',
'mint',
'turquoise',
'cyan',
'sky',
'blue',
'iris',
'violet',
'purple',
'plum',
'pink',
'bronze',
'gold',
'brown',
'gray',
];
@@ -29,6 +29,8 @@ export { FILES_FIELD_MAX_NUMBER_OF_VALUES } from './FilesFieldMaxNumberOfValues'
export { GROUP_BY_DATE_GRANULARITY_THAT_REQUIRE_TIME_ZONE } from './GroupByDateGranularityThatRequireTimeZone';
export { IANA_TIME_ZONES } from './IanaTimeZones';
export { LABEL_IDENTIFIER_FIELD_METADATA_TYPES } from './LabelIdentifierFieldMetadataTypes';
export type { ThemeColorName } from './MainColorNames';
export { MAIN_COLOR_NAMES } from './MainColorNames';
export { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from './MultiItemFieldDefaultMaxValues';
export { MULTI_ITEM_FIELD_MIN_MAX_VALUES } from './MultiItemFieldMinMaxValues';
export { MUTATION_MAX_MERGE_RECORDS } from './MutationMaxMergeRecords';
@@ -1,5 +1,5 @@
import { MAIN_COLORS_LIGHT } from './MainColorsLight';
import { MAIN_COLOR_NAMES, type ThemeColorName } from 'twenty-shared/constants';
export const MAIN_COLOR_NAMES = Object.keys(MAIN_COLORS_LIGHT) as ThemeColor[];
export { MAIN_COLOR_NAMES };
export type ThemeColor = keyof typeof MAIN_COLORS_LIGHT;
export type ThemeColor = ThemeColorName;
@@ -1,7 +1,8 @@
import * as RadixColors from '@radix-ui/colors';
import type { ThemeColorName } from 'twenty-shared/constants';
import { GRAY_SCALE_LIGHT } from './GrayScaleLight';
export const MAIN_COLORS_LIGHT = {
export const MAIN_COLORS_LIGHT: Record<ThemeColorName, string> = {
// Reds
red: RadixColors.redP3.red9,
ruby: RadixColors.rubyP3.ruby9,