add dynamic dates for webhookGraphDataUsage (#7720)
**Before:** Only last 5 days where displayed on Developers Settings Webhook Usage Graph.  **Now** Added component where you can select the time range where you want to view the webhook usage. To do better the styling and content depassing . <img width="652" alt="Screenshot 2024-10-15 at 16 56 45" src="https://github.com/user-attachments/assets/d06e7f4c-a689-49a0-8839-f015ce36bab9"> **In order to test** 1. Set ANALYTICS_ENABLED to true 2. Set TINYBIRD_TOKEN to your token from the workspace twenty_analytics_playground 3. Write your client tinybird token in SettingsDeveloppersWebhookDetail.tsx in line 93 4. Create a Webhook in twenty and set wich events it needs to track 5. Run twenty-worker in order to make the webhooks work. 6. Do your tasks in order to populate the data 7. Enter to settings> webhook>your webhook and the statistics section should be displayed. 8. Select the desired time range in the dropdown **To do list** - Tooltip is truncated when accessing values at the right end of the graph - DateTicks needs to follow a more clear standard - Update this PR with more representative images
This commit is contained in:
committed by
GitHub
parent
0c24001e23
commit
8cadcdf577
@ -0,0 +1,11 @@
|
||||
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||
|
||||
type DateFormatWithoutYear = {
|
||||
[K in keyof typeof DateFormat]: string;
|
||||
};
|
||||
export const DATE_FORMAT_WITHOUT_YEAR: DateFormatWithoutYear = {
|
||||
SYSTEM: 'SYSTEM',
|
||||
MONTH_FIRST: 'MMM d',
|
||||
DAY_FIRST: 'd MMM',
|
||||
YEAR_FIRST: 'MMM d',
|
||||
};
|
||||
@ -1,8 +1,7 @@
|
||||
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||
import { detectDateFormat } from '@/localization/utils/detectDateFormat';
|
||||
|
||||
describe('detectDateFormat', () => {
|
||||
it('should return DateFormat.MONTH_FIRST if the detected format starts with month', () => {
|
||||
it('should return MONTH_FIRST if the detected format starts with month', () => {
|
||||
// Mock the Intl.DateTimeFormat to return a specific format
|
||||
const mockDateTimeFormat = jest.fn().mockReturnValue({
|
||||
formatToParts: () => [
|
||||
@ -16,10 +15,10 @@ describe('detectDateFormat', () => {
|
||||
|
||||
const result = detectDateFormat();
|
||||
|
||||
expect(result).toBe(DateFormat.MONTH_FIRST);
|
||||
expect(result).toBe('MONTH_FIRST');
|
||||
});
|
||||
|
||||
it('should return DateFormat.DAY_FIRST if the detected format starts with day', () => {
|
||||
it('should return DAY_FIRST if the detected format starts with day', () => {
|
||||
// Mock the Intl.DateTimeFormat to return a specific format
|
||||
const mockDateTimeFormat = jest.fn().mockReturnValue({
|
||||
formatToParts: () => [
|
||||
@ -32,10 +31,10 @@ describe('detectDateFormat', () => {
|
||||
|
||||
const result = detectDateFormat();
|
||||
|
||||
expect(result).toBe(DateFormat.DAY_FIRST);
|
||||
expect(result).toBe('DAY_FIRST');
|
||||
});
|
||||
|
||||
it('should return DateFormat.YEAR_FIRST if the detected format starts with year', () => {
|
||||
it('should return YEAR_FIRST if the detected format starts with year', () => {
|
||||
// Mock the Intl.DateTimeFormat to return a specific format
|
||||
const mockDateTimeFormat = jest.fn().mockReturnValue({
|
||||
formatToParts: () => [
|
||||
@ -48,10 +47,10 @@ describe('detectDateFormat', () => {
|
||||
|
||||
const result = detectDateFormat();
|
||||
|
||||
expect(result).toBe(DateFormat.YEAR_FIRST);
|
||||
expect(result).toBe('YEAR_FIRST');
|
||||
});
|
||||
|
||||
it('should return DateFormat.MONTH_FIRST by default if the detected format does not match any specific order', () => {
|
||||
it('should return MONTH_FIRST by default if the detected format does not match any specific order', () => {
|
||||
// Mock the Intl.DateTimeFormat to return a specific format
|
||||
const mockDateTimeFormat = jest.fn().mockReturnValue({
|
||||
formatToParts: () => [
|
||||
@ -64,6 +63,6 @@ describe('detectDateFormat', () => {
|
||||
|
||||
const result = detectDateFormat();
|
||||
|
||||
expect(result).toBe(DateFormat.MONTH_FIRST);
|
||||
expect(result).toBe('MONTH_FIRST');
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { TimeFormat } from '@/localization/constants/TimeFormat';
|
||||
import { detectTimeFormat } from '@/localization/utils/detectTimeFormat';
|
||||
|
||||
describe('detectTimeFormat', () => {
|
||||
it('should return TimeFormat.HOUR_12 if the hour format is 12-hour', () => {
|
||||
it('should return HOUR_12 if the hour format is 12-hour', () => {
|
||||
// Mock the resolvedOptions method to return hour12 as true
|
||||
const mockResolvedOptions = jest.fn(() => ({ hour12: true }));
|
||||
Intl.DateTimeFormat = jest.fn().mockImplementation(() => ({
|
||||
@ -11,11 +10,11 @@ describe('detectTimeFormat', () => {
|
||||
|
||||
const result = detectTimeFormat();
|
||||
|
||||
expect(result).toBe(TimeFormat.HOUR_12);
|
||||
expect(result).toBe('HOUR_12');
|
||||
expect(mockResolvedOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return TimeFormat.HOUR_24 if the hour format is 24-hour', () => {
|
||||
it('should return HOUR_24 if the hour format is 24-hour', () => {
|
||||
// Mock the resolvedOptions method to return hour12 as false
|
||||
const mockResolvedOptions = jest.fn(() => ({ hour12: false }));
|
||||
Intl.DateTimeFormat = jest.fn().mockImplementation(() => ({
|
||||
@ -24,7 +23,7 @@ describe('detectTimeFormat', () => {
|
||||
|
||||
const result = detectTimeFormat();
|
||||
|
||||
expect(result).toBe(TimeFormat.HOUR_24);
|
||||
expect(result).toBe('HOUR_24');
|
||||
expect(mockResolvedOptions).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,90 @@
|
||||
import { detectDateFormat } from '@/localization/utils/detectDateFormat';
|
||||
import { formatDateISOStringToDateTimeSimplified } from '@/localization/utils/formatDateISOStringToDateTimeSimplified';
|
||||
import { formatInTimeZone } from 'date-fns-tz';
|
||||
// Mock the imported modules
|
||||
jest.mock('@/localization/utils/detectDateFormat');
|
||||
jest.mock('date-fns-tz');
|
||||
|
||||
describe('formatDateISOStringToDateTimeSimplified', () => {
|
||||
const mockDate = new Date('2023-08-15T10:30:00Z');
|
||||
const mockTimeZone = 'America/New_York';
|
||||
const mockTimeFormat = 'HH:mm';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should format the date correctly when DATE_FORMAT is MONTH_FIRST', () => {
|
||||
detectDateFormat.mockReturnValue('MONTH_FIRST');
|
||||
formatInTimeZone.mockReturnValue('Oct 15 · 06:30');
|
||||
|
||||
const result = formatDateISOStringToDateTimeSimplified(
|
||||
mockDate,
|
||||
mockTimeZone,
|
||||
mockTimeFormat,
|
||||
);
|
||||
|
||||
expect(detectDateFormat).toHaveBeenCalled();
|
||||
expect(formatInTimeZone).toHaveBeenCalledWith(
|
||||
mockDate,
|
||||
mockTimeZone,
|
||||
'MMM d · HH:mm',
|
||||
);
|
||||
expect(result).toBe('Oct 15 · 06:30');
|
||||
});
|
||||
|
||||
it('should format the date correctly when DATE_FORMAT is DAY_FIRST', () => {
|
||||
detectDateFormat.mockReturnValue('DAY_FIRST');
|
||||
formatInTimeZone.mockReturnValue('15 Oct · 06:30');
|
||||
|
||||
const result = formatDateISOStringToDateTimeSimplified(
|
||||
mockDate,
|
||||
mockTimeZone,
|
||||
mockTimeFormat,
|
||||
);
|
||||
|
||||
expect(detectDateFormat).toHaveBeenCalled();
|
||||
expect(formatInTimeZone).toHaveBeenCalledWith(
|
||||
mockDate,
|
||||
mockTimeZone,
|
||||
'd MMM · HH:mm',
|
||||
);
|
||||
expect(result).toBe('15 Oct · 06:30');
|
||||
});
|
||||
|
||||
it('should use the provided time format', () => {
|
||||
detectDateFormat.mockReturnValue('MONTH_FIRST');
|
||||
formatInTimeZone.mockReturnValue('Oct 15 · 6:30 AM');
|
||||
|
||||
const result = formatDateISOStringToDateTimeSimplified(
|
||||
mockDate,
|
||||
mockTimeZone,
|
||||
'h:mm aa',
|
||||
);
|
||||
|
||||
expect(formatInTimeZone).toHaveBeenCalledWith(
|
||||
mockDate,
|
||||
mockTimeZone,
|
||||
'MMM d · h:mm aa',
|
||||
);
|
||||
expect(result).toBe('Oct 15 · 6:30 AM');
|
||||
});
|
||||
|
||||
it('should handle different time zones', () => {
|
||||
detectDateFormat.mockReturnValue('MONTH_FIRST');
|
||||
formatInTimeZone.mockReturnValue('Oct 16 · 02:30');
|
||||
|
||||
const result = formatDateISOStringToDateTimeSimplified(
|
||||
mockDate,
|
||||
'Asia/Tokyo',
|
||||
mockTimeFormat,
|
||||
);
|
||||
|
||||
expect(formatInTimeZone).toHaveBeenCalledWith(
|
||||
mockDate,
|
||||
'Asia/Tokyo',
|
||||
'MMM d · HH:mm',
|
||||
);
|
||||
expect(result).toBe('Oct 16 · 02:30');
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||
|
||||
export const detectDateFormat = (): DateFormat => {
|
||||
export const detectDateFormat = (): keyof typeof DateFormat => {
|
||||
const date = new Date();
|
||||
const formatter = new Intl.DateTimeFormat(navigator.language);
|
||||
const parts = formatter.formatToParts(date);
|
||||
@ -9,9 +9,9 @@ export const detectDateFormat = (): DateFormat => {
|
||||
.filter((part) => ['year', 'month', 'day'].includes(part.type))
|
||||
.map((part) => part.type);
|
||||
|
||||
if (partOrder[0] === 'month') return DateFormat.MONTH_FIRST;
|
||||
if (partOrder[0] === 'day') return DateFormat.DAY_FIRST;
|
||||
if (partOrder[0] === 'year') return DateFormat.YEAR_FIRST;
|
||||
if (partOrder[0] === 'month') return 'MONTH_FIRST';
|
||||
if (partOrder[0] === 'day') return 'DAY_FIRST';
|
||||
if (partOrder[0] === 'year') return 'YEAR_FIRST';
|
||||
|
||||
return DateFormat.MONTH_FIRST;
|
||||
return 'MONTH_FIRST';
|
||||
};
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { TimeFormat } from '@/localization/constants/TimeFormat';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const detectTimeFormat = () => {
|
||||
export const detectTimeFormat = (): keyof typeof TimeFormat => {
|
||||
const isHour12 = Intl.DateTimeFormat(navigator.language, {
|
||||
hour: 'numeric',
|
||||
}).resolvedOptions().hour12;
|
||||
|
||||
if (isDefined(isHour12) && isHour12) {
|
||||
return TimeFormat.HOUR_12;
|
||||
return 'HOUR_12';
|
||||
}
|
||||
|
||||
return TimeFormat.HOUR_24;
|
||||
return 'HOUR_24';
|
||||
};
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import { DATE_FORMAT_WITHOUT_YEAR } from '@/localization/constants/DateFormatWithoutYear';
|
||||
import { TimeFormat } from '@/localization/constants/TimeFormat';
|
||||
import { detectDateFormat } from '@/localization/utils/detectDateFormat';
|
||||
import { formatInTimeZone } from 'date-fns-tz';
|
||||
|
||||
export const formatDateISOStringToDateTimeSimplified = (
|
||||
date: Date,
|
||||
timeZone: string,
|
||||
timeFormat: TimeFormat,
|
||||
) => {
|
||||
const simplifiedDateFormat = DATE_FORMAT_WITHOUT_YEAR[detectDateFormat()];
|
||||
|
||||
return formatInTimeZone(
|
||||
date,
|
||||
timeZone,
|
||||
`${simplifiedDateFormat} · ${timeFormat}`,
|
||||
);
|
||||
};
|
||||
@ -7,7 +7,7 @@ export const getDateFormatFromWorkspaceDateFormat = (
|
||||
) => {
|
||||
switch (workspaceDateFormat) {
|
||||
case WorkspaceMemberDateFormatEnum.System:
|
||||
return detectDateFormat();
|
||||
return DateFormat[detectDateFormat()];
|
||||
case WorkspaceMemberDateFormatEnum.MonthFirst:
|
||||
return DateFormat.MONTH_FIRST;
|
||||
case WorkspaceMemberDateFormatEnum.DayFirst:
|
||||
|
||||
@ -7,7 +7,7 @@ export const getTimeFormatFromWorkspaceTimeFormat = (
|
||||
) => {
|
||||
switch (workspaceTimeFormat) {
|
||||
case WorkspaceMemberTimeFormatEnum.System:
|
||||
return detectTimeFormat();
|
||||
return TimeFormat[detectTimeFormat()];
|
||||
case WorkspaceMemberTimeFormatEnum.Hour_24:
|
||||
return TimeFormat.HOUR_24;
|
||||
case WorkspaceMemberTimeFormatEnum.Hour_12:
|
||||
|
||||
Reference in New Issue
Block a user