Navbar with AI chats (#18161)

## Summary
Add Home/Chat tabs and a dedicated threads list in the navigation
drawer.

## Changes
- **Navbar tabs:** Tabs in the drawer to switch between Home and Chat
(with “New chat” button). Shown on desktop when expanded and on mobile
below the workspace selector.
- **Navbar threads list:** New `NavigationDrawerAIChatThreadsList` for
the Chat tab with date groups (Today / Yesterday / Older), thread rows
as `NavigationDrawerItem` (IconComment, title, timestamp). Shared
`useAIChatThreadClick` hook used by navbar and command menu; navbar
passes `resetNavigationStack: true`.
- **NavigationDrawerItem:** New `alwaysShowRightOptions` prop so the
timestamp is always visible (no hover-only).

---------

Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Abdul Rahman
2026-02-28 04:07:14 +05:30
committed by GitHub
parent 9f5a8735c9
commit e806d36099
34 changed files with 970 additions and 291 deletions
@@ -10,6 +10,7 @@ import {
beautifyExactDate,
beautifyExactDateTime,
beautifyPastDateRelativeToNow,
beautifyPastDateRelativeToNowShort,
hasDatePassed,
parseDate,
} from '~/utils/date-utils';
@@ -122,6 +123,66 @@ describe('beautifyPastDateRelativeToNow', () => {
});
});
describe('beautifyPastDateRelativeToNowShort', () => {
it('should return "now" for dates less than 60 seconds ago', () => {
const result = beautifyPastDateRelativeToNowShort(
'2023-12-31T23:59:15.000Z',
);
expect(result).toBe('now');
});
it('should return minutes format', () => {
const result = beautifyPastDateRelativeToNowShort(
'2023-12-31T23:55:00.000Z',
);
expect(result).toBe('5m');
});
it('should return hours format', () => {
const result = beautifyPastDateRelativeToNowShort(
'2023-12-31T22:00:00.000Z',
);
expect(result).toBe('2h');
});
it('should return days format', () => {
const result = beautifyPastDateRelativeToNowShort(
'2023-12-29T00:00:00.000Z',
);
expect(result).toBe('3d');
});
it('should return weeks format', () => {
const result = beautifyPastDateRelativeToNowShort(
'2023-12-18T00:00:00.000Z',
);
expect(result).toBe('2w');
});
it('should return months format', () => {
const result = beautifyPastDateRelativeToNowShort(
'2023-08-01T00:00:00.000Z',
);
expect(result).toBe('5mo');
});
it('should return years format', () => {
const result = beautifyPastDateRelativeToNowShort(
'2022-01-01T00:00:00.000Z',
);
expect(result).toBe('2y');
});
it('should return empty string and log error for invalid date', () => {
const result = beautifyPastDateRelativeToNowShort('invalid-date-string');
expect(logError).toHaveBeenCalledWith(
Error('Invalid date passed to formatPastDate: "invalid-date-string"'),
);
expect(result).toBe('');
});
});
describe('hasDatePassed', () => {
it('should log an error and return false when passed an invalid date string', () => {
const result = hasDatePassed('invalid-date-string');