Create the Dashboard record show page (#14423)
Closes https://github.com/twentyhq/core-team-issues/issues/1438 - Reorganized PageLayout module - Created `DashboardRenderer` and `PageLayoutRenderer` - Created stories for the `PageLayoutRenderer` - Refactored the Widget components https://github.com/user-attachments/assets/27e9ac8f-b237-4c21-8494-3fab6d65af3a
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
export const calculateAngularGradient = (angle: number) => {
|
||||
const gradientAngle = (angle * Math.PI) / 180 + Math.PI / 2;
|
||||
|
||||
const dx = Math.sin(gradientAngle);
|
||||
const dy = -Math.cos(gradientAngle);
|
||||
|
||||
return {
|
||||
x1: `${50 - dx * 50}%`,
|
||||
y1: `${50 - dy * 50}%`,
|
||||
x2: `${50 + dx * 50}%`,
|
||||
y2: `${50 + dy * 50}%`,
|
||||
};
|
||||
};
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { type GraphColorScheme } from '../types/GraphColorScheme';
|
||||
import { calculateAngularGradient } from './calculateAngularGradient';
|
||||
|
||||
export const createGradientDef = (
|
||||
colorScheme: GraphColorScheme,
|
||||
id: string,
|
||||
isHovered: boolean = false,
|
||||
angle?: number,
|
||||
reverseGradient: boolean = false,
|
||||
) => {
|
||||
const colors = isHovered
|
||||
? colorScheme.gradient.hover
|
||||
: colorScheme.gradient.normal;
|
||||
|
||||
const coords =
|
||||
angle !== undefined
|
||||
? calculateAngularGradient(angle)
|
||||
: { x1: '0%', y1: '0%', x2: '0%', y2: '100%' };
|
||||
|
||||
return {
|
||||
id,
|
||||
type: 'linearGradient' as const,
|
||||
...coords,
|
||||
colors: reverseGradient
|
||||
? [
|
||||
{ offset: 0, color: colors[1] },
|
||||
{ offset: 100, color: colors[0] },
|
||||
]
|
||||
: [
|
||||
{ offset: 0, color: colors[0] },
|
||||
{ offset: 100, color: colors[1] },
|
||||
],
|
||||
};
|
||||
};
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { type ThemeType } from 'twenty-ui/theme';
|
||||
|
||||
import { type GraphColorRegistry } from '../types/GraphColorRegistry';
|
||||
|
||||
export const createGraphColorRegistry = (
|
||||
theme: ThemeType,
|
||||
): GraphColorRegistry => ({
|
||||
blue: {
|
||||
name: 'blue',
|
||||
gradient: {
|
||||
normal: [theme.adaptiveColors.blue1, theme.adaptiveColors.blue2],
|
||||
hover: [theme.adaptiveColors.blue3, theme.adaptiveColors.blue4],
|
||||
},
|
||||
solid: theme.color.blue,
|
||||
},
|
||||
purple: {
|
||||
name: 'purple',
|
||||
gradient: {
|
||||
normal: [theme.adaptiveColors.purple1, theme.adaptiveColors.purple2],
|
||||
hover: [theme.adaptiveColors.purple3, theme.adaptiveColors.purple4],
|
||||
},
|
||||
solid: theme.color.purple,
|
||||
},
|
||||
turquoise: {
|
||||
name: 'turquoise',
|
||||
gradient: {
|
||||
normal: [
|
||||
theme.adaptiveColors.turquoise1,
|
||||
theme.adaptiveColors.turquoise2,
|
||||
],
|
||||
hover: [theme.adaptiveColors.turquoise3, theme.adaptiveColors.turquoise4],
|
||||
},
|
||||
solid: theme.color.turquoise,
|
||||
},
|
||||
orange: {
|
||||
name: 'orange',
|
||||
gradient: {
|
||||
normal: [theme.adaptiveColors.orange1, theme.adaptiveColors.orange2],
|
||||
hover: [theme.adaptiveColors.orange3, theme.adaptiveColors.orange4],
|
||||
},
|
||||
solid: theme.color.orange,
|
||||
},
|
||||
pink: {
|
||||
name: 'pink',
|
||||
gradient: {
|
||||
normal: [theme.adaptiveColors.pink1, theme.adaptiveColors.pink2],
|
||||
hover: [theme.adaptiveColors.pink3, theme.adaptiveColors.pink4],
|
||||
},
|
||||
solid: theme.color.pink,
|
||||
},
|
||||
yellow: {
|
||||
name: 'yellow',
|
||||
gradient: {
|
||||
normal: [theme.adaptiveColors.yellow1, theme.adaptiveColors.yellow2],
|
||||
hover: [theme.adaptiveColors.yellow3, theme.adaptiveColors.yellow4],
|
||||
},
|
||||
solid: theme.color.yellow,
|
||||
},
|
||||
red: {
|
||||
name: 'red',
|
||||
gradient: {
|
||||
normal: [theme.adaptiveColors.red1, theme.adaptiveColors.red2],
|
||||
hover: [theme.adaptiveColors.red3, theme.adaptiveColors.red4],
|
||||
},
|
||||
solid: theme.color.red,
|
||||
},
|
||||
green: {
|
||||
name: 'green',
|
||||
gradient: {
|
||||
normal: [theme.adaptiveColors.green1, theme.adaptiveColors.green2],
|
||||
hover: [theme.adaptiveColors.green3, theme.adaptiveColors.green4],
|
||||
},
|
||||
solid: theme.color.green,
|
||||
},
|
||||
sky: {
|
||||
name: 'sky',
|
||||
gradient: {
|
||||
normal: [theme.adaptiveColors.sky1, theme.adaptiveColors.sky2],
|
||||
hover: [theme.adaptiveColors.sky3, theme.adaptiveColors.sky4],
|
||||
},
|
||||
solid: theme.color.sky,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type GraphColor } from '../types/GraphColor';
|
||||
import { type GraphColorRegistry } from '../types/GraphColorRegistry';
|
||||
import { type GraphColorScheme } from '../types/GraphColorScheme';
|
||||
import { getColorSchemeByIndex } from './getColorSchemeByIndex';
|
||||
|
||||
export const getColorScheme = (
|
||||
registry: GraphColorRegistry,
|
||||
colorName?: GraphColor,
|
||||
fallbackIndex?: number,
|
||||
): GraphColorScheme => {
|
||||
if (isDefined(colorName) && isDefined(registry[colorName])) {
|
||||
return registry[colorName];
|
||||
}
|
||||
return getColorSchemeByIndex(registry, fallbackIndex || 0);
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type GraphColorRegistry } from '../types/GraphColorRegistry';
|
||||
import { type GraphColorScheme } from '../types/GraphColorScheme';
|
||||
|
||||
export const getColorSchemeByIndex = (
|
||||
registry: GraphColorRegistry,
|
||||
index: number,
|
||||
): GraphColorScheme => {
|
||||
const schemes = Object.values(registry);
|
||||
return schemes[index % schemes.length];
|
||||
};
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { type GraphColorRegistry } from '../types/GraphColorRegistry';
|
||||
import { type GraphColorScheme } from '../types/GraphColorScheme';
|
||||
|
||||
export const getColorSchemeByName = (
|
||||
registry: GraphColorRegistry,
|
||||
name: string,
|
||||
): GraphColorScheme | undefined => {
|
||||
return registry[name];
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { formatAmount } from '~/utils/format/formatAmount';
|
||||
import { formatNumber } from '~/utils/format/number';
|
||||
|
||||
export type GraphValueFormatOptions = {
|
||||
displayType?: 'percentage' | 'number' | 'shortNumber' | 'currency' | 'custom';
|
||||
decimals?: number;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
customFormatter?: (value: number) => string;
|
||||
};
|
||||
|
||||
export const formatGraphValue = (
|
||||
value: number,
|
||||
options?: GraphValueFormatOptions,
|
||||
): string => {
|
||||
const {
|
||||
displayType = 'number',
|
||||
decimals,
|
||||
prefix = '',
|
||||
suffix = '',
|
||||
customFormatter,
|
||||
} = options || {};
|
||||
|
||||
if (displayType === 'custom' && isDefined(customFormatter)) {
|
||||
return customFormatter(value);
|
||||
}
|
||||
|
||||
switch (displayType) {
|
||||
case 'percentage':
|
||||
return `${formatNumber(value * 100, decimals)}%`;
|
||||
|
||||
case 'shortNumber':
|
||||
return `${prefix}${formatAmount(value)}${suffix}`;
|
||||
|
||||
case 'currency': {
|
||||
const currencyPrefix = prefix || '$';
|
||||
return `${currencyPrefix}${formatNumber(value, decimals)}${suffix}`;
|
||||
}
|
||||
|
||||
case 'number':
|
||||
default:
|
||||
return `${prefix}${formatNumber(value, decimals)}${suffix}`;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user