diff --git a/api/src/services/migration.service.ts b/api/src/services/migration.service.ts index ebcc7dcf6..a11b5acf3 100644 --- a/api/src/services/migration.service.ts +++ b/api/src/services/migration.service.ts @@ -986,13 +986,14 @@ const getLogs = async (req: Request): Promise => { } if (fs.existsSync(absolutePath)) { let index = 0; - const logs = await fs.promises.readFile(absolutePath, "utf8"); + const logs = await fs?.promises?.readFile?.(absolutePath, "utf8"); let logEntries = logs ?.split("\n") ?.map((line) => { try { const parsedLine = JSON?.parse(line) - parsedLine['id'] = index; + parsedLine && (parsedLine['id'] = index); + ++index; return parsedLine ? parsedLine : null; } catch (error) { diff --git a/api/src/services/runCli.service.ts b/api/src/services/runCli.service.ts index f457db1a7..caf61f79a 100644 --- a/api/src/services/runCli.service.ts +++ b/api/src/services/runCli.service.ts @@ -47,8 +47,9 @@ const determineLogLevel = (text: string): string => { * Strips ANSI color codes from text to create clean logs */ const stripAnsiCodes = (text: string): string => { + const ESC = '\u001b'; // This regex removes all ANSI escape sequences (color codes) - return text.replace(/\u001b\[\d+m/g, ''); + return text?.replace(new RegExp(`${ESC}\\[[0-9;]*m`, 'g'), ''); }; /** @@ -206,34 +207,6 @@ export const runCli = async ( // Debug which log path is being used console.info(`Log path for CLI commands: ${transformePath}`); - // Test writing all log levels directly to the file - try { - const testLogs = [ - { - level: 'info', - message: 'TEST INFO LOG', - timestamp: new Date().toISOString(), - }, - { - level: 'warn', - message: 'TEST WARNING LOG', - timestamp: new Date().toISOString(), - }, - { - level: 'error', - message: 'TEST ERROR LOG', - timestamp: new Date().toISOString(), - }, - ]; - - for (const log of testLogs) { - fs.appendFileSync(transformePath, JSON.stringify(log) + '\n'); - } - console.info('Test logs written successfully'); - } catch (err) { - console.error('Failed to write test logs:', err); - } - // Make sure to set the global.currentLogFile to the project log file // This is the key part - setting the log file path to the migration service log file await setLogFilePath(transformePath); @@ -377,4 +350,4 @@ export const runCli = async ( } }; -export const utilsCli = { runCli }; +export const utilsCli = { runCli }; \ No newline at end of file diff --git a/ui/src/components/Common/Modal/FilterModal/FilterModal.tsx b/ui/src/components/Common/Modal/FilterModal/FilterModal.tsx index 403f30cd6..080f4d8a1 100644 --- a/ui/src/components/Common/Modal/FilterModal/FilterModal.tsx +++ b/ui/src/components/Common/Modal/FilterModal/FilterModal.tsx @@ -2,8 +2,9 @@ import { Button, Checkbox, Icon, Radio, Search, Tooltip } from '@contentstack/ve import React, { useEffect, useState } from 'react'; import WordWrapper from '../../WordWrapper/WordWrapper'; -import { IFilterStatusType, IFilterType } from './filterModal.interface'; + import './FilterModal.scss'; +import { IFilterStatusType, IFilterType } from './filterModal.interface'; /** * Props for the FilterModal component. diff --git a/ui/src/components/Common/Modal/LogModal/LogModal.scss b/ui/src/components/Common/Modal/LogModal/LogModal.scss new file mode 100644 index 000000000..eea1c767d --- /dev/null +++ b/ui/src/components/Common/Modal/LogModal/LogModal.scss @@ -0,0 +1,8 @@ +@import '../../../../scss/variables'; + +.text { + font-size: $size-font-medium; + line-height: $line-height-reset; + color: #3d3f4c; + text-transform: capitalize; +} \ No newline at end of file diff --git a/ui/src/components/Common/Modal/LogModal/LogModal.tsx b/ui/src/components/Common/Modal/LogModal/LogModal.tsx new file mode 100644 index 000000000..a89960257 --- /dev/null +++ b/ui/src/components/Common/Modal/LogModal/LogModal.tsx @@ -0,0 +1,23 @@ +import { ModalBody, ModalHeader } from '@contentstack/venus-components' +import { LogEntry } from '../../../ExecutionLogs/executionlog.interface'; +import './LogModal.scss' + +interface LogModalProps { + readonly props: { + closeModal: () => void; + }; + readonly data: LogEntry; +} + +export default function LogModal({ props, data }: LogModalProps) { + return ( + <> + + + + {data?.methodName && (<>

Method Name: {data?.methodName}


)} +

{data?.message}

+
+ + ); +} diff --git a/ui/src/components/Common/Settings/Settings.scss b/ui/src/components/Common/Settings/Settings.scss index 439c434b8..1adc0c097 100644 --- a/ui/src/components/Common/Settings/Settings.scss +++ b/ui/src/components/Common/Settings/Settings.scss @@ -114,4 +114,8 @@ .back-button { cursor: pointer; margin-bottom: 20px; -} \ No newline at end of file +} + +.PageLayout--primary--v2 .PageLayout__body--left-drawer { + display: none; +} diff --git a/ui/src/components/Common/Settings/index.tsx b/ui/src/components/Common/Settings/index.tsx index 3ba2ac8f4..8ea61d05c 100644 --- a/ui/src/components/Common/Settings/index.tsx +++ b/ui/src/components/Common/Settings/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; import { Params, useNavigate, useParams } from 'react-router'; import { Icon, @@ -31,9 +31,8 @@ import DeleteProjectModal from '../DeleteProjectModal'; //stylesheet import './Settings.scss'; -import { useDispatch } from 'react-redux'; import { updateNewMigrationData } from '../../../store/slice/migrationDataSlice'; -import { DEFAULT_NEW_MIGRATION } from '../../../context/app/app.interface'; +import { DEFAULT_NEW_MIGRATION, INewMigration } from '../../../context/app/app.interface'; import ExecutionLog from '../../../components/ExecutionLogs'; import AuditLogs from '../../AuditLogs'; @@ -45,16 +44,19 @@ const Settings = () => { const params: Params = useParams(); const [cmsData, setCmsData] = useState(); - const [active, setActive] = useState(); const [currentHeader, setCurrentHeader] = useState(); const [projectName, setProjectName] = useState(''); - const [projectId, setProjectId] = useState(''); + const [projectDescription, setProjectDescription] = useState(''); const selectedOrganisation = useSelector( (state: RootState) => state?.authentication?.selectedOrganisation ); + const newMigrationData = useSelector((state: RootState) => state?.migration?.newMigrationData); + + const active_state = newMigrationData?.settings?.active_state; + const currentStep = useSelector( (state: RootState) => state?.migration?.newMigrationData?.project_current_step ); @@ -68,7 +70,6 @@ const Settings = () => { getCMSDataFromFile(CS_ENTRIES.SETTING) .then((data) => { setCmsData(data); - setActive(data?.project?.title); setCurrentHeader(data?.project?.title); }) .catch((err) => { @@ -84,7 +85,6 @@ const Settings = () => { if (status === HTTP_CODES.OK) { setProjectName(data?.name); setProjectDescription(data?.description); - setProjectId(params?.projectId ?? ''); } }; @@ -133,13 +133,11 @@ const Settings = () => { }; const handleDeleteProject = async (closeModal: () => void): Promise => { - //setIsLoading(true); const response = await deleteProject(selectedOrganisation?.value, params?.projectId ?? ''); - if (response?.status === HTTP_CODES.OK) { - //setIsLoading(false); + if (response?.status === HTTP_CODES?.OK) { closeModal(); - dispatch(updateNewMigrationData(DEFAULT_NEW_MIGRATION)); + dispatch(updateNewMigrationData(newMigrationData)); setTimeout(() => { navigate('/projects'); }, 800); @@ -158,7 +156,8 @@ const Settings = () => { const handleBack = () => { navigate(`/projects/${params?.projectId}/migration/steps/${currentStep}`); - } + dispatch(updateNewMigrationData({...newMigrationData, settings: DEFAULT_NEW_MIGRATION?.settings })); + }; const handleClick = () => { cbModal({ @@ -207,7 +206,7 @@ const Settings = () => { const content = { component: (
- {active === cmsData?.project?.title && ( + {active_state === cmsData?.project?.title && (
{cmsData?.project?.general} @@ -261,13 +260,8 @@ const Settings = () => {
)} - {active === cmsData?.execution_logs?.title && ( - - )} - {active === cmsData?.audit_logs?.title && - - - } + {active_state === cmsData?.execution_logs?.title && } + {active_state === cmsData?.audit_logs?.title && }
) }; @@ -291,7 +285,7 @@ const Settings = () => { withTooltip={true} tooltipContent={'Back'} tooltipPosition="right" - className='back-button' + className="back-button" /> {cmsData?.title} @@ -299,34 +293,52 @@ const Settings = () => { } onClick={() => { - setActive(cmsData?.project?.title); setCurrentHeader(cmsData?.project?.title); + const activeTabState: INewMigration = { + ...newMigrationData, + settings: { + active_state: cmsData?.project?.title ?? '' + } + }; + dispatch(updateNewMigrationData(activeTabState)); }} version="v2" /> } onClick={() => { - setActive(cmsData?.execution_logs?.title); setCurrentHeader(cmsData?.execution_logs?.title); + const activeTabState: INewMigration = { + ...newMigrationData, + settings: { + active_state: cmsData?.execution_logs?.title ?? '' + } + }; + dispatch(updateNewMigrationData(activeTabState)); }} version="v2" /> } onClick={() => { - setActive(cmsData?.audit_logs?.title); + const activeTabState: INewMigration = { + ...newMigrationData, + settings: { + active_state: cmsData?.audit_logs?.title ?? '' + } + }; + dispatch(updateNewMigrationData(activeTabState)); setCurrentHeader(cmsData?.audit_logs?.title); }} version="v2" @@ -338,18 +350,18 @@ const Settings = () => { const header = { component: (
- {active === cmsData?.project?.title ? ( + {active_state === cmsData?.project?.title ? ( ) : ( )}
diff --git a/ui/src/components/ExecutionLogs/index.scss b/ui/src/components/ExecutionLogs/index.scss index e90794b27..2ef2c4d7b 100644 --- a/ui/src/components/ExecutionLogs/index.scss +++ b/ui/src/components/ExecutionLogs/index.scss @@ -1,3 +1,7 @@ +.Search-input-show { + margin-bottom: 8px; + width: 300px; +} .Search .Search__search-icon { top: calc(50% - 10px); width: 20px; @@ -11,7 +15,7 @@ margin-bottom: 8px; } -.PageLayout--primary .PageLayout__leftSidebar+.PageLayout__content .PageLayout__body { +.PageLayout--primary .PageLayout__leftSidebar + .PageLayout__content .PageLayout__body { width: calc(100% - 15.4rem); } @@ -21,8 +25,9 @@ justify-content: space-between; } -.Table__head { - height: auto; + +.Table:has(.custom-empty-state) { + height: 40.5rem; } .custom-empty-state { @@ -39,18 +44,47 @@ margin-bottom: 8px; } +.center-dash { + display: flex; + align-items: center; + justify-content: center; + margin: auto; +} + +.Table__head__row { + height: 60px !important; +} +.Table{ + padding-bottom:20px +} + +.message-cell { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} -.PageLayout__body { - .table-height { - .Table { - height: calc(100vh - 20rem) !important; - } +.PageLayout__body { + .executionTable-height { + .Table__body { + height: calc(100vh - 11rem) !important; + } + .Table.TableWithPaginated { + .Table__body { + height: calc(100vh - 11rem) !important; + } + } + } +} - .Table.TableWithPaginated { - .Table__body { - height: calc(100vh - 25rem) !important; - } - } +.table-row-action { + display: flex; + gap: 16px; + .text-size{ + padding-top: 1px; + } + .VerticalActionTooltip__actions{ + width: 100px !important; } -} \ No newline at end of file +} diff --git a/ui/src/components/ExecutionLogs/index.tsx b/ui/src/components/ExecutionLogs/index.tsx index e8f389644..d6b12f083 100644 --- a/ui/src/components/ExecutionLogs/index.tsx +++ b/ui/src/components/ExecutionLogs/index.tsx @@ -4,36 +4,44 @@ import { InfiniteScrollTable, Button, EmptyState, - Select + Select, + cbModal, + Icon } from '@contentstack/venus-components'; import { RootState } from '../../store'; import { DropdownOption, FilterOption, LogEntry, StackIds } from './executionlog.interface'; import './index.scss'; -import FilterModal from '../FilterModale'; import { getMigrationLogs } from '../../services/api/migration.service'; import { EXECUTION_LOGS_UI_TEXT } from '../../utilities/constants'; +import LogModal from '../Common/Modal/LogModal/LogModal'; +import { useParams } from 'react-router'; +import FilterModal from '../FilterModal/FilterModal'; -const ExecutionLogs = ({ projectId }: { projectId: string }) => { +const ExecutionLogs = () => { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [totalCounts, setTotalCounts] = useState(0); const [searchText, setSearchText] = useState(''); - const [filterOption, setFilterOption] = useState([]); + const [selectedFilterOption, setSelectedFilterOption] = useState([]); const [isCursorInside, setIsCursorInside] = useState(false); const [isFilterApplied, setIsFilterApplied] = useState(false); const [isFilterDropdownOpen, setIsFilterDropdownOpen] = useState(false); const [filterValue, setFilterValue] = useState('all'); + const [filterOptions, setFilterOptions] = useState([]); - const selectedOrganisation = useSelector( + const { projectId } = useParams<{ projectId: string }>(); + const v2Features = { + pagination: true, + isNewEmptyState: true, + tableRowAction: true + }; + const selectedOrganisation = useSelector( (state: RootState) => state?.authentication?.selectedOrganisation ); - - - const testStacks = useSelector( (state: RootState) => state?.migration?.newMigrationData?.testStacks ); @@ -58,12 +66,10 @@ const ExecutionLogs = ({ projectId }: { projectId: string }) => { }); } - const [selectedStack, setSelectedStack] = useState( - { - label: stackIds?.[stackIds?.length - 1]?.label ?? '', - value: stackIds?.[stackIds?.length - 1]?.value ?? '' - } - ); + const [selectedStack, setSelectedStack] = useState({ + label: stackIds?.[stackIds?.length - 1]?.label ?? '', + value: stackIds?.[stackIds?.length - 1]?.value ?? '' + }); useEffect(() => { if (selectedStack) { @@ -91,7 +97,7 @@ const ExecutionLogs = ({ projectId }: { projectId: string }) => { //Method to maintain filter value const updateValue = ({ value, isChecked }: { value: FilterOption; isChecked: boolean }) => { try { - let filterValueCopy: FilterOption[] = [...filterOption]; + let filterValueCopy: FilterOption[] = [...selectedFilterOption]; if (!filterValueCopy?.length && isChecked) { filterValueCopy?.push(value); @@ -103,7 +109,7 @@ const ExecutionLogs = ({ projectId }: { projectId: string }) => { filterValueCopy = filterValueCopy?.filter((v) => v?.value !== value?.value); } - setFilterOption(filterValueCopy); + setSelectedFilterOption(filterValueCopy); } catch (error) { console.error('Error updating filter value:', error); } @@ -112,19 +118,20 @@ const ExecutionLogs = ({ projectId }: { projectId: string }) => { // Method to handle Apply const onApply = () => { try { - if (!filterOption?.length) { + if (!selectedFilterOption?.length) { const newFilter = 'all'; setFilterValue(newFilter); fetchData({ filter: newFilter }); closeModal(); return; } + setIsFilterApplied(true); - const usersQueryArray = filterOption?.map((item) => item?.value); - const newFilter = usersQueryArray?.length > 1 ? usersQueryArray?.join('-') : usersQueryArray?.[0]; + const usersQueryArray = selectedFilterOption?.map?.((item) => item?.value); + const newFilter = + usersQueryArray?.length > 1 ? usersQueryArray?.join('-') : usersQueryArray?.[0]; setFilterValue(newFilter); fetchData({ filter: newFilter }); - setIsFilterApplied(true); closeModal(); } catch (error) { console.error('Error applying filter:', error); @@ -140,8 +147,8 @@ const ExecutionLogs = ({ projectId }: { projectId: string }) => { const iconProps = { className: isFilterApplied - ? EXECUTION_LOGS_UI_TEXT.FILTER_ICON.FILTER_ON - : EXECUTION_LOGS_UI_TEXT.FILTER_ICON.FILTER_OFF, + ? EXECUTION_LOGS_UI_TEXT?.FILTER_ICON?.FILTER_ON + : EXECUTION_LOGS_UI_TEXT?.FILTER_ICON?.FILTER_OFF, withTooltip: true, tooltipContent: 'Filter', tooltipPosition: 'left' @@ -154,7 +161,8 @@ const ExecutionLogs = ({ projectId }: { projectId: string }) => { }} onMouseLeave={() => { setIsCursorInside(false); - }}> + }} + className="filter-button">