diff --git a/package.json b/package.json index 2e32981b..39589470 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "framer-motion": "^12.5.0", "html-react-parser": "^5.2.3", "i18next": "^23.15.1", - "mixpanel-browser": "^2.55.1", + "mixpanel-browser": "^2.64.0", "prismjs": "^1.29.0", "qs": "^6.13.0", "react": "^18.3.1", diff --git a/src/api/docgen.api.ts b/src/api/docgen.api.ts index 7c1d846d..76f415e3 100644 --- a/src/api/docgen.api.ts +++ b/src/api/docgen.api.ts @@ -108,7 +108,7 @@ export const triggerDocGen = ( archDocConfig?: ArchDocConfig, ): Promise => { const data = buildDocGenData(orgName, repoName, vendor); - const url = `/v1/git/generate/${genType}/doc`; + const url = `/v1/git/job/${genType}/start`; return httpApi.post(url, { ...data, style_guide: styleGuide, arch_doc_config: archDocConfig }).then(({ data }) => data); }; @@ -119,15 +119,14 @@ export const getDocGenStatus = ( genType: string, lroJobId: string, ): Promise => { - const data = buildDocGenData(orgName, repoName, vendor); - const url = `/v1/git/generate/${genType}/doc/status?jobId=${lroJobId}`; - return httpApi.post(url, { ...data }).then(({ data }) => data); + const url = `/v1/git/job/${lroJobId}/status`; + return httpApi.get(url).then(({ data }) => data); }; export const terminateJob = ( lroJobId: string, ): Promise> => { - const url = `/v1/git/lro/${lroJobId}/terminate`; + const url = `/v1/git/job/${lroJobId}/terminate`; return httpApi.post>(url).then(({ data }) => data); }; @@ -165,7 +164,7 @@ export const getAdvancedRepoAnalysisDetails = ( analysisType: string, ): Promise> => { const data = buildDocGenData(orgName, repoName, vendor); - const url = `/v1/git/analyze/repo/${analysisType}/details`; + const url = `/v1/git/job/${analysisType}/details`; return httpApi.post>(url, { ...data }).then(({ data }) => data); // Extract the message }; diff --git a/src/api/git.api.ts b/src/api/git.api.ts index 633beb90..3467edf5 100644 --- a/src/api/git.api.ts +++ b/src/api/git.api.ts @@ -49,12 +49,6 @@ export enum FunctionOutdatedStatusEnum { CURRENT = "CURRENT" } -export type FunctionOutdatedResponseSchema = { - status: FunctionOutdatedStatusEnum; - explanation: string; - docstring?: string; - grammarError?: boolean; -} export type FunctionDetail = { name: string; @@ -69,10 +63,14 @@ export type FunctionDetail = { halsteadVolume?: number; halsteadDifficulty?: number; halsteadEffort?: number; + grammarError: boolean; + funStartLine: number; + funEndLine: number; + suggestedDocstring?: string; complexityCategory?: ComplexityCategory; - languageType: string; - outdatedStatus?: FunctionOutdatedResponseSchema; + plName: string; + outdatedDocstring?: boolean; urgentDocumentation?: boolean; hasDocstring: boolean; @@ -126,6 +124,11 @@ export type GitAppMetaData = { analysisReport: AnalysisReport; }; +export type GitAppAdvancedMetaData = { + analysisReport: AnalysisReport; +}; + + export enum PlanType { FREE = "FREE", PREMIUM = "PREMIUM", @@ -140,11 +143,11 @@ export type GitAppUsageType = { vendor: string; planType: PlanType; gitAppMetaData: GitAppMetaData; + gitAppAdvancedMeta: GitAppAdvancedMetaData; isActive: boolean; isPrivate: boolean; isInstalled: boolean; userId: number; - full_repo_count: number; }; export const getGitAppUsageForRepository = ( diff --git a/src/components/common/BaseMenu/BaseMenu.styles.ts b/src/components/common/BaseMenu/BaseMenu.styles.ts index b789bc81..984120c8 100644 --- a/src/components/common/BaseMenu/BaseMenu.styles.ts +++ b/src/components/common/BaseMenu/BaseMenu.styles.ts @@ -3,14 +3,14 @@ import { Menu as AntMenu } from 'antd'; export const Menu = styled(AntMenu)` &.ant-menu .ant-menu-item-icon { - font-size: ${({ theme }) => theme.fontSizes.xl}; - width: 1.25rem; + font-size: ${({ theme }) => theme.fontSizes.md}; + width: 1rem; } .ant-menu-item, .ant-menu-submenu { font-size: ${({ theme }) => theme.fontSizes.xs}; - border-radius: 0; + border-radius: 6px; } .ant-menu-item { diff --git a/src/components/dashboard/DashboardHeader/OutdatedDocsModal.tsx b/src/components/dashboard/DashboardHeader/OutdatedDocsModal.tsx index 53a21046..ac225c1f 100644 --- a/src/components/dashboard/DashboardHeader/OutdatedDocsModal.tsx +++ b/src/components/dashboard/DashboardHeader/OutdatedDocsModal.tsx @@ -76,6 +76,27 @@ const SuggestedDocBlock = styled.pre` word-break: break-all; `; +const HoverLink = styled.div` + display: none; + position: absolute; + background: #f0f0f0; + border: 1px solid #d9d9d9; + padding: 4px 8px; + border-radius: 4px; + z-index: 1000; + margin-top: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + font-size: 12px; + + .link-container:hover & { + display: block; + } +`; + +const LinkContainer = styled.div` + position: relative; +`; + const OutdatedDocsModal: React.FC = ({ visible, onClose, @@ -88,7 +109,7 @@ const OutdatedDocsModal: React.FC = ({ // Filter for outdated and urgent docs const outdatedFunctions = functionDetails?.filter( - func => func.outdatedStatus?.status === FunctionOutdatedStatusEnum.OUTDATED + func => func.outdatedDocstring ) || []; const urgentFunctions = functionDetails?.filter( @@ -135,7 +156,7 @@ const OutdatedDocsModal: React.FC = ({ // Outdated by language chart data const outdatedByLanguage = outdatedFunctions.reduce((acc, func) => { - const lang = func.languageType; + const lang = func.plName; acc[lang] = (acc[lang] || 0) + 1; return acc; }, {} as Record); @@ -236,14 +257,26 @@ const OutdatedDocsModal: React.FC = ({ const outdatedColumns = [ { title: 'Function Name', - dataIndex: 'name', - key: 'name', + dataIndex: 'methodName', + key: 'methodName', render: (text: string, record: FunctionDetail) => ( - - {text} + + + {text} +
{record.fileName} -
+ + + {record.gitLocation} + + + ), }, { @@ -253,7 +286,7 @@ const OutdatedDocsModal: React.FC = ({ render: (text: ComplexityCategory, record: FunctionDetail) => { const color = text === ComplexityCategory.HIGH ? 'red' : text === ComplexityCategory.MEDIUM ? 'orange' : 'green'; - return {text}; + return <>{text} ; }, sorter: (a: FunctionDetail, b: FunctionDetail) => { const order = { [ComplexityCategory.HIGH]: 3, [ComplexityCategory.MEDIUM]: 2, [ComplexityCategory.LOW]: 1 }; @@ -273,25 +306,40 @@ const OutdatedDocsModal: React.FC = ({ }, { title: 'Language', - dataIndex: 'languageType', + dataIndex: 'plName', key: 'language', render: (text: string) => {text} }, { title: 'Issue', - dataIndex: 'outdatedStatus', + dataIndex: 'outdatedDocstring', key: 'issue', - render: (status: FunctionDetail['outdatedStatus']) => { + render: (status: FunctionDetail['outdatedDocstring'], record: FunctionDetail) => { if (!status) return '-'; return ( - + } color="warning"> - {status.grammarError ? 'Grammar Error' : 'Outdated'} + {record.grammarError ? 'Grammar Error' : 'Outdated'} ); } }, + // { + // title: 'CP', + // dataIndex: 'cyclomaticComplexity', + // key: 'cyclomaticComplexity', + // render: (status: FunctionDetail['cyclomaticComplexity'], record: FunctionDetail) => { + // if (!status) return '-'; + // return ( + // + // } > + // {record.grammarError ? 'Grammar Error' : 'Outdated'} + // + // + // ); + // } + // }, { title: 'Urgent', dataIndex: 'urgentDocumentation', @@ -377,24 +425,16 @@ const OutdatedDocsModal: React.FC = ({ )} - {record.outdatedStatus?.docstring && ( + {record.suggestedDocstring && ( <> Suggested Docstring: - {record.outdatedStatus.docstring} + {record.suggestedDocstring} )} Function Code: {record.content} - - {record.outdatedStatus?.explanation && ( - <> - Issue Explanation: - - {record.outdatedStatus.explanation} - - - )} + ), }} @@ -418,18 +458,26 @@ const OutdatedDocsModal: React.FC = ({ {record.docstring} )} + + {record.suggestedDocstring && ( + <> + Suggested Docstring: + {record.suggestedDocstring} + + )} + Function Code: {record.content} - {record.outdatedStatus?.explanation && ( + {/* {record.suggestedDocstring && ( <> Issue Explanation: - {record.outdatedStatus.explanation} + {record.suggestedDocstring} - )} + )} */} ), }} diff --git a/src/components/dashboard/DashboardHeader/ReadmeAnalysisModal.tsx b/src/components/dashboard/DashboardHeader/ReadmeAnalysisModal.tsx index eda81307..1734c7c2 100644 --- a/src/components/dashboard/DashboardHeader/ReadmeAnalysisModal.tsx +++ b/src/components/dashboard/DashboardHeader/ReadmeAnalysisModal.tsx @@ -155,14 +155,14 @@ const ReadmeAnalysisModal: React.FC = ({ } suffix={`words`} /> } suffix={`lines`} /> @@ -180,15 +180,15 @@ const ReadmeAnalysisModal: React.FC = ({ } /> } - valueStyle={{ color: readmeAnalysis.brokenLinks.length > 0 ? '#f5222d' : '#52c41a' }} + valueStyle={{ color: readmeAnalysis?.brokenLinks?.length > 0 ? '#f5222d' : '#52c41a' }} /> = ({
Flesch Reading Ease: - {(readmeAnalysis.textStatistics.fleschReadingEase||0).toFixed(1)}% + {(readmeAnalysis?.textStatistics?.fleschReadingEase||0).toFixed(1)}% = 70 ? '#52c41a' : '#faad14'} + strokeColor={(readmeAnalysis?.textStatistics?.fleschReadingEase || 0) >= 70 ? '#52c41a' : '#faad14'} showInfo={false} />
Flesch-Kincaid Grade: - {(readmeAnalysis.textStatistics.fleschKincaidGrade||0).toFixed(1)}% + {(readmeAnalysis?.textStatistics?.fleschKincaidGrade||0).toFixed(1)}% = 70 ? '#52c41a' : '#faad14'} + strokeColor={(readmeAnalysis?.textStatistics?.fleschKincaidGrade || 0) >= 70 ? '#52c41a' : '#faad14'} showInfo={false} />
SMOG Index: - {(readmeAnalysis.textStatistics.smogIndex||0).toFixed(1)}% + {(readmeAnalysis?.textStatistics?.smogIndex||0).toFixed(1)}% = 70 ? '#52c41a' : '#faad14'} + strokeColor={(readmeAnalysis?.textStatistics?.smogIndex || 0) >= 70 ? '#52c41a' : '#faad14'} showInfo={false} /> diff --git a/src/components/dashboard/DashboardHeader/RepoMetricsCards.tsx b/src/components/dashboard/DashboardHeader/RepoMetricsCards.tsx index 4d442442..b98f7e53 100644 --- a/src/components/dashboard/DashboardHeader/RepoMetricsCards.tsx +++ b/src/components/dashboard/DashboardHeader/RepoMetricsCards.tsx @@ -10,7 +10,7 @@ import { CodeOutlined } from '@ant-design/icons'; import { AnalysisReport, BasicFunctionDocAnalysis, GitAppUsageType, FunctionDetail } from '@app/api/git.api'; -import { triggerAnalysisRepo, getAdvancedRepoAnalysisDetails, getAnalysisRepoStatus, terminateJob } from '@app/api/docgen.api'; +import { triggerDocGen, getAdvancedRepoAnalysisDetails, getDocGenStatus, terminateJob } from '@app/api/docgen.api'; import DocumentationMetricsModal from './DocumentationMetricsModal'; import { checkAzurePAT } from '@app/api/azure.api'; import AzurePATModal from '../AzurePATModal/AzurePATModal'; @@ -146,11 +146,14 @@ const RepoMetricsCards: React.FC = ({ repoDetails, onRunF }; useEffect(() => { - if (repoDetails?.gitAppMetaData?.analysisReport) { - const report = repoDetails.gitAppMetaData.analysisReport; + console.log('RepoMetricsCards mounted with repoDetails:', repoDetails.organizationName); + if (repoDetails?.gitAppAdvancedMeta?.analysisReport) { + console.log('Basic function doc analysis found11111:'); + const report = repoDetails.gitAppAdvancedMeta.analysisReport; updateMetricsFromReport(report); if (report.basicFuncDocAnalysis?.totalFunctions > 0) { + console.log('Basic function doc analysis found:', report.basicFuncDocAnalysis); setHasRepoAnalysis(true); } @@ -218,7 +221,7 @@ const RepoMetricsCards: React.FC = ({ repoDetails, onRunF } addTerminalMessage(`Triggering analysis...`, MessageType.INFO); - const response = await triggerAnalysisRepo(orgName, repoName, vendor, analysisType); + const response = await triggerDocGen(orgName, repoName, vendor, analysisType || ''); setLroJobId(response.lroJobId); addTerminalMessage(`Analysis triggered successfully`, MessageType.SUCCESS); } catch (error) { @@ -337,13 +340,12 @@ const RepoMetricsCards: React.FC = ({ repoDetails, onRunF // Update the analysis report with the new data const newAnalysisReport = response; - if(!updatedRepoDetails.gitAppMetaData) { - updatedRepoDetails.gitAppMetaData = { - siteUrl: '', + if(!updatedRepoDetails.gitAppAdvancedMeta) { + updatedRepoDetails.gitAppAdvancedMeta = { analysisReport: {} as AnalysisReport }; } - updatedRepoDetails.gitAppMetaData.analysisReport = newAnalysisReport; + updatedRepoDetails.gitAppAdvancedMeta.analysisReport = newAnalysisReport; // Update the local metrics based on the new report updateMetricsFromReport(newAnalysisReport); @@ -582,7 +584,7 @@ const RepoMetricsCards: React.FC = ({ repoDetails, onRunF
- {outdatedDocs}/{documentedFunctions} + {outdatedDocs+(totalFunctions-documentedFunctions)}/{totalFunctions} Outdated Docs
@@ -664,11 +666,11 @@ const RepoMetricsCards: React.FC = ({ repoDetails, onRunF - {readmeScore.toFixed(1)}%/100 + {readmeScore?.toFixed(1)|| 0}% Readme Quality Score 70 ? '#52c41a' : readmeScore > 40 ? '#faad14' : '#f5222d'} /> @@ -706,7 +708,7 @@ const RepoMetricsCards: React.FC = ({ repoDetails, onRunF setIsReadmeModalVisible(false)} - readmeAnalysis={repoDetails?.gitAppMetaData?.analysisReport?.readmeAnalysis} + readmeAnalysis={repoDetails?.gitAppAdvancedMeta?.analysisReport?.readmeAnalysis} repoName={repoDetails?.repoName} orgName={repoDetails?.organizationName} readmeSuggestion={readMeDetails} @@ -728,7 +730,7 @@ const RepoMetricsCards: React.FC = ({ repoDetails, onRunF orgName={orgName} repoName={repoName} vendor={repoDetails.vendor} - statusCheckFn={getAnalysisRepoStatus} + statusCheckFn={getDocGenStatus} onStatusUpdate={handleStatusUpdate} // Add the new status update handler /> diff --git a/src/components/dashboard/DashboardTerminal/ArchConfigModal.tsx b/src/components/dashboard/DashboardTerminal/ArchConfigModal.tsx index f27823fe..c5896fcd 100644 --- a/src/components/dashboard/DashboardTerminal/ArchConfigModal.tsx +++ b/src/components/dashboard/DashboardTerminal/ArchConfigModal.tsx @@ -95,6 +95,7 @@ interface ArchConfigModalProps { onCancel: () => void; onConfirm: (archConfig: ArchDocConfig) => void; loading?: boolean; + isPrivate?: boolean; } /** @@ -112,6 +113,7 @@ const ArchConfigModal: React.FC = ({ onCancel, onConfirm, loading = false, + isPrivate = false, }) => { const [form] = Form.useForm(); @@ -119,7 +121,8 @@ const ArchConfigModal: React.FC = ({ // Set default values when modal becomes visible if (visible) { form.setFieldsValue({ - hostDocumentation: true, + // For private repositories, hostDocumentation should be false by default + hostDocumentation: !isPrivate, generatePdf: true, generateMd: true, generateHtml: true, @@ -130,12 +133,23 @@ const ArchConfigModal: React.FC = ({ path: 'docs' }); } - }, [visible, form]); + }, [visible, form, isPrivate]); /** * Handle form submission and pass the config to the parent component */ - const handleFinish = (values: any) => { + const handleFinish = (values: { + hostDocumentation: boolean; + generatePdf: boolean; + generateMd: boolean; + generateHtml: boolean; + generateRst: boolean; + generateRtf: boolean; + generateXml: boolean; + commitToGit: boolean; + branch: string; + path: string; + }) => { const { commitToGit, branch, path, ...rest } = values; const archConfig: ArchDocConfig = { @@ -306,8 +320,8 @@ const ArchConfigModal: React.FC = ({ {({ getFieldValue, setFieldsValue }) => { const generateHtml = getFieldValue('generateHtml'); - // If HTML is unchecked, also uncheck the host documentation option - if (!generateHtml && getFieldValue('hostDocumentation')) { + // If HTML is unchecked or it's a private repository, also uncheck the host documentation option + if ((!generateHtml || isPrivate) && getFieldValue('hostDocumentation')) { setFieldsValue({ hostDocumentation: false }); } @@ -316,13 +330,17 @@ const ArchConfigModal: React.FC = ({ name="hostDocumentation" valuePropName="checked" > - - + + Host Documentation Online - - + "HTML format must be selected to enable online hosting" + }> + diff --git a/src/components/dashboard/DashboardTerminal/CustomTerminal.tsx b/src/components/dashboard/DashboardTerminal/CustomTerminal.tsx index 3e7ff8c4..34145632 100644 --- a/src/components/dashboard/DashboardTerminal/CustomTerminal.tsx +++ b/src/components/dashboard/DashboardTerminal/CustomTerminal.tsx @@ -395,8 +395,8 @@ const CustomTerminal: React.FC = ({ // Propagate status update to parent component if (onStatusUpdate && gitData) { - if(gitData.gitAppMetaData && gitData.gitAppMetaData.analysisReport) { - onStatusUpdate(100, gitData.gitAppMetaData.analysisReport); + if(gitData.gitAppAdvancedMeta && gitData.gitAppAdvancedMeta.analysisReport) { + onStatusUpdate(100, gitData.gitAppAdvancedMeta.analysisReport); } else { onStatusUpdate(100, undefined); } @@ -440,6 +440,19 @@ const CustomTerminal: React.FC = ({ } }; + const parseTimestamp = (timestamp: string): Date => { + // Attempt to parse the timestamp string directly + const date = new Date(timestamp); + + // If the date is invalid, return current date + if (isNaN(date.getTime())) { + console.error('Invalid timestamp format:', timestamp); + return new Date(); + } + + return date; + } + // Parse messages from the status response const parseAndAddMessage = (message: string) => { // Skip empty messages @@ -451,62 +464,59 @@ const CustomTerminal: React.FC = ({ // If we have at least 3 parts, we have a properly formatted message if (parts.length >= 3) { const messageType = parts[0]; - let timestamp = new Date(); - let content = parts.slice(2).join('::'); // Join the rest back in case message itself contains :: - - // Try to parse the timestamp - try { - timestamp = new Date(parts[1]); - } catch (error) { - console.error('Error parsing timestamp:', error); - } - - // Determine message type - let type = MessageType.INFO; + const timestamp = parseTimestamp(parts[1]); // Parse the timestamp from the second part + const contentList = parts.slice(2).join('::').split('\n'); + + for (let content of contentList) { + if( !content || content.trim() === '') continue; // Skip empty lines - switch (messageType) { - case 'ERROR': - type = MessageType.ERROR; - break; - case 'WARNING': - type = MessageType.WARNING; - break; - case 'INFO': - type = MessageType.INFO; - break; - case 'SUCCESS': - type = MessageType.SUCCESS; - break; - case 'DEBUG': - type = MessageType.DEBUG; - break; - case 'COMMAND': - type = MessageType.COMMAND; - break; - case 'JSON': - try { - // Parse JSON to validate and then stringify for formatting - const jsonContent = JSON.parse(content); - // Instead of just adding
 tags, create a message with special JSON formatting
-            addJsonMessage(jsonContent);
-            return; // Return early as we're handling this message separately
-          } catch (error) {
-            console.error('Error parsing JSON:', error);
-            content = `
${content}
`; + // Determine message type + let type = MessageType.INFO; + + switch (messageType) { + case 'ERROR': + type = MessageType.ERROR; + break; + case 'WARNING': + type = MessageType.WARNING; + break; + case 'INFO': type = MessageType.INFO; - } - break; - case 'URL': - // Make URLs clickable with HTML links - const urlRegex = /(https?:\/\/[^\s]+)/g; - content = content.replace(urlRegex, (url) => `${url}`); - type = MessageType.URL; - break; - default: - type = MessageType.INFO; + break; + case 'SUCCESS': + type = MessageType.SUCCESS; + break; + case 'DEBUG': + type = MessageType.DEBUG; + break; + case 'COMMAND': + type = MessageType.COMMAND; + break; + case 'JSON': + try { + // Parse JSON to validate and then stringify for formatting + const jsonContent = JSON.parse(content); + // Instead of just adding
 tags, create a message with special JSON formatting
+              addJsonMessage(jsonContent);
+              return; // Return early as we're handling this message separately
+            } catch (error) {
+              console.error('Error parsing JSON:', error);
+              content = `
${content}
`; + type = MessageType.INFO; + } + break; + case 'URL': + // Make URLs clickable with HTML links + const urlRegex = /(https?:\/\/[^\s]+)/g; + content = content.replace(urlRegex, (url) => `${url}`); + type = MessageType.URL; + break; + default: + type = MessageType.INFO; + } + + addMessage(content, type, timestamp); } - - addMessage(content, type, timestamp); } else { // If the message doesn't match the expected format, add it as-is diff --git a/src/components/dashboard/DashboardTerminal/DashboardTerminal.tsx b/src/components/dashboard/DashboardTerminal/DashboardTerminal.tsx index af226f25..93f46a23 100644 --- a/src/components/dashboard/DashboardTerminal/DashboardTerminal.tsx +++ b/src/components/dashboard/DashboardTerminal/DashboardTerminal.tsx @@ -60,7 +60,7 @@ export const DashboardTerminal: React.FC = ({ repoDetail const user = useAppSelector((state) => state.user.user); const { t } = useTranslation(); let hasAnalysisReport = false; - if (repoDetails?.gitAppMetaData?.analysisReport) { + if (repoDetails?.gitAppAdvancedMeta?.analysisReport) { hasAnalysisReport = true; } const [hasRunAnalysis, setHasRunAnalysis] = useState(true); @@ -84,7 +84,7 @@ export const DashboardTerminal: React.FC = ({ repoDetail if (!user) return null; // Get language data from repoDetails if available - const languageData = repoDetails?.gitAppMetaData?.analysisReport?.programmingLanguages || {}; + const languageData = repoDetails?.gitAppAdvancedMeta?.analysisReport?.programmingLanguages || {}; return ( diff --git a/src/components/dashboard/DashboardTerminal/DocstringStyleModal.tsx b/src/components/dashboard/DashboardTerminal/DocstringStyleModal.tsx index 3e9d0a99..a819e42f 100644 --- a/src/components/dashboard/DashboardTerminal/DocstringStyleModal.tsx +++ b/src/components/dashboard/DashboardTerminal/DocstringStyleModal.tsx @@ -173,27 +173,15 @@ const DocstringStyleModal: React.FC = ({
Configure Docstring Styles - Personalize how your docstring is generated. - -
- - - e.stopPropagation()} - > - Learn more - - - } - description="Configure the docstring style and max line length." - type="info" - showIcon - /> + >Learn more. + +
+
= ({ onCancel={handleArchConfigModalCancel} onConfirm={handleArchConfigConfirm} loading={isLoading} + isPrivate={repoDetails.isPrivate} /> {/* Azure DevOps PAT Modal */} diff --git a/src/components/dashboard/DashboardTerminal/DocumentationInsights.tsx b/src/components/dashboard/DashboardTerminal/DocumentationInsights.tsx index a19d6724..946903da 100644 --- a/src/components/dashboard/DashboardTerminal/DocumentationInsights.tsx +++ b/src/components/dashboard/DashboardTerminal/DocumentationInsights.tsx @@ -93,7 +93,7 @@ const DocumentationInsights: React.FC = ({ repoDetai const [hasData, setHasData] = useState(false); // Extract data from repoDetails if available - const analysisReport = repoDetails?.gitAppMetaData?.analysisReport; + const analysisReport = repoDetails?.gitAppAdvancedMeta?.analysisReport; const basicFuncDocAnalysis = analysisReport?.basicFuncDocAnalysis; const advancedFuncDocAnalysis = analysisReport?.advancedFuncDocAnalysis; const readmeAnalysis = analysisReport?.readmeAnalysis; @@ -193,7 +193,7 @@ const DocumentationInsights: React.FC = ({ repoDetai icon: , iconColor: '#faad14', title: 'README needs improvement', - description: `Current quality score: ${readmeAnalysis.calculatedQualityScore.toFixed(1)}%`, + description: `Current quality score: ${readmeAnalysis?.calculatedQualityScore?.toFixed(1)}%`, priority: readmeAnalysis.calculatedQualityScore < 30 ? 'high' : 'medium' }); } else if (readmeAnalysis.calculatedQualityScore >= 80) { @@ -201,7 +201,7 @@ const DocumentationInsights: React.FC = ({ repoDetai icon: , iconColor: '#52c41a', title: 'README quality is excellent', - description: `Quality score: ${readmeAnalysis.calculatedQualityScore.toFixed(1)}%`, + description: `Quality score: ${readmeAnalysis?.calculatedQualityScore?.toFixed(1)}%`, priority: 'low' }); } diff --git a/src/components/dashboard/DashboardTerminal/DocumentationTools.tsx b/src/components/dashboard/DashboardTerminal/DocumentationTools.tsx index ab79c83d..ce2f8d16 100644 --- a/src/components/dashboard/DashboardTerminal/DocumentationTools.tsx +++ b/src/components/dashboard/DashboardTerminal/DocumentationTools.tsx @@ -37,6 +37,7 @@ const StyledTabs = styled(Tabs)` .ant-tabs-tab-btn { font-weight: 500; + font-size: 13px; display: flex; align-items: center; gap: 8px; @@ -100,7 +101,8 @@ const CardTitle = styled.div` h4 { margin: 0; - font-weight: 600; + font-weight: 500; + font-size: 16px; } .badge-container { @@ -119,8 +121,8 @@ const TabDescription = styled(Paragraph)` border-left: 4px solid #1890ff; .title { - font-weight: 600; - font-size: 16px; + font-weight: 500; + font-size: 15px; margin-bottom: 8px; color: #111; } @@ -128,14 +130,16 @@ const TabDescription = styled(Paragraph)` .description { margin: 0; color: rgba(0, 0, 0, 0.65); + font-size: 13px; } `; const StyledTag = styled(Tag)` margin-left: 8px; - font-weight: 500; + font-weight: 400; padding: 2px 8px; border-radius: 4px; + font-size: 12px; `; const ValueProposition = styled.div` @@ -154,6 +158,7 @@ const ValuePoint = styled.div` color: #52c41a; margin-right: 10px; margin-top: 2px; + font-size: 14px; } .content { @@ -163,14 +168,18 @@ const ValuePoint = styled.div` .title { font-weight: 500; margin-bottom: 2px; + font-size: 14px; } .description { color: rgba(0, 0, 0, 0.65); - font-size: 13px; + font-size: 12px; } `; +// These styled components might be used in the future +// Temporarily commented out to avoid unused component warnings +/* const BeforeAfterPanel = styled.div` margin: 20px 0; `; @@ -223,6 +232,7 @@ const GradientBadge = styled.div` margin-right: 4px; } `; +*/ interface DocumentationToolsProps { repoDetails: GitAppUsageType; @@ -230,7 +240,8 @@ interface DocumentationToolsProps { const DocumentationTools: React.FC = ({ repoDetails }) => { const user = useAppSelector((state) => state.user.user); - const [activeTab, setActiveTab] = useState('code'); + // Track active tab for future use + const [, setActiveTab] = useState('code'); if (!user) return null; @@ -278,7 +289,7 @@ function calculateMetrics(data) { const tutorialCardForDocGen = ( <>
- + How it looks like? - + <Title level={5} style={{ marginTop: 0, marginBottom: 16, fontSize: '15px', fontWeight: 500 }}> Why document your code? @@ -333,7 +344,7 @@ function calculateMetrics(data) { - Repository Documentation Tools + Repository Documentation Tools {/*
{user.countRepoGen > 0 && ( @@ -365,7 +376,7 @@ function calculateMetrics(data) { key="code" >
- +
Create comprehensive docstrings for functions and classes throughout your codebase. @@ -397,7 +408,7 @@ function calculateMetrics(data) { key="architecture" >
- +
Generate Architecture Documentation
Create high-level architecture documentation that explains @@ -423,7 +434,7 @@ function calculateMetrics(data) { key="api" >
- +
Generate API Documentation
Generate detailed API documentation for endpoints, request/response formats, and diff --git a/src/components/jobs/AllJobsList/AllJobsList.tsx b/src/components/jobs/AllJobsList/AllJobsList.tsx index e702d551..15d80fe9 100644 --- a/src/components/jobs/AllJobsList/AllJobsList.tsx +++ b/src/components/jobs/AllJobsList/AllJobsList.tsx @@ -158,10 +158,14 @@ export const AllJobsList: React.FC = () => { try { const { logs } = await getLroJobLogs(job.id || -1); - for (const log of logs.split('\n')) { + // this is the seperator we use to split logs in backend + // '\n|||' is used to separate log messages in the backend + // it is used to split logs into individual messages + for (const log of logs.split('\n|||')) { if (!log.trim()) continue; const [logType, ts, msg] = log.split('::'); + if(!msg ) continue; const message: TerminalMessage = { content: msg, type: MessageType[logType.toUpperCase() as keyof typeof MessageType] || MessageType.INFO, diff --git a/src/components/layouts/main/sider/SiderMenu/RepoNav.styles.ts b/src/components/layouts/main/sider/SiderMenu/RepoNav.styles.ts new file mode 100644 index 00000000..47ca5535 --- /dev/null +++ b/src/components/layouts/main/sider/SiderMenu/RepoNav.styles.ts @@ -0,0 +1,98 @@ +import styled, { css } from 'styled-components'; + +export const RepoSectionContainer = styled.div` + margin: 0 0 8px 0; + padding: 0; +`; + +export const RepoHeader = styled.div` + display: flex; + align-items: center; + padding: 8px 12px; + border-radius: 6px; + margin: 0 10px 8px; + background-color: rgba(56, 189, 248, 0.1); + border-left: 3px solid #38BDF8; + overflow: hidden; +`; + +export const RepoIcon = styled.div` + width: 24px; + height: 24px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + background-color: #1f2937; + margin-right: 12px; + flex-shrink: 0; + + svg { + font-size: 16px; + color: #38BDF8; + } +`; + +export const RepoInfo = styled.div` + display: flex; + flex-direction: column; + min-width: 0; +`; + +export const RepoName = styled.div` + font-weight: 500; + font-size: 13px; + color: white; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +`; + +export const RepoOrg = styled.div` + font-size: 11px; + color: #94a3b8; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +`; + +export const RepoNavContainer = styled.div` + margin: 0 10px; +`; + +export const RepoNavItem = styled.div<{ isActive?: boolean }>` + display: flex; + align-items: center; + padding: 8px 12px; + color: #e2e8f0; + border-radius: 6px; + margin-bottom: 4px; + cursor: pointer; + transition: all 0.2s ease; + font-size: 13px; + height: 36px; + line-height: 20px; + + ${props => props.isActive && css` + background-color: rgba(56, 189, 248, 0.15); + font-weight: 500; + color: white; + + svg { + color: #38BDF8; + } + + &:hover { + background-color: rgba(56, 189, 248, 0.2); + } + `} + + &:hover { + background-color: rgba(255, 255, 255, 0.05); + } + + svg { + font-size: 16px; + margin-right: 8px; + } +`; diff --git a/src/components/layouts/main/sider/SiderMenu/RepoNav.tsx b/src/components/layouts/main/sider/SiderMenu/RepoNav.tsx new file mode 100644 index 00000000..e39f1fba --- /dev/null +++ b/src/components/layouts/main/sider/SiderMenu/RepoNav.tsx @@ -0,0 +1,90 @@ +import React, { useMemo } from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { + AreaChartOutlined, + FileTextOutlined, + SafetyOutlined, + GithubOutlined +} from '@ant-design/icons'; +import * as S from './RepoNav.styles'; +import { Badge, Tooltip } from 'antd'; + +interface RepoNavProps { + vendor: string; + orgName: string; + repoName: string; +} + +const RepoNav: React.FC = ({ vendor, orgName, repoName }) => { + const { t } = useTranslation(); + const location = useLocation(); + const currentPath = location.pathname; + + // Define navigation items + const navItems = useMemo(() => [ + // { + // key: 'analyze', + // title: t('sidebar.analyze'), + // icon: , + // url: `/repositories/${vendor}/${orgName}/${repoName}/analyze`, + // badge: null + // }, + // { + // key: 'docufy', + // title: t('sidebar.docufy'), + // icon: , + // url: `/repositories/${vendor}/${orgName}/${repoName}/docufy`, + // badge: null + // }, + // { + // key: 'security', + // title: t('sidebar.security'), + // icon: , + // url: `/repositories/${vendor}/${orgName}/${repoName}/security`, + // badge: null + // } + ], [t, vendor, orgName, repoName]); + + return ( + + + + + + + {repoName} + {orgName} + + + + + {navItems.map(item => { + const isActive = currentPath === item.url; + + return ( + + + {item.icon} + {item.title} + {item.badge && ( + + + + )} + + + ); + })} + + + ); +}; + +export default RepoNav; diff --git a/src/components/layouts/main/sider/SiderMenu/SiderMenu.styles.ts b/src/components/layouts/main/sider/SiderMenu/SiderMenu.styles.ts index 6375c7d9..13301a94 100644 --- a/src/components/layouts/main/sider/SiderMenu/SiderMenu.styles.ts +++ b/src/components/layouts/main/sider/SiderMenu/SiderMenu.styles.ts @@ -4,7 +4,7 @@ import { BaseMenu } from '@app/components/common/BaseMenu/BaseMenu'; export const MainMenuWrapper = styled.div` flex: 1; overflow-y: auto; - padding-top: 12px; + padding-top: 8px; &::-webkit-scrollbar { width: 4px; @@ -42,12 +42,13 @@ export const Menu = styled(BaseMenu)` width: 100%; display: block; color: #E2E8F0 !important; - font-weight: 500; + font-weight: 400; + font-size: 13px; } .ant-menu-item.ant-menu-item-selected { background-color: rgba(56, 189, 248, 0.15); - font-weight: 600; + font-weight: 500; color: #ffffff !important; position: relative; box-sizing: border-box; @@ -86,19 +87,20 @@ export const Menu = styled(BaseMenu)` } .ant-menu-item { - border-radius: 8px; + border-radius: 6px; transition: all 0.3s ease; margin: 4px 10px; - padding: 0 14px !important; - height: 40px; - line-height: 40px; + padding: 0 12px !important; + height: 36px; + line-height: 36px; color: #E2E8F0 !important; - letter-spacing: 0.3px; + letter-spacing: 0.2px; text-align: left; + font-size: 13px; .anticon { - font-size: 18px; - margin-right: 12px; + font-size: 16px; + margin-right: 8px; color: #94A3B8; transition: all 0.2s ease; } @@ -120,7 +122,7 @@ export const Menu = styled(BaseMenu)` // Reset unwanted padding from ant design &.ant-menu-item-selected { - padding: 0 14px !important; + padding: 0 12px !important; } } @@ -136,10 +138,11 @@ export const Menu = styled(BaseMenu)` background: rgba(15, 23, 42, 0.6); margin: 2px 10px; border-radius: 6px; - padding: 0 14px !important; + padding: 0 12px !important; + font-size: 13px; .anticon { - font-size: 15px; + font-size: 14px; color: #94A3B8; } @@ -157,8 +160,8 @@ export const SectionDivider = styled.div` .ant-divider { color: #64748B; font-size: 10px; - font-weight: 600; - letter-spacing: 0.8px; + font-weight: 500; + letter-spacing: 0.6px; text-transform: uppercase; margin: 6px 0; @@ -171,7 +174,7 @@ export const SectionDivider = styled.div` export const SectionFooter = styled.div` margin-top: 12px; - padding: 10px; + padding: 8px; text-align: center; font-size: 11px; color: #64748B; @@ -183,6 +186,6 @@ export const SectionFooter = styled.div` margin-left: 6px; color: #475569; font-size: 10px; - letter-spacing: 0.5px; + letter-spacing: 0.4px; } `; diff --git a/src/components/layouts/main/sider/SiderMenu/SiderMenu.tsx b/src/components/layouts/main/sider/SiderMenu/SiderMenu.tsx index 1363e1a9..38555030 100644 --- a/src/components/layouts/main/sider/SiderMenu/SiderMenu.tsx +++ b/src/components/layouts/main/sider/SiderMenu/SiderMenu.tsx @@ -1,10 +1,11 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { Link, useLocation } from 'react-router-dom'; +import { Link, useLocation, useParams } from 'react-router-dom'; import * as S from './SiderMenu.styles'; import { sidebarNavigation, SidebarNavigationItem } from '../sidebarNavigation'; import { Divider } from 'antd'; import { useAppSelector } from '@app/hooks/reduxHooks'; +import RepoNav from './RepoNav'; interface SiderContentProps { setCollapsed: (isCollapsed: boolean) => void; @@ -19,6 +20,7 @@ const sidebarNavFlat = sidebarNavigation.reduce( const SiderMenu: React.FC = ({ setCollapsed }) => { const { t } = useTranslation(); const location = useLocation(); + const { vendor, orgName, repoName } = useParams(); const user = useAppSelector((state) => state.user.user); const currentMenuItem = sidebarNavFlat.find(({ url }) => url === location.pathname); @@ -33,6 +35,9 @@ const SiderMenu: React.FC = ({ setCollapsed }) => { const appItems = sidebarNavigation.filter(item => item.section === 'app'); const userItems = sidebarNavigation.filter(item => item.section === 'user'); + // Check if we're in a repository context + const isRepoContext = Boolean(vendor && orgName && repoName); + // Function to create menu items const createMenuItems = (navItems: SidebarNavigationItem[]) => { return navItems.map((nav) => { @@ -58,6 +63,25 @@ const SiderMenu: React.FC = ({ setCollapsed }) => { return ( <> + {/* Repository-specific navigation when in repo context */} + {isRepoContext && vendor && orgName && repoName && ( + <> + + + + )} + + {/* Main app navigation */} { } /> + + + + } + /> + + + + + } + /> + + + + + } + /> + { + const { t } = useTranslation(); + const { orgName, repoName } = useParams(); + + return ( + <> + {t('sidebar.analyze')} + + + + + + <AreaChartOutlined style={{ marginRight: 8, color: '#1890ff' }} /> + Repository Analysis: {orgName}/{repoName} + + + Comprehensive code metrics and performance insights for your repository + + + + + + + + Based on best practices and industry standards + + + + + + + Percentage of code with proper documentation + + + + + + } + /> + Overall complexity rating of the codebase + + + + + + + + + + } + /> + + + + + } + /> + + + } + /> + + + } + /> + + + + + + + + ); +}; + +export default AnalyzePage; diff --git a/src/pages/DashboardPages/DocGenDashboardPage/RepoPages/DocufyPage.tsx b/src/pages/DashboardPages/DocGenDashboardPage/RepoPages/DocufyPage.tsx new file mode 100644 index 00000000..04e7dc91 --- /dev/null +++ b/src/pages/DashboardPages/DocGenDashboardPage/RepoPages/DocufyPage.tsx @@ -0,0 +1,160 @@ +import React from 'react'; +import { useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { PageTitle } from '@app/components/common/PageTitle/PageTitle'; +import { DashboardWrapper } from '@app/components/dashboard/DashboardWrapper'; +import { + BookOutlined, + FileTextOutlined, + FileMarkdownOutlined, + ApiOutlined, + SyncOutlined, + RocketOutlined +} from '@ant-design/icons'; +import { Button, Card, Col, Progress, Row, Space, Tabs, Typography } from 'antd'; + +const { Text, Title, Paragraph } = Typography; +const { TabPane } = Tabs; + +const DocufyPage: React.FC = () => { + const { t } = useTranslation(); + const { orgName, repoName } = useParams(); + + return ( + <> + {t('sidebar.docufy')} + + + + + + <FileTextOutlined style={{ marginRight: 8, color: '#1890ff' }} /> + Documentation Tools: {orgName}/{repoName} + + + Generate comprehensive documentation and keep it in sync with your code + + + + + + + + + + Overview + + } + key="overview" + > + + + + + Documentation Status + + } + bordered={false} + style={{ height: '100%' }} + > +
+
+ Overall Documentation Coverage + 62% +
+ +
+ +
+
+ API Documentation + 78% +
+ +
+ +
+
+ README Quality + 85% +
+ +
+ +
+
+ User Guides + 45% +
+ +
+
+ + + + + + Documentation Actions + + } + bordered={false} + style={{ height: '100%' }} + > + + + + + + + + +
+
+ + + + API Documentation + + } + key="api" + > + API documentation content will appear here. + + + + + Markdown Docs + + } + key="markdown" + > + Markdown documentation content will appear here. + +
+
+ +
+
+ + ); +}; + +export default DocufyPage; diff --git a/src/pages/DashboardPages/DocGenDashboardPage/RepoPages/SecurityPage.tsx b/src/pages/DashboardPages/DocGenDashboardPage/RepoPages/SecurityPage.tsx new file mode 100644 index 00000000..2e12353c --- /dev/null +++ b/src/pages/DashboardPages/DocGenDashboardPage/RepoPages/SecurityPage.tsx @@ -0,0 +1,252 @@ +import React from 'react'; +import { useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { PageTitle } from '@app/components/common/PageTitle/PageTitle'; +import { DashboardWrapper } from '@app/components/dashboard/DashboardWrapper'; +import { + SafetyOutlined, + WarningOutlined, + LockOutlined, + CheckCircleOutlined, + CloseCircleOutlined, + ExclamationCircleOutlined, + InfoCircleOutlined +} from '@ant-design/icons'; +import { Badge, Button, Card, Col, List, Row, Tag, Typography } from 'antd'; + +const { Text, Title, Paragraph } = Typography; + +// Mock security vulnerabilities data +const vulnerabilitiesData = [ + { + id: 1, + title: 'CVE-2023-44487 in axios@0.21.1', + severity: 'high', + path: 'package.json', + description: 'Axios before 1.6.0 allows attackers to cause a denial of service via a crafted HTTP/2 stream cancelation.', + status: 'open' + }, + { + id: 2, + title: 'SQL Injection Vulnerability', + severity: 'critical', + path: 'src/api/user.api.ts', + description: 'Potential SQL injection vulnerability in user input handling.', + status: 'open' + }, + { + id: 3, + title: 'Cross-Site Scripting (XSS)', + severity: 'medium', + path: 'src/components/common/UserInput.tsx', + description: 'Unvalidated user input could lead to XSS attacks.', + status: 'fixed' + }, + { + id: 4, + title: 'Authentication Bypass', + severity: 'low', + path: 'src/services/auth.service.ts', + description: 'Edge case in authentication logic could allow bypassing under specific conditions.', + status: 'open' + } +]; + +// Helper function for severity colors and icons +const getSeverityProps = (severity: string) => { + switch (severity.toLowerCase()) { + case 'critical': + return { color: '#ff4d4f', icon: }; + case 'high': + return { color: '#fa8c16', icon: }; + case 'medium': + return { color: '#faad14', icon: }; + case 'low': + return { color: '#52c41a', icon: }; + default: + return { color: '#1890ff', icon: }; + } +}; + +const SecurityPage: React.FC = () => { + const { t } = useTranslation(); + const { orgName, repoName } = useParams(); + + return ( + <> + {t('sidebar.security')} + + + + + + <SafetyOutlined style={{ marginRight: 8, color: '#1890ff' }} /> + Security Insights: {orgName}/{repoName} + + + Comprehensive security analysis and vulnerability detection + + + + + + + + Security Score + + } + bordered={false} + > +
+
+ 76% +
+ + Your repository has a good security score, + but there are some issues that should be addressed. + + +
+
+ + + + + + Vulnerabilities Summary + + } + bordered={false} + extra={ + + } + > + + + + 1 + Critical + + + + + 1 + High + + + + + 1 + Medium + + + + + 1 + Low + + + + + + + + + + Security Vulnerabilities + + } + bordered={false} + > + { + const severityProps = getSeverityProps(item.severity); + + return ( + View Details, + item.status === 'open' ? + : + + ]} + > + + } + title={ +
+ {item.title} + + {item.severity} + + {item.status === 'fixed' && ( + + Fixed + + )} +
+ } + description={ +
+ + {item.path} + + + {item.description} + +
+ } + /> +
+ ); + }} + /> +
+ +
+
+ + ); +}; + +export default SecurityPage;