From 86bfb30b6a15691dcff2137bda495021d22f3ecc Mon Sep 17 00:00:00 2001 From: David Veszelovszki Date: Thu, 23 May 2024 21:22:39 +0200 Subject: [PATCH] SSC: Fix invite date formatting (#62858) --- client/web/BUILD.bazel | 1 + .../web/src/cody/team/TeamMemberList.test.ts | 36 +++++++++++++++++++ client/web/src/cody/team/TeamMemberList.tsx | 15 +++++++- client/web/src/cody/util.ts | 4 +-- 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 client/web/src/cody/team/TeamMemberList.test.ts diff --git a/client/web/BUILD.bazel b/client/web/BUILD.bazel index a9a2579cfd8..c47b3efcf20 100644 --- a/client/web/BUILD.bazel +++ b/client/web/BUILD.bazel @@ -1897,6 +1897,7 @@ ts_project( "src/codeintel/ReferencesPanel.mocks.ts", "src/codeintel/ReferencesPanel.test.tsx", "src/cody/management/api/hooks/useApiClient.test.tsx", + "src/cody/team/TeamMemberList.test.ts", "src/cody/useCodyIgnore.test.ts", "src/components/ErrorBoundary.test.tsx", "src/components/FilteredConnection/FilteredConnection.test.tsx", diff --git a/client/web/src/cody/team/TeamMemberList.test.ts b/client/web/src/cody/team/TeamMemberList.test.ts new file mode 100644 index 00000000000..ad4a548c911 --- /dev/null +++ b/client/web/src/cody/team/TeamMemberList.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest' + +import { formatInviteDate } from './TeamMemberList' + +describe('formatInviteDate', () => { + it('shows relative descriptions of time in the desired format', () => { + // ISO-8601 (RFC3339) strings, just the way the backend returns them + const inputDates = [ + '2024-05-22T15:59:55.000000+00:00', + '2024-05-22T14:17:55.000000+00:00', + '2024-05-21T14:17:55.000000+00:00', + '2024-05-15T14:17:55.000000+00:00', + ] + + const now = new Date('2024-05-22T16:00:00.000000+00:00') + + const expectedOutput = ['5 seconds ago', '1 hour ago', 'yesterday', 'last week'] + + const outputDates = inputDates.map(date => formatInviteDate(date, now)) + + expect(outputDates).toEqual(expectedOutput) + }) + + it('handles malformed input', () => { + // These are in the format of the normal output of our Go back end + const inputDates = [null, '', '1T14:17:55.000000+00:00', '2024-05-15T14:17:55'] + + const now = new Date('2024-05-22T16:00:00.000000+00:00') + + const expectedOutput = ['', '', '', 'last week'] + + const outputDates = inputDates.map(date => formatInviteDate(date, now)) + + expect(outputDates).toEqual(expectedOutput) + }) +}) diff --git a/client/web/src/cody/team/TeamMemberList.tsx b/client/web/src/cody/team/TeamMemberList.tsx index ec62248e84f..60fa35b3fa8 100644 --- a/client/web/src/cody/team/TeamMemberList.tsx +++ b/client/web/src/cody/team/TeamMemberList.tsx @@ -1,6 +1,7 @@ import { type FunctionComponent, useMemo, useCallback, useState } from 'react' import classNames from 'classnames' +import { intlFormatDistance } from 'date-fns' import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry' import { H2, Text, Badge, Link, ButtonLink } from '@sourcegraph/wildcard' @@ -34,6 +35,18 @@ export interface TeamInvite { acceptedAt: string | null } +// This tiny function is extracted to make it testable. Same for the "now" parameter. +export const formatInviteDate = (sentAt: string | null, now?: Date): string => { + try { + if (sentAt) { + return intlFormatDistance(sentAt, now ?? new Date()) + } + return '' + } catch { + return '' + } +} + export const TeamMemberList: FunctionComponent = ({ teamId, teamMembers, @@ -266,7 +279,7 @@ export const TeamMemberList: FunctionComponent = ({ )}
- Invite sent {invite.sentAt /* TODO format this */} + Invite sent {formatInviteDate(invite.sentAt)}
{isAdmin && ( <> diff --git a/client/web/src/cody/util.ts b/client/web/src/cody/util.ts index 53924952ae4..9c5084a17fd 100644 --- a/client/web/src/cody/util.ts +++ b/client/web/src/cody/util.ts @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react' // URL the user needs to navigate to in order to modify their Cody Pro subscription. export const manageSubscriptionRedirectURL = `${ - window.context.frontendCodyProConfig?.sscBaseUrl || 'https://accounts.sourcegraph.com/cody' + window.context?.frontendCodyProConfig?.sscBaseUrl || 'https://accounts.sourcegraph.com/cody' }/subscription` /** @@ -14,7 +14,7 @@ export const manageSubscriptionRedirectURL = `${ * for managing their Cody Pro subscription information. */ export function isEmbeddedCodyProUIEnabled(): boolean { - return !!(window.context.frontendCodyProConfig as { stripePublishableKey: string } | undefined) + return !!(window.context?.frontendCodyProConfig as { stripePublishableKey: string } | undefined) ?.stripePublishableKey }