diff --git a/web/src/cde-gitness/components/EventTimeline/EventTimeline.tsx b/web/src/cde-gitness/components/EventTimeline/EventTimeline.tsx index cf29db3f1..6c0e19042 100644 --- a/web/src/cde-gitness/components/EventTimeline/EventTimeline.tsx +++ b/web/src/cde-gitness/components/EventTimeline/EventTimeline.tsx @@ -7,7 +7,7 @@ import { useStrings } from 'framework/strings' import { formatTimestamp } from './EventTimeline.utils' import css from './EventTimeline.module.scss' -const EventTimeline = ({ data }: { data?: TypesGitspaceEventResponse[] | null; polling?: boolean }) => { +const EventTimeline = ({ data }: { data?: TypesGitspaceEventResponse[] | null }) => { const localRef = useRef(null) const scrollContainerRef = useRef(null) @@ -50,7 +50,7 @@ const EventTimeline = ({ data }: { data?: TypesGitspaceEventResponse[] | null; p {data?.map((item, index) => { return ( - + { + if (timestamp > 1e12) { + timestamp = Math.floor(timestamp / 1e6) + } + return timestamp +} + export const formatTimestamp = (timestamp: number) => { - const inputDate = moment(timestamp) + const convertedTimeStamp = convertToMilliSecs(timestamp) + + const inputDate = moment(convertedTimeStamp) const currentDate = moment() if (inputDate.isSame(currentDate, 'day')) { diff --git a/web/src/cde-gitness/components/EventTimelineSummary/EventTimelineSummary.tsx b/web/src/cde-gitness/components/EventTimelineSummary/EventTimelineSummary.tsx index 3b74d86d6..83c11bda7 100644 --- a/web/src/cde-gitness/components/EventTimelineSummary/EventTimelineSummary.tsx +++ b/web/src/cde-gitness/components/EventTimelineSummary/EventTimelineSummary.tsx @@ -3,9 +3,11 @@ import moment from 'moment' import { Container, Text, Layout } from '@harnessio/uicore' import { Color, FontVariation } from '@harnessio/design-system' import { useStrings } from 'framework/strings' +import { convertToMilliSecs } from '../EventTimeline/EventTimeline.utils' const EventTimelineSummary = ({ timestamp, message }: { timestamp?: number; message?: string }) => { const { getString } = useStrings() + const convertedTimeStamp = convertToMilliSecs(timestamp || 0) return ( @@ -17,7 +19,7 @@ const EventTimelineSummary = ({ timestamp, message }: { timestamp?: number; mess {message} - {moment(timestamp).format('DD MMM, YYYY hh:mma')} + {moment(convertedTimeStamp).format('DD MMM, YYYY hh:mma')} )} diff --git a/web/src/cde-gitness/components/GitspaceListing/ListGitspaces.tsx b/web/src/cde-gitness/components/GitspaceListing/ListGitspaces.tsx index ebb0e331c..7e65c7abe 100644 --- a/web/src/cde-gitness/components/GitspaceListing/ListGitspaces.tsx +++ b/web/src/cde-gitness/components/GitspaceListing/ListGitspaces.tsx @@ -539,8 +539,10 @@ export const RenderActions = ({ row, refreshList }: RenderActionsProps) => { const handleDelete = async (e: React.MouseEvent) => { confirmDelete({ - title: getString('cde.deleteGitspaceTitle'), - message: getString('cde.deleteGitspaceText', { name: name }), + intent: 'danger', + title: getString('cde.deleteGitspaceTitle', { name: name }), + message: getString('cde.deleteGitspaceText'), + confirmText: getString('delete'), action: async () => { try { e.preventDefault() diff --git a/web/src/cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm.module.scss b/web/src/cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm.module.scss index 1d64a6ebc..5ce182398 100644 --- a/web/src/cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm.module.scss +++ b/web/src/cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm.module.scss @@ -1,4 +1,6 @@ .formFields { + div[data-id='branch-1'], + div[data-id='code_repo_url-0'], [class*='repoInput'], [class*='branchDropdown'] { width: 100% !important; diff --git a/web/src/cde-gitness/hooks/useGetLogStream.tsx b/web/src/cde-gitness/hooks/useGetLogStream.tsx index 667e7464c..05fd70f25 100644 --- a/web/src/cde-gitness/hooks/useGetLogStream.tsx +++ b/web/src/cde-gitness/hooks/useGetLogStream.tsx @@ -1,4 +1,6 @@ +import { get } from 'lodash-es' import { useEffect, useState } from 'react' +import { useStrings, type UseStringsReturn } from 'framework/strings' export interface LogData { pos: number @@ -6,24 +8,29 @@ export interface LogData { time: number } -export function parseLog(log: string): LogData[] { +export function parseLog(log: string, getString: UseStringsReturn['getString']): LogData[] { const logLines = log.trim().split('\n\n') const parsedData: LogData[] = [] - logLines.forEach(line => { - const dataMatch = line.match(/data: (.+)/) + try { + logLines.forEach(line => { + const dataMatch = line.match(/data: (.+)/) - if (dataMatch && dataMatch[1] !== 'eof') { - const eventData: LogData = JSON.parse(dataMatch[1]) + if (dataMatch && dataMatch[1] !== 'eof') { + const eventData: LogData = JSON.parse(dataMatch[1]) - parsedData.push(eventData) - } - }) + parsedData.push(eventData) + } + }) + } catch (error) { + parsedData.push({ pos: 1, out: get(error, 'message') || getString('cde.details.logsFailed'), time: 0 }) + } return parsedData } export const useGetLogStream = ({ response }: { response: any }) => { + const { getString } = useStrings() const [data, setData] = useState('') useEffect(() => { @@ -50,5 +57,5 @@ export const useGetLogStream = ({ response }: { response: any }) => { } }, [response]) - return { data: parseLog(data) } + return { data: parseLog(data, getString) } } diff --git a/web/src/cde-gitness/pages/GitspaceDetails/GitspaceDetails.tsx b/web/src/cde-gitness/pages/GitspaceDetails/GitspaceDetails.tsx index 81ad4d979..47ac6c15a 100644 --- a/web/src/cde-gitness/pages/GitspaceDetails/GitspaceDetails.tsx +++ b/web/src/cde-gitness/pages/GitspaceDetails/GitspaceDetails.tsx @@ -22,7 +22,7 @@ import { useAppContext } from 'AppContext' import { useStrings } from 'framework/strings' import EventTimelineAccordion from 'cde-gitness/components/EventTimelineAccordion/EventTimelineAccordion' import { DetailsCard } from 'cde-gitness/components/DetailsCard/DetailsCard' -import type { TypesGitspaceConfig, TypesGitspaceEventResponse } from 'cde-gitness/services' +import type { EnumGitspaceStateType, TypesGitspaceConfig, TypesGitspaceEventResponse } from 'cde-gitness/services' import { GitspaceActionType, GitspaceStatus } from 'cde/constants' import { useQueryParams } from 'hooks/useQueryParams' import { useUpdateQueryParams } from 'hooks/useUpdateQueryParams' @@ -33,6 +33,7 @@ import vscodeIcon from 'cde/icons/VSCode.svg?url' import pauseIcon from 'cde-gitness/assests/pause.svg?url' import { StandaloneIDEType } from 'cde-gitness/constants' import homeIcon from 'cde-gitness/assests/home.svg?url' +import { useConfirmAct } from 'hooks/useConfirmAction' import ContainerLogs from '../../components/ContainerLogs/ContainerLogs' import { useGetLogStream } from '../../hooks/useGetLogStream' import css from './GitspaceDetails.module.scss' @@ -41,9 +42,10 @@ export const GitspaceDetails = () => { const space = useGetSpaceParam() const { getString } = useStrings() const { routes } = useAppContext() - const { showError } = useToaster() + const { showError, showSuccess } = useToaster() const history = useHistory() const [startTriggred, setStartTriggred] = useState(false) + const [triggerPollingOnStart, setTriggerPollingOnStart] = useState() const { gitspaceId = '' } = useParams<{ gitspaceId?: string }>() const [isStreamingLogs, setIsStreamingLogs] = useState(false) @@ -60,7 +62,11 @@ export const GitspaceDetails = () => { debounce: 500 }) - const { refetch: refetchLogsData, response } = useGet({ + const { + refetch: refetchLogsData, + response, + error: streamLogsError + } = useGet({ path: `api/v1/gitspaces/${space}/${gitspaceId}/+/logs/stream`, debounce: 500, lazy: true @@ -79,12 +85,11 @@ export const GitspaceDetails = () => { const { updateQueryParams } = useUpdateQueryParams<{ redirectFrom?: string }>() const { redirectFrom = '' } = useQueryParams<{ redirectFrom?: string }>() - const pollingCondition = [ - GitspaceStatus.RUNNING, - GitspaceStatus.STOPPED, - GitspaceStatus.ERROR, - GitspaceStatus.UNINITIALIZED - ].includes(data?.state as GitspaceStatus) + const pollingCondition = triggerPollingOnStart + ? false + : [GitspaceStatus.RUNNING, GitspaceStatus.STOPPED, GitspaceStatus.ERROR, GitspaceStatus.UNINITIALIZED].includes( + data?.state as GitspaceStatus + ) const disabledActionButtons = [GitspaceStatus.STARTING, GitspaceStatus.STOPPING].includes( data?.state as GitspaceStatus @@ -99,15 +104,21 @@ export const GitspaceDetails = () => { if (disabledActionButtons && filteredEvent?.length && !isStreamingLogs) { refetchLogsData() setIsStreamingLogs(true) - } else if (filteredEvent?.length && !disabledActionButtons && isStreamingLogs) { + } else if ( + (filteredEvent?.length && !disabledActionButtons && isStreamingLogs) || + (isStreamingLogs && streamLogsError) + ) { setIsStreamingLogs(false) } - }, [eventData, data?.instance?.updated, disabledActionButtons]) + }, [eventData, data?.instance?.updated, disabledActionButtons, streamLogsError]) usePolling( async () => { await refetchEventData() await refetch() + if (triggerPollingOnStart) { + setTriggerPollingOnStart(undefined) + } }, { pollingInterval: 10000, @@ -120,7 +131,11 @@ export const GitspaceDetails = () => { if (redirectFrom && !startTriggred && !mutateLoading) { try { setStartTriggred(true) - await actionMutate({ action: GitspaceActionType.START }) + const resp = await actionMutate({ action: GitspaceActionType.START }) + if (resp?.state === GitspaceStatus.STARTING) { + setTriggerPollingOnStart(resp.state) + } + await refetchEventData() await refetch() updateQueryParams({ redirectFrom: undefined }) } catch (err) { @@ -136,6 +151,28 @@ export const GitspaceDetails = () => { const formattedlogsdata = useGetLogStream({ response }) + const confirmDelete = useConfirmAct() + + const handleDelete = async (e: React.MouseEvent) => { + confirmDelete({ + intent: 'danger', + title: getString('cde.deleteGitspaceTitle', { name: data?.name }), + message: getString('cde.deleteGitspaceText'), + confirmText: getString('delete'), + action: async () => { + try { + e.preventDefault() + e.stopPropagation() + await deleteGitspace({}) + showSuccess(getString('cde.deleteSuccess')) + history.push(routes.toCDEGitspaces({ space })) + } catch (exception) { + showError(getErrorMessage(exception)) + } + } + }) + } + return ( <> { }} /> void} + onClick={handleDelete as Unknown as () => void} text={