diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index ce902b70ee484..3329dfa731067 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -911,6 +911,8 @@
"Calendar_settings": "Calendar settings",
"Call": "Call",
"Call_Already_Ended": "Call Already Ended",
+ "Call_ID": "Call ID",
+ "Call_info": "Call info",
"Call_Information": "Call Information",
"Call_again": "Call again",
"Call_back": "Call back",
@@ -1803,6 +1805,7 @@
"Duplicate_file_name_found": "Duplicate file name found.",
"Duplicate_private_group_name": "A Private Group with name '%s' exists",
"Duplicated_Email_address_will_be_ignored": "Duplicated email address will be ignored.",
+ "Duration": "Duration",
"E2EE_Composer_Unencrypted_Message": "You're sending an unencrypted message",
"E2EE_password_reset": "E2EE password reset",
"E2EE_alert": "Enabling E2EE affects other functionalities
- - Encrypted content cannot be found by search
- - Encrypted content cannot be audited
- - Bot interactions may not work with encrypted messages
",
@@ -2569,6 +2572,7 @@
"Incoming_call_from": "Incoming call from",
"Incoming_call_from__roomName__": "Incoming call from {{roomName}}",
"Incoming_call_transfer": "Incoming call transfer",
+ "Incoming_voice_call": "Incoming voice call",
"Incoming_voice_call_canceled_suddenly": "An Incoming Voice Call was canceled suddenly.",
"Incoming_voice_call_canceled_user_not_registered": "An Incoming Voice Call was canceled due to an unexpected error.",
"Industry": "Industry",
@@ -3969,6 +3973,7 @@
"Out_of_seats": "Out of Seats",
"Outdated": "Outdated",
"Outgoing": "Outgoing",
+ "Outgoing_voice_call": "Outgoing voice call",
"Outgoing_WebHook": "Outgoing WebHook",
"Outgoing_WebHook_Description": "Get data out of Rocket.Chat in real-time.",
"Outlook_Calendar": "Outlook Calendar",
@@ -5525,6 +5530,7 @@
"UserData_MessageLimitPerRequest": "Message Limit per Request",
"UserData_ProcessingFrequency": "Processing Frequency (Minutes)",
"User_Info": "User Info",
+ "User_info": "User info",
"User_Interface": "User Interface",
"User_Presence": "User Presence",
"User_Settings": "User Settings",
diff --git a/packages/ui-voip/package.json b/packages/ui-voip/package.json
index 4202640ad8890..a25827fe0a302 100644
--- a/packages/ui-voip/package.json
+++ b/packages/ui-voip/package.json
@@ -13,9 +13,9 @@
"lint": "eslint --ext .js,.jsx,.ts,.tsx .",
"lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix",
"storybook": "storybook dev -p 6006",
- "test": "jest",
+ "test": "yarn testunit",
"test-storybook": "npx concurrently -k -s first -n \"SB,TEST\" \"yarn storybook --ci\" \"npx wait-on tcp:127.0.0.1:6006 && yarn exec test-storybook\"",
- "testunit": "jest",
+ "testunit": "TZ=UTC jest",
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"dependencies": {
@@ -35,6 +35,7 @@
"@rocket.chat/fuselage": "^0.70.0",
"@rocket.chat/fuselage-hooks": "~0.38.1",
"@rocket.chat/fuselage-tokens": "~0.33.2",
+ "@rocket.chat/fuselage-ui-kit": "workspace:^",
"@rocket.chat/icons": "~0.46.0",
"@rocket.chat/jest-presets": "workspace:~",
"@rocket.chat/mock-providers": "workspace:~",
@@ -79,6 +80,7 @@
"@rocket.chat/css-in-js": "*",
"@rocket.chat/fuselage": "*",
"@rocket.chat/fuselage-hooks": "*",
+ "@rocket.chat/fuselage-ui-kit": "workspace:^",
"@rocket.chat/icons": "*",
"@rocket.chat/styled": "*",
"@rocket.chat/ui-avatar": "workspace:^",
diff --git a/packages/ui-voip/src/index.ts b/packages/ui-voip/src/index.ts
index f81a2ded8a160..2f8c0ce602141 100644
--- a/packages/ui-voip/src/index.ts
+++ b/packages/ui-voip/src/index.ts
@@ -4,4 +4,7 @@ export { MediaCallContext, useMediaCallExternalContext as useMediaCallContext, t
export { useMediaCallAction } from './hooks';
+export { CallHistoryContextualBar } from './views';
+export type { InternalCallHistoryContact, ExternalCallHistoryContact, CallHistoryData } from './views';
+
export { getHistoryMessagePayload } from './ui-kit/getHistoryMessagePayload';
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.stories.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.stories.tsx
new file mode 100644
index 0000000000000..be86fed7e4e6e
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.stories.tsx
@@ -0,0 +1,58 @@
+import { mockAppRoot } from '@rocket.chat/mock-providers';
+import type { Meta, StoryObj } from '@storybook/react';
+import type { ReactElement } from 'react';
+
+import type { HistoryActionCallbacks } from './CallHistoryActions';
+import CallHistoryActions from './CallHistoryActions';
+
+const noop = () => undefined;
+
+const meta = {
+ title: 'V2/Views/CallHistoryContextualbar/CallHistoryActions',
+ component: CallHistoryActions,
+ decorators: [
+ mockAppRoot()
+ .withTranslations('en', 'core', {
+ Options: 'Options',
+ Voice_call: 'Voice call',
+ Video_call: 'Video call',
+ Jump_to_message: 'Jump to message',
+ Direct_Message: 'Direct Message',
+ User_info: 'User info',
+ })
+ .withDefaultLanguage('en-US')
+ .buildStoryDecorator(),
+ (Story): ReactElement => ,
+ ],
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+const actionList = ['voiceCall', 'videoCall', 'jumpToMessage', 'directMessage', 'userInfo'];
+
+const getArgs = (index: number) => {
+ return Object.fromEntries(actionList.slice(0, index).map((action) => [action, noop])) as HistoryActionCallbacks;
+};
+
+export const Default: Story = {
+ args: {
+ onClose: noop,
+ actions: getArgs(5),
+ },
+};
+
+export const WithLessActions: Story = {
+ args: {
+ onClose: noop,
+ actions: getArgs(3),
+ },
+};
+
+export const WithSingleAction: Story = {
+ args: {
+ onClose: noop,
+ actions: getArgs(1),
+ },
+};
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.tsx
new file mode 100644
index 0000000000000..fce4d09644634
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.tsx
@@ -0,0 +1,56 @@
+import type { Keys as IconName } from '@rocket.chat/icons';
+import { ContextualbarActions, ContextualbarClose, GenericMenu } from '@rocket.chat/ui-client';
+import type { TFunction } from 'i18next';
+import { useTranslation } from 'react-i18next';
+
+type HistoryActions = 'voiceCall' | 'videoCall' | 'jumpToMessage' | 'directMessage' | 'userInfo';
+
+export type HistoryActionCallbacks = {
+ [K in HistoryActions]?: () => void;
+};
+
+type CallHistoryActionsProps = {
+ onClose: () => void;
+ actions: HistoryActionCallbacks;
+};
+
+const iconDictionary: Record = {
+ voiceCall: 'phone',
+ videoCall: 'video',
+ jumpToMessage: 'jump',
+ directMessage: 'balloon',
+ userInfo: 'user',
+} as const;
+
+const i18nDictionary: Record = {
+ voiceCall: 'Voice_call',
+ videoCall: 'Video_call',
+ jumpToMessage: 'Jump_to_message',
+ directMessage: 'Direct_Message',
+ userInfo: 'User_info',
+} as const;
+
+const getItems = (actions: HistoryActionCallbacks, t: TFunction) => {
+ return (Object.entries(actions) as [HistoryActions, () => void][])
+ .filter(([_, callback]) => callback)
+ .map(([action, callback]) => ({
+ id: action,
+ icon: iconDictionary[action],
+ content: t(i18nDictionary[action]),
+ onClick: callback,
+ }));
+};
+
+const CallHistoryActions = ({ onClose, actions }: CallHistoryActionsProps) => {
+ const { t } = useTranslation();
+
+ const items = getItems(actions, t);
+ return (
+
+ {items.length > 0 && }
+
+
+ );
+};
+
+export default CallHistoryActions;
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.spec.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.spec.tsx
new file mode 100644
index 0000000000000..d084e50e6e398
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.spec.tsx
@@ -0,0 +1,20 @@
+import { mockAppRoot } from '@rocket.chat/mock-providers';
+import { composeStories } from '@storybook/react';
+import { render } from '@testing-library/react';
+import { axe } from 'jest-axe';
+
+import * as contextualbarStories from './CallHistoryContextualbar.stories';
+
+const testCases = Object.values(composeStories(contextualbarStories)).map((Story) => [Story.storyName || 'Story', Story]);
+
+test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
+ const view = render(, { wrapper: mockAppRoot().build() });
+ expect(view.baseElement).toMatchSnapshot();
+});
+
+test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
+ const { container } = render(, { wrapper: mockAppRoot().build() });
+
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+});
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.stories.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.stories.tsx
new file mode 100644
index 0000000000000..93d09034e6f1c
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.stories.tsx
@@ -0,0 +1,88 @@
+import { mockAppRoot } from '@rocket.chat/mock-providers';
+import type { Meta, StoryObj } from '@storybook/react';
+import type { ReactElement } from 'react';
+
+import CallHistoryContextualbar from './CallHistoryContextualbar';
+
+const noop = () => undefined;
+
+const meta = {
+ title: 'V2/Views/CallHistoryContextualbar',
+ component: CallHistoryContextualbar,
+ decorators: [
+ mockAppRoot()
+ .withTranslations('en', 'core', {
+ Call_info: 'Call info',
+ Direct_message: 'Direct message',
+ Call: 'Call',
+ Call_ended_bold: '*Voice call ended*',
+ Incoming_voice_call: 'Incoming voice call',
+ Outgoing_voice_call: 'Outgoing voice call',
+ Duration: 'Duration',
+ Voice_call_extension: 'Voice call extension',
+ Call_ID: 'Call ID',
+ Options: 'Options',
+ Voice_call: 'Voice call',
+ Video_call: 'Video call',
+ Jump_to_message: 'Jump to message',
+ Direct_Message: 'Direct Message',
+ User_info: 'User info',
+ })
+ .withDefaultLanguage('en-US')
+ .buildStoryDecorator(),
+ (Story): ReactElement => ,
+ ],
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+const externalContact = {
+ number: '1234567890',
+};
+
+const internalContact = {
+ _id: '1234567890',
+ name: 'John Doe',
+ username: 'john.doe',
+ voiceCallExtension: '0000',
+};
+
+export const Default: Story = {
+ args: {
+ onClose: noop,
+ actions: {
+ voiceCall: noop,
+ videoCall: noop,
+ jumpToMessage: noop,
+ directMessage: noop,
+ userInfo: noop,
+ },
+ contact: internalContact,
+ data: {
+ callId: '1234567890',
+ direction: 'inbound',
+ duration: 100,
+ startedAt: new Date('2025-02-07T12:00:00.000Z'),
+ state: 'ended',
+ },
+ },
+};
+
+export const ExternalContact: Story = {
+ args: {
+ onClose: noop,
+ actions: {
+ voiceCall: noop,
+ },
+ data: {
+ callId: '1234567890',
+ direction: 'inbound',
+ duration: 100,
+ startedAt: new Date('2025-02-07T12:00:00.000Z'),
+ state: 'ended',
+ },
+ contact: externalContact,
+ },
+};
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.tsx
new file mode 100644
index 0000000000000..5f84e6f014fdd
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryContextualbar.tsx
@@ -0,0 +1,132 @@
+import { Box, Button, ButtonGroup, Icon, MessageBlock } from '@rocket.chat/fuselage';
+import { UiKitComponent, UiKitMessage as UiKitMessageSurfaceRender, UiKitContext } from '@rocket.chat/fuselage-ui-kit';
+import {
+ ContextualbarDialog,
+ ContextualbarHeader,
+ ContextualbarTitle,
+ ContextualbarFooter,
+ ContextualbarIcon,
+ ContextualbarScrollableContent,
+ InfoPanel,
+ InfoPanelSection,
+ InfoPanelLabel,
+ InfoPanelText,
+} from '@rocket.chat/ui-client';
+import { useTranslation } from 'react-i18next';
+
+import type { HistoryActionCallbacks } from './CallHistoryActions';
+import CallHistoryActions from './CallHistoryActions';
+import CallHistoryExternalUser from './CallHistoryExternalUser';
+import CallHistoryInternalUser from './CallHistoryInternalUser';
+import { useFullStartDate } from './useFullStartDate';
+import { getHistoryMessagePayload } from '../../ui-kit/getHistoryMessagePayload';
+
+export type InternalCallHistoryContact = {
+ _id: string;
+ name?: string;
+ username: string;
+ voiceCallExtension?: string;
+};
+
+export type ExternalCallHistoryContact = {
+ number: string;
+};
+
+export type CallHistoryData = {
+ callId: string;
+ direction: 'inbound' | 'outbound';
+ duration: number;
+ startedAt: Date;
+ state: 'ended' | 'not-answered' | 'failed' | 'error' | 'transferred';
+ messageId?: string;
+};
+
+type CallHistoryContextualBarProps = {
+ onClose: () => void;
+ actions: HistoryActionCallbacks;
+ contact: InternalCallHistoryContact | ExternalCallHistoryContact;
+ data: CallHistoryData;
+};
+
+const isInternalCallHistoryContact = (
+ contact: InternalCallHistoryContact | ExternalCallHistoryContact,
+): contact is InternalCallHistoryContact => {
+ return '_id' in contact;
+};
+
+const contextValue = {
+ action: () => undefined,
+ rid: '',
+ values: {},
+};
+
+const CallHistoryContextualBar = ({ onClose, actions, contact, data }: CallHistoryContextualBarProps) => {
+ const { t } = useTranslation();
+
+ const { voiceCall, directMessage } = actions;
+ const { duration, callId, direction, startedAt } = data;
+
+ const date = useFullStartDate(startedAt);
+ return (
+
+
+
+ {t('Call_info')}
+
+
+
+
+
+ {isInternalCallHistoryContact(contact) ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {direction === 'inbound' ? t('Incoming_voice_call') : t('Outgoing_voice_call')}
+
+
+
+
+
+
+
+
+ {date}
+
+
+ {t('Call_ID')}
+ {callId}
+
+ {isInternalCallHistoryContact(contact) && contact.voiceCallExtension && (
+
+ {t('Voice_call_extension')}
+ {contact.voiceCallExtension}
+
+ )}
+
+
+
+
+ {isInternalCallHistoryContact(contact) && directMessage && (
+
+ )}
+ {voiceCall && (
+
+ )}
+
+
+
+ );
+};
+
+export default CallHistoryContextualBar;
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryExternalUser.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryExternalUser.tsx
new file mode 100644
index 0000000000000..9bda24ea09c03
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryExternalUser.tsx
@@ -0,0 +1,21 @@
+import { Box, Icon, FramedIcon } from '@rocket.chat/fuselage';
+
+type CallHistoryExternalUserProps = {
+ number: string;
+};
+
+const CallHistoryExternalUser = ({ number }: CallHistoryExternalUserProps) => {
+ return (
+
+
+
+
+
+
+
+ {number.startsWith('+') ? number : `+${number}`}
+
+ );
+};
+
+export default CallHistoryExternalUser;
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryInternalUser.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryInternalUser.tsx
new file mode 100644
index 0000000000000..20edfebdf1c7b
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryInternalUser.tsx
@@ -0,0 +1,36 @@
+import { Box, Icon, Avatar, StatusBullet } from '@rocket.chat/fuselage';
+import { useUserDisplayName } from '@rocket.chat/ui-client';
+import { useUserAvatarPath, useUserPresence, useUserCard } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+type CallHistoryInternalUserProps = {
+ username: string;
+ name?: string;
+ _id: string;
+};
+
+const CallHistoryInternalUser = ({ username, name, _id }: CallHistoryInternalUserProps) => {
+ const getUserAvatarPath = useUserAvatarPath();
+
+ const avatarUrl = useMemo(() => {
+ return getUserAvatarPath({ username });
+ }, [username, getUserAvatarPath]);
+
+ const displayName = useUserDisplayName({ username, name });
+
+ const userStatus = useUserPresence(_id);
+
+ const { triggerProps, openUserCard } = useUserCard();
+
+ return (
+ openUserCard(e, username)} {...triggerProps}>
+ {avatarUrl ? : }
+
+
+
+ {displayName}
+
+ );
+};
+
+export default CallHistoryInternalUser;
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/__snapshots__/CallHistoryContextualbar.spec.tsx.snap b/packages/ui-voip/src/views/CallHistoryContextualbar/__snapshots__/CallHistoryContextualbar.spec.tsx.snap
new file mode 100644
index 0000000000000..85936952ebd7c
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/__snapshots__/CallHistoryContextualbar.spec.tsx.snap
@@ -0,0 +1,604 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`renders Default without crashing 1`] = `
+
+
+
+
+
+
+
+
+
+
+ Call info
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ john.doe
+
+
+
+
+
+
+
+
+ Incoming voice call
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Voice call ended
+
+
+
+
+
+
+
+
+
+ Friday, February 7, 2025 at 12:00:00 PM
+
+
+
+
+ Call ID
+
+
+ 1234567890
+
+
+
+
+ Voice call extension
+
+
+ 0000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`renders ExternalContact without crashing 1`] = `
+
+
+
+
+
+
+
+
+
+
+ Call info
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +1234567890
+
+
+
+
+
+
+
+
+ Incoming voice call
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Voice call ended
+
+
+
+
+
+
+
+
+
+ Friday, February 7, 2025 at 12:00:00 PM
+
+
+
+
+ Call ID
+
+
+ 1234567890
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/__snapshots__/useFullStartDate.spec.tsx.snap b/packages/ui-voip/src/views/CallHistoryContextualbar/__snapshots__/useFullStartDate.spec.tsx.snap
new file mode 100644
index 0000000000000..70831981b82dd
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/__snapshots__/useFullStartDate.spec.tsx.snap
@@ -0,0 +1,51 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`renders Default without crashing 1`] = `
+
+
+
+ Friday, February 7, 2025 at 12:00:00 PM
+
+
+
+`;
+
+exports[`renders French without crashing 1`] = `
+
+
+
+ vendredi 22 août 2025 à 12:00:00
+
+
+
+`;
+
+exports[`renders German without crashing 1`] = `
+
+
+
+ Dienstag, 11. November 2025 um 12:00:00
+
+
+
+`;
+
+exports[`renders Japanese without crashing 1`] = `
+
+
+
+ 2025年12月16日火曜日 12:00:00
+
+
+
+`;
+
+exports[`renders Spanish without crashing 1`] = `
+
+
+
+ lunes, 7 de julio de 2025, 12:00:00
+
+
+
+`;
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/index.ts b/packages/ui-voip/src/views/CallHistoryContextualbar/index.ts
new file mode 100644
index 0000000000000..67951ea0aec6a
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/index.ts
@@ -0,0 +1,2 @@
+export { default as CallHistoryContextualBar } from './CallHistoryContextualbar';
+export type { InternalCallHistoryContact, ExternalCallHistoryContact, CallHistoryData } from './CallHistoryContextualbar';
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.spec.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.spec.tsx
new file mode 100644
index 0000000000000..7592f3eb4f204
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.spec.tsx
@@ -0,0 +1,20 @@
+import { mockAppRoot } from '@rocket.chat/mock-providers';
+import { composeStories } from '@storybook/react';
+import { render } from '@testing-library/react';
+import { axe } from 'jest-axe';
+
+import * as useFullStartDateStories from './useFullStartDate.stories';
+
+const testCases = Object.values(composeStories(useFullStartDateStories)).map((Story) => [Story.storyName || 'Story', Story]);
+
+test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
+ const view = render(, { wrapper: mockAppRoot().build() });
+ expect(view.baseElement).toMatchSnapshot();
+});
+
+test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
+ const { container } = render(, { wrapper: mockAppRoot().build() });
+
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+});
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.stories.tsx b/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.stories.tsx
new file mode 100644
index 0000000000000..0c732041f32e1
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.stories.tsx
@@ -0,0 +1,52 @@
+import { mockAppRoot } from '@rocket.chat/mock-providers';
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { useFullStartDate } from './useFullStartDate';
+
+const FullStartDate = ({ date }: { date: Date }) => {
+ return {useFullStartDate(date)}
;
+};
+
+const meta = {
+ title: 'V2/Views/CallHistoryContextualbar/useFullStartDate',
+ component: FullStartDate,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ decorators: [mockAppRoot().withDefaultLanguage('en-US').buildStoryDecorator()],
+ args: {
+ date: new Date('2025-02-07T12:00:00.000Z'),
+ },
+};
+
+export const Spanish: Story = {
+ decorators: [mockAppRoot().withDefaultLanguage('es-ES').buildStoryDecorator()],
+ args: {
+ date: new Date('2025-07-07T12:00:00.000Z'),
+ },
+};
+
+export const French: Story = {
+ decorators: [mockAppRoot().withDefaultLanguage('fr-FR').buildStoryDecorator()],
+ args: {
+ date: new Date('2025-08-22T12:00:00.000Z'),
+ },
+};
+
+export const German: Story = {
+ decorators: [mockAppRoot().withDefaultLanguage('de-DE').buildStoryDecorator()],
+ args: {
+ date: new Date('2025-11-11T12:00:00.000Z'),
+ },
+};
+
+export const Japanese: Story = {
+ decorators: [mockAppRoot().withDefaultLanguage('ja-JP').buildStoryDecorator()],
+ args: {
+ date: new Date('2025-12-16T12:00:00.000Z'),
+ },
+};
diff --git a/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.ts b/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.ts
new file mode 100644
index 0000000000000..40e3c2763c0ab
--- /dev/null
+++ b/packages/ui-voip/src/views/CallHistoryContextualbar/useFullStartDate.ts
@@ -0,0 +1,12 @@
+import { useLanguage } from '@rocket.chat/ui-contexts';
+import { useMemo } from 'react';
+
+export const useFullStartDate = (startedAt: Date) => {
+ const locale = useLanguage();
+
+ const date = useMemo(() => {
+ return new Intl.DateTimeFormat(locale, { dateStyle: 'full', timeStyle: 'medium' }).format(startedAt);
+ }, [locale, startedAt]);
+
+ return date;
+};
diff --git a/packages/ui-voip/src/views/index.ts b/packages/ui-voip/src/views/index.ts
index 1deba45f179e7..7380fae28044b 100644
--- a/packages/ui-voip/src/views/index.ts
+++ b/packages/ui-voip/src/views/index.ts
@@ -1,3 +1,4 @@
export { default as TransferModal } from './TransferModal';
export * from './MediaCallWidget';
export { default as PermissionFlowModal, type PermissionFlowModalType } from './PermissionFlow/PermissionFlowModal';
+export * from './CallHistoryContextualbar';
diff --git a/yarn.lock b/yarn.lock
index b5ce68fe6b697..36545d2a25bb3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10521,6 +10521,7 @@ __metadata:
"@rocket.chat/fuselage": "npm:^0.70.0"
"@rocket.chat/fuselage-hooks": "npm:~0.38.1"
"@rocket.chat/fuselage-tokens": "npm:~0.33.2"
+ "@rocket.chat/fuselage-ui-kit": "workspace:^"
"@rocket.chat/icons": "npm:~0.46.0"
"@rocket.chat/jest-presets": "workspace:~"
"@rocket.chat/media-signaling": "workspace:~"
@@ -10568,6 +10569,7 @@ __metadata:
"@rocket.chat/css-in-js": "*"
"@rocket.chat/fuselage": "*"
"@rocket.chat/fuselage-hooks": "*"
+ "@rocket.chat/fuselage-ui-kit": "workspace:^"
"@rocket.chat/icons": "*"
"@rocket.chat/styled": "*"
"@rocket.chat/ui-avatar": "workspace:^"