diff --git a/src/app/api/native/git/branches/route.ts b/src/app/api/native/git/branches/route.ts index d79fa18d..088a7191 100644 --- a/src/app/api/native/git/branches/route.ts +++ b/src/app/api/native/git/branches/route.ts @@ -6,6 +6,8 @@ import path from 'path'; // Get the repository path from the environment variable const LOCAL_TAXONOMY_ROOT_DIR = process.env.NEXT_PUBLIC_LOCAL_TAXONOMY_ROOT_DIR || `${process.env.HOME}/.instructlab-ui`; +const REMOTE_TAXONOMY_REPO_DIR = process.env.NEXT_PUBLIC_TAXONOMY_REPO_DIR || ''; +const REMOTE_TAXONOMY_REPO_CONTAINER_MOUNT_DIR = process.env.NEXT_PUBLIC_TAXONOMY_REPO_CONTAINER_MOUNT_DIR || '/tmp/.instructlab-ui/taxonomy'; interface Diffs { file: string; @@ -28,15 +30,23 @@ export async function GET() { const branchCommit = await git.resolveRef({ fs, dir: REPO_DIR, ref: branch }); const commitDetails = await git.readCommit({ fs, dir: REPO_DIR, oid: branchCommit }); + const commitMessage = commitDetails.commit.message; + + // Check for Signed-off-by line + const signoffMatch = commitMessage.match(/^Signed-off-by: (.+)$/m); + const signoff = signoffMatch ? signoffMatch[1] : null; + const messageStr = commitMessage.split('Signed-off-by'); branchDetails.push({ name: branch, - creationDate: commitDetails.commit.committer.timestamp * 1000 // Convert to milliseconds + creationDate: commitDetails.commit.committer.timestamp * 1000, // Convert to milliseconds + message: messageStr[0].replace(/\n+$/, ''), + author: signoff }); } branchDetails.sort((a, b) => b.creationDate - a.creationDate); // Sort by creation date, newest first - console.log('Branches present in native taxonomy:', branchDetails); + console.log('Total branches present in native taxonomy:', branchDetails.length); return NextResponse.json({ branches: branchDetails }, { status: 200 }); } catch (error) { console.error('Failed to list branches:', error); @@ -47,8 +57,8 @@ export async function GET() { // Handle POST requests for merge or branch comparison export async function POST(req: NextRequest) { const LOCAL_TAXONOMY_DIR = path.join(LOCAL_TAXONOMY_ROOT_DIR, '/taxonomy'); - const { branchName, action, remoteTaxonomyRepoDir } = await req.json(); - console.log('Received POST request:', { branchName, action, remoteTaxonomyRepoDir }); + const { branchName, action } = await req.json(); + console.log('Received POST request:', { branchName, action }); if (action === 'delete') { return handleDelete(branchName, LOCAL_TAXONOMY_DIR); @@ -59,6 +69,21 @@ export async function POST(req: NextRequest) { } if (action === 'publish') { + let remoteTaxonomyRepoDir: string = ''; + // Check if directory pointed by remoteTaxonomyRepoDir exists and not empty + if (fs.existsSync(REMOTE_TAXONOMY_REPO_CONTAINER_MOUNT_DIR) && fs.readdirSync(REMOTE_TAXONOMY_REPO_CONTAINER_MOUNT_DIR).length !== 0) { + remoteTaxonomyRepoDir = REMOTE_TAXONOMY_REPO_CONTAINER_MOUNT_DIR; + } else { + if (fs.existsSync(REMOTE_TAXONOMY_REPO_DIR) && fs.readdirSync(REMOTE_TAXONOMY_REPO_DIR).length !== 0) { + remoteTaxonomyRepoDir = REMOTE_TAXONOMY_REPO_DIR; + } + } + if (remoteTaxonomyRepoDir === '') { + return NextResponse.json({ error: 'Remote taxonomy repository path does not exist.' }, { status: 400 }); + } + + console.log('Remote taxonomy repository path:', remoteTaxonomyRepoDir); + return handlePublish(branchName, LOCAL_TAXONOMY_DIR, remoteTaxonomyRepoDir); } return NextResponse.json({ error: 'Invalid action specified' }, { status: 400 }); @@ -73,7 +98,7 @@ async function handleDelete(branchName: string, localTaxonomyDir: string) { // Delete the target branch await git.deleteBranch({ fs, dir: localTaxonomyDir, ref: branchName }); - return NextResponse.json({ message: `Successfully deleted branch ${branchName}.` }, { status: 200 }); + return NextResponse.json({ message: `Successfully deleted contribution ${branchName}.` }, { status: 200 }); } catch (error) { console.error(`Failed to delete contribution ${branchName}:`, error); return NextResponse.json( @@ -182,7 +207,7 @@ async function getTopCommitDetails(dir: string, ref: string = 'HEAD') { async function handlePublish(branchName: string, localTaxonomyDir: string, remoteTaxonomyDir: string) { try { if (!branchName || branchName === 'main') { - return NextResponse.json({ error: 'Invalid branch name for publish' }, { status: 400 }); + return NextResponse.json({ error: 'Invalid contribution name for publish' }, { status: 400 }); } console.log(`Publishing contribution from ${branchName} to remote taxonomy repo at ${remoteTaxonomyDir}`); @@ -244,9 +269,9 @@ async function handlePublish(branchName: string, localTaxonomyDir: string, remot } }); console.log(`Successfully published contribution from ${branchName} to remote taxonomy repo at ${remoteTaxonomyDir}`); - return NextResponse.json({ message: `Successfully published contribution to ${remoteTaxonomyDir}.` }, { status: 200 }); + return NextResponse.json({ message: `Successfully published contribution to ${REMOTE_TAXONOMY_REPO_DIR}.` }, { status: 200 }); } else { - return NextResponse.json({ message: `No changes to publish from ${branchName}.` }, { status: 200 }); + return NextResponse.json({ message: `No changes to publish from contribution ${branchName}.` }, { status: 200 }); } } catch (error) { console.error(`Failed to publish contribution from ${branchName}:`, error); diff --git a/src/app/contribute/knowledge/page.tsx b/src/app/contribute/knowledge/page.tsx index 19836b45..9caebe86 100644 --- a/src/app/contribute/knowledge/page.tsx +++ b/src/app/contribute/knowledge/page.tsx @@ -1,11 +1,23 @@ // src/app/contribute/knowledge/page.tsx -import KnowledgeFormNative from '@/components/Contribute/Knowledge/Native'; -import * as React from 'react'; -import { AppLayout } from '../../../components/AppLayout'; -import { KnowledgeFormGithub } from '../../../components/Contribute/Knowledge/Github'; +'use client'; +import { AppLayout } from '@/components/AppLayout'; +import { KnowledgeFormGithub } from '@/components/Contribute/Knowledge/Github/index'; +import KnowledgeFormNative from '@/components/Contribute/Knowledge/Native/index'; +import { useEffect, useState } from 'react'; -const KnowledgeFormPage: React.FC = () => { - return {process.env.IL_UI_DEPLOYMENT === 'native' ? : }; +const KnowledgeFormPage: React.FunctionComponent = () => { + const [deploymentType, setDeploymentType] = useState(); + + useEffect(() => { + const getEnvVariables = async () => { + const res = await fetch('/api/envConfig'); + const envConfig = await res.json(); + setDeploymentType(envConfig.DEPLOYMENT_TYPE); + }; + getEnvVariables(); + }, []); + + return {deploymentType === 'native' ? : }; }; export default KnowledgeFormPage; diff --git a/src/app/contribute/skill/page.tsx b/src/app/contribute/skill/page.tsx index 01f1d1cb..b4d31d35 100644 --- a/src/app/contribute/skill/page.tsx +++ b/src/app/contribute/skill/page.tsx @@ -1,11 +1,23 @@ // src/app/contribute/skill/page.tsx -import SkillFormNative from '@/components/Contribute/Skill/Native'; -import * as React from 'react'; -import { AppLayout } from '../../../components/AppLayout'; -import { SkillFormGithub } from '../../../components/Contribute/Skill/Github'; +'use client'; +import { AppLayout } from '@/components/AppLayout'; +import { SkillFormGithub } from '@/components/Contribute/Skill/Github/index'; +import { SkillFormNative } from '@/components/Contribute/Skill/Native/index'; +import { useEffect, useState } from 'react'; -const SkillFormPage: React.FC = () => { - return {process.env.IL_UI_DEPLOYMENT === 'native' ? : }; +const SkillFormPage: React.FunctionComponent = () => { + const [deploymentType, setDeploymentType] = useState(); + + useEffect(() => { + const getEnvVariables = async () => { + const res = await fetch('/api/envConfig'); + const envConfig = await res.json(); + setDeploymentType(envConfig.DEPLOYMENT_TYPE); + }; + getEnvVariables(); + }, []); + + return {deploymentType === 'native' ? : }; }; export default SkillFormPage; diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 2d0fa212..ee28b27a 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -1,15 +1,16 @@ -// src/app/page.tsx +// src/app/dashboard/page.tsx 'use client'; -import * as React from 'react'; import '@patternfly/react-core/dist/styles/base.css'; import { AppLayout } from '@/components/AppLayout'; import { DashboardGithub } from '@/components/Dashboard/Github/dashboard'; import { DashboardNative } from '@/components/Dashboard/Native/dashboard'; +import { useEffect, useState } from 'react'; + const Home: React.FunctionComponent = () => { - const [deploymentType, setDeploymentType] = React.useState(); + const [deploymentType, setDeploymentType] = useState(); - React.useEffect(() => { + useEffect(() => { const getEnvVariables = async () => { const res = await fetch('/api/envConfig'); const envConfig = await res.json(); diff --git a/src/components/AppLayout.tsx b/src/components/AppLayout.tsx index 740427f1..3ffc88a8 100644 --- a/src/components/AppLayout.tsx +++ b/src/components/AppLayout.tsx @@ -21,7 +21,7 @@ import { PageSidebar } from '@patternfly/react-core/dist/dynamic/components/Page import { PageSidebarBody } from '@patternfly/react-core/dist/dynamic/components/Page'; import { SkipToContent } from '@patternfly/react-core/dist/dynamic/components/SkipToContent'; import { Spinner } from '@patternfly/react-core/dist/dynamic/components/Spinner'; -import { Bullseye } from '@patternfly/react-core'; +import { Bullseye } from '@patternfly/react-core/dist/dynamic/layouts/Bullseye'; import UserMenu from './UserMenu/UserMenu'; import { useSession } from 'next-auth/react'; // import { useTheme } from '../context/ThemeContext'; diff --git a/src/components/Contribute/Knowledge/DownloadAttribution/DownloadAttribution.tsx b/src/components/Contribute/Knowledge/DownloadAttribution/DownloadAttribution.tsx index 53b4da89..d61394fe 100644 --- a/src/components/Contribute/Knowledge/DownloadAttribution/DownloadAttribution.tsx +++ b/src/components/Contribute/Knowledge/DownloadAttribution/DownloadAttribution.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { DropdownItem } from '@patternfly/react-core/dist/esm/components/Dropdown/DropdownItem'; -import { Icon } from '@patternfly/react-core'; +import { Icon } from '@patternfly/react-core/dist/dynamic/components/Icon'; import FileIcon from '@patternfly/react-icons/dist/esm/icons/file-icon'; import { KnowledgeFormData } from '@/types'; diff --git a/src/components/Contribute/Knowledge/DownloadDropdown/DownloadDropdown.tsx b/src/components/Contribute/Knowledge/DownloadDropdown/DownloadDropdown.tsx index 76a03b59..2dc99c3f 100644 --- a/src/components/Contribute/Knowledge/DownloadDropdown/DownloadDropdown.tsx +++ b/src/components/Contribute/Knowledge/DownloadDropdown/DownloadDropdown.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Dropdown } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; import { DropdownList } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; -import { Icon } from '@patternfly/react-core'; +import { Icon } from '@patternfly/react-core/dist/dynamic/components/Icon'; import { MenuToggle, MenuToggleElement } from '@patternfly/react-core/dist/dynamic/components/MenuToggle'; import DownloadYaml from '../DownloadYaml/DownloadYaml'; import DownloadAttribution from '../DownloadAttribution/DownloadAttribution'; diff --git a/src/components/Contribute/Knowledge/DownloadYaml/DownloadYaml.tsx b/src/components/Contribute/Knowledge/DownloadYaml/DownloadYaml.tsx index 6edb94ad..f39b6c2a 100644 --- a/src/components/Contribute/Knowledge/DownloadYaml/DownloadYaml.tsx +++ b/src/components/Contribute/Knowledge/DownloadYaml/DownloadYaml.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { KnowledgeFormData, KnowledgeYamlData } from '@/types'; import { KnowledgeSchemaVersion } from '@/types/const'; import { dumpYaml } from '@/utils/yamlConfig'; -import { Icon } from '@patternfly/react-core'; +import { Icon } from '@patternfly/react-core/dist/dynamic/components/Icon'; import { DropdownItem } from '@patternfly/react-core/dist/esm/components/Dropdown/DropdownItem'; import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon'; diff --git a/src/components/Contribute/Knowledge/Github/index.tsx b/src/components/Contribute/Knowledge/Github/index.tsx index 36cf3008..be899e8f 100644 --- a/src/components/Contribute/Knowledge/Github/index.tsx +++ b/src/components/Contribute/Knowledge/Github/index.tsx @@ -1,4 +1,4 @@ -// src/components/Experimental/ContributeLocal/Knowledge/index.tsx +// src/components/Contribute/Knowledge/Github/index.tsx 'use client'; import React, { useEffect, useMemo, useState } from 'react'; import '../knowledge.css'; diff --git a/src/components/Contribute/Knowledge/Native/index.tsx b/src/components/Contribute/Knowledge/Native/index.tsx index 6e43fc5c..ffb5b9d7 100644 --- a/src/components/Contribute/Knowledge/Native/index.tsx +++ b/src/components/Contribute/Knowledge/Native/index.tsx @@ -1,4 +1,4 @@ -// src/components/Experimental/ContributeLocal/Knowledge/index.tsx +// src/components/Contribute/Native/Knowledge/index.tsx 'use client'; import React, { useEffect, useMemo, useState } from 'react'; import '../knowledge.css'; diff --git a/src/components/Contribute/Knowledge/ViewDropdown/ViewDropdown.tsx b/src/components/Contribute/Knowledge/ViewDropdown/ViewDropdown.tsx index c30e85eb..2dad0480 100644 --- a/src/components/Contribute/Knowledge/ViewDropdown/ViewDropdown.tsx +++ b/src/components/Contribute/Knowledge/ViewDropdown/ViewDropdown.tsx @@ -8,7 +8,7 @@ import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon'; import { AttributionData, KnowledgeFormData, KnowledgeYamlData } from '@/types'; import { KnowledgeSchemaVersion } from '@/types/const'; import { dumpYaml } from '@/utils/yamlConfig'; -import { Icon } from '@patternfly/react-core'; +import { Icon } from '@patternfly/react-core/dist/dynamic/components/Icon'; import FileIcon from '@patternfly/react-icons/dist/dynamic/icons/file-icon'; import EyeIcon from '@patternfly/react-icons/dist/esm/icons/eye-icon'; diff --git a/src/components/Contribute/Skill/Github/index.tsx b/src/components/Contribute/Skill/Github/index.tsx index f4924a66..6dfce0a0 100644 --- a/src/components/Contribute/Skill/Github/index.tsx +++ b/src/components/Contribute/Skill/Github/index.tsx @@ -1,4 +1,4 @@ -// src/components/Contribute/Skill/index.tsx +// src/components/Contribute/Skill/Github/index.tsx 'use client'; import React, { useEffect, useState } from 'react'; import './skills.css'; diff --git a/src/components/Contribute/Skill/Native/index.tsx b/src/components/Contribute/Skill/Native/index.tsx index 4a263e49..4bf8e368 100644 --- a/src/components/Contribute/Skill/Native/index.tsx +++ b/src/components/Contribute/Skill/Native/index.tsx @@ -1,4 +1,4 @@ -// src/components/contribute/Skill/index.tsx +// src/components/Contribute/Skill/Native/index.tsx 'use client'; import React, { useEffect, useState } from 'react'; import './skills.css'; @@ -392,8 +392,8 @@ export const SkillFormNative: React.FunctionComponent = ({ skill actionGroupAlertContent.success && actionGroupAlertContent.url && actionGroupAlertContent.url.trim().length > 0 && ( - - View your new branch + + View your skill contribution )}

diff --git a/src/components/Dashboard/Native/dashboard.tsx b/src/components/Dashboard/Native/dashboard.tsx index f5ffdf07..149c3b21 100644 --- a/src/components/Dashboard/Native/dashboard.tsx +++ b/src/components/Dashboard/Native/dashboard.tsx @@ -22,19 +22,16 @@ import { Alert, AlertProps, AlertVariant } from '@patternfly/react-core/dist/esm import { AlertActionCloseButton } from '@patternfly/react-core/dist/esm/components/Alert/AlertActionCloseButton'; import { PencilAltIcon } from '@patternfly/react-icons/dist/esm/icons/pencil-alt-icon'; import { UploadIcon } from '@patternfly/react-icons/dist/esm/icons/upload-icon'; -import { ModalHeader } from '@patternfly/react-core/dist/esm/components/Modal/ModalHeader'; -import { ModalBody } from '@patternfly/react-core/dist/esm/components/Modal/ModalBody'; -import { FormGroup } from '@patternfly/react-core/dist/esm/components/Form/FormGroup'; -import { Form } from '@patternfly/react-core/dist/esm/components/Form/Form'; -import { TextInput } from '@patternfly/react-core/dist/esm/components/TextInput/TextInput'; -import { ModalFooter } from '@patternfly/react-core/dist/esm/components/Modal/ModalFooter'; +import { Content } from '@patternfly/react-core/dist/esm/components/Content/Content'; +import { Popover } from '@patternfly/react-core/dist/esm/components/Popover/Popover'; +import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon'; +import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons/dist/esm/icons/outlined-question-circle-icon'; const InstructLabLogo: React.FC = () => InstructLab Logo; const DashboardNative: React.FunctionComponent = () => { - const [branches, setBranches] = React.useState<{ name: string; creationDate: number }[]>([]); - const [selectedTaxonomyRepoDir, setSelectedTaxonomyRepoDir] = React.useState(''); - const [defaultTaxonomyRepoDir, setDefaultTaxonomyRepoDir] = React.useState(''); + const [branches, setBranches] = React.useState<{ name: string; creationDate: number; message: string; author: string }[]>([]); + const [taxonomyRepoDir, setTaxonomyRepoDir] = React.useState(''); const [isLoading, setIsLoading] = React.useState(true); const [mergeStatus] = React.useState<{ branch: string; message: string; success: boolean } | null>(null); const [diffData, setDiffData] = React.useState<{ branch: string; changes: { file: string; status: string }[] } | null>(null); @@ -43,6 +40,7 @@ const DashboardNative: React.FunctionComponent = () => { const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState(false); const [isPublishModalOpen, setIsPublishModalOpen] = React.useState(false); const [selectedBranch, setSelectedBranch] = React.useState(null); + const [isPublishing, setIsPublishing] = React.useState(false); const getUniqueId = () => new Date().getTime(); @@ -53,8 +51,7 @@ const DashboardNative: React.FunctionComponent = () => { const getEnvVariables = async () => { const res = await fetch('/api/envConfig'); const envConfig = await res.json(); - setDefaultTaxonomyRepoDir(envConfig.TAXONOMY_REPO_DIR); - setSelectedTaxonomyRepoDir(envConfig.TAXONOMY_REPO_DIR); + setTaxonomyRepoDir(envConfig.TAXONOMY_REPO_DIR); }; getEnvVariables(); @@ -126,28 +123,6 @@ const DashboardNative: React.FunctionComponent = () => { return `${date.toLocaleDateString()} ${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`; }; - // Disabling Merge for now, leaving the code for when we re-implement the feature. - // const handleMerge = async (branchName: string) => { - // setMergeStatus(null); // Clear previous status - // try { - // const response = await fetch('/api/native/git/branches', { - // method: 'POST', - // headers: { 'Content-Type': 'application/json' }, - // body: JSON.stringify({ branchName, action: 'merge' }) - // }); - // - // const result = await response.json(); - // if (response.ok) { - // setMergeStatus({ branch: branchName, message: result.message, success: true }); - // } else { - // setMergeStatus({ branch: branchName, message: result.error, success: false }); - // } - // } catch (error) { - // setMergeStatus({ branch: branchName, message: 'Merge failed due to an unexpected error.', success: false }); - // console.error('Error merging branch:', error); - // } - // }; - const handleShowChanges = async (branchName: string) => { try { const response = await fetch('/api/native/git/branches', { @@ -224,18 +199,19 @@ const DashboardNative: React.FunctionComponent = () => { }; const handlePublishContributionConfirm = async () => { + setIsPublishing(true); if (selectedBranch) { try { const response = await fetch('/api/native/git/branches', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ branchName: selectedBranch, action: 'publish', remoteTaxonomyRepoDir: selectedTaxonomyRepoDir }) + body: JSON.stringify({ branchName: selectedBranch, action: 'publish' }) }); const result = await response.json(); if (response.ok) { + setIsPublishing(false); addSuccessAlert(result.message); - setSelectedTaxonomyRepoDir(defaultTaxonomyRepoDir); setSelectedBranch(null); setIsPublishModalOpen(false); } else { @@ -247,10 +223,10 @@ const DashboardNative: React.FunctionComponent = () => { } else { addDangerAlert('No branch selected to publish'); } + setIsPublishing(false); }; const handlePublishContributionCancel = () => { - setSelectedTaxonomyRepoDir(defaultTaxonomyRepoDir); setSelectedBranch(null); setIsPublishModalOpen(false); }; @@ -264,8 +240,27 @@ const DashboardNative: React.FunctionComponent = () => { - Local Git Repository Branches + My Submissions + + View and manage your taxonomy contributions. + + Taxonomy contributions help tune the InstructLab model. Contributions can include skills that teach the model how to do something or + knowledge that teaches the model facts, data, or references.{' '} + + Learn more + + + } + > + + + @@ -329,6 +324,9 @@ const DashboardNative: React.FunctionComponent = () => { Branch Name: {branch.name}
+ Contribution Title: {branch.message} +
+ Author: {branch.author} {' '} Created on: {formatDateTime(branch.creationDate)}
@@ -397,40 +395,21 @@ const DashboardNative: React.FunctionComponent = () => { setIsPublishModalOpen(false)} - aria-labelledby="form-modal-title" - aria-describedby="modal-box-description-form" - > - - - - - - - , + - + ]} + > +

are you sure you want to publish contribution to remote taxonomy repository present at : {taxonomyRepoDir}?