diff --git a/.env.native.example b/.env.example similarity index 98% rename from .env.native.example rename to .env.example index 275ffac4..e41be1e9 100644 --- a/.env.native.example +++ b/.env.example @@ -1,4 +1,3 @@ -IL_UI_DEPLOYMENT=native IL_UI_ADMIN_USERNAME=admin IL_UI_ADMIN_PASSWORD=password diff --git a/.env.github.example b/.env.github.example deleted file mode 100644 index adac4f2d..00000000 --- a/.env.github.example +++ /dev/null @@ -1,31 +0,0 @@ -IL_UI_DEPLOYMENT=github # Start UI stack in github mode. -OAUTH_GITHUB_ID= -OAUTH_GITHUB_SECRET= -GITHUB_TOKEN= - -NEXTAUTH_SECRET=your_super_secret_random_string -NEXTAUTH_URL=http://localhost:3000 - -NEXT_PUBLIC_TAXONOMY_DOCUMENTS_REPO=github.com/instructlab-public/taxonomy-knowledge-docs -NEXT_PUBLIC_AUTHENTICATION_ORG= -NEXT_PUBLIC_TAXONOMY_REPO_OWNER= -NEXT_PUBLIC_TAXONOMY_REPO= -NEXT_PUBLIC_EXPERIMENTAL_FEATURES=false - -SLACK_WEBHOOK_URL= - -# (Optional) Enable this option if you want to enable UI features that helps in development, such as form Auto-Fill feature. -# Default: false -IL_ENABLE_DEV_MODE=false - -# (Optional) Enable document conversion. Any non-markdown file will be converted to markdown file -# Default: false -IL_ENABLE_DOC_CONVERSION=false - -# (Optional) Enable this option if you want to enable UI features that allow Skills contributions -# Default: false -IL_ENABLE_SKILLS_FEATURES=true - -# (Optional) Enable this option if you want to enable UI playground features (Chat with a model, Custom model endpoints) -# Default: false -IL_ENABLE_PLAYGROUND_FEATURES=true diff --git a/docs/development.md b/docs/development.md index f97505f8..2bce3e41 100644 --- a/docs/development.md +++ b/docs/development.md @@ -6,14 +6,9 @@ This is a [NextJS](https://nextjs.org) framework with [Patternfly](https://www.p Podman is a requirement for using the Makefile. Install and init instructions [here](https://podman.io/docs/installation). -Set the .env in the ui directory by copying the example env files in the root directory. The`IL_ENABLE_DEV_MODE` flag enables assistive features that help you automate the time consuming and repetitive tasks, such as filling skill and knowledge forms for testing. Once .env file is setup, run the following: +Set the .env in the ui directory by copying the `example.env` file in the root directory. The`IL_ENABLE_DEV_MODE` flag enables assistive features that help you automate the time consuming and repetitive tasks, such as filling skill and knowledge forms for testing. Once .env file is setup, run the following: ```bash -# Github mode development .env -cp .env.github.example .env -# or native mode .env -cp .env.native.example .env - make start-dev-local ``` @@ -49,10 +44,7 @@ Choose the .env file to use and start the Next.js service with the following: ```bash cd ui/ -# Github mode development .env -cp .env.github.example .env -# or native mode .env -cp .env.native.example .env +cp .env.xample .env # start a development instance with hot module replacement on port 3000 npm install npm run dev @@ -83,27 +75,9 @@ npm run pretty npm run type-check ``` -UI stack supports two mode of deployments: - -- github - This is the default mode and it allows users to push their knowledge and skill contribution to the github taxonomy repository. -- native - This mode allow users to keep the skill and knowledge contribution in their local machine. - -## Running the UI in Native Deployment Mode - -To enable the native mode, set the `IL_UI_DEPLOYMENT=native` in the .env file. Once the flag is set, the UI will not push the knowledge and skill contribution to the github repository. Instead, it will keep the contribution in the local machine. In the `native` mode, the UI login page will show username and password input box to authenticate the user. You can setup the username and password in the .env file through the `IL_UI_ADMIN_USERNAME` and `IL_UI_ADMIN_PASSWORD` flags. - -## Running the UI in Github Deployment Mode - -To enable the github mode, set the `IL_UI_DEPLOYMENT=github` in the .env file. Once the flag is set, the UI will push the knowledge and skill contribution to the github taxonomy repository. In the `github` mode, the UI login page will show the github login button to authenticate the user. - -### OAuth Configuration for Github Deployment Mode - -You can either set up the Oauth app in your -[GitHub](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) -account or use the user/pass defined in `.env`. To change those defaults, create -the `/ui/.env` file and fill in the account user/pass with the following. +## Running the UI -Example [.env](../.env.example) file. +Contributions will be kept on the local machine. The UI login page will show username and password input box to authenticate the user. You can setup the username and password in the .env file through the `IL_UI_ADMIN_USERNAME` and `IL_UI_ADMIN_PASSWORD` flags. ## Local Dev Chat Environment diff --git a/src/app/api/native/clone-repo/route.ts b/src/app/api/clone-repo/route.ts similarity index 85% rename from src/app/api/native/clone-repo/route.ts rename to src/app/api/clone-repo/route.ts index da951f24..73f7d89b 100644 --- a/src/app/api/native/clone-repo/route.ts +++ b/src/app/api/clone-repo/route.ts @@ -1,4 +1,4 @@ -// src/pages/api/native/clone-repo.ts +// src/pages/api/clone-repo.ts import { NextResponse } from 'next/server'; import * as git from 'isomorphic-git'; import http from 'isomorphic-git/http/node'; @@ -15,8 +15,8 @@ export async function POST() { if (fs.existsSync(taxonomyDirectoryPath)) { const files = fs.readdirSync(taxonomyDirectoryPath); if (files.length > 0) { - console.log(`Using existing native Taxonomy repository at ${taxonomyDirectoryPath}.`); - return NextResponse.json({ message: `Using existing native Taxonomy repository at ${taxonomyDirectoryPath}.` }, { status: 200 }); + console.log(`Using existing Taxonomy repository at ${taxonomyDirectoryPath}.`); + return NextResponse.json({ message: `Using existing Taxonomy repository at ${taxonomyDirectoryPath}.` }, { status: 200 }); } fs.rmdirSync(taxonomyDirectoryPath, { recursive: true }); } diff --git a/src/app/api/native/download/route.ts b/src/app/api/download/route.ts similarity index 97% rename from src/app/api/native/download/route.ts rename to src/app/api/download/route.ts index e22587c2..aac0d2b1 100644 --- a/src/app/api/native/download/route.ts +++ b/src/app/api/download/route.ts @@ -1,4 +1,4 @@ -// src/app/api/native/download/route.ts +// src/app/api/download/route.ts 'use server'; import { NextRequest, NextResponse } from 'next/server'; import { spawn } from 'child_process'; diff --git a/src/app/api/envConfig/route.ts b/src/app/api/envConfig/route.ts index 4468de78..4786140c 100644 --- a/src/app/api/envConfig/route.ts +++ b/src/app/api/envConfig/route.ts @@ -14,7 +14,6 @@ export async function GET() { MERLINITE_MODEL_NAME: process.env.IL_MERLINITE_MODEL_NAME || '', UPSTREAM_REPO_OWNER: process.env.NEXT_PUBLIC_TAXONOMY_REPO_OWNER || '', UPSTREAM_REPO_NAME: process.env.NEXT_PUBLIC_TAXONOMY_REPO || '', - DEPLOYMENT_TYPE: process.env.IL_UI_DEPLOYMENT || '', ENABLE_DEV_MODE: process.env.IL_ENABLE_DEV_MODE || 'false', ENABLE_DOC_CONVERSION: process.env.IL_ENABLE_DOC_CONVERSION || 'false', ENABLE_SKILLS_FEATURES: process.env.IL_ENABLE_SKILLS_FEATURES || '', diff --git a/src/app/api/fine-tune/git/branches/route.ts b/src/app/api/fine-tune/git/branches/route.ts index c4472832..135ab7c8 100644 --- a/src/app/api/fine-tune/git/branches/route.ts +++ b/src/app/api/fine-tune/git/branches/route.ts @@ -1,4 +1,4 @@ -// src/app/api/native/fine-tune/git/branches/route.ts +// src/app/api/fine-tune/git/branches/route.ts import { NextResponse } from 'next/server'; import * as git from 'isomorphic-git'; import fs from 'fs'; @@ -56,7 +56,7 @@ export async function GET() { // Sort by creation date, newest first branchDetails.sort((a, b) => b.creationDate - a.creationDate); - console.log('Total branches present in native taxonomy (fine-tune):', branchDetails.length); + console.log('Total branches present in taxonomy (fine-tune):', branchDetails.length); return NextResponse.json({ branches: branchDetails }, { status: 200 }); } catch (error) { diff --git a/src/app/api/native/git/branches/route.ts b/src/app/api/git/branches/route.ts similarity index 98% rename from src/app/api/native/git/branches/route.ts rename to src/app/api/git/branches/route.ts index b95c88e4..8e2c4dc1 100644 --- a/src/app/api/native/git/branches/route.ts +++ b/src/app/api/git/branches/route.ts @@ -1,9 +1,9 @@ -// src/app/api/native/git/branches/route.ts +// src/app/api/git/branches/route.ts import { NextRequest, NextResponse } from 'next/server'; import * as git from 'isomorphic-git'; import fs from 'fs'; import path from 'path'; -import { findTaxonomyRepoPath } from '@/app/api/native/utils'; +import { findTaxonomyRepoPath } from '@/app/api/utils'; // 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`; @@ -52,7 +52,7 @@ export async function GET() { } branchDetails.sort((a, b) => b.creationDate - a.creationDate); // Sort by creation date, newest first - console.log('Total branches present in native taxonomy:', branchDetails.length); + console.log('Total branches present in taxonomy:', branchDetails.length); return NextResponse.json({ branches: branchDetails }, { status: 200 }); } catch (error) { diff --git a/src/app/api/github/download/route.ts b/src/app/api/github/download/route.ts deleted file mode 100644 index 1578c778..00000000 --- a/src/app/api/github/download/route.ts +++ /dev/null @@ -1,54 +0,0 @@ -// src/app/api/github/download/route.ts -'use server'; -import { NextRequest, NextResponse } from 'next/server'; -import { getToken } from 'next-auth/jwt'; -import { GITHUB_API_URL } from '@/types/const'; -import { getGitHubUsername } from '@/utils/github'; - -const UPSTREAM_REPO_NAME = process.env.NEXT_PUBLIC_TAXONOMY_REPO!; - -export async function POST(req: NextRequest) { - const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET! }); - - if (!token || !token.accessToken) { - console.error('Unauthorized: Missing or invalid access token'); - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); - } - - const githubToken = token.accessToken as string; - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${githubToken}`, - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28' - }; - - const githubUsername = await getGitHubUsername(headers); - try { - const { branchName } = await req.json(); - - if (!branchName || typeof branchName !== 'string') { - return NextResponse.json({ error: 'contribution branch does not exist on remote taxonomy.' }, { status: 400 }); - } - - const tarballUrl = `${GITHUB_API_URL}/repos/${githubUsername}/${UPSTREAM_REPO_NAME}/tarball/${branchName}`; - const tarballRes = await fetch(tarballUrl, { - headers: headers - }); - - if (!tarballRes.ok) { - return NextResponse.json({ error: 'Failed to download taxonomy for the contribution.' }, { status: 500 }); - } - - return new NextResponse(tarballRes.body, { - headers: { - 'Content-Type': 'application/gzip', - 'Content-Disposition': `attachment`, - 'Cache-Control': 'no-store' - } - }); - } catch (error) { - console.error('failed to download taxonomy for the contribution:', error); - return NextResponse.json({ error: error }, { status: 500 }); - } -} diff --git a/src/app/api/github/knowledge-files/route.ts b/src/app/api/github/knowledge-files/route.ts deleted file mode 100644 index d73e2001..00000000 --- a/src/app/api/github/knowledge-files/route.ts +++ /dev/null @@ -1,498 +0,0 @@ -// src/app/api/github/knowledge-files/route.ts -import { NextRequest, NextResponse } from 'next/server'; -import { getToken } from 'next-auth/jwt'; -import { - BASE_BRANCH, - checkIfRepoExists, - forkRepo, - getBranchSha, - getGitHubUsernameAndEmail, - readCommit, - GITHUB_API_URL, - TAXONOMY_DOCUMENTS_REPO -} from '@/app/api/github/utils'; -import path from 'path'; - -// Interface for the response -interface KnowledgeFile { - filename: string; - content: string; - commitSha: string; - commitDate: string; -} - -export async function POST(req: NextRequest) { - const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET! }); - - if (!token || !token.accessToken) { - console.error('Unauthorized: Missing or invalid access token'); - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); - } - - const githubToken = token.accessToken as string; - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${githubToken}`, - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28' - }; - - try { - const body = await req.json(); - const { branchName, currentCommitSHA, newFiles, updatedExistingFiles } = body; - - // Fetch GitHub username and email - const { githubUsername, userEmail } = await getGitHubUsernameAndEmail(headers); - - // Split the TAXONOMY_DOCUMENTS_REPO into owner and repo name - const repoPath = TAXONOMY_DOCUMENTS_REPO.replace('github.com/', ''); - const [repoOwner, repoName] = repoPath.split('/'); - - // Check if the repository is already forked - const repoForked = await checkIfRepoExists(headers, githubUsername, repoName); - console.log(`Repository forked: ${repoForked}`); - if (!repoForked) { - // Fork the repository if it is not already forked - await forkRepo(headers, repoOwner, repoName, githubUsername); - // Add a delay to ensure the fork operation completes to avoid a race condition when retrieving the bas SHA - // This only occurs if this is the first time submitting and the fork isn't present. - // TODO change to a retry - console.log('Pause 5s for the forking operation to complete'); - await new Promise((resolve) => setTimeout(resolve, 5000)); - console.log('Repository forked'); - } - - // Fetch the latest commit SHA of the base branch - const baseBranchSha = await getBranchSha(headers, githubUsername, repoName, BASE_BRANCH); - console.log(`Base branch SHA: ${baseBranchSha}`); - - // New directory for the files - const newSubDirectory = new Date().toISOString().replace(/[-:.]/g, '').replace('T', 'T').slice(0, -1); - - // Read the preview commit sha - let contributionName = branchName; - let existingSubDirectory = ''; - const finalFiles = new Map(); - - const commitMsg = await readCommit(headers, githubUsername, repoName, currentCommitSHA); - if (commitMsg.length != 0) { - const name = extractContributionName(commitMsg); - if (name.length === 0) { - console.warn('Contribution name not found. Looks like the SHA is not correct.'); - console.log('Continue uploading the newly provided document.'); - } else { - console.log(`Contribution name found for the sha ${currentCommitSHA}:`, contributionName); - contributionName = name; - } - - const directory = extractSubDirectoryName(commitMsg); - if (directory.length === 0) { - console.warn('No subdirectory exist. Either the commit sha is invalid, or the docs are manually deleted.'); - console.log('Continue uploading the newly provided document.'); - } else { - console.log(`Document sub directory exist for the sha ${currentCommitSHA}:`, directory); - existingSubDirectory = directory; - } - } else { - console.log('Uploading the documents for the first time for the contribution'); - } - - let existingFiles = []; - if (existingSubDirectory.length != 0) { - // Read all the files from the existing directory. - existingFiles = await fetchAllFilesFromDir(headers, githubUsername, repoName, path.join(contributionName, existingSubDirectory)); - existingFiles.map((existingFile: { fileName: string; filePath: string; fileSha: string; fileContent: string }) => { - if (updatedExistingFiles.some((file: { fileName: string; fileContent: string }) => file.fileName === existingFile.fileName)) { - console.log('Re-uploading existing file : ', existingFile.filePath); - finalFiles.set(existingFile.fileName, { - fileName: path.join(contributionName, newSubDirectory, existingFile.fileName), - fileContent: existingFile.fileContent - }); - } else { - console.log(`${existingFile.fileName} is either deleted or replaced with newer version`); - } - }); - } - - // Create files in the main branch with unique filenames e.g. foo-20240618T203521842.md - newFiles.map((file: { fileName: string; fileContent: string; action: string }) => { - console.log(`Uploading new file ${file.fileName} from knowledge contribution ${branchName}`); - finalFiles.set(file.fileName, { - fileName: path.join(contributionName, newSubDirectory, file.fileName), - fileContent: file.fileContent - }); - }); - - const commitSha = await createFilesCommit( - headers, - githubUsername, - repoName, - BASE_BRANCH, - contributionName, - newSubDirectory, - finalFiles, - userEmail, - baseBranchSha - ); - console.log(`Created files commit SHA: ${commitSha}`); - - if (existingSubDirectory.length != 0 && existingFiles.length != 0) { - // Deleting the existing files - await deleteExistingFiles(headers, githubUsername, repoName, BASE_BRANCH, contributionName, existingSubDirectory); - console.log('Existing files are cleaned up'); - } - - return NextResponse.json( - { - repoUrl: `https://github.com/${githubUsername}/${repoName}`, - commitSha, - documentNames: Array.from(finalFiles.values()).map((file: { fileName: string }) => file.fileName), //TODO: - prUrl: `https://github.com/${githubUsername}/${repoName}` - }, - { status: 201 } - ); - } catch (error) { - console.error('Failed to upload documents:', error); - return NextResponse.json({ error: 'Failed to upload documents' }, { status: 500 }); - } -} - -async function createFilesCommit( - headers: HeadersInit, - owner: string, - repo: string, - branchName: string, - contributionName: string, - subDirectory: string, - files: Map, - userEmail: string, - baseSha: string -): Promise { - console.log('Creating files commit...'); - // Create blobs for each file - const filesArray = Array.from(files.values()); - const blobs = await Promise.all( - filesArray.map((file) => - fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/blobs`, { - method: 'POST', - headers, - body: JSON.stringify({ - content: file.fileContent, - encoding: 'utf-8' - }) - }).then((response) => response.json()) - ) - ); - - // Create tree - const createTreeResponse = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/trees`, { - method: 'POST', - headers, - body: JSON.stringify({ - base_tree: baseSha, - tree: filesArray.map((file, index) => ({ - path: file.fileName, - mode: '100644', - type: 'blob', - sha: blobs[index].sha - })) - }) - }); - - if (!createTreeResponse.ok) { - const errorText = await createTreeResponse.text(); - console.error('Failed to create tree:', createTreeResponse.status, errorText); - throw new Error('Failed to create tree'); - } - - const treeData = await createTreeResponse.json(); - console.log('Tree created:', treeData); - - // Create commit with DCO sign-off - // TODO: if the user's github does not have an associated github email, we need to specify one in the upload section - // or reuse the one from the form. If we use the email field from the form, it needs to be null checked when - // the user clicks the upload documents button. - const createCommitResponse = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/commits`, { - method: 'POST', - headers, - body: JSON.stringify({ - message: `Contribution Name:${contributionName}\n\nDocument uploaded: ${filesArray.map((file) => file.fileName).join(', ')}\n\nSub-Directory:${subDirectory}\n\nSigned-off-by: ${owner} <${userEmail}>`, - tree: treeData.sha, - parents: [baseSha] - }) - }); - - if (!createCommitResponse.ok) { - const errorText = await createCommitResponse.text(); - console.error('Failed to create commit:', createCommitResponse.status, errorText); - throw new Error('Failed to create commit'); - } - - const commitData = await createCommitResponse.json(); - console.log('Commit created:', commitData); - - // Update branch reference - const updateBranchResponse = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${branchName}`, { - method: 'PATCH', - headers, - body: JSON.stringify({ sha: commitData.sha }) - }); - - if (!updateBranchResponse.ok) { - const errorText = await updateBranchResponse.text(); - console.error('Failed to update branch reference:', updateBranchResponse.status, errorText); - throw new Error('Failed to update branch reference'); - } - console.log('Branch reference updated'); - - return commitData.sha; -} - -async function deleteExistingFiles( - headers: HeadersInit, - owner: string, - repo: string, - branchName: string, - contributionName: string, - subDirectory: string -) { - console.log('Deleting existing files for contribution : ', contributionName); - const baseBranchSha = await getBranchSha(headers, owner, repo, branchName); - console.log(`${contributionName}: base branch sha :`, baseBranchSha); - - try { - let response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/trees/${baseBranchSha}?recursive=1`, { - headers - }); - - if (!response.ok) { - console.error('Failed to fetch the git tree', response.statusText); - } - const data = await response.json(); - - const dirPath = path.join(contributionName, subDirectory); - const updatedTree = data.tree.map((item: { path: string; sha: string }) => { - if (item.path.startsWith(dirPath)) { - return { ...item, sha: null }; // Mark for deletion - } - return item; - }); - - response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/trees`, { - method: 'POST', - headers, - body: JSON.stringify({ - base_tree: baseBranchSha, - tree: updatedTree - }) - }); - - if (!response.ok) { - console.error('Failed to update the git tree', response.statusText); - } - const treeUpdateData = await response.json(); - - response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/commits`, { - method: 'POST', - headers, - body: JSON.stringify({ - message: `Deleting subdirectory ${subDirectory} for contribution ${contributionName}`, - tree: treeUpdateData.sha, - parents: [baseBranchSha] - }) - }); - - if (!response.ok) { - console.error('Failed to create a delete commit for contribution:', contributionName); - } - - const commitData = await response.json(); - console.log('Delete commit created:', commitData); - - // Update branch reference - const updateBranchResponse = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${branchName}`, { - method: 'PATCH', - headers, - body: JSON.stringify({ sha: commitData.sha }) - }); - - if (!updateBranchResponse.ok) { - const errorText = await updateBranchResponse.text(); - console.error('Failed to update branch reference:', updateBranchResponse.status, errorText); - throw new Error('Failed to update branch reference'); - } - console.log('Branch reference updated'); - console.log(`${contributionName}: new branch sha :`, commitData.sha); - } catch (error) { - console.error(`Failed to delete files for the contribution : ${contributionName}`, error); - } -} - -export async function GET(req: NextRequest) { - try { - const url = new URL(req.url); - const commitSHA = url.searchParams.get('commitSHA'); - if (commitSHA == null) { - return NextResponse.json({ files: [] }, { status: 200 }); - } - - const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET! }); - - if (!token || !token.accessToken) { - console.error('Unauthorized: Missing or invalid access token'); - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); - } - - const githubToken = token.accessToken as string; - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${githubToken}`, - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28' - }; - - // Fetch GitHub username and email - const { githubUsername } = await getGitHubUsernameAndEmail(headers); - - // Split the TAXONOMY_DOCUMENTS_REPO into owner and repo name - const repoPath = TAXONOMY_DOCUMENTS_REPO.replace('github.com/', ''); - const [_, repoName] = repoPath.split('/'); - - // Read the preview commit sha - let contributionName = ''; - let existingSubDirectory = ''; - const finalFiles: KnowledgeFile[] = []; - - const commitMsg = await readCommit(headers, githubUsername, repoName, commitSHA); - if (commitMsg.length != 0) { - const name = extractContributionName(commitMsg); - if (name.length === 0) { - console.warn('Contribution name not found. Looks like the SHA is not correct.'); - console.log('Continue uploading the newly provided document.'); - } else { - console.log(`Contribution name found for the sha ${commitSHA}:`, contributionName); - contributionName = name; - } - - const directory = extractSubDirectoryName(commitMsg); - if (directory.length === 0) { - console.warn('No subdirectory exist. Either the commit sha is invalid, or the docs are manually deleted.'); - console.log('Continue uploading the newly provided document.'); - } else { - console.log(`Document sub directory exist for the sha ${commitSHA}:`, directory); - existingSubDirectory = directory; - } - } else { - console.log('Uploading the documents for the first time for the contribution'); - } - - if (existingSubDirectory.length != 0) { - // Read all the files from the existing directory. - const existingFiles = await fetchAllFilesFromDir(headers, githubUsername, repoName, path.join(contributionName, existingSubDirectory)); - existingFiles.map((file: { fileName: string; filePath: string; fileSha: string; fileContent: string }) => { - console.log('Existing file found : ', file.filePath); - finalFiles.push({ - filename: file.fileName, - content: file.fileContent, - commitSha: file.fileSha, - commitDate: '' - }); - }); - } - return NextResponse.json({ files: finalFiles }, { status: 200 }); - } catch (error) { - console.error('Failed to process GET request:', error); - return NextResponse.json({ error: (error as Error).message }, { status: 500 }); - } -} - -// Fetch the content of a file from the repository -async function fetchFileContent(headers: HeadersInit, owner: string, repo: string, filePath: string): Promise { - try { - const response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/contents/${filePath}`, { headers }); - if (!response.ok) { - const errorText = await response.text(); - console.error(`Failed to fetch content of file ${filePath} :`, response.status, errorText); - throw new Error(`Failed to fetch content of file ${filePath}.`); - } - - const data = await response.json(); - if (data.content) { - return Buffer.from(data.content, 'base64').toString('utf-8'); - } - if (!data.sha) { - console.error(`Failed to fetch content of file ${filePath}, no file sha found.`); - throw new Error(`Failed to fetch content of file ${filePath}.`); - } - - const blobResponse = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/blobs/${data.sha}`, { headers }); - - if (!blobResponse.ok) { - const errorText = await blobResponse.text(); - console.error(`Failed to fetch blob content of file ${filePath} :`, blobResponse.status, errorText); - throw new Error(`Failed to fetch content of file ${filePath}.`); - } - - const blobData = await blobResponse.json(); - return Buffer.from(blobData.content, 'base64').toString('utf-8'); - } catch (error) { - console.error(`Error fetching content for ${filePath}:`, error); - return ''; - } -} - -async function fetchAllFilesFromDir( - headers: HeadersInit, - owner: string, - repo: string, - filePath: string -): Promise<{ fileName: string; filePath: string; fileSha: string; fileContent: string }[]> { - try { - const contentsResponse = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/contents/${filePath}`, { - method: 'GET', - headers - }); - - const contentsData = await contentsResponse.json(); - if (!Array.isArray(contentsData)) { - throw new Error(`${filePath} is not a directory`); - } - const results = Promise.all( - contentsData.map(async (file) => { - const content = await fetchFileContent(headers, owner, repo, file.path); - return { - fileName: file.name, - filePath: file.path, - fileSha: file.sha, - fileContent: content - }; - }) - ); - return results; - } catch (error) { - console.error(`Failed to read content from :${filePath}`, error); - } - return []; -} - -/** - * Extract the sub directory name where documents are stored. - * @param commitMessage Commit message from the provided SHA - * @returns - */ -function extractSubDirectoryName(commitMessage: string): string { - const match = commitMessage.match(/Sub-Directory:(.+)/); - if (!match) return ''; - return match[1].trim(); -} - -/** - * Extract the contribution name where documents are stored. - * @param commitMessage Commit message from the provided SHA - * @returns - */ -function extractContributionName(commitMessage: string): string { - const match = commitMessage.match(/Contribution Name:(.+)/); - if (!match) return ''; - return match[1].trim(); -} diff --git a/src/app/api/github/pr/knowledge/route.ts b/src/app/api/github/pr/knowledge/route.ts deleted file mode 100644 index d4387146..00000000 --- a/src/app/api/github/pr/knowledge/route.ts +++ /dev/null @@ -1,114 +0,0 @@ -// src/app/api/github/pr/knowledge/route.ts -import { NextRequest, NextResponse } from 'next/server'; -import { getToken } from 'next-auth/jwt'; -import yaml from 'js-yaml'; -import { KnowledgeYamlData, AttributionData } from '@/types'; -import { GITHUB_API_URL, BASE_BRANCH } from '@/types/const'; -import { dumpYaml } from '@/utils/yamlConfig'; -import { checkUserForkExists, createBranch, createFilesInSingleCommit, createFork, getBaseBranchSha, getGitHubUsername } from '@/utils/github'; -import { prInfoFromSummary } from '@/app/api/github/utils'; - -const KNOWLEDGE_DIR = 'knowledge'; -const UPSTREAM_REPO_OWNER = process.env.NEXT_PUBLIC_TAXONOMY_REPO_OWNER!; -const UPSTREAM_REPO_NAME = process.env.NEXT_PUBLIC_TAXONOMY_REPO!; - -export async function POST(req: NextRequest) { - const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET! }); - - if (!token || !token.accessToken) { - console.error('Unauthorized: Missing or invalid access token'); - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); - } - - const githubToken = token.accessToken as string; - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${githubToken}`, - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28' - }; - - try { - const body = await req.json(); - const { branchName, content, attribution, name, email, submissionSummary, filePath } = body; - - const knowledgeData: KnowledgeYamlData = yaml.load(content) as KnowledgeYamlData; - const attributionData: AttributionData = attribution; - - // Fetch GitHub username - const githubUsername = await getGitHubUsername(headers); - console.log('Knowledge contribution from gitHub Username:', githubUsername); - - // Check if user's fork exists, if not, create it - const forkExists = await checkUserForkExists(headers, githubUsername, UPSTREAM_REPO_NAME); - if (!forkExists) { - await createFork(headers, UPSTREAM_REPO_OWNER, UPSTREAM_REPO_NAME, githubUsername); - } - - const newYamlFilePath = `${KNOWLEDGE_DIR}/${filePath}qna.yaml`; - const newAttributionFilePath = `${KNOWLEDGE_DIR}/${filePath}attribution.txt`; - - const yamlString = dumpYaml(knowledgeData); - - const attributionContent = `Title of work: ${attributionData.title_of_work} -Link to work: ${attributionData.link_to_work} -Revision: ${attributionData.revision} -License of the work: ${attributionData.license_of_the_work} -Creator names: ${attributionData.creator_names} -`; - - // Get the base branch SHA - const baseBranchSha = await getBaseBranchSha(headers, githubUsername, UPSTREAM_REPO_NAME); - console.log(`Base branch SHA: ${baseBranchSha}`); - - // Create a new branch in the user's fork - await createBranch(headers, githubUsername, UPSTREAM_REPO_NAME, branchName, baseBranchSha); - - const { prTitle, prBody, commitMessage } = prInfoFromSummary(submissionSummary); - - // Create both files in a single commit with DCO sign-off - await createFilesInSingleCommit( - headers, - githubUsername, - UPSTREAM_REPO_NAME, - [ - { path: newYamlFilePath, content: yamlString }, - { path: newAttributionFilePath, content: attributionContent } - ], - branchName, - `${commitMessage}\n\nSigned-off-by: ${name} <${email}>` - ); - - // Create a pull request from the user's fork to the upstream repository - const pr = await createPullRequest(headers, githubUsername, branchName, prTitle, prBody); - - return NextResponse.json(pr, { status: 201 }); - } catch (error) { - console.error('Failed to create pull request:', error); - return NextResponse.json({ error: 'Failed to create pull request' }, { status: 500 }); - } -} - -async function createPullRequest(headers: HeadersInit, username: string, branchName: string, prTitle: string, prBody?: string) { - const response = await fetch(`${GITHUB_API_URL}/repos/${UPSTREAM_REPO_OWNER}/${UPSTREAM_REPO_NAME}/pulls`, { - method: 'POST', - headers, - body: JSON.stringify({ - title: prTitle, - head: `${username}:${branchName}`, - body: prBody, - base: BASE_BRANCH - }) - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error('Failed to create pull request:', response.status, errorText); - throw new Error('Failed to create pull request'); - } - - const responseData = await response.json(); - console.log('Pull request created successfully:', responseData); - - return responseData; -} diff --git a/src/app/api/github/pr/skill/route.ts b/src/app/api/github/pr/skill/route.ts deleted file mode 100644 index 9646d3fd..00000000 --- a/src/app/api/github/pr/skill/route.ts +++ /dev/null @@ -1,112 +0,0 @@ -// src/app/api/github/pr/skill/route.ts -import { NextRequest, NextResponse } from 'next/server'; -import { getToken } from 'next-auth/jwt'; -import yaml from 'js-yaml'; -import { SkillYamlData, AttributionData } from '@/types'; -import { GITHUB_API_URL, BASE_BRANCH } from '@/types/const'; -import { dumpYaml } from '@/utils/yamlConfig'; -import { checkUserForkExists, createBranch, createFilesInSingleCommit, createFork, getBaseBranchSha, getGitHubUsername } from '@/utils/github'; -import { prInfoFromSummary } from '@/app/api/github/utils'; - -const SKILLS_DIR = 'compositional_skills'; -const UPSTREAM_REPO_OWNER = process.env.NEXT_PUBLIC_TAXONOMY_REPO_OWNER!; -const UPSTREAM_REPO_NAME = process.env.NEXT_PUBLIC_TAXONOMY_REPO!; - -export async function POST(req: NextRequest) { - const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET! }); - - if (!token || !token.accessToken) { - console.error('Unauthorized: Missing or invalid access token'); - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); - } - - const githubToken = token.accessToken as string; - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${githubToken}`, - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28' - }; - - try { - const body = await req.json(); - const { branchName, content, attribution, name, email, submissionSummary, filePath } = body; - - const githubUsername = await getGitHubUsername(headers); - console.log('Skill contribution from gitHub Username:', githubUsername); - - // Check if user's fork exists, if not, create it - const forkExists = await checkUserForkExists(headers, githubUsername, UPSTREAM_REPO_NAME); - if (!forkExists) { - await createFork(headers, UPSTREAM_REPO_OWNER, UPSTREAM_REPO_NAME, githubUsername); - } - - const newYamlFilePath = `${SKILLS_DIR}/${filePath}qna.yaml`; - const newAttributionFilePath = `${SKILLS_DIR}/${filePath}attribution.txt`; - - const skillData = yaml.load(content) as SkillYamlData; - const attributionData = attribution as AttributionData; - - const yamlString = dumpYaml(skillData); - - const attributionString = `Title of work: ${attributionData.title_of_work} -License of the work: ${attributionData.license_of_the_work} -Creator names: ${attributionData.creator_names} -`; - - // Get the base branch SHA - const baseBranchSha = await getBaseBranchSha(headers, githubUsername, UPSTREAM_REPO_NAME); - - console.log(`Base branch SHA: ${baseBranchSha}`); - - // Create a new branch in the user's fork - await createBranch(headers, githubUsername, UPSTREAM_REPO_NAME, branchName, baseBranchSha); - - const { prTitle, prBody, commitMessage } = prInfoFromSummary(submissionSummary); - - // Create both files in a single commit - await createFilesInSingleCommit( - headers, - githubUsername, - UPSTREAM_REPO_NAME, - [ - { path: newYamlFilePath, content: yamlString }, - { path: newAttributionFilePath, content: attributionString } - ], - branchName, - `${commitMessage}\n\nSigned-off-by: ${name} <${email}>` - ); - - // Create a pull request from the user's fork to the upstream repository - const pr = await createPullRequest(headers, githubUsername, branchName, prTitle, prBody); - - return NextResponse.json(pr, { status: 201 }); - } catch (error) { - console.error('Failed to create pull request:', error); - return NextResponse.json({ error: 'Failed to create pull request' }, { status: 500 }); - } -} - -async function createPullRequest(headers: HeadersInit, username: string, branchName: string, prTitle: string, prBody?: string) { - const response = await fetch(`${GITHUB_API_URL}/repos/${UPSTREAM_REPO_OWNER}/${UPSTREAM_REPO_NAME}/pulls`, { - method: 'POST', - headers, - body: JSON.stringify({ - title: prTitle, - body: prBody, - head: `${username}:${branchName}`, - base: BASE_BRANCH - }) - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error('Failed to create pull request:', response.status, errorText); - throw new Error('Failed to create pull request'); - } - - const responseData = await response.json(); - console.log('Pull request created successfully:', responseData); - - return responseData; -} diff --git a/src/app/api/github/tree/route.ts b/src/app/api/github/tree/route.ts deleted file mode 100644 index d446c1d2..00000000 --- a/src/app/api/github/tree/route.ts +++ /dev/null @@ -1,119 +0,0 @@ -// src/app/api/github/tree/route.ts -import { NextRequest, NextResponse } from 'next/server'; -import * as git from 'isomorphic-git'; -import http from 'isomorphic-git/http/node'; -import path from 'path'; -import fs from 'fs'; - -const LOCAL_TAXONOMY_ROOT_DIR = process.env.NEXT_PUBLIC_LOCAL_TAXONOMY_ROOT_DIR || `${process.env.HOME}/.instructlab-ui`; -const LOCAL_TAXONOMY_DIR = path.join(LOCAL_TAXONOMY_ROOT_DIR, '/taxonomy'); -const TAXONOMY_REPO_URL = process.env.NEXT_PUBLIC_TAXONOMY_REPO_URL || 'https://github.com/instructlab/taxonomy.git'; -const SKILLS = 'compositional_skills'; -const KNOWLEDGE = 'knowledge'; -const CHECK_INTERVAL = 300000; // 5 minute -let lastChecked = 0; - -async function cloneTaxonomyRepo(): Promise { - try { - await git.clone({ - fs, - http, - dir: LOCAL_TAXONOMY_DIR, - url: TAXONOMY_REPO_URL, - singleBranch: true - }); - - // Include the full path in the response for client display - console.log(`Local Taxonomy repository cloned successfully to ${LOCAL_TAXONOMY_DIR}.`); - return true; - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; - console.error(`Failed to clone local taxonomy repository: ${errorMessage}`); - return false; - } -} - -function deleteTaxonomyRepo() { - if (fs.existsSync(LOCAL_TAXONOMY_DIR)) { - fs.rmdirSync(LOCAL_TAXONOMY_DIR, { recursive: true }); - } -} - -async function getRemoteHeadHash(): Promise { - try { - const remoteRefs = await git.listServerRefs({ http, url: TAXONOMY_REPO_URL }); - const mainRef = remoteRefs.find((ref) => ref.ref.endsWith('refs/heads/main')); - return mainRef?.oid || null; - } catch (error) { - console.error('Failed to get remote head hash:', error); - return null; - } -} - -async function getLocalHeadHash(): Promise { - try { - const head = await git.resolveRef({ fs, dir: LOCAL_TAXONOMY_DIR, ref: 'HEAD' }); - return head || null; - } catch (error) { - console.error('Failed to get local head hash:', error); - return null; - } -} - -async function checkForUpdates(): Promise { - if (!fs.existsSync(LOCAL_TAXONOMY_DIR)) { - await cloneTaxonomyRepo(); - return; - } - - const currentTime = Date.now(); - if (currentTime - lastChecked < CHECK_INTERVAL) { - return; - } - lastChecked = currentTime; - const timestamp = new Date().toISOString(); - console.log(`${timestamp}: Checking for updates... `); - const remoteHash = await getRemoteHeadHash(); - const localHash = await getLocalHeadHash(); - - if (remoteHash && localHash && remoteHash !== localHash) { - console.log(`${timestamp}: New changes detected, updating repository...`); - deleteTaxonomyRepo(); - await cloneTaxonomyRepo(); - } else { - console.log(`${timestamp}: No new changes detected in taxonomy repo.`); - } -} - -async function getFirstLevelDirectories(directoryPath: string): Promise { - try { - await checkForUpdates(); - return fs - .readdirSync(directoryPath) - .map((name) => path.join(directoryPath, name)) - .filter((source) => fs.statSync(source).isDirectory()) - .map((dir) => path.basename(dir)); - } catch (error) { - console.error('Error reading directory:', error); - return []; - } -} - -export async function POST(req: NextRequest) { - const body = await req.json(); - const { root_path, dir_name } = body; - - try { - let dirPath = ''; - if (root_path === 'skills') { - dirPath = path.join(LOCAL_TAXONOMY_DIR, SKILLS, dir_name); - } else { - dirPath = path.join(LOCAL_TAXONOMY_DIR, KNOWLEDGE, dir_name); - } - const dirs = await getFirstLevelDirectories(dirPath); - return NextResponse.json({ data: dirs }, { status: 201 }); - } catch (error) { - console.error('Failed to get the tree for path:', root_path, error); - return NextResponse.json({ error: 'Failed to get the tree for path' }, { status: 500 }); - } -} diff --git a/src/app/api/github/utils.ts b/src/app/api/github/utils.ts deleted file mode 100644 index 7a08403e..00000000 --- a/src/app/api/github/utils.ts +++ /dev/null @@ -1,150 +0,0 @@ -export const GITHUB_API_URL = 'https://api.github.com'; -export const TAXONOMY_DOCUMENTS_REPO = - process.env.NEXT_PUBLIC_TAXONOMY_DOCUMENTS_REPO || 'https://github.com/instructlab-public/taxonomy-knowledge-docs'; -export const BASE_BRANCH = 'main'; - -export const prInfoFromSummary = (summaryString: string): { prTitle: string; prBody?: string; commitMessage: string } => { - const prTitle = summaryString.length > 60 ? `${summaryString.slice(0, 60)}...` : summaryString; - const prBody = summaryString.length > 60 ? `...${summaryString.slice(60)}` : undefined; - const commitMessage = `${prTitle}${prBody ? `\n\n${prBody}` : ''}`; - return { prTitle, prBody, commitMessage }; -}; - -export const checkIfRepoExists = async (headers: HeadersInit, owner: string, repo: string): Promise => { - const response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}`, { headers }); - const exists = response.ok; - if (!exists) { - const errorText = await response.text(); - console.error('Repository does not exist:', response.status, errorText); - } - return exists; -}; - -export const forkRepo = async (headers: HeadersInit, owner: string, repo: string, forkOwner: string) => { - const response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/forks`, { - method: 'POST', - headers - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error('Failed to fork repository:', response.status, errorText); - throw new Error('Failed to fork repository'); - } - - // Wait for the fork to be created - let forkCreated = false; - for (let i = 0; i < 10; i++) { - const forkExists = await checkIfRepoExists(headers, forkOwner, repo); - if (forkExists) { - forkCreated = true; - break; - } - await new Promise((resolve) => setTimeout(resolve, 3000)); - } - - if (!forkCreated) { - throw new Error('Failed to confirm fork creation'); - } -}; - -export const getBranchSha = async (headers: HeadersInit, owner: string, repo: string, branch: string): Promise => { - console.log(`Fetching branch SHA for ${branch}...`); - const response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/ref/heads/${branch}`, { headers }); - - if (!response.ok) { - const errorText = await response.text(); - console.error('Failed to get branch SHA:', response.status, errorText); - if (response.status === 409 && errorText.includes('Git Repository is empty')) { - throw new Error('Git Repository is empty.'); - } - throw new Error('Failed to get branch SHA'); - } - - const data = await response.json(); - console.log('Branch SHA:', data.object.sha); - return data.object.sha; -}; - -// Fetch all markdown files from the main branch -export const fetchMarkdownFiles = async ( - headers: HeadersInit, - owner: string, - repo: string, - branchName: string -): Promise<{ path: string; content: string }[]> => { - try { - const response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/git/trees/${branchName}?recursive=1`, { headers }); - if (!response.ok) { - const errorText = await response.text(); - console.error('Failed to fetch files from knowledge document repository:', response.status, errorText); - throw new Error('Failed to fetch file from knowledge document repository:'); - } - - const data = await response.json(); - const files = data.tree.filter( - (item: { type: string; path: string }) => item.type === 'blob' && item.path.endsWith('.md') && item.path !== 'README.md' - ); - return files.map((file: { path: string; content: string }) => ({ path: file.path, content: file.content })); - } catch (error) { - console.error('Error fetching files from knowledge document repository:', error); - return []; - } -}; - -// Fetch the latest commit info for a file -export const fetchCommitInfo = async ( - headers: HeadersInit, - owner: string, - repo: string, - filePath: string -): Promise<{ sha: string; date: string } | null> => { - try { - const response = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/commits?path=${filePath}`, { headers }); - if (!response.ok) { - const errorText = await response.text(); - console.error('Failed to fetch commit information for file:', response.status, errorText); - throw new Error('Failed to fetch commit information for file.'); - } - - const data = await response.json(); - if (data.length === 0) return null; - - return { - sha: data[0].sha, - date: data[0].commit.committer.date - }; - } catch (error) { - console.error(`Error fetching commit info for ${filePath}:`, error); - return null; - } -}; - -export async function getGitHubUsernameAndEmail(headers: HeadersInit): Promise<{ githubUsername: string; userEmail: string }> { - const response = await fetch(`${GITHUB_API_URL}/user`, { headers }); - - if (!response.ok) { - const errorText = await response.text(); - console.error('Failed to fetch GitHub username and email:', response.status, errorText); - throw new Error('Failed to fetch GitHub username and email'); - } - - const data = await response.json(); - return { githubUsername: data.login, userEmail: data.email }; -} - -export async function readCommit(headers: HeadersInit, owner: string, repo: string, commitSHA: string): Promise { - try { - const commitResponse = await fetch(`${GITHUB_API_URL}/repos/${owner}/${repo}/commits/${commitSHA}`, { - method: 'GET', - headers - }); - - const commitData = await commitResponse.json(); - console.log('Commit Message:', commitData.commit.message); - return commitData.commit.message; - } catch (error) { - console.error(`Failed to fetch commit:${commitSHA}`, error); - } - return ''; -} diff --git a/src/app/api/native/knowledge-files/route.ts b/src/app/api/knowledge-files/route.ts similarity index 98% rename from src/app/api/native/knowledge-files/route.ts rename to src/app/api/knowledge-files/route.ts index 9952db74..04fa723f 100644 --- a/src/app/api/native/knowledge-files/route.ts +++ b/src/app/api/knowledge-files/route.ts @@ -1,11 +1,11 @@ -// src/app/api/native/knowledge-files/route.ts +// src/app/api/knowledge-files/route.ts 'use server'; import { NextRequest, NextResponse } from 'next/server'; import * as git from 'isomorphic-git'; import fs from 'fs'; import path from 'path'; -import { cloneTaxonomyDocsRepo, findTaxonomyDocRepoPath, TAXONOMY_DOCS_ROOT_DIR } from '@/app/api/native/utils'; +import { cloneTaxonomyDocsRepo, findTaxonomyDocRepoPath, TAXONOMY_DOCS_ROOT_DIR } from '@/app/api/utils'; const BASE_BRANCH = 'main'; diff --git a/src/app/api/native/pr/knowledge/route.ts b/src/app/api/pr/knowledge/route.ts similarity index 96% rename from src/app/api/native/pr/knowledge/route.ts rename to src/app/api/pr/knowledge/route.ts index 7187f8c9..da83078e 100644 --- a/src/app/api/native/pr/knowledge/route.ts +++ b/src/app/api/pr/knowledge/route.ts @@ -1,4 +1,4 @@ -// src/app/api/native/pr/knowledge/route.ts +// src/app/api/pr/knowledge/route.ts import { NextResponse } from 'next/server'; import { NextRequest } from 'next/server'; @@ -8,7 +8,7 @@ import path from 'path'; import { dumpYaml } from '@/utils/yamlConfig'; import { KnowledgeYamlData } from '@/types'; import yaml from 'js-yaml'; -import { prInfoFromSummary } from '@/app/api/github/utils'; +import { prInfoFromSummary } from '@/app/api/utils'; // Define paths and configuration const LOCAL_TAXONOMY_ROOT_DIR = process.env.NEXT_PUBLIC_LOCAL_TAXONOMY_ROOT_DIR || `${process.env.HOME}/.instructlab-ui`; diff --git a/src/app/api/native/pr/skill/route.ts b/src/app/api/pr/skill/route.ts similarity index 96% rename from src/app/api/native/pr/skill/route.ts rename to src/app/api/pr/skill/route.ts index 8b5a47d2..e022496d 100644 --- a/src/app/api/native/pr/skill/route.ts +++ b/src/app/api/pr/skill/route.ts @@ -1,4 +1,4 @@ -// src/app/api/native/pr/skill/route.ts +// src/app/api/pr/skill/route.ts import { NextResponse } from 'next/server'; import { NextRequest } from 'next/server'; import * as git from 'isomorphic-git'; @@ -7,7 +7,7 @@ import path from 'path'; import yaml from 'js-yaml'; import { SkillYamlData } from '@/types'; import { dumpYaml } from '@/utils/yamlConfig'; -import { prInfoFromSummary } from '@/app/api/github/utils'; +import { prInfoFromSummary } from '@/app/api/utils'; // Define paths and configuration const LOCAL_TAXONOMY_ROOT_DIR = process.env.NEXT_PUBLIC_LOCAL_TAXONOMY_ROOT_DIR || `${process.env.HOME}/.instructlab-ui`; diff --git a/src/app/api/native/tree/route.ts b/src/app/api/tree/route.ts similarity index 92% rename from src/app/api/native/tree/route.ts rename to src/app/api/tree/route.ts index 889e087f..43bd4e2b 100644 --- a/src/app/api/native/tree/route.ts +++ b/src/app/api/tree/route.ts @@ -1,8 +1,8 @@ -// src/app/api/native/tree/route.ts +// src/app/api/tree/route.ts import { NextRequest, NextResponse } from 'next/server'; import path from 'path'; import * as fs from 'fs'; -import { findTaxonomyRepoPath } from '@/app/api/native/utils'; +import { findTaxonomyRepoPath } from '@/app/api/utils'; const SKILLS = 'compositional_skills'; const KNOWLEDGE = 'knowledge'; diff --git a/src/app/api/native/utils.ts b/src/app/api/utils.ts similarity index 91% rename from src/app/api/native/utils.ts rename to src/app/api/utils.ts index 0b915392..6a22be95 100644 --- a/src/app/api/native/utils.ts +++ b/src/app/api/utils.ts @@ -9,6 +9,13 @@ export const TAXONOMY_DOCS_CONTAINER_MOUNT_DIR = '/tmp/.instructlab-ui'; export const TAXONOMY_KNOWLEDGE_DOCS_REPO_URL = process.env.NEXT_PUBLIC_TAXONOMY_DOCUMENTS_REPO || 'https://github.com/instructlab-public/taxonomy-knowledge-docs'; +export const prInfoFromSummary = (summaryString: string): { prTitle: string; prBody?: string; commitMessage: string } => { + const prTitle = summaryString.length > 60 ? `${summaryString.slice(0, 60)}...` : summaryString; + const prBody = summaryString.length > 60 ? `...${summaryString.slice(60)}` : undefined; + const commitMessage = `${prTitle}${prBody ? `\n\n${prBody}` : ''}`; + return { prTitle, prBody, commitMessage }; +}; + export const cloneTaxonomyDocsRepo = async (): Promise => { // Check the location of the taxonomy repository and create the taxonomy-knowledge-doc repository parallel to that. const remoteTaxonomyRepoDirFinal: string = findTaxonomyRepoPath(); diff --git a/src/app/contribute/knowledge/native/[...slug]/page.tsx b/src/app/contribute/knowledge/[...slug]/page.tsx similarity index 69% rename from src/app/contribute/knowledge/native/[...slug]/page.tsx rename to src/app/contribute/knowledge/[...slug]/page.tsx index d8c01870..932eebb0 100644 --- a/src/app/contribute/knowledge/native/[...slug]/page.tsx +++ b/src/app/contribute/knowledge/[...slug]/page.tsx @@ -1,13 +1,13 @@ -// src/app/contribute/knowledge/native/[...slug]/page.tsx +// src/app/contribute/knowledge/[...slug]/page.tsx import * as React from 'react'; import { AppLayout } from '@/components/AppLayout'; -import ViewKnowledgePage from '@/components/Contribute/Knowledge/View/native/ViewKnowledgePage'; +import ViewKnowledgePage from '@/components/Contribute/Knowledge/View/ViewKnowledgePage'; type PageProps = { params: Promise<{ slug: string[] }>; }; -const KnowledgeNativePage = async ({ params }: PageProps) => { +const KnowledgeViewPage = async ({ params }: PageProps) => { const resolvedParams = await params; return ( @@ -17,4 +17,4 @@ const KnowledgeNativePage = async ({ params }: PageProps) => { ); }; -export default KnowledgeNativePage; +export default KnowledgeViewPage; diff --git a/src/app/contribute/knowledge/edit/github/[...slug]/page.tsx b/src/app/contribute/knowledge/edit/[...slug]/page.tsx similarity index 69% rename from src/app/contribute/knowledge/edit/github/[...slug]/page.tsx rename to src/app/contribute/knowledge/edit/[...slug]/page.tsx index 2ab6826c..cfcc684d 100644 --- a/src/app/contribute/knowledge/edit/github/[...slug]/page.tsx +++ b/src/app/contribute/knowledge/edit/[...slug]/page.tsx @@ -1,13 +1,13 @@ -// src/app/contribute/knowledge/edit/github/[...slug]/page.tsx +// src/app/contribute/knowledge/edit/[...slug]/page.tsx import * as React from 'react'; import { AppLayout } from '@/components/AppLayout'; -import EditKnowledge from '@/components/Contribute/Knowledge/Edit/github/EditKnowledge'; +import EditKnowledge from '@/components/Contribute/Knowledge/Edit/EditKnowledge'; type PageProps = { params: Promise<{ slug: string[] }>; }; -const EditKnowledgeGithubPage = async ({ params }: PageProps) => { +const EditKnowledgePage = async ({ params }: PageProps) => { const resolvedParams = await params; return ( @@ -17,4 +17,4 @@ const EditKnowledgeGithubPage = async ({ params }: PageProps) => { ); }; -export default EditKnowledgeGithubPage; +export default EditKnowledgePage; diff --git a/src/app/contribute/knowledge/edit/native/[...slug]/page.tsx b/src/app/contribute/knowledge/edit/native/[...slug]/page.tsx deleted file mode 100644 index e753d08a..00000000 --- a/src/app/contribute/knowledge/edit/native/[...slug]/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// src/app/contribute/knowledge/edit/native/[...slug]/page.tsx -import * as React from 'react'; -import { AppLayout } from '@/components/AppLayout'; -import EditKnowledge from '@/components/Contribute/Knowledge/Edit/native/EditKnowledge'; - -type PageProps = { - params: Promise<{ slug: string[] }>; -}; - -const EditKnowledgeNativePage = async ({ params }: PageProps) => { - const resolvedParams = await params; - - return ( - - - - ); -}; - -export default EditKnowledgeNativePage; diff --git a/src/app/contribute/knowledge/github/[...slug]/page.tsx b/src/app/contribute/knowledge/github/[...slug]/page.tsx deleted file mode 100644 index 25a0e386..00000000 --- a/src/app/contribute/knowledge/github/[...slug]/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// src/app/contribute/knowledge/github/[...slug]/page.tsx -import * as React from 'react'; -import { AppLayout } from '@/components/AppLayout'; -import ViewKnowledgePage from '@/components/Contribute/Knowledge/View/github/ViewKnowledgePage'; - -type PageProps = { - params: Promise<{ slug: string[] }>; -}; - -const KnowledgeGithubPage = async ({ params }: PageProps) => { - const resolvedParams = await params; - - return ( - - - - ); -}; - -export default KnowledgeGithubPage; diff --git a/src/app/contribute/knowledge/page.tsx b/src/app/contribute/knowledge/page.tsx index f376e3e5..aa136a26 100644 --- a/src/app/contribute/knowledge/page.tsx +++ b/src/app/contribute/knowledge/page.tsx @@ -1,29 +1,13 @@ // src/app/contribute/knowledge/page.tsx 'use client'; import React from 'react'; -import { Flex, Spinner } from '@patternfly/react-core'; -import { t_global_spacer_xl as XlSpacerSize } from '@patternfly/react-tokens'; import { AppLayout } from '@/components/AppLayout'; -import { useEnvConfig } from '@/context/EnvConfigContext'; import KnowledgeWizard from '@/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeWizard'; -const KnowledgeFormPage: React.FunctionComponent = () => { - const { - loaded, - envConfig: { isGithubMode } - } = useEnvConfig(); +const AddKnowledgePage: React.FunctionComponent = () => ( + + + +); - return ( - - {loaded ? ( - - ) : ( - - - - )} - - ); -}; - -export default KnowledgeFormPage; +export default AddKnowledgePage; diff --git a/src/app/contribute/skill/github/[...slug]/page.tsx b/src/app/contribute/skill/[...slug]/page.tsx similarity index 61% rename from src/app/contribute/skill/github/[...slug]/page.tsx rename to src/app/contribute/skill/[...slug]/page.tsx index ce786308..73aa88da 100644 --- a/src/app/contribute/skill/github/[...slug]/page.tsx +++ b/src/app/contribute/skill/[...slug]/page.tsx @@ -1,13 +1,13 @@ -// src/app/contribute/skill/github/[...slug]/page.tsx +// src/app/contribute/skill/[...slug]/page.tsx import * as React from 'react'; import { AppLayout } from '@/components/AppLayout'; -import ViewSkillPage from '@/components/Contribute/Skill/View/github/ViewSkillPage'; +import ViewSkillPage from '@/components/Contribute/Skill/View/ViewSkillPage'; type PageProps = { params: Promise<{ slug: string[] }>; }; -const ViewSkillGithubPage = async ({ params }: PageProps) => { +const SkillViewPage = async ({ params }: PageProps) => { const resolvedParams = await params; return ( @@ -17,4 +17,4 @@ const ViewSkillGithubPage = async ({ params }: PageProps) => { ); }; -export default ViewSkillGithubPage; +export default SkillViewPage; diff --git a/src/app/contribute/skill/edit/github/[...slug]/page.tsx b/src/app/contribute/skill/edit/[...slug]/page.tsx similarity index 61% rename from src/app/contribute/skill/edit/github/[...slug]/page.tsx rename to src/app/contribute/skill/edit/[...slug]/page.tsx index 50c90b18..d630b755 100644 --- a/src/app/contribute/skill/edit/github/[...slug]/page.tsx +++ b/src/app/contribute/skill/edit/[...slug]/page.tsx @@ -1,13 +1,13 @@ -// src/app/contribute/skill/edit/github/[...slug]/page.tsx +// src/app/contribute/skill/edit/[...slug]/page.tsx import * as React from 'react'; import { AppLayout } from '@/components/AppLayout'; -import EditSkill from '@/components/Contribute/Skill/Edit/github/EditSkill'; +import EditSkill from '@/components/Contribute/Skill/Edit/EditSkill'; type PageProps = { params: Promise<{ slug: string[] }>; }; -const EditSkillGithubPage = async ({ params }: PageProps) => { +const EditSkillPage = async ({ params }: PageProps) => { const resolvedParams = await params; return ( @@ -17,4 +17,4 @@ const EditSkillGithubPage = async ({ params }: PageProps) => { ); }; -export default EditSkillGithubPage; +export default EditSkillPage; diff --git a/src/app/contribute/skill/edit/native/[...slug]/page.tsx b/src/app/contribute/skill/edit/native/[...slug]/page.tsx deleted file mode 100644 index 461a20d1..00000000 --- a/src/app/contribute/skill/edit/native/[...slug]/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// src/app/contribute/skill/edit/native/[...slug]/page.tsx -import * as React from 'react'; -import { AppLayout } from '@/components/AppLayout'; -import EditSkill from '@/components/Contribute/Skill/Edit/native/EditSkill'; - -type PageProps = { - params: Promise<{ slug: string[] }>; -}; - -const EditSkillNativePage = async ({ params }: PageProps) => { - const resolvedParams = await params; - - return ( - - - - ); -}; - -export default EditSkillNativePage; diff --git a/src/app/contribute/skill/native/[...slug]/page.tsx b/src/app/contribute/skill/native/[...slug]/page.tsx deleted file mode 100644 index 7ee6fb6e..00000000 --- a/src/app/contribute/skill/native/[...slug]/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// src/app/contribute/skill/native/[...slug]/page.tsx -import * as React from 'react'; -import { AppLayout } from '@/components/AppLayout'; -import ViewSkillPage from '@/components/Contribute/Skill/View/native/ViewSkillPage'; - -type PageProps = { - params: Promise<{ slug: string[] }>; -}; - -const ViewSkillNativePage = async ({ params }: PageProps) => { - const resolvedParams = await params; - - return ( - - - - ); -}; - -export default ViewSkillNativePage; diff --git a/src/app/contribute/skill/page.tsx b/src/app/contribute/skill/page.tsx index 27ceb8a6..d4ebe38f 100644 --- a/src/app/contribute/skill/page.tsx +++ b/src/app/contribute/skill/page.tsx @@ -1,29 +1,13 @@ // src/app/contribute/skill/page.tsx 'use client'; import React from 'react'; -import { Flex, Spinner } from '@patternfly/react-core'; -import { t_global_spacer_xl as XlSpacerSize } from '@patternfly/react-tokens/dist/esm/t_global_spacer_xl'; import { AppLayout } from '@/components/AppLayout'; -import { useEnvConfig } from '@/context/EnvConfigContext'; import SkillWizard from '@/components/Contribute/Skill/SkillWizard/SkillWizard'; -const SkillFormPage: React.FunctionComponent = () => { - const { - loaded, - envConfig: { isGithubMode } - } = useEnvConfig(); +const AddSkillPage: React.FunctionComponent = () => ( + + + +); - return ( - - {loaded ? ( - - ) : ( - - - - )} - - ); -}; - -export default SkillFormPage; +export default AddSkillPage; diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 1260b907..dc422479 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -2,35 +2,14 @@ 'use client'; import React from 'react'; -import { Flex, Spinner } from '@patternfly/react-core'; -import { t_global_spacer_xl as XlSpacerSize } from '@patternfly/react-tokens/dist/esm/t_global_spacer_xl'; import '@patternfly/react-core/dist/styles/base.css'; import { AppLayout } from '@/components/AppLayout'; -import { DashboardGithub } from '@/components/Dashboard/Github/DashboardPage'; -import { DashboardNative } from '@/components/Dashboard/Native/DashboardPage'; -import { useEnvConfig } from '@/context/EnvConfigContext'; +import DashboardPage from '@/components/Dashboard/DashboardPage'; -const Home: React.FunctionComponent = () => { - const { - loaded, - envConfig: { isGithubMode } - } = useEnvConfig(); - - return ( - - {loaded ? ( - !isGithubMode ? ( - - ) : ( - - ) - ) : ( - - - - )} - - ); -}; +const Home: React.FunctionComponent = () => ( + + + +); export default Home; diff --git a/src/app/login/githublogin.tsx b/src/app/login/githublogin.tsx deleted file mode 100644 index 32d77361..00000000 --- a/src/app/login/githublogin.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { signIn } from 'next-auth/react'; -import { useRouter, useSearchParams } from 'next/navigation'; -import { Button, Content, Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant, Alert } from '@patternfly/react-core'; -import { GithubIcon } from '@patternfly/react-icons'; -import LoginLinks from '@/app/login/LoginLinks'; - -const GithubLogin: React.FC = () => { - const router = useRouter(); - const searchParams = useSearchParams(); - const [showError, setShowError] = useState(false); - const [errorMsg, setErrorMsg] = useState('Something went wrong.'); - const [githubUsername, setGithubUsername] = useState(null); - const [invalidToken, setInvalidToken] = useState(false); - - useEffect(() => { - const githubUsername = searchParams.get('user'); - setGithubUsername(githubUsername); - const error = searchParams.get('error'); - if (error === 'NotOrgMember') { - const errorMessage = - 'To access the InstructLab UI, you need to become a member of the \ - InstructLab public GitHub organization. First, send yourself an invite on GitHub. \ - Then, accept the invite and try accessing the InstructLab UI again.'; - setErrorMsg(errorMessage); - setShowError(true); - } - if (error === 'InvalidToken') { - setInvalidToken(true); - } - }, [searchParams]); - - const handleGitHubLogin = () => { - signIn('github', { callbackUrl: '/' }); // Redirect to home page after login - }; - - const handleOnClose = () => { - setShowError(false); - router.push('/'); - }; - - const sendInvite = async () => { - console.log('Sending invitation to:', githubUsername); // Log the GitHub username - try { - const response = await fetch('/api/invite', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ githubUsername }) - }); - - const responseBody = await response.text(); - console.log('API response body:', responseBody); - - if (response.ok) { - alert('You have been invited to the GitHub organization!'); - setShowError(false); - router.push('/'); - } else { - console.log('Failed to send invitation:', responseBody); - alert(`Failed to send invitation: ${responseBody}`); - router.push('/'); - } - } catch (error) { - console.error('Error while sending the invitation:', error); - alert('An error occurred while sending the invitation.'); - router.push('/'); - } - }; - - return ( -
- - - Sign in to your account - - - - - Join the novel, community based movement to

create truly open source LLMs -
-
- {invalidToken && } -
- -
- - New to GitHub? - - Create an account - - - - {showError && ( -
- handleOnClose()} - aria-labelledby="join-ilab-modal-title" - aria-describedby="join-ilab-body-variant" - > - - -

{errorMsg}

-
- - - , - - -
-
- )} -
- ); -}; - -export default GithubLogin; diff --git a/src/app/login/nativelogin.tsx b/src/app/login/nativelogin.tsx deleted file mode 100644 index e1ae083e..00000000 --- a/src/app/login/nativelogin.tsx +++ /dev/null @@ -1,64 +0,0 @@ -// src/app/login/NativeLogin.tsx -import React, { useState } from 'react'; -import { signIn } from 'next-auth/react'; -import { Alert, Content, LoginForm } from '@patternfly/react-core'; -import LoginLinks from '@/app/login/LoginLinks'; - -const NativeLogin: React.FunctionComponent = () => { - const [, setShowHelperText] = useState(false); - const [username, setUsername] = useState(''); - const [invalidLogin, setInvalidLogin] = useState(false); - const [inProgress, setInProgress] = useState(false); - const [password, setPassword] = useState(''); - - const handleLogin = async (e: React.FormEvent) => { - e.preventDefault(); - setInvalidLogin(false); - setInProgress(true); - const result = await signIn('credentials', { redirect: false, username, password }); - if (result?.error) { - setShowHelperText(true); - setInvalidLogin(true); - setInProgress(false); - } else { - window.location.href = '/dashboard'; - } - }; - - const handleUsernameChange = (_event: React.FormEvent, value: string) => { - setUsername(value); - }; - - const handlePasswordChange = (_event: React.FormEvent, value: string) => { - setPassword(value); - }; - - return ( -
-
- - Log in to your account - - - Join the novel, community-based movement to create truly open-source LLMs - - {invalidLogin ? : null} - - -
-
- ); -}; - -export default NativeLogin; diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 0fba3caa..41293608 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -1,29 +1,69 @@ // src/app/login/page.tsx 'use client'; -import React from 'react'; -import { Spinner } from '@patternfly/react-core'; -import NativeLogin from '@/app/login/nativelogin'; -import GithubLogin from '@/app/login/githublogin'; +import React, { useState } from 'react'; +import { Alert, Content, LoginForm } from '@patternfly/react-core'; +import { signIn } from 'next-auth/react'; +import LoginLinks from '@/app/login/LoginLinks'; import './login-page.css'; -import { useEnvConfig } from '@/context/EnvConfigContext'; const Login: React.FunctionComponent = () => { - const { - loaded, - envConfig: { isGithubMode } - } = useEnvConfig(); + const [, setShowHelperText] = useState(false); + const [username, setUsername] = useState(''); + const [invalidLogin, setInvalidLogin] = useState(false); + const [inProgress, setInProgress] = useState(false); + const [password, setPassword] = useState(''); + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault(); + setInvalidLogin(false); + setInProgress(true); + const result = await signIn('credentials', { redirect: false, username, password }); + if (result?.error) { + setShowHelperText(true); + setInvalidLogin(true); + setInProgress(false); + } else { + window.location.href = '/dashboard'; + } + }; + + const handleUsernameChange = (_event: React.FormEvent, value: string) => { + setUsername(value); + }; + + const handlePasswordChange = (_event: React.FormEvent, value: string) => { + setPassword(value); + }; return (
- {!loaded ? ( -
- - Loading... +
+
+
+ + Log in to your account + + + Join the novel, community-based movement to create truly open-source LLMs + + {invalidLogin ? : null} + + +
- ) : ( -
{!isGithubMode ? : }
- )} +
); }; diff --git a/src/app/page.tsx b/src/app/page.tsx index 6588d295..c68b1bfc 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,22 +2,8 @@ 'use client'; import * as React from 'react'; -import { useRouter } from 'next/navigation'; -import { GithubAccessPopup } from '@/components/GithubAccessPopup'; -import { AppLayout } from '../components/AppLayout'; +import Home from '@/app/dashboard/page'; -const HomePage: React.FC = () => { - const router = useRouter(); - - const handleWarningConditionAccepted = () => { - router.push('/dashboard'); - }; - - return ( - - - - ); -}; +const HomePage: React.FC = () => ; export default HomePage; diff --git a/src/components/Contribute/ContributeAlertGroup.tsx b/src/components/Contribute/ContributeAlertGroup.tsx index 364f96fb..690cea4f 100644 --- a/src/components/Contribute/ContributeAlertGroup.tsx +++ b/src/components/Contribute/ContributeAlertGroup.tsx @@ -1,4 +1,4 @@ -// src/components/Contribute/Native/Knowledge/index.tsx +// src/components/Contribute/ContributeAlertGroup.tsx 'use client'; import React from 'react'; import { AlertGroup, Alert, AlertActionCloseButton, Spinner, Button, Flex, FlexItem } from '@patternfly/react-core'; diff --git a/src/components/Contribute/ContributionWizard/ContributionWizard.tsx b/src/components/Contribute/ContributionWizard/ContributionWizard.tsx index 5516e045..0f61aeaf 100644 --- a/src/components/Contribute/ContributionWizard/ContributionWizard.tsx +++ b/src/components/Contribute/ContributionWizard/ContributionWizard.tsx @@ -1,4 +1,4 @@ -// src/components/Contribute/Knowledge/Github/index.tsx +// src/components/Contribute/ContributionWizard.tsx 'use client'; import React from 'react'; import { useSession } from 'next-auth/react'; @@ -6,7 +6,6 @@ import { Button, Content, Flex, FlexItem, PageGroup, PageSection, Title, Wizard, import { ContributionFormData, EditFormData } from '@/types'; import { useRouter } from 'next/navigation'; import { getAutoFillKnowledgeFields, getAutoFillSkillsFields } from '@/components/Contribute/AutoFill'; -import { getGitHubUserInfo } from '@/utils/github'; import ContributionWizardFooter from '@/components/Contribute/ContributionWizard/ContributionWizardFooter'; import { deleteDraftData } from '@/components/Contribute/Utils/autoSaveUtils'; import { useEnvConfig } from '@/context/EnvConfigContext'; @@ -39,11 +38,10 @@ export interface Props { editFormData?: EditFormData; formData: ContributionFormData; setFormData: React.Dispatch>; - isGithubMode: boolean; isSkillContribution: boolean; steps: StepType[]; convertToYaml: (formData: ContributionFormData) => unknown; - onSubmit: (githubUsername: string) => Promise; + onSubmit: () => Promise; } export const ContributionWizard: React.FunctionComponent = ({ @@ -53,7 +51,6 @@ export const ContributionWizard: React.FunctionComponent = ({ editFormData, formData, setFormData, - isGithubMode, isSkillContribution, steps, convertToYaml, @@ -63,7 +60,6 @@ export const ContributionWizard: React.FunctionComponent = ({ envConfig: { isDevMode } } = useEnvConfig(); const { data: session } = useSession(); - const [githubUsername, setGithubUsername] = React.useState(''); const [submitEnabled, setSubmitEnabled] = React.useState(false); // **New State Added** const [activeStepIndex, setActiveStepIndex] = React.useState(0); @@ -83,45 +79,12 @@ export const ContributionWizard: React.FunctionComponent = ({ const getStepIndex = (stepId: string) => stepIds.indexOf(stepId); React.useEffect(() => { - let canceled = false; - - if (isGithubMode) { - const fetchUserInfo = async () => { - if (session?.accessToken) { - try { - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${session.accessToken}`, - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28' - }; - const fetchedUserInfo = await getGitHubUserInfo(headers); - if (!canceled) { - setGithubUsername(fetchedUserInfo.login); - setFormData((prev) => ({ - ...prev, - name: fetchedUserInfo.name, - email: fetchedUserInfo.email - })); - } - } catch (error) { - console.error('Failed to fetch GitHub user info:', error); - } - } - }; - fetchUserInfo(); - } else { - setFormData((prev) => ({ - ...prev, - name: session?.user?.name ? session.user.name : prev.name, - email: session?.user?.email ? session.user.email : prev.email - })); - } - - return () => { - canceled = true; - }; - }, [isGithubMode, session?.accessToken, session?.user?.name, session?.user?.email, setFormData]); + setFormData((prev) => ({ + ...prev, + name: session?.user?.name ? session.user.name : prev.name, + email: session?.user?.email ? session.user.email : prev.email + })); + }, [session?.accessToken, session?.user?.name, session?.user?.email, setFormData]); const autoFillForm = (): void => { setFormData(isSkillContribution ? getAutoFillSkillsFields() : getAutoFillKnowledgeFields()); @@ -173,8 +136,7 @@ export const ContributionWizard: React.FunctionComponent = ({ onSubmit(githubUsername)} + onSubmit={() => onSubmit()} showSubmit={submitEnabled} isEdit={!!editFormData} convertToYaml={convertToYaml} diff --git a/src/components/Contribute/ContributionWizard/ContributionWizardFooter.tsx b/src/components/Contribute/ContributionWizard/ContributionWizardFooter.tsx index c2569383..50c0229e 100644 --- a/src/components/Contribute/ContributionWizard/ContributionWizardFooter.tsx +++ b/src/components/Contribute/ContributionWizard/ContributionWizardFooter.tsx @@ -16,7 +16,6 @@ import { ContributionFormData } from '@/types'; interface Props { formData: ContributionFormData; - isGithubMode: boolean; showSubmit: boolean; isEdit: boolean; onSubmit: () => Promise; @@ -24,7 +23,7 @@ interface Props { onCancel: () => void; } -const ContributionWizardFooter: React.FC = ({ formData, isGithubMode, showSubmit, onSubmit, convertToYaml, isEdit }) => { +const ContributionWizardFooter: React.FC = ({ formData, showSubmit, onSubmit, convertToYaml, isEdit }) => { const { steps, activeStep, goToNextStep, goToPrevStep, goToStepByIndex, close } = useWizardContext(); const prevDisabled = steps.indexOf(activeStep) < 1; @@ -80,7 +79,7 @@ const ContributionWizardFooter: React.FC = ({ formData, isGithubMode, sho - + diff --git a/src/components/Contribute/ContributionWizard/DetailsPage/DetailsPage.tsx b/src/components/Contribute/ContributionWizard/DetailsPage/DetailsPage.tsx index f1d28acf..43ea01e5 100644 --- a/src/components/Contribute/ContributionWizard/DetailsPage/DetailsPage.tsx +++ b/src/components/Contribute/ContributionWizard/DetailsPage/DetailsPage.tsx @@ -25,7 +25,6 @@ import { MAX_SUMMARY_CHARS } from '@/components/Contribute/Utils/validationUtils import EditContributorModal from '@/components/Contribute/ContributionWizard/DetailsPage/EditContributorModal'; interface Props { - isGithubMode: boolean; infoSectionHelp?: React.ReactNode; isEditForm?: boolean; email: string; @@ -39,7 +38,6 @@ interface Props { setFilePath: (val: string) => void; } const DetailsPage: React.FC = ({ - isGithubMode, infoSectionHelp, isEditForm, email, @@ -162,8 +160,7 @@ const DetailsPage: React.FC = ({ rootPath={rootPath} path={filePath} handlePathChange={setFilePath} - helperText={`Specify the file path for the question-and-answer (Q and A)${isGithubMode ? ' and attribution' : ''} files. `} - isGithubMode={isGithubMode} + helperText={`Specify the file path for the question-and-answer (Q and A) files. `} /> diff --git a/src/components/Contribute/ContributionWizard/ReviewSubmission/ReviewSubmission.tsx b/src/components/Contribute/ContributionWizard/ReviewSubmission/ReviewSubmission.tsx index c8f9cb88..0665c4b9 100644 --- a/src/components/Contribute/ContributionWizard/ReviewSubmission/ReviewSubmission.tsx +++ b/src/components/Contribute/ContributionWizard/ReviewSubmission/ReviewSubmission.tsx @@ -10,7 +10,6 @@ interface ReviewSubmissionProps { contributionFormData: ContributionFormData; seedExamples: React.ReactNode; isSkillContribution: boolean; - isGithubMode: boolean; onUpdateSeedExamples: (seedExamples: KnowledgeSeedExample[] | SkillSeedExample[]) => void; } @@ -18,7 +17,6 @@ export const ReviewSubmission: React.FC = ({ contributionFormData, seedExamples, isSkillContribution, - isGithubMode, onUpdateSeedExamples }) => { React.useEffect(() => { @@ -77,55 +75,6 @@ export const ReviewSubmission: React.FC = ({ /> - {/* Attribution Information */} - {isGithubMode ? ( - - - Title - -
{contributionFormData.titleWork}
-
- , - ...(!isSkillContribution - ? [ - - Link to work - -
{(contributionFormData as KnowledgeFormData).linkWork}
-
-
- ] - : []), - ...(!isSkillContribution - ? [ - - Revision - -
{(contributionFormData as KnowledgeFormData).revision}
-
-
- ] - : []), - - License of the work - -
{contributionFormData.licenseWork}
-
-
, - - Authors - -
{contributionFormData.creators}
-
-
- ]} - /> -
- ) : null} - {/* Seed Examples */} unknown; - isGithubMode: boolean; } -export const ViewDropdownButton: React.FunctionComponent = ({ formData, convertToYaml, isGithubMode }) => { - const [isOpen, setIsOpen] = useState(false); +export const ViewDropdownButton: React.FunctionComponent = ({ formData, convertToYaml }) => { const [isYamlModalOpen, setIsYamlModalOpen] = useState(false); - const [isAttributionModalOpen, setIsAttributionModalOpen] = useState(false); const [modalContent, setModalContent] = useState(''); const handleViewYaml = () => { @@ -37,47 +33,6 @@ export const ViewDropdownButton: React.FunctionComponent = ({ formData, c document.body.removeChild(a); }; - const handleViewAttribution = () => { - const attributionData: AttributionData = { - title_of_work: formData.titleWork!, - link_to_work: (formData as KnowledgeFormData).linkWork!, - revision: (formData as KnowledgeFormData).revision!, - license_of_the_work: formData.licenseWork!, - creator_names: formData.creators - }; - const attributionString = dumpYaml(attributionData); - setModalContent(attributionString); - setIsAttributionModalOpen(true); - }; - - const handleSaveAttribution = () => { - const attributionData: AttributionData = { - title_of_work: formData.titleWork!, - link_to_work: (formData as KnowledgeFormData).linkWork!, - revision: (formData as KnowledgeFormData).revision!, - license_of_the_work: formData.licenseWork!, - creator_names: formData.creators - }; - const yamlString = dumpYaml(attributionData); - const blob = new Blob([yamlString], { type: 'application/x-yaml' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'attribution.yaml'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - }; - - const onToggleClick = () => { - setIsOpen(!isOpen); - }; - - const onSelect = () => { - // eslint-disable-next-line no-console - setIsOpen(false); - }; - return ( <> {isYamlModalOpen ? ( @@ -88,66 +43,9 @@ export const ViewDropdownButton: React.FunctionComponent = ({ formData, c onSave={handleSaveYaml} /> ) : null} - {isAttributionModalOpen ? ( - setIsAttributionModalOpen(!isAttributionModalOpen)} - yamlContent={modalContent} - onSave={handleSaveAttribution} - /> - ) : null} - {isGithubMode ? ( - setIsOpen(isOpen)} - toggle={(toggleRef: React.Ref) => ( - - )} - ouiaId="DownloadDropdown" - shouldFocusToggleOnSelect - > - - - - - } - > - {' '} - YAML Content - - {isGithubMode && ( - - - - } - > - {' '} - Attribution Content - - )} - - - ) : ( - - )} + ); }; diff --git a/src/components/Contribute/Knowledge/Edit/native/EditKnowledge.tsx b/src/components/Contribute/Knowledge/Edit/EditKnowledge.tsx similarity index 85% rename from src/components/Contribute/Knowledge/Edit/native/EditKnowledge.tsx rename to src/components/Contribute/Knowledge/Edit/EditKnowledge.tsx index 88d48c1e..d313b425 100644 --- a/src/components/Contribute/Knowledge/Edit/native/EditKnowledge.tsx +++ b/src/components/Contribute/Knowledge/Edit/EditKnowledge.tsx @@ -1,4 +1,4 @@ -// src/app/components/contribute/EditKnowledge/native/EditKnowledge.tsx +// src/app/components/contribute/Knowledge/Edit/EditKnowledge.tsx 'use client'; import * as React from 'react'; @@ -16,7 +16,7 @@ interface EditKnowledgeClientComponentProps { isDraft: boolean; } -const EditKnowledgeNative: React.FC = ({ branchName, isDraft }) => { +const EditKnowledge: React.FC = ({ branchName, isDraft }) => { const { data: session } = useSession(); const [isLoading, setIsLoading] = useState(true); const [loadingMsg, setLoadingMsg] = useState(''); @@ -57,7 +57,7 @@ const EditKnowledgeNative: React.FC = ({ bran ); } - return ; + return ; }; -export default EditKnowledgeNative; +export default EditKnowledge; diff --git a/src/components/Contribute/Knowledge/Edit/github/EditKnowledge.tsx b/src/components/Contribute/Knowledge/Edit/github/EditKnowledge.tsx deleted file mode 100644 index 81d596ce..00000000 --- a/src/components/Contribute/Knowledge/Edit/github/EditKnowledge.tsx +++ /dev/null @@ -1,77 +0,0 @@ -// src/app/components/contribute/EditKnowledge/github/EditKnowledge.tsx -'use client'; - -import * as React from 'react'; -import { useSession } from 'next-auth/react'; -import { KnowledgeEditFormData, PullRequest } from '@/types'; -import { useEffect, useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { Modal, ModalVariant, ModalBody } from '@patternfly/react-core'; -import { useEnvConfig } from '@/context/EnvConfigContext'; -import { fetchDraftKnowledgeChanges } from '@/components/Contribute/Utils/autoSaveUtils'; -import { fetchKnowledgePRData } from '@/components/Contribute/fetchUtils'; -import KnowledgeWizard from '@/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeWizard'; -import { fetchPullRequests } from '@/utils/github'; - -interface EditKnowledgeClientComponentProps { - branchName: string; - isDraft: boolean; -} - -const EditKnowledge: React.FC = ({ branchName, isDraft }) => { - const { data: session } = useSession(); - const { envConfig, loaded } = useEnvConfig(); - const [isLoading, setIsLoading] = useState(true); - const [loadingMsg, setLoadingMsg] = useState(''); - const [knowledgeEditFormData, setKnowledgeEditFormData] = useState(); - const router = useRouter(); - - useEffect(() => { - if (isDraft) { - fetchDraftKnowledgeChanges({ branchName, setIsLoading, setLoadingMsg, setKnowledgeEditFormData }); - return; - } - - setLoadingMsg('Fetching knowledge data from PR : ' + branchName); - - const fetchPRData = async () => { - if (!loaded || !session?.accessToken) { - return; - } - - const pullRequests: PullRequest[] = await fetchPullRequests(session.accessToken, envConfig); - const pr = pullRequests.find((pullRequest) => pullRequest.head.ref === branchName); - if (!pr) { - return; - } - - const { editFormData, error } = await fetchKnowledgePRData(session, envConfig, String(pr.number)); - if (error) { - setLoadingMsg(error); - return; - } - setIsLoading(false); - setKnowledgeEditFormData(editFormData); - }; - fetchPRData(); - }, [session, loaded, envConfig, branchName, isDraft]); - - const handleOnClose = () => { - router.push('/dashboard'); - setIsLoading(false); - }; - - if (isLoading) { - return ( - handleOnClose()}> - -
{loadingMsg}
-
-
- ); - } - - return ; -}; - -export default EditKnowledge; diff --git a/src/components/Contribute/Knowledge/KnowledgeWizard/DocumentInformation/DocumentInformation.tsx b/src/components/Contribute/Knowledge/KnowledgeWizard/DocumentInformation/DocumentInformation.tsx index 7738a73e..a839b0fc 100644 --- a/src/components/Contribute/Knowledge/KnowledgeWizard/DocumentInformation/DocumentInformation.tsx +++ b/src/components/Contribute/Knowledge/KnowledgeWizard/DocumentInformation/DocumentInformation.tsx @@ -1,4 +1,4 @@ -// src/components/Contribute/Knowledge/Native/DocumentInformation/DocumentInformation.tsx +// src/components/Contribute/Knowledge/KnowledgeWizard/DocumentInformation/DocumentInformation.tsx import React from 'react'; import { Button, Flex, FlexItem } from '@patternfly/react-core'; import { KnowledgeFile } from '@/types'; diff --git a/src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExampleCard.tsx b/src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExampleCard.tsx index 576b303b..4b54ba7d 100644 --- a/src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExampleCard.tsx +++ b/src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExampleCard.tsx @@ -1,4 +1,4 @@ -// src/components/Contribute/Knowledge/KnowledgeSeedExampleNative/KnowledgeQuestionAnswerPairsNative.tsx +// src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExampleCard.tsx import React from 'react'; import { Alert, @@ -62,7 +62,6 @@ const getScrollParent = (node: HTMLElement | null): HTMLElement | null => { }; interface Props { - isGithubMode: boolean; knowledgeFiles: KnowledgeFile[]; seedExampleIndex: number; seedExample: KnowledgeSeedExample; diff --git a/src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExamples.tsx b/src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExamples.tsx index fde87b48..39b05b23 100644 --- a/src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExamples.tsx +++ b/src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExamples.tsx @@ -1,4 +1,4 @@ -// src/components/Contribute/Knowledge/KnowledgeSeedExampleNative/KnowledgeQuestionAnswerPairsNative.tsx +// src/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExamples.tsx import React, { useState } from 'react'; import { Alert, Bullseye, Button, Flex, FlexItem, Spinner } from '@patternfly/react-core'; import { PlusCircleIcon } from '@patternfly/react-icons'; @@ -13,14 +13,13 @@ import { import KnowledgeSeedExampleCard from '@/components/Contribute/Knowledge/KnowledgeWizard/KnowledgeSeedExamples/KnowledgeSeedExampleCard'; interface Props { - isGithubMode: boolean; filesToUpload: File[]; uploadedFiles: KnowledgeFile[]; seedExamples: KnowledgeSeedExample[]; onUpdateSeedExamples: (seedExamples: KnowledgeSeedExample[]) => void; } -const KnowledgeSeedExamples: React.FC = ({ isGithubMode, filesToUpload, uploadedFiles, seedExamples, onUpdateSeedExamples }) => { +const KnowledgeSeedExamples: React.FC = ({ filesToUpload, uploadedFiles, seedExamples, onUpdateSeedExamples }) => { const [knowledgeFiles, setKnowledgeFiles] = useState([]); const [isLoading, setIsLoading] = useState(false); const [bodyRef, setBodyRef] = React.useState(); @@ -125,7 +124,6 @@ const KnowledgeSeedExamples: React.FC = ({ isGithubMode, filesToUpload, u = ({ knowledgeEditFormData, isGithubMode }) => { - const { data: session } = useSession(); - const { envConfig } = useEnvConfig(); +const STEP_IDS = ['details', 'resource-documentation', 'uploaded-documents', 'seed-examples', 'review']; +export const KnowledgeWizard: React.FunctionComponent = ({ knowledgeEditFormData }) => { const [knowledgeFormData, setKnowledgeFormData] = React.useState( knowledgeEditFormData?.formData ? { @@ -120,32 +104,8 @@ export const KnowledgeWizard: React.FunctionComponent = ({ k storeDraft(); }, [knowledgeEditFormData?.isSubmitted, knowledgeEditFormData?.oldFilesPath, knowledgeFormData]); - const steps: StepType[] = React.useMemo(() => { - const documentInformationStep = { - id: STEP_IDS[2], - name: 'Upload documents', - component: ( - - setKnowledgeFormData((prev) => ({ - ...prev, - uploadedFiles: files - })) - } - filesToUpload={knowledgeFormData.filesToUpload} - setFilesToUpload={(files) => - setKnowledgeFormData((prev) => ({ - ...prev, - filesToUpload: files - })) - } - /> - ), - status: isDocumentInfoValid(knowledgeFormData) ? StepStatus.Success : StepStatus.Error - }; - - return [ + const steps: StepType[] = React.useMemo( + () => [ { id: STEP_IDS[0], name: 'Details', @@ -153,7 +113,6 @@ export const KnowledgeWizard: React.FunctionComponent = ({ k setKnowledgeFormData((prev) => ({ ...prev, email }))} name={knowledgeFormData.name} @@ -172,70 +131,34 @@ export const KnowledgeWizard: React.FunctionComponent = ({ k ), status: isDetailsValid(knowledgeFormData) ? StepStatus.Success : StepStatus.Error }, - ...(isGithubMode - ? [ - { - id: STEP_IDS[1], - name: 'Data sources', - isExpandable: true, - subSteps: [ - documentInformationStep, - { - id: STEP_IDS[3], - name: 'Source attribution', - component: ( - - setKnowledgeFormData((prev) => ({ - ...prev, - titleWork - })) - } - linkWork={knowledgeFormData.linkWork} - setLinkWork={(linkWork) => - setKnowledgeFormData((prev) => ({ - ...prev, - linkWork - })) - } - revision={knowledgeFormData.revision} - setRevision={(revision) => - setKnowledgeFormData((prev) => ({ - ...prev, - revision - })) - } - licenseWork={knowledgeFormData.licenseWork} - setLicenseWork={(licenseWork) => - setKnowledgeFormData((prev) => ({ - ...prev, - licenseWork - })) - } - creators={knowledgeFormData.creators} - setCreators={(creators) => - setKnowledgeFormData((prev) => ({ - ...prev, - creators - })) - } - /> - ), - status: isKnowledgeAttributionInformationValid(knowledgeFormData) ? StepStatus.Success : StepStatus.Error - } - ] + { + id: STEP_IDS[2], + name: 'Upload documents', + component: ( + + setKnowledgeFormData((prev) => ({ + ...prev, + uploadedFiles: files + })) } - ] - : [documentInformationStep]), + filesToUpload={knowledgeFormData.filesToUpload} + setFilesToUpload={(files) => + setKnowledgeFormData((prev) => ({ + ...prev, + filesToUpload: files + })) + } + /> + ), + status: isDocumentInfoValid(knowledgeFormData) ? StepStatus.Success : StepStatus.Error + }, { - id: STEP_IDS[4], + id: STEP_IDS[3], name: 'Create seed data', component: ( = ({ k status: isKnowledgeSeedExamplesValid(knowledgeFormData) ? StepStatus.Success : StepStatus.Error }, { - id: STEP_IDS[5], + id: STEP_IDS[4], name: 'Review', component: ( } onUpdateSeedExamples={(seedExamples) => setKnowledgeFormData((prev) => ({ @@ -268,8 +190,9 @@ export const KnowledgeWizard: React.FunctionComponent = ({ k ), status: StepStatus.Default } - ]; - }, [isGithubMode, knowledgeEditFormData?.isEditForm, knowledgeFormData, setFilePath]); + ], + [knowledgeEditFormData?.isEditForm, knowledgeFormData, setFilePath] + ); const convertToYaml = (contributionFormData: ContributionFormData) => { const formData = contributionFormData as KnowledgeFormData; @@ -295,30 +218,20 @@ export const KnowledgeWizard: React.FunctionComponent = ({ k return yamlData; }; - const handleSubmit = async (githubUsername: string): Promise => { + const handleSubmit = async (): Promise => { // Upload the knowledge related documents - const isDocUploaded = await UploadKnowledgeDocuments(isGithubMode, knowledgeFormData, updateActionGroupAlertContent); + const isDocUploaded = await UploadKnowledgeDocuments(knowledgeFormData, updateActionGroupAlertContent); if (!isDocUploaded) { console.error('Document upload failed for knowledge contribution :', knowledgeFormData.branchName); return isDocUploaded; } - if (knowledgeEditFormData && knowledgeEditFormData.isSubmitted) { - const result = isGithubMode - ? await updateGithubKnowledgeData(session, envConfig, knowledgeFormData, knowledgeEditFormData, updateActionGroupAlertContent) - : await updateNativeKnowledgeData(knowledgeFormData, knowledgeEditFormData, updateActionGroupAlertContent); - if (result) { - //Remove draft if present in the local storage - deleteDraftData(knowledgeEditFormData.formData.branchName); - - router.push('/dashboard'); - } - return false; - } - const result = isGithubMode - ? await submitGithubKnowledgeData(knowledgeFormData, githubUsername, updateActionGroupAlertContent) - : await submitNativeKnowledgeData(knowledgeFormData, updateActionGroupAlertContent); + const result = await submitKnowledgeData( + knowledgeFormData, + updateActionGroupAlertContent, + knowledgeEditFormData?.isSubmitted ? knowledgeEditFormData : undefined + ); if (result) { //Remove draft if present in the local storage deleteDraftData(knowledgeFormData.branchName); @@ -372,7 +285,6 @@ export const KnowledgeWizard: React.FunctionComponent = ({ k } formData={knowledgeFormData} setFormData={setKnowledgeFormData as React.Dispatch>} - isGithubMode={isGithubMode} isSkillContribution={false} steps={steps} convertToYaml={convertToYaml} diff --git a/src/components/Contribute/Knowledge/View/ViewKnowledge.tsx b/src/components/Contribute/Knowledge/View/ViewKnowledge.tsx index fe0a301d..26d7a999 100644 --- a/src/components/Contribute/Knowledge/View/ViewKnowledge.tsx +++ b/src/components/Contribute/Knowledge/View/ViewKnowledge.tsx @@ -1,4 +1,4 @@ -// src/app/components/contribute/EditKnowledge/native/EditKnowledge.tsx +// src/app/components/Contribute/Knowledge/View/ViewKnowledge.tsx 'use client'; import * as React from 'react'; @@ -20,7 +20,6 @@ import { DescriptionListDescription } from '@patternfly/react-core'; import { CatalogIcon, PficonTemplateIcon } from '@patternfly/react-icons'; -import { useEnvConfig } from '@/context/EnvConfigContext'; import ViewContributionSection from '@/components/Common/ViewContributionSection'; import ViewKnowledgeSeedExample from '@/components/Contribute/Knowledge/View/ViewKnowledgeSeedExample'; @@ -28,11 +27,8 @@ interface ViewKnowledgeProps { knowledgeEditFormData: KnowledgeEditFormData; } -const ViewKnowledgeNative: React.FC = ({ knowledgeEditFormData }) => { +const ViewKnowledge: React.FC = ({ knowledgeEditFormData }) => { const router = useRouter(); - const { - envConfig: { isGithubMode } - } = useEnvConfig(); return ( @@ -131,47 +127,6 @@ const ViewKnowledgeNative: React.FC = ({ knowledgeEditFormDa />
- {/* Attribution Information */} - {isGithubMode ? ( - - - Title - -
{knowledgeEditFormData.formData.titleWork}
-
- , - - Link to work - -
{knowledgeEditFormData.formData.linkWork}
-
-
, - - Revision - -
{knowledgeEditFormData.formData.revision}
-
-
, - - License of the work - -
{knowledgeEditFormData.formData.licenseWork}
-
-
, - - Authors - -
{knowledgeEditFormData.formData.creators}
-
-
- ]} - /> -
- ) : null} - {/* Seed Examples */} = ({ knowledgeEditFormDa ); }; -export default ViewKnowledgeNative; +export default ViewKnowledge; diff --git a/src/components/Contribute/Knowledge/View/native/ViewKnowledgePage.tsx b/src/components/Contribute/Knowledge/View/ViewKnowledgePage.tsx similarity index 96% rename from src/components/Contribute/Knowledge/View/native/ViewKnowledgePage.tsx rename to src/components/Contribute/Knowledge/View/ViewKnowledgePage.tsx index 542ae996..d6f50749 100644 --- a/src/components/Contribute/Knowledge/View/native/ViewKnowledgePage.tsx +++ b/src/components/Contribute/Knowledge/View/ViewKnowledgePage.tsx @@ -1,4 +1,4 @@ -// src/app/components/contribute/EditKnowledge/native/EditKnowledge.tsx +// src/app/components/Contribute/Knowledge/View/ViewKnowledgePage.tsx 'use client'; import * as React from 'react'; diff --git a/src/components/Contribute/Knowledge/View/ViewKnowledgeSeedExample.tsx b/src/components/Contribute/Knowledge/View/ViewKnowledgeSeedExample.tsx index 0922d7a0..a4ca27bc 100644 --- a/src/components/Contribute/Knowledge/View/ViewKnowledgeSeedExample.tsx +++ b/src/components/Contribute/Knowledge/View/ViewKnowledgeSeedExample.tsx @@ -1,4 +1,4 @@ -// src/components/Contribute/Knowledge/Native/KnowledgeSeedExampleNative/KnowledgeQuestionAnswerPairsNative.tsx +// src/components/Contribute/Knowledge/View/ViewKnowledgeSeedExample.tsx import React from 'react'; import { Accordion, diff --git a/src/components/Contribute/Knowledge/View/github/ViewKnowledgePage.tsx b/src/components/Contribute/Knowledge/View/github/ViewKnowledgePage.tsx deleted file mode 100644 index bef5a8e0..00000000 --- a/src/components/Contribute/Knowledge/View/github/ViewKnowledgePage.tsx +++ /dev/null @@ -1,77 +0,0 @@ -// src/app/components/contribute/EditKnowledge/github/EditKnowledge.tsx -'use client'; - -import * as React from 'react'; -import { useSession } from 'next-auth/react'; -import { KnowledgeEditFormData, PullRequest } from '@/types'; -import { useEffect, useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { Modal, ModalVariant, ModalBody } from '@patternfly/react-core'; -import { useEnvConfig } from '@/context/EnvConfigContext'; -import { fetchDraftKnowledgeChanges } from '@/components/Contribute/Utils/autoSaveUtils'; -import { fetchKnowledgePRData } from '@/components/Contribute/fetchUtils'; -import ViewKnowledge from '@/components/Contribute/Knowledge/View/ViewKnowledge'; -import { fetchPullRequests } from '@/utils/github'; - -interface Props { - branchName: string; - isDraft: boolean; -} - -const ViewKnowledgePage: React.FC = ({ branchName, isDraft }) => { - const router = useRouter(); - const { data: session } = useSession(); - const { envConfig, loaded } = useEnvConfig(); - const [isLoading, setIsLoading] = useState(true); - const [loadingMsg, setLoadingMsg] = useState(''); - const [knowledgeEditFormData, setKnowledgeEditFormData] = useState(); - - useEffect(() => { - if (isDraft) { - fetchDraftKnowledgeChanges({ branchName, setIsLoading, setLoadingMsg, setKnowledgeEditFormData }); - return; - } - - setLoadingMsg('Fetching knowledge data from PR : ' + branchName); - - const fetchPRData = async () => { - if (!loaded || !session?.accessToken) { - return; - } - - const pullRequests: PullRequest[] = await fetchPullRequests(session.accessToken, envConfig); - const pr = pullRequests.find((pullRequest) => pullRequest.head.ref === branchName); - if (!pr) { - return; - } - - const { editFormData, error } = await fetchKnowledgePRData(session, envConfig, String(pr.number)); - if (error) { - setLoadingMsg(error); - return; - } - setIsLoading(false); - setKnowledgeEditFormData(editFormData); - }; - fetchPRData(); - }, [session, loaded, envConfig, branchName, isDraft]); - - const handleOnClose = () => { - router.push('/dashboard'); - setIsLoading(false); - }; - - if (isLoading || !knowledgeEditFormData?.formData) { - return ( - handleOnClose()}> - -
{loadingMsg}
-
-
- ); - } - - return ; -}; - -export default ViewKnowledgePage; diff --git a/src/components/Contribute/Skill/Edit/native/EditSkill.tsx b/src/components/Contribute/Skill/Edit/EditSkill.tsx similarity index 86% rename from src/components/Contribute/Skill/Edit/native/EditSkill.tsx rename to src/components/Contribute/Skill/Edit/EditSkill.tsx index 3dff1b96..0a649b7f 100644 --- a/src/components/Contribute/Skill/Edit/native/EditSkill.tsx +++ b/src/components/Contribute/Skill/Edit/EditSkill.tsx @@ -1,4 +1,4 @@ -// src/app/components/contribute/EditSkill/native/EditSkill.tsx +// src/components/Contribute/Skill/Edit/EditSkill.tsx 'use client'; import * as React from 'react'; @@ -16,7 +16,7 @@ interface EditSkillClientComponentProps { isDraft: boolean; } -const EditSkillNative: React.FC = ({ branchName, isDraft }) => { +const EditSkill: React.FC = ({ branchName, isDraft }) => { const { data: session } = useSession(); const [isLoading, setIsLoading] = useState(true); const [loadingMsg, setLoadingMsg] = useState(''); @@ -57,7 +57,7 @@ const EditSkillNative: React.FC = ({ branchName, ); } - return ; + return ; }; -export default EditSkillNative; +export default EditSkill; diff --git a/src/components/Contribute/Skill/Edit/github/EditSkill.tsx b/src/components/Contribute/Skill/Edit/github/EditSkill.tsx deleted file mode 100644 index 8e3491d8..00000000 --- a/src/components/Contribute/Skill/Edit/github/EditSkill.tsx +++ /dev/null @@ -1,76 +0,0 @@ -// src/app/components/contribute/EditSkill/github/EditSkill.tsx -'use client'; - -import * as React from 'react'; -import { useSession } from 'next-auth/react'; -import { useEffect, useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { PullRequest, SkillEditFormData } from '@/types'; -import { Modal, ModalVariant, ModalBody } from '@patternfly/react-core'; -import { useEnvConfig } from '@/context/EnvConfigContext'; -import { fetchDraftSkillChanges } from '@/components/Contribute/Utils/autoSaveUtils'; -import { fetchSkillPRData } from '@/components/Contribute/fetchUtils'; -import SkillWizard from '@/components/Contribute/Skill/SkillWizard/SkillWizard'; -import { fetchPullRequests } from '@/utils/github'; - -interface EditSkillClientComponentProps { - branchName: string; - isDraft: boolean; -} - -const EditSkill: React.FC = ({ branchName, isDraft }) => { - const router = useRouter(); - const { data: session } = useSession(); - const { envConfig, loaded } = useEnvConfig(); - const [isLoading, setIsLoading] = useState(true); - const [loadingMsg, setLoadingMsg] = useState(''); - const [skillEditFormData, setSkillEditFormData] = useState(); - - useEffect(() => { - if (isDraft) { - fetchDraftSkillChanges({ branchName, setIsLoading, setLoadingMsg, setSkillEditFormData }); - return; - } - - const fetchPRData = async () => { - setLoadingMsg('Fetching skills data from PR : ' + branchName); - if (!loaded || !session?.accessToken) { - return; - } - - const pullRequests: PullRequest[] = await fetchPullRequests(session.accessToken, envConfig); - const pr = pullRequests.find((pullRequest) => pullRequest.head.ref === branchName); - if (!pr) { - return; - } - - const { editFormData, error } = await fetchSkillPRData(session, envConfig, String(pr.number)); - if (error) { - setLoadingMsg(error); - return; - } - setIsLoading(false); - setSkillEditFormData(editFormData); - }; - fetchPRData(); - }, [session, loaded, envConfig, branchName, isDraft]); - - const handleOnClose = () => { - router.push('/dashboard'); - setIsLoading(false); - }; - - if (isLoading) { - return ( - - -
{loadingMsg}
-
-
- ); - } - - return ; -}; - -export default EditSkill; diff --git a/src/components/Contribute/Skill/SkillWizard/SkillSeedExamples/SkillSeedExamplesReviewSection.tsx b/src/components/Contribute/Skill/SkillWizard/SkillSeedExamples/SkillSeedExamplesReviewSection.tsx index c50534ab..6f4096f5 100644 --- a/src/components/Contribute/Skill/SkillWizard/SkillSeedExamples/SkillSeedExamplesReviewSection.tsx +++ b/src/components/Contribute/Skill/SkillWizard/SkillSeedExamples/SkillSeedExamplesReviewSection.tsx @@ -1,4 +1,4 @@ -// src/components/Contribute/Knowledge/Native/KnowledgeSeedExampleNative/KnowledgeQuestionAnswerPairsNative.tsx +// src/components/Contribute/Skill/SkillWizard/SkillSeedExamples/SkillSeedExamplesReviewSection.tsx import React from 'react'; import { Accordion, diff --git a/src/components/Contribute/Skill/SkillWizard/SkillWizard.tsx b/src/components/Contribute/Skill/SkillWizard/SkillWizard.tsx index b9322417..92441bb4 100644 --- a/src/components/Contribute/Skill/SkillWizard/SkillWizard.tsx +++ b/src/components/Contribute/Skill/SkillWizard/SkillWizard.tsx @@ -1,23 +1,15 @@ // src/components/Contribute/Skill/SkillWizard/SkillWizard.tsx 'use client'; import React, { useEffect, useState } from 'react'; -import { useSession } from 'next-auth/react'; import { useRouter } from 'next/navigation'; import { ValidatedOptions, Button, PageBreadcrumb, Breadcrumb, BreadcrumbItem } from '@patternfly/react-core'; import { SkillSchemaVersion } from '@/types/const'; import { ContributionFormData, SkillEditFormData, SkillFormData, SkillSeedExample, SkillYamlData } from '@/types'; -import { useEnvConfig } from '@/context/EnvConfigContext'; import { ActionGroupAlertContent } from '@/components/Contribute/types'; -import { isAttributionInformationValid, isSkillSeedExamplesValid, isDetailsValid } from '@/components/Contribute/Utils/validationUtils'; -import { - submitGithubSkillData, - submitNativeSkillData, - updateGithubSkillData, - updateNativeSkillData -} from '@/components/Contribute/Utils/submitUtils'; +import { isSkillSeedExamplesValid, isDetailsValid } from '@/components/Contribute/Utils/validationUtils'; +import { submitSkillData } from '@/components/Contribute/Utils/submitUtils'; import { addYamlUploadSkill } from '@/components/Contribute/Utils/uploadUtils'; import { getDefaultSkillFormData } from '@/components/Contribute/Utils/contributionUtils'; -import AttributionInformation from '@/components/Contribute/ContributionWizard/AttributionInformation/AttributionInformation'; import { ContributionWizard, StepStatus, StepType } from '@/components/Contribute/ContributionWizard/ContributionWizard'; import { YamlFileUploadModal } from '@/components/Contribute/YamlFileUploadModal'; import ContributeAlertGroup from '@/components/Contribute/ContributeAlertGroup'; @@ -31,14 +23,11 @@ import './skills.css'; export interface Props { skillEditFormData?: SkillEditFormData; - isGithubMode: boolean; } -const STEP_IDS = ['details', 'seed-examples', 'attribution-info', 'review-submission']; +const STEP_IDS = ['details', 'seed-examples', 'review-submission']; -export const SkillWizard: React.FunctionComponent = ({ skillEditFormData, isGithubMode }) => { - const { data: session } = useSession(); - const { envConfig } = useEnvConfig(); +export const SkillWizard: React.FunctionComponent = ({ skillEditFormData }) => { const [skillFormData, setSkillFormData] = React.useState( skillEditFormData?.formData ? { @@ -102,7 +91,6 @@ export const SkillWizard: React.FunctionComponent = ({ skillEditFormData, setSkillFormData((prev) => ({ ...prev, email }))} name={skillFormData.name} @@ -132,35 +120,13 @@ export const SkillWizard: React.FunctionComponent = ({ skillEditFormData, ), status: isSkillSeedExamplesValid(skillFormData) ? StepStatus.Success : StepStatus.Error }, - ...(isGithubMode - ? [ - { - id: STEP_IDS[2], - name: 'Source attribution', - component: ( - setSkillFormData((prev) => ({ ...prev, titleWork }))} - licenseWork={skillFormData.licenseWork} - setLicenseWork={(licenseWork) => setSkillFormData((prev) => ({ ...prev, licenseWork }))} - creators={skillFormData.creators} - setCreators={(creators) => setSkillFormData((prev) => ({ ...prev, creators }))} - /> - ), - status: isAttributionInformationValid(skillFormData) ? StepStatus.Success : StepStatus.Error - } - ] - : []), { - id: STEP_IDS[3], + id: STEP_IDS[2], name: 'Review', component: ( } onUpdateSeedExamples={(seedExamples) => setSkillFormData((prev) => ({ ...prev, seedExamples: seedExamples as SkillSeedExample[] }))} /> @@ -168,7 +134,7 @@ export const SkillWizard: React.FunctionComponent = ({ skillEditFormData, status: StepStatus.Default } ], - [isGithubMode, setFilePath, skillEditFormData?.isEditForm, skillFormData] + [setFilePath, skillEditFormData?.isEditForm, skillFormData] ); const convertToYaml = (contributionFormData: ContributionFormData) => { @@ -187,28 +153,14 @@ export const SkillWizard: React.FunctionComponent = ({ skillEditFormData, return yamlData; }; - const handleSubmit = async (githubUsername: string): Promise => { - //SkillEditFormData will be generated for local storage as well. - // If the PR number is present it means the draft is for the submitted PR. - if (skillEditFormData && skillEditFormData.isSubmitted) { - const result = isGithubMode - ? await updateGithubSkillData(session, envConfig, skillFormData, skillEditFormData, updateActionGroupAlertContent) - : await updateNativeSkillData(skillFormData, skillEditFormData, updateActionGroupAlertContent); - if (result) { - //Remove draft if present in the local storage - deleteDraftData(skillEditFormData.formData.branchName); - router.push('/dashboard'); - } - return false; - } - const result = isGithubMode - ? await submitGithubSkillData(skillFormData, githubUsername, updateActionGroupAlertContent) - : await submitNativeSkillData(skillFormData, updateActionGroupAlertContent); + const handleSubmit = async (): Promise => { + // SkillEditFormData will be generated for local storage as well, only pass if it has already been submitted + const result = await submitSkillData( + skillFormData, + updateActionGroupAlertContent, + skillEditFormData?.isSubmitted ? skillEditFormData : undefined + ); if (result) { - const newFormData = { ...getDefaultSkillFormData() }; - newFormData.name = skillFormData.name; - newFormData.email = skillFormData.email; - //Remove draft if present in the local storage deleteDraftData(skillFormData.branchName); @@ -260,7 +212,6 @@ export const SkillWizard: React.FunctionComponent = ({ skillEditFormData, } formData={skillFormData} setFormData={setSkillFormData as React.Dispatch>} - isGithubMode={isGithubMode} isSkillContribution steps={steps} convertToYaml={convertToYaml} diff --git a/src/components/Contribute/Skill/View/ViewSkill.tsx b/src/components/Contribute/Skill/View/ViewSkill.tsx index ee788a4d..2c13dffb 100644 --- a/src/components/Contribute/Skill/View/ViewSkill.tsx +++ b/src/components/Contribute/Skill/View/ViewSkill.tsx @@ -20,19 +20,15 @@ import { DescriptionListDescription } from '@patternfly/react-core'; import { CatalogIcon, PficonTemplateIcon } from '@patternfly/react-icons'; -import { useEnvConfig } from '@/context/EnvConfigContext'; import ViewContributionSection from '@/components/Common/ViewContributionSection'; import ViewSkillSeedExample from '@/components/Contribute/Skill/View/ViewSkillSeedExample'; -interface ViewKnowledgeProps { +interface ViewSkillProps { skillEditFormData: SkillEditFormData; } -const ViewKnowledgeNative: React.FC = ({ skillEditFormData }) => { +const ViewSkill: React.FC = ({ skillEditFormData }) => { const router = useRouter(); - const { - envConfig: { isGithubMode } - } = useEnvConfig(); return ( @@ -121,35 +117,6 @@ const ViewKnowledgeNative: React.FC = ({ skillEditFormData } />
- {/* Attribution Information */} - {isGithubMode ? ( - - - Title - -
{skillEditFormData.formData.titleWork}
-
- , - - License of the work - -
{skillEditFormData.formData.licenseWork}
-
-
, - - Authors - -
{skillEditFormData.formData.creators}
-
-
- ]} - /> -
- ) : null} - {/* Seed Examples */} = ({ skillEditFormData } ); }; -export default ViewKnowledgeNative; +export default ViewSkill; diff --git a/src/components/Contribute/Skill/View/native/ViewSkillPage.tsx b/src/components/Contribute/Skill/View/ViewSkillPage.tsx similarity index 96% rename from src/components/Contribute/Skill/View/native/ViewSkillPage.tsx rename to src/components/Contribute/Skill/View/ViewSkillPage.tsx index a7047375..9292bca5 100644 --- a/src/components/Contribute/Skill/View/native/ViewSkillPage.tsx +++ b/src/components/Contribute/Skill/View/ViewSkillPage.tsx @@ -1,4 +1,4 @@ -// src/app/components/Contribute/Skill/View/native/ViewSkillPage.tsx +// src/components/Contribute/Skill/View/ViewSkillPage.tsx 'use client'; import * as React from 'react'; diff --git a/src/components/Contribute/Skill/View/github/ViewSkillPage.tsx b/src/components/Contribute/Skill/View/github/ViewSkillPage.tsx deleted file mode 100644 index b1c162e8..00000000 --- a/src/components/Contribute/Skill/View/github/ViewSkillPage.tsx +++ /dev/null @@ -1,77 +0,0 @@ -// src/app/components/Contribute/Skill/View/github/ViewSkillPage.tsx -'use client'; - -import * as React from 'react'; -import { useSession } from 'next-auth/react'; -import { PullRequest, SkillEditFormData } from '@/types'; -import { useEffect, useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { Modal, ModalVariant, ModalBody } from '@patternfly/react-core'; -import { useEnvConfig } from '@/context/EnvConfigContext'; -import { fetchDraftSkillChanges } from '@/components/Contribute/Utils/autoSaveUtils'; -import { fetchSkillPRData } from '@/components/Contribute/fetchUtils'; -import ViewSkill from '@/components/Contribute/Skill/View/ViewSkill'; -import { fetchPullRequests } from '@/utils/github'; - -interface Props { - branchName: string; - isDraft: boolean; -} - -const ViewSkillPage: React.FC = ({ branchName, isDraft }) => { - const router = useRouter(); - const { data: session } = useSession(); - const { envConfig, loaded } = useEnvConfig(); - const [isLoading, setIsLoading] = useState(true); - const [loadingMsg, setLoadingMsg] = useState(''); - const [skillEditFormData, setSkillEditFormData] = useState(); - - useEffect(() => { - if (isDraft) { - fetchDraftSkillChanges({ branchName, setIsLoading, setLoadingMsg, setSkillEditFormData }); - return; - } - - setLoadingMsg('Fetching knowledge data from PR : ' + branchName); - - const fetchPRData = async () => { - if (!loaded || !session?.accessToken) { - return; - } - - const pullRequests: PullRequest[] = await fetchPullRequests(session.accessToken, envConfig); - const pr = pullRequests.find((pullRequest) => pullRequest.head.ref === branchName); - if (!pr) { - return; - } - - const { editFormData, error } = await fetchSkillPRData(session, envConfig, String(pr.number)); - if (error) { - setLoadingMsg(error); - return; - } - setIsLoading(false); - setSkillEditFormData(editFormData); - }; - fetchPRData(); - }, [session, envConfig, loaded, branchName, isDraft]); - - const handleOnClose = () => { - router.push('/dashboard'); - setIsLoading(false); - }; - - if (isLoading || !skillEditFormData?.formData) { - return ( - handleOnClose()}> - -
{loadingMsg}
-
-
- ); - } - - return ; -}; - -export default ViewSkillPage; diff --git a/src/components/Contribute/Utils/documentUtils.ts b/src/components/Contribute/Utils/documentUtils.ts index 554b9f07..f5d29c6c 100644 --- a/src/components/Contribute/Utils/documentUtils.ts +++ b/src/components/Contribute/Utils/documentUtils.ts @@ -1,11 +1,7 @@ import { ActionGroupAlertContent } from '@/components/Contribute/types'; import { KnowledgeFile, KnowledgeFormData } from '@/types'; -const GITHUB_KNOWLEDGE_FILES_URL = '/api/github/knowledge-files'; -const NATIVE_GIT_KNOWLEDGE_FILES_URL = '/api/native/knowledge-files'; - export const UploadKnowledgeDocuments = async ( - isGithubMode: boolean, knowledgeFormData: KnowledgeFormData, setActionGroupAlertContent: (content: ActionGroupAlertContent) => void ): Promise => { @@ -44,7 +40,7 @@ export const UploadKnowledgeDocuments = async ( // Trigger the upload only if all the newly uploaded files were read successfully and there are existing uploaded files. if (newFiles.length === knowledgeFormData.filesToUpload.length && (newFiles.length !== 0 || updatedExistingFiles.length !== 0)) { try { - const response = await fetch(isGithubMode ? GITHUB_KNOWLEDGE_FILES_URL : NATIVE_GIT_KNOWLEDGE_FILES_URL, { + const response = await fetch('/api/knowledge-files', { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -101,11 +97,9 @@ export const UploadKnowledgeDocuments = async ( return true; }; -export const fetchExistingKnowledgeDocuments = async (isGithubMode: boolean, knowledgeDocumentCommit: string): Promise => { +export const fetchExistingKnowledgeDocuments = async (knowledgeDocumentCommit: string): Promise => { try { - const url = isGithubMode ? GITHUB_KNOWLEDGE_FILES_URL : NATIVE_GIT_KNOWLEDGE_FILES_URL; - - const response = await fetch(`${url}?commitSHA=${knowledgeDocumentCommit}`, { + const response = await fetch(`/api/knowledge-files?commitSHA=${knowledgeDocumentCommit}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); diff --git a/src/components/Contribute/Utils/submitUtils.ts b/src/components/Contribute/Utils/submitUtils.ts index 9945f553..943e70b0 100644 --- a/src/components/Contribute/Utils/submitUtils.ts +++ b/src/components/Contribute/Utils/submitUtils.ts @@ -1,23 +1,9 @@ -import { - AttributionData, - EnvConfigType, - KnowledgeEditFormData, - KnowledgeFormData, - KnowledgeYamlData, - SkillEditFormData, - SkillFormData, - SkillYamlData -} from '@/types'; +import { KnowledgeEditFormData, KnowledgeFormData, KnowledgeYamlData, SkillEditFormData, SkillFormData, SkillYamlData } from '@/types'; import { KnowledgeSchemaVersion, SkillSchemaVersion } from '@/types/const'; import { dumpYaml } from '@/utils/yamlConfig'; import { ActionGroupAlertContent } from '@/components/Contribute/types'; -import { amendCommit, getGitHubUsername, updatePullRequest } from '@/utils/github'; -import { Session } from 'next-auth'; import { validateKnowledgeFormFields, validateSkillFormFields } from '@/components/Contribute/Utils/validation'; -const KNOWLEDGE_DIR = 'knowledge/'; -const SKILLS_DIR = 'compositional_skills/'; - const domainFromFilePath = (filePath: string): string => { if (!filePath) { return ''; @@ -26,187 +12,15 @@ const domainFromFilePath = (filePath: string): string => { return pathElements[pathElements.length - 1] || ''; }; -export const submitNativeKnowledgeData = async ( +export const submitKnowledgeData = async ( knowledgeFormData: KnowledgeFormData, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void + setActionGroupAlertContent: (content: ActionGroupAlertContent) => void, + knowledgeEditFormData?: KnowledgeEditFormData ): Promise => { - if (!validateKnowledgeFormFields(knowledgeFormData, setActionGroupAlertContent, true)) { + if (!validateKnowledgeFormFields(knowledgeFormData, setActionGroupAlertContent)) { return false; } - // Strip leading slash and ensure trailing slash in the file path - let sanitizedFilePath = knowledgeFormData.filePath!.startsWith('/') ? knowledgeFormData.filePath!.slice(1) : knowledgeFormData.filePath; - sanitizedFilePath = sanitizedFilePath!.endsWith('/') ? sanitizedFilePath : `${sanitizedFilePath}/`; - - const knowledgeYamlData: KnowledgeYamlData = { - created_by: knowledgeFormData.email, - version: KnowledgeSchemaVersion, - domain: domainFromFilePath(knowledgeFormData.filePath), - document_outline: knowledgeFormData.submissionSummary, - seed_examples: knowledgeFormData.seedExamples.map((example) => ({ - context: example.context!, - questions_and_answers: example.questionAndAnswers.map((questionAndAnswer) => ({ - question: questionAndAnswer.question, - answer: questionAndAnswer.answer - })) - })), - document: { - repo: knowledgeFormData.knowledgeDocumentRepositoryUrl!, - commit: knowledgeFormData.knowledgeDocumentCommit!, - patterns: knowledgeFormData.documentName!.split(',').map((pattern) => pattern.trim()) - } - }; - - const yamlString = dumpYaml(knowledgeYamlData); - - const waitForSubmissionAlert: ActionGroupAlertContent = { - title: 'Knowledge contribution submission in progress.', - message: `Once the submission is successful, it will provide the link to the newly created Pull Request.`, - success: true, - waitAlert: true, - timeout: false - }; - setActionGroupAlertContent(waitForSubmissionAlert); - - const { branchName, name, submissionSummary, email } = knowledgeFormData; - - const response = await fetch('/api/native/pr/knowledge', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - action: 'submit', - branchName: branchName, - content: yamlString, - name, - email, - submissionSummary, - documentOutline: submissionSummary, - filePath: sanitizedFilePath, - oldFilesPath: sanitizedFilePath - }) - }); - - if (!response.ok) { - const actionGroupAlertContent: ActionGroupAlertContent = { - title: `Failed data submission`, - message: response.statusText, - success: false - }; - setActionGroupAlertContent(actionGroupAlertContent); - return false; - } - - await response.json(); - const actionGroupAlertContent: ActionGroupAlertContent = { - title: 'Knowledge contribution submitted successfully!', - message: `Thank you for your contribution!`, - url: '/dashboard/', - success: true - }; - setActionGroupAlertContent(actionGroupAlertContent); - return true; -}; - -export const submitGithubKnowledgeData = async ( - knowledgeFormData: KnowledgeFormData, - githubUsername: string, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void -): Promise => { - if (!validateKnowledgeFormFields(knowledgeFormData, setActionGroupAlertContent, false)) { - return false; - } - // Strip leading slash and ensure trailing slash in the file path - let sanitizedFilePath = knowledgeFormData.filePath!.startsWith('/') ? knowledgeFormData.filePath!.slice(1) : knowledgeFormData.filePath; - sanitizedFilePath = sanitizedFilePath!.endsWith('/') ? sanitizedFilePath : `${sanitizedFilePath}/`; - - const knowledgeYamlData: KnowledgeYamlData = { - created_by: githubUsername!, - version: KnowledgeSchemaVersion, - domain: domainFromFilePath(knowledgeFormData.filePath), - document_outline: knowledgeFormData.submissionSummary, - seed_examples: knowledgeFormData.seedExamples.map((example) => ({ - context: example.context!, - questions_and_answers: example.questionAndAnswers.map((questionAndAnswer) => ({ - question: questionAndAnswer.question, - answer: questionAndAnswer.answer - })) - })), - document: { - repo: knowledgeFormData.knowledgeDocumentRepositoryUrl!, - commit: knowledgeFormData.knowledgeDocumentCommit!, - patterns: knowledgeFormData.documentName!.split(',').map((pattern) => pattern.trim()) - } - }; - - const yamlString = dumpYaml(knowledgeYamlData); - - const attributionData: AttributionData = { - title_of_work: knowledgeFormData.titleWork!, - link_to_work: knowledgeFormData.linkWork!, - revision: knowledgeFormData.revision!, - license_of_the_work: knowledgeFormData.licenseWork!, - creator_names: knowledgeFormData.creators! - }; - - const waitForSubmissionAlert: ActionGroupAlertContent = { - title: 'Knowledge contribution submission in progress.', - message: `Once the submission is successful, it will provide the link to the newly created Pull Request.`, - success: true, - waitAlert: true, - timeout: false - }; - setActionGroupAlertContent(waitForSubmissionAlert); - - const { branchName, name, submissionSummary, email } = knowledgeFormData; - const response = await fetch('/api/github/pr/knowledge', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - branchName: branchName, - content: yamlString, - attribution: attributionData, - name, - email, - submissionSummary, - documentOutline: submissionSummary, - filePath: sanitizedFilePath - }) - }); - - if (!response.ok) { - const actionGroupAlertContent: ActionGroupAlertContent = { - title: `Failed data submission`, - message: response.statusText, - success: false - }; - setActionGroupAlertContent(actionGroupAlertContent); - return false; - } - - const result = await response.json(); - const actionGroupAlertContent: ActionGroupAlertContent = { - title: 'Knowledge contribution submitted successfully!', - message: `Thank you for your contribution!`, - url: `${result.html_url}`, - isUrlExternal: true, - success: true - }; - setActionGroupAlertContent(actionGroupAlertContent); - return true; -}; - -export const updateNativeKnowledgeData = async ( - knowledgeFormData: KnowledgeFormData, - knowledgeEditFormData: KnowledgeEditFormData, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void -): Promise => { - if (!validateKnowledgeFormFields(knowledgeFormData, setActionGroupAlertContent, true)) { - return false; - } // Strip leading slash and ensure trailing slash in the file path let sanitizedFilePath = knowledgeFormData.filePath?.startsWith('/') ? knowledgeFormData.filePath!.slice(1) : knowledgeFormData.filePath; sanitizedFilePath = sanitizedFilePath?.endsWith('/') ? sanitizedFilePath : `${sanitizedFilePath}/`; @@ -242,14 +56,14 @@ export const updateNativeKnowledgeData = async ( setActionGroupAlertContent(waitForSubmissionAlert); const { branchName, name, email, submissionSummary } = knowledgeFormData; - const { oldFilesPath } = knowledgeEditFormData; - const response = await fetch('/api/native/pr/knowledge', { + + const response = await fetch('/api/pr/knowledge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - action: 'update', + action: knowledgeEditFormData ? 'update' : 'submit', branchName: branchName, content: yamlString, name, @@ -257,7 +71,7 @@ export const updateNativeKnowledgeData = async ( submissionSummary, documentOutline: submissionSummary, filePath: sanitizedFilePath, - oldFilesPath: oldFilesPath + oldFilesPath: knowledgeEditFormData ? knowledgeEditFormData.oldFilesPath : sanitizedFilePath }) }); @@ -273,7 +87,7 @@ export const updateNativeKnowledgeData = async ( await response.json(); const actionGroupAlertContent: ActionGroupAlertContent = { - title: 'Knowledge contribution updated successfully!', + title: 'Knowledge contribution submitted successfully!', message: `Thank you for your contribution!`, url: '/dashboard/', success: true @@ -282,146 +96,12 @@ export const updateNativeKnowledgeData = async ( return true; }; -export const updateGithubKnowledgeData = async ( - session: Session | null, - envConfig: EnvConfigType, - knowledgeFormData: KnowledgeFormData, - knowledgeEditFormData: KnowledgeEditFormData, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void -): Promise => { - if (!validateKnowledgeFormFields(knowledgeFormData, setActionGroupAlertContent, false)) { - return false; - } - - if (session?.accessToken) { - try { - console.log(`Updating PR with number: ${knowledgeEditFormData.pullRequestNumber}`); - await updatePullRequest(session.accessToken, envConfig, knowledgeEditFormData.pullRequestNumber, { - title: knowledgeFormData.submissionSummary - }); - - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${session.accessToken}`, - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28' - }; - - const githubUsername = await getGitHubUsername(headers); - console.log(`GitHub username: ${githubUsername}`); - - const knowledgeYamlData: KnowledgeYamlData = { - created_by: githubUsername!, - version: KnowledgeSchemaVersion, - domain: domainFromFilePath(knowledgeFormData.filePath), - document_outline: knowledgeFormData.submissionSummary, - seed_examples: knowledgeFormData.seedExamples.map((example) => ({ - context: example.context!, - questions_and_answers: example.questionAndAnswers.map((questionAndAnswer) => ({ - question: questionAndAnswer.question, - answer: questionAndAnswer.answer - })) - })), - document: { - repo: knowledgeFormData.knowledgeDocumentRepositoryUrl!, - commit: knowledgeFormData.knowledgeDocumentCommit!, - patterns: knowledgeFormData.documentName!.split(',').map((pattern) => pattern.trim()) - } - }; - - const yamlString = dumpYaml(knowledgeYamlData); - console.log('Updated knowledge YAML content:', yamlString); - - const attributionData: AttributionData = { - title_of_work: knowledgeFormData.titleWork!, - link_to_work: knowledgeFormData.linkWork!, - revision: knowledgeFormData.revision!, - license_of_the_work: knowledgeFormData.licenseWork!, - creator_names: knowledgeFormData.creators! - }; - const attributionContent = `Title of work: ${attributionData.title_of_work} -Link to work: ${attributionData.link_to_work} -Revision: ${attributionData.revision} -License of the work: ${attributionData.license_of_the_work} -Creator names: ${attributionData.creator_names} -`; - - console.log('Updated knowledge attribution content:', attributionData); - - const commitMessage = `Amend commit with updated content\n\nSigned-off-by: ${knowledgeFormData.name} <${knowledgeFormData.email}>`; - - // Ensure proper file paths for the edit - const finalYamlPath = KNOWLEDGE_DIR + knowledgeFormData.filePath.replace(/^\//, '').replace(/\/?$/, '/') + 'qna.yaml'; - const finalAttributionPath = KNOWLEDGE_DIR + knowledgeFormData.filePath.replace(/^\//, '').replace(/\/?$/, '/') + 'attribution.txt'; - - const oldFilePath = { - yaml: KNOWLEDGE_DIR + knowledgeEditFormData.oldFilesPath.replace(/^\//, '').replace(/\/?$/, '/') + 'qna.yaml', - attribution: KNOWLEDGE_DIR + knowledgeEditFormData.oldFilesPath.replace(/^\//, '').replace(/\/?$/, '/') + 'attribution.txt' - }; - - console.log('Knowledge update old file path : ', oldFilePath); - - const newFilePath = { - yaml: finalYamlPath, - attribution: finalAttributionPath - }; - console.log('Knowledge update new file path : ', oldFilePath); - - const waitForSubmissionAlert: ActionGroupAlertContent = { - title: 'Knowledge contribution update is in progress.!', - message: `Once the update is successful, it will provide the link to the updated Pull Request.`, - success: true, - waitAlert: true, - timeout: false - }; - setActionGroupAlertContent(waitForSubmissionAlert); - - const { upstreamRepoName, upstreamRepoOwner } = envConfig; - - const amendedCommitResponse = await amendCommit( - session.accessToken, - githubUsername, - upstreamRepoName, - oldFilePath, - newFilePath, - yamlString, - attributionContent, - knowledgeFormData.branchName, - commitMessage - ); - console.log('Amended commit response:', amendedCommitResponse); - - const prLink = `https://github.com/${upstreamRepoOwner}/${upstreamRepoName}/pull/${knowledgeEditFormData.pullRequestNumber}`; - const actionGroupAlertContent: ActionGroupAlertContent = { - title: 'Knowledge contribution updated successfully!', - message: `Thank you for your contribution!`, - url: `${prLink}`, - isUrlExternal: true, - success: true - }; - setActionGroupAlertContent(actionGroupAlertContent); - // Knowledge is updated, wait for a bit and let's go back to dashboard. - await new Promise((r) => setTimeout(r, 4000)); - } catch (error) { - console.error('Error updating PR:', error); - const actionGroupAlertContent: ActionGroupAlertContent = { - title: `Failed to update PR with number: ${knowledgeEditFormData.pullRequestNumber}`, - message: `PR update failed because of ${error}`, - success: false - }; - setActionGroupAlertContent(actionGroupAlertContent); - return false; - } - return true; - } - return false; -}; - -export const submitNativeSkillData = async ( +export const submitSkillData = async ( skillFormData: SkillFormData, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void + setActionGroupAlertContent: (content: ActionGroupAlertContent) => void, + skillEditFormData?: SkillEditFormData ): Promise => { - if (!validateSkillFormFields(skillFormData, setActionGroupAlertContent, true)) { + if (!validateSkillFormFields(skillFormData, setActionGroupAlertContent)) { return false; } @@ -440,10 +120,6 @@ export const submitNativeSkillData = async ( })) }; - console.log(`========== Submitting Skill ==============`); - console.log(`YamlData: `, skillYamlData); - console.log(`FormData: `, skillFormData); - const yamlString = dumpYaml(skillYamlData); const waitForSubmissionAlert: ActionGroupAlertContent = { @@ -457,20 +133,20 @@ export const submitNativeSkillData = async ( const { branchName, name, email, submissionSummary } = skillFormData; - const response = await fetch('/api/native/pr/skill/', { + const response = await fetch('/api/pr/skill/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - action: 'submit', + action: skillEditFormData ? 'update' : 'submit', branchName: branchName, content: yamlString, name, email, submissionSummary, filePath: sanitizedFilePath, - oldFilesPath: sanitizedFilePath + oldFilesPath: skillEditFormData ? skillEditFormData.oldFilesPath : sanitizedFilePath }) }); @@ -494,281 +170,3 @@ export const submitNativeSkillData = async ( setActionGroupAlertContent(actionGroupAlertContent); return true; }; - -export const submitGithubSkillData = async ( - skillFormData: SkillFormData, - githubUsername: string, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void -): Promise => { - if (!validateSkillFormFields(skillFormData, setActionGroupAlertContent, false)) { - return false; - } - - // Strip leading slash and ensure trailing slash in the file path - let sanitizedFilePath = skillFormData.filePath!.startsWith('/') ? skillFormData.filePath!.slice(1) : skillFormData.filePath; - sanitizedFilePath = sanitizedFilePath!.endsWith('/') ? sanitizedFilePath : `${sanitizedFilePath}/`; - - const skillYamlData: SkillYamlData = { - created_by: githubUsername!, - version: SkillSchemaVersion, - task_description: skillFormData.submissionSummary!, - seed_examples: skillFormData.seedExamples.map((example) => ({ - context: example.context, - question: example.questionAndAnswer.question, - answer: example.questionAndAnswer.answer - })) - }; - - const yamlString = dumpYaml(skillYamlData); - - const attributionData: AttributionData = { - title_of_work: skillFormData.titleWork!, - license_of_the_work: skillFormData.licenseWork!, - creator_names: skillFormData.creators!, - link_to_work: '', - revision: '' - }; - - const waitForSubmissionAlert: ActionGroupAlertContent = { - title: 'Skill contribution submission in progress.!', - message: `Once the submission is successful, it will provide the link to the newly created Pull Request.`, - success: true, - waitAlert: true, - timeout: false - }; - setActionGroupAlertContent(waitForSubmissionAlert); - - const { branchName, name, email, submissionSummary } = skillFormData; - const response = await fetch('/api/github/pr/skill', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - branchName: branchName, - content: yamlString, - attribution: attributionData, - name, - email, - submissionSummary, - filePath: sanitizedFilePath - }) - }); - - if (!response.ok) { - const actionGroupAlertContent: ActionGroupAlertContent = { - title: `Failed data submission`, - message: response.statusText, - success: false - }; - setActionGroupAlertContent(actionGroupAlertContent); - return false; - } - - const result = await response.json(); - const actionGroupAlertContent: ActionGroupAlertContent = { - title: 'Skill contribution submitted successfully!', - message: `Thank you for your contribution!`, - url: `${result.html_url}`, - isUrlExternal: true, - success: true - }; - setActionGroupAlertContent(actionGroupAlertContent); - return true; -}; - -export const updateNativeSkillData = async ( - skillFormData: SkillFormData, - skillEditFormData: SkillEditFormData, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void -): Promise => { - if (!validateSkillFormFields(skillFormData, setActionGroupAlertContent, true)) { - return false; - } - // Strip leading slash and ensure trailing slash in the file path - let sanitizedFilePath = skillFormData.filePath!.startsWith('/') ? skillFormData.filePath!.slice(1) : skillFormData.filePath; - sanitizedFilePath = sanitizedFilePath!.endsWith('/') ? sanitizedFilePath : `${sanitizedFilePath}/`; - - const skillYamlData: SkillYamlData = { - created_by: skillFormData.name, - version: SkillSchemaVersion, - task_description: skillFormData.submissionSummary, - seed_examples: skillFormData.seedExamples.map((example) => ({ - context: example.context, - question: example.questionAndAnswer.question, - answer: example.questionAndAnswer.answer - })) - }; - - const yamlString = dumpYaml(skillYamlData); - - const waitForSubmissionAlert: ActionGroupAlertContent = { - title: 'Skill contribution submission in progress!', - message: `Once the submission is successful, it will provide the link to the newly created Pull Request.`, - success: true, - waitAlert: true, - timeout: false - }; - setActionGroupAlertContent(waitForSubmissionAlert); - - const { branchName, name, email, submissionSummary } = skillFormData; - const { oldFilesPath } = skillEditFormData; - - const response = await fetch('/api/native/pr/skill/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - action: 'update', - branchName: branchName, - content: yamlString, - name, - email, - submissionSummary, - filePath: sanitizedFilePath, - oldFilesPath: oldFilesPath - }) - }); - - if (!response.ok) { - const actionGroupAlertContent: ActionGroupAlertContent = { - title: `Failed data submission`, - message: response.statusText, - success: false - }; - setActionGroupAlertContent(actionGroupAlertContent); - return false; - } - - await response.json(); - const actionGroupAlertContent: ActionGroupAlertContent = { - title: 'Skill contribution updated successfully!', - message: `Thank you for your contribution!`, - url: '/dashboard', - success: true - }; - setActionGroupAlertContent(actionGroupAlertContent); - return true; -}; - -export const updateGithubSkillData = async ( - session: Session | null, - envConfig: EnvConfigType, - skillFormData: SkillFormData, - skillEditFormData: SkillEditFormData, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void -): Promise => { - if (!validateSkillFormFields(skillFormData, setActionGroupAlertContent, false)) { - return false; - } - if (session?.accessToken) { - const { pullRequestNumber, oldFilesPath } = skillEditFormData; - try { - console.log(`Updating PR with number: ${pullRequestNumber}`); - await updatePullRequest(session.accessToken, envConfig, pullRequestNumber, { - title: skillFormData.submissionSummary - }); - - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${session.accessToken}`, - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28' - }; - const githubUsername = await getGitHubUsername(headers); - console.log(`GitHub username: ${githubUsername}`); - - const skillYamlData: SkillYamlData = { - created_by: githubUsername!, - version: SkillSchemaVersion, - task_description: skillFormData.submissionSummary, - seed_examples: skillFormData.seedExamples.map((example) => ({ - context: example.context, - question: example.questionAndAnswer.question, - answer: example.questionAndAnswer.answer - })) - }; - - const yamlString = dumpYaml(skillYamlData); - console.log('Updated YAML content:', yamlString); - - const attributionData: AttributionData = { - title_of_work: skillFormData.titleWork!, - license_of_the_work: skillFormData.licenseWork!, - creator_names: skillFormData.creators!, - link_to_work: '', - revision: '' - }; - const attributionContent = `Title of work: ${attributionData.title_of_work} -License of the work: ${attributionData.license_of_the_work} -Creator names: ${attributionData.creator_names} -`; - - console.log('Updated Attribution content:', attributionData); - - const commitMessage = `Amend commit with updated content\n\nSigned-off-by: ${skillFormData.name} <${skillFormData.email}>`; - - // Ensure proper file paths for the edit - const finalYamlPath = SKILLS_DIR + skillFormData.filePath.replace(/^\//, '').replace(/\/?$/, '/') + 'qna.yaml'; - const finalAttributionPath = SKILLS_DIR + skillFormData.filePath.replace(/^\//, '').replace(/\/?$/, '/') + 'attribution.txt'; - - const oldFilePath = { - yaml: SKILLS_DIR + oldFilesPath.replace(/^\//, '').replace(/\/?$/, '/') + 'qna.yaml', - attribution: SKILLS_DIR + oldFilesPath.replace(/^\//, '').replace(/\/?$/, '/') + 'attribution.txt' - }; - - const newFilePath = { - yaml: finalYamlPath, - attribution: finalAttributionPath - }; - - const { upstreamRepoName, upstreamRepoOwner } = envConfig; - - const waitForSubmissionAlert: ActionGroupAlertContent = { - title: 'Skill contribution update is in progress.!', - message: `Once the update is successful, it will provide the link to the updated Pull Request.`, - success: true, - waitAlert: true, - timeout: false - }; - setActionGroupAlertContent(waitForSubmissionAlert); - - const amendedCommitResponse = await amendCommit( - session.accessToken, - githubUsername, - upstreamRepoName, - oldFilePath, - newFilePath, - yamlString, - attributionContent, - skillFormData.branchName, - commitMessage - ); - console.log('Amended commit response:', amendedCommitResponse); - - const prLink = `https://github.com/${upstreamRepoOwner}/${upstreamRepoName}/pull/${pullRequestNumber}`; - const actionGroupAlertContent: ActionGroupAlertContent = { - title: 'Skill contribution updated successfully!', - message: `Thank you for your contribution!`, - url: `${prLink}`, - isUrlExternal: true, - success: true - }; - setActionGroupAlertContent(actionGroupAlertContent); - // Skill is updated, wait for a bit and let's go back to dashboard. - await new Promise((r) => setTimeout(r, 4000)); - } catch (error) { - console.error('Error updating PR:', error); - const actionGroupAlertContent: ActionGroupAlertContent = { - title: `Failed to update PR with number: ${pullRequestNumber}`, - message: `PR update failed because of ${error}`, - success: false - }; - setActionGroupAlertContent(actionGroupAlertContent); - return false; - } - return true; - } - return false; -}; diff --git a/src/components/Contribute/Utils/validation.ts b/src/components/Contribute/Utils/validation.ts index 513a684d..9a14d466 100644 --- a/src/components/Contribute/Utils/validation.ts +++ b/src/components/Contribute/Utils/validation.ts @@ -52,17 +52,16 @@ const validateQuestionAndAnswerPairs = (seedExample: KnowledgeSeedExample): { su return { success: true, currLength: totalLength }; }; -const nativeKnowledgeOptionalKeys = ['titleWork', 'linkWork', 'revision', 'licenseWork', 'creators']; +const knowledgeOptionalKeys = ['titleWork', 'linkWork', 'revision', 'licenseWork', 'creators']; export const validateKnowledgeFormFields = ( knowledgeFormData: KnowledgeFormData, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void, - isNativeMode: boolean + setActionGroupAlertContent: (content: ActionGroupAlertContent) => void ): boolean => { // validate that data has been entered into all fields for (const [key, value] of Object.entries(knowledgeFormData)) { if (!value) { - if (isNativeMode && nativeKnowledgeOptionalKeys.includes(key)) { + if (knowledgeOptionalKeys.includes(key)) { continue; } else { const actionGroupAlertContent: ActionGroupAlertContent = { @@ -158,7 +157,7 @@ export const validateKnowledgeFormFields = ( return true; }; -const nativeSkillOptionalKeys = ['titleWork', 'licenseWork', 'creators']; +const skillOptionalKeys = ['titleWork', 'licenseWork', 'creators']; const hasDuplicateSkillSeedExamples = (seedExamples: SkillSeedExample[]): { duplicate: boolean; index: number } => { const question = new Set(); @@ -175,13 +174,12 @@ const hasDuplicateSkillSeedExamples = (seedExamples: SkillSeedExample[]): { dupl export const validateSkillFormFields = ( skillFormData: SkillFormData, - setActionGroupAlertContent: (content: ActionGroupAlertContent) => void, - isNativeMode: boolean + setActionGroupAlertContent: (content: ActionGroupAlertContent) => void ): boolean => { // validate that data has been entered into all fields for (const [key, value] of Object.entries(skillFormData)) { if (!value) { - if (isNativeMode && nativeSkillOptionalKeys.includes(key)) { + if (skillOptionalKeys.includes(key)) { continue; } else { const actionGroupAlertContent: ActionGroupAlertContent = { diff --git a/src/components/Contribute/fetchUtils.ts b/src/components/Contribute/fetchUtils.ts index d56d9e17..1f1c49dc 100644 --- a/src/components/Contribute/fetchUtils.ts +++ b/src/components/Contribute/fetchUtils.ts @@ -1,15 +1,11 @@ -import axios from 'axios'; import yaml from 'js-yaml'; import { Session } from 'next-auth'; import { ValidatedOptions } from '@patternfly/react-core'; -import { fetchFileContent, fetchPullRequest, fetchPullRequestFiles } from '@/utils/github'; import { AttributionData, - EnvConfigType, KnowledgeEditFormData, KnowledgeFormData, KnowledgeYamlData, - PullRequestFile, QuestionAndAnswerPair, SkillEditFormData, SkillFormData, @@ -72,100 +68,12 @@ const updateKnowledgeFormDataFromAttributionData = (knowledgeExistingFormData: K knowledgeExistingFormData.creators = attributionData.creator_names; }; -export const fetchKnowledgePRData = async ( - session: Session | null, - envConfig: EnvConfigType, - prNumber: string -): Promise<{ editFormData?: KnowledgeEditFormData; error?: string }> => { - if (session?.accessToken) { - try { - const prNum = parseInt(prNumber, 10); - const prData = await fetchPullRequest(session.accessToken, envConfig, prNum); - - // Create KnowledgeFormData from existing form. - const knowledgeExistingFormData: KnowledgeFormData = { - branchName: '', - email: '', - name: '', - submissionSummary: '', - filePath: '', - seedExamples: [], - knowledgeDocumentRepositoryUrl: '', - knowledgeDocumentCommit: '', - documentName: '', - titleWork: '', - linkWork: '', - revision: '', - licenseWork: '', - creators: '', - filesToUpload: [], - uploadedFiles: [] - }; - - const knowledgeEditFormData: KnowledgeEditFormData = { - isEditForm: true, - isSubmitted: true, - version: KnowledgeSchemaVersion, - formData: knowledgeExistingFormData, - pullRequestNumber: prNum, - oldFilesPath: '' - }; - - knowledgeExistingFormData.submissionSummary = prData.title; - knowledgeExistingFormData.branchName = prData.head.ref; // Store the branch name from the pull request - - const prFiles: PullRequestFile[] = await fetchPullRequestFiles(session.accessToken, envConfig, prNum); - - const foundYamlFile = prFiles.find((file: PullRequestFile) => file.filename.endsWith('.yaml')); - if (!foundYamlFile) { - const errorMsg = 'No YAML file found in the pull request.'; - console.error('Error fetching pull request data: ', errorMsg); - return { error: 'Error fetching knowledge data from PR : ' + prNumber + ' [' + errorMsg + ']' + '. Please try again.' }; - } - const existingFilesPath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); - - // Set the current Yaml file path as a old files path - knowledgeEditFormData.oldFilesPath = existingFilesPath + '/'; - - const yamlContent = await fetchFileContent(session.accessToken, envConfig, foundYamlFile.filename, prData.head.sha); - const yamlData: KnowledgeYamlData = yaml.load(yamlContent) as KnowledgeYamlData; - updateKnowledgeFormDataFromYaml(knowledgeExistingFormData, yamlData); - - // Set the file path from the current YAML file (remove the root folder name from the path) - const currentFilePath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); - knowledgeEditFormData.formData.filePath = currentFilePath + '/'; - - // Fetch and parse attribution file if it exists - const foundAttributionFile = prFiles.find((file: PullRequestFile) => file.filename.includes('attribution')); - if (foundAttributionFile) { - const attributionContent = await fetchFileContent(session.accessToken, envConfig, foundAttributionFile.filename, prData.head.sha); - const attributionData = parseAttributionContent(attributionContent); - updateKnowledgeFormDataFromAttributionData(knowledgeExistingFormData, attributionData); - } - const existingFiles = await fetchExistingKnowledgeDocuments(true, knowledgeEditFormData.formData.knowledgeDocumentCommit); - if (existingFiles.length != 0) { - knowledgeExistingFormData.uploadedFiles.push(...existingFiles); - } - return { editFormData: knowledgeEditFormData }; - } catch (error) { - if (axios.isAxiosError(error)) { - console.error('Error fetching pull request data:', error.response ? error.response.data : error.message); - return { error: 'Error fetching knowledge data from PR : ' + prNumber + '. Please try again.' }; - } else if (error instanceof Error) { - console.error('Error fetching pull request data:', error.message); - return { error: 'Error fetching knowledge data from PR : ' + prNumber + ' [' + error.message + ']' + '. Please try again.' }; - } - } - } - return { error: 'Error fetching knowledge data from PR : ' + prNumber + '. Please try again.' }; -}; - export const fetchKnowledgeBranchChanges = async ( session: Session | null, branchName: string ): Promise<{ editFormData?: KnowledgeEditFormData; error?: string }> => { try { - const response = await fetch('/api/native/git/branches', { + const response = await fetch('/api/git/branches', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ branchName, action: 'diff' }) @@ -233,7 +141,7 @@ export const fetchKnowledgeBranchChanges = async ( } } }); - const existingFiles = await fetchExistingKnowledgeDocuments(false, knowledgeEditFormData.formData.knowledgeDocumentCommit); + const existingFiles = await fetchExistingKnowledgeDocuments(knowledgeEditFormData.formData.knowledgeDocumentCommit); if (existingFiles.length != 0) { console.log(`Contribution has ${existingFiles.length} existing knowledge documents`); knowledgeExistingFormData.uploadedFiles.push(...existingFiles); @@ -248,110 +156,12 @@ export const fetchKnowledgeBranchChanges = async ( return { error: 'Error fetching knowledge data from branch : ' + branchName + '. Please try again.' }; }; -export const fetchSkillPRData = async ( - session: Session | null, - envConfig: EnvConfigType, - prNumber: string -): Promise<{ editFormData?: SkillEditFormData; error?: string }> => { - if (session?.accessToken) { - try { - const prNum = parseInt(prNumber, 10); - const prData = await fetchPullRequest(session.accessToken, envConfig, prNum); - - const skillExistingFormData: SkillFormData = { - branchName: '', - email: '', - name: '', - submissionSummary: '', - filePath: '', - seedExamples: [], - titleWork: '', - licenseWork: '', - creators: '' - }; - - const skillEditFormData: SkillEditFormData = { - isEditForm: true, - isSubmitted: true, - version: SkillSchemaVersion, - formData: skillExistingFormData, - pullRequestNumber: prNum, - oldFilesPath: '' - }; - - skillExistingFormData.branchName = prData.head.ref; // Store the branch name from the pull request - - const prFiles: PullRequestFile[] = await fetchPullRequestFiles(session.accessToken, envConfig, prNum); - - const foundYamlFile = prFiles.find((file: PullRequestFile) => file.filename.endsWith('.yaml')); - if (!foundYamlFile) { - const errorMsg = 'No YAML file found in the pull request.'; - console.error('Error fetching pull request data:', errorMsg); - return { error: 'Error fetching skills data from PR: ' + prNumber + ' [' + errorMsg + ']. Please try again.' }; - } - - const existingFilesPath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); - - // Set the current Yaml file path as a old files path - skillEditFormData.oldFilesPath = existingFilesPath + '/'; - - const yamlContent = await fetchFileContent(session.accessToken, envConfig, foundYamlFile.filename, prData.head.sha); - const yamlData: SkillYamlData = yaml.load(yamlContent) as SkillYamlData; - console.log('Parsed YAML data:', yamlData); - - // Populate the form fields with YAML data - skillExistingFormData.submissionSummary = yamlData.task_description; - - skillExistingFormData.seedExamples = yamlData.seed_examples.map((seed, index) => ({ - immutable: index < 5, - isExpanded: true, - context: seed.context, - isContextValid: ValidatedOptions.success, - questionAndAnswer: { - immutable: index < 5, - question: seed.question, - isQuestionValid: ValidatedOptions.success, - answer: seed.answer, - isAnswerValid: ValidatedOptions.success - } - })); - - // Set the file path from the current YAML file - const currentFilePath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); - skillEditFormData.formData.filePath = currentFilePath + '/'; - - // Fetch and parse attribution file if it exists - const foundAttributionFile = prFiles.find((file: PullRequestFile) => file.filename.includes('attribution')); - if (foundAttributionFile) { - const attributionContent = await fetchFileContent(session.accessToken, envConfig, foundAttributionFile.filename, prData.head.sha); - const attributionData = parseAttributionContent(attributionContent); - console.log('Parsed attribution data:', attributionData); - - // Populate the form fields with attribution data - skillExistingFormData.titleWork = attributionData.title_of_work; - skillExistingFormData.licenseWork = attributionData.license_of_the_work; - skillExistingFormData.creators = attributionData.creator_names; - } - return { editFormData: skillEditFormData }; - } catch (error) { - if (axios.isAxiosError(error)) { - console.error('Error fetching pull request data:', error.response ? error.response.data : error.message); - return { error: 'Error fetching knowledge data from PR : ' + prNumber + '. Please try again.' }; - } else if (error instanceof Error) { - console.error('Error fetching pull request data:', error.message); - return { error: 'Error fetching skills data from PR: ' + prNumber + ' [' + error.message + ']. Please try again.' }; - } - } - } - return { error: 'Error fetching knowledge data from PR : ' + prNumber + '. Please try again.' }; -}; - export const fetchSkillBranchChanges = async ( session: Session | null, branchName: string ): Promise<{ editFormData?: SkillEditFormData; error?: string }> => { try { - const response = await fetch('/api/native/git/branches', { + const response = await fetch('/api/git/branches', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ branchName, action: 'diff' }) diff --git a/src/components/Dashboard/ContributionActions.tsx b/src/components/Dashboard/ContributionActions.tsx index 1b38239c..2a633b22 100644 --- a/src/components/Dashboard/ContributionActions.tsx +++ b/src/components/Dashboard/ContributionActions.tsx @@ -34,7 +34,7 @@ interface Props { const ContributionActions: React.FC = ({ contribution, onUpdateContributions, addAlert }) => { const router = useRouter(); const { - envConfig: { isGithubMode, taxonomyRootDir } + envConfig: { taxonomyRootDir } } = useEnvConfig(); const [isActionMenuOpen, setIsActionMenuOpen] = React.useState(false); const [isChangeModalOpen, setIsChangeModalOpen] = React.useState(false); @@ -50,13 +50,13 @@ const ContributionActions: React.FC = ({ contribution, onUpdateContributi const handleEditContribution = () => { router.push( - `/contribute/${contribution.isKnowledge ? 'knowledge' : 'skill'}/edit/${isGithubMode ? 'github' : 'native'}/${contribution.branchName}${contribution.isDraft ? '/isDraft' : ''}` + `/contribute/${contribution.isKnowledge ? 'knowledge' : 'skill'}/edit/${contribution.branchName}${contribution.isDraft ? '/isDraft' : ''}` ); }; const deleteContribution = async (branchName: string) => { try { - const response = await fetch('/api/native/git/branches', { + const response = await fetch('/api/git/branches', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ branchName, action: 'delete' }) @@ -98,7 +98,7 @@ const ContributionActions: React.FC = ({ contribution, onUpdateContributi const handlePublishContribution = async () => { setIsPublishing(true); try { - const response = await fetch('/api/native/git/branches', { + const response = await fetch('/api/git/branches', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ branchName: contribution.branchName, action: 'publish' }) @@ -186,7 +186,6 @@ const ContributionActions: React.FC = ({ contribution, onUpdateContributi setIsDownloadOpen(true); handleTaxonomyDownload({ branchName: contribution.branchName, - isGithubMode: false, setIsDownloadDone: (done) => setIsDownloadOpen(!done) }); }} @@ -196,7 +195,7 @@ const ContributionActions: React.FC = ({ contribution, onUpdateContributi {contribution.isDraft ? ( - setIsDeleteModalOpen(true)}> + setIsDeleteModalOpen(true)}> Delete draft ) : null} diff --git a/src/components/Dashboard/ContributionCard.tsx b/src/components/Dashboard/ContributionCard.tsx index f08f0877..b69a70d1 100644 --- a/src/components/Dashboard/ContributionCard.tsx +++ b/src/components/Dashboard/ContributionCard.tsx @@ -17,7 +17,6 @@ import { } from '@patternfly/react-core'; import { t_global_color_disabled_100 as DisabledColor } from '@patternfly/react-tokens'; import { ContributionInfo } from '@/types'; -import { useEnvConfig } from '@/context/EnvConfigContext'; import TruncatedText from '@/components/Common/TruncatedText'; import TableRowTitleDescription from '@/components/Table/TableRowTitleDescription'; import { getTaxonomyDir, getFormattedLastUpdatedDate } from '@/components/Dashboard/const'; @@ -33,9 +32,6 @@ interface Props { const ContributionCard: React.FC = ({ contribution, onUpdateContributions, addAlert }) => { const router = useRouter(); - const { - envConfig: { isGithubMode } - } = useEnvConfig(); return ( @@ -62,7 +58,7 @@ const ContributionCard: React.FC = ({ contribution, onUpdateContributions isInline onClick={() => router.push( - `/contribute/${contribution.isKnowledge ? 'knowledge' : 'skill'}/${isGithubMode ? 'github' : 'native'}/${contribution.branchName}${contribution.isDraft ? '/isDraft' : ''}` + `/contribute/${contribution.isKnowledge ? 'knowledge' : 'skill'}/${contribution.branchName}${contribution.isDraft ? '/isDraft' : ''}` ) } > diff --git a/src/components/Dashboard/ContributionChangesModal.tsx b/src/components/Dashboard/ContributionChangesModal.tsx index 4f856ce3..3fd8a0e7 100644 --- a/src/components/Dashboard/ContributionChangesModal.tsx +++ b/src/components/Dashboard/ContributionChangesModal.tsx @@ -24,7 +24,7 @@ const ContributionChangesModel: React.FC = ({ contribution, onClose }) => React.useEffect(() => { const fetchChangeData = async () => { try { - const response = await fetch('/api/native/git/branches', { + const response = await fetch('/api/git/branches', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ branchName: contribution.branchName, action: 'diff' }) diff --git a/src/components/Dashboard/ContributionTableRow.tsx b/src/components/Dashboard/ContributionTableRow.tsx index c89414e7..ebe7575c 100644 --- a/src/components/Dashboard/ContributionTableRow.tsx +++ b/src/components/Dashboard/ContributionTableRow.tsx @@ -4,7 +4,6 @@ import { Button, Flex, FlexItem } from '@patternfly/react-core'; import { Tr, Td } from '@patternfly/react-table'; import { t_global_color_disabled_100 as DisabledColor } from '@patternfly/react-tokens/dist/esm/t_global_color_disabled_100'; import { ContributionInfo } from '@/types'; -import { useEnvConfig } from '@/context/EnvConfigContext'; import TruncatedText from '@/components/Common/TruncatedText'; import TableRowTitleDescription from '@/components/Table/TableRowTitleDescription'; import { getTaxonomyDir, getFormattedLastUpdatedDate } from '@/components/Dashboard/const'; @@ -20,9 +19,6 @@ interface Props { const ContributionTableRow: React.FC = ({ contribution, onUpdateContributions, addAlert }) => { const router = useRouter(); - const { - envConfig: { isGithubMode } - } = useEnvConfig(); return ( @@ -36,7 +32,7 @@ const ContributionTableRow: React.FC = ({ contribution, onUpdateContribut isInline onClick={() => router.push( - `/contribute/${contribution.isKnowledge ? 'knowledge' : 'skill'}/${isGithubMode ? 'github' : 'native'}/${contribution.branchName}${contribution.isDraft ? '/isDraft' : ''}` + `/contribute/${contribution.isKnowledge ? 'knowledge' : 'skill'}/${contribution.branchName}${contribution.isDraft ? '/isDraft' : ''}` ) } > diff --git a/src/components/Dashboard/Native/DashboardPage.tsx b/src/components/Dashboard/DashboardPage.tsx similarity index 93% rename from src/components/Dashboard/Native/DashboardPage.tsx rename to src/components/Dashboard/DashboardPage.tsx index 6d910a5f..fe214781 100644 --- a/src/components/Dashboard/Native/DashboardPage.tsx +++ b/src/components/Dashboard/DashboardPage.tsx @@ -1,4 +1,4 @@ -// src/components/Dashboard/Native/DashboardPage.tsx +// src/components/Dashboard/DashboardPage.tsx import * as React from 'react'; import { AlertProps } from '@patternfly/react-core'; import { v4 as uuidv4 } from 'uuid'; @@ -10,7 +10,7 @@ import Dashboard, { AlertItem } from '@/components/Dashboard/Dashboard'; const fetchBranchTaxonomy = async (branchName: string) => { let taxonomy = ''; try { - const response = await fetch('/api/native/git/branches', { + const response = await fetch('/api/git/branches', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ branchName, action: 'diff' }) @@ -37,9 +37,9 @@ const fetchBranchTaxonomy = async (branchName: string) => { return taxonomy; }; -const cloneNativeTaxonomyRepo = async (): Promise => { +const cloneTaxonomyRepo = async (): Promise => { try { - const response = await fetch('/api/native/clone-repo', { + const response = await fetch('/api/clone-repo', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); @@ -57,7 +57,7 @@ const cloneNativeTaxonomyRepo = async (): Promise => { } }; -const DashboardNative: React.FunctionComponent = () => { +const DashboardPage: React.FunctionComponent = () => { const { featureFlags: { skillFeaturesEnabled } } = useFeatureFlags(); @@ -80,7 +80,7 @@ const DashboardNative: React.FunctionComponent = () => { const fetchBranches = React.useCallback(async () => { try { - const response = await fetch('/api/native/git/branches'); + const response = await fetch('/api/git/branches'); const result = await response.json(); if (response.ok) { // Filter out 'main' branch @@ -105,7 +105,7 @@ const DashboardNative: React.FunctionComponent = () => { React.useEffect(() => { let refreshIntervalId: NodeJS.Timeout; - cloneNativeTaxonomyRepo().then((success) => { + cloneTaxonomyRepo().then((success) => { if (success) { fetchBranches().then(() => { setIsLoading(false); @@ -182,4 +182,4 @@ const DashboardNative: React.FunctionComponent = () => { ); }; -export { DashboardNative }; +export default DashboardPage; diff --git a/src/components/Dashboard/Github/DashboardPage.tsx b/src/components/Dashboard/Github/DashboardPage.tsx deleted file mode 100644 index 9dd50e79..00000000 --- a/src/components/Dashboard/Github/DashboardPage.tsx +++ /dev/null @@ -1,142 +0,0 @@ -// src/components/Dashboard/Github/DashboardPage.tsx -import * as React from 'react'; -import { useSession } from 'next-auth/react'; -import { v4 as uuidv4 } from 'uuid'; -import { ContributionInfo, DraftEditFormInfo, EnvConfigType, PullRequest, PullRequestFile } from '@/types'; -import { useState } from 'react'; -import { AlertProps } from '@patternfly/react-core'; -import { useEnvConfig } from '@/context/EnvConfigContext'; -import { fetchDraftContributions } from '@/components/Contribute/Utils/autoSaveUtils'; -import { fetchPullRequestFiles, fetchPullRequests, getGitHubUsername } from '@/utils/github'; -import Dashboard, { AlertItem } from '@/components/Dashboard/Dashboard'; - -const fetchPrTaxonomy = async (accessToken: string, envConfig: EnvConfigType, prNumber: number) => { - try { - const prFiles: PullRequestFile[] = await fetchPullRequestFiles(accessToken, envConfig, prNumber); - - const foundYamlFile = prFiles.find((file: PullRequestFile) => file.filename.includes('qna.yaml')); - if (foundYamlFile) { - const currentFilePath = foundYamlFile.filename.split('/').slice(1, -1).join('/'); - return currentFilePath + '/'; - } - - const errorMsg = 'No YAML file found in the pull request.'; - console.error('Error fetching pull request data: ', errorMsg); - } catch (error) { - console.error('Error fetching branch changes:', error); - } - - return ''; -}; - -const DashboardGithub: React.FunctionComponent = () => { - const { data: session } = useSession(); - const { envConfig } = useEnvConfig(); - const [pullRequests, setPullRequests] = React.useState([]); - const [draftContributions, setDraftContributions] = React.useState([]); - const [isLoading, setIsLoading] = useState(true); - const [alerts, setAlerts] = React.useState([]); - - const addAlert = React.useCallback((title: string, variant: AlertProps['variant']) => { - const alertKey = uuidv4(); - const newAlert: AlertItem = { title, variant, key: alertKey }; - setAlerts((prevAlerts) => [...prevAlerts, newAlert]); - }, []); - - const removeAlert = (alertToRemove: AlertItem) => { - setAlerts((prevAlerts) => prevAlerts.filter((alert) => alert.key !== alertToRemove.key)); - }; - - const fetchAndSetPullRequests = React.useCallback(async () => { - if (session?.accessToken) { - try { - const header = { - Authorization: `Bearer ${session.accessToken}`, - Accept: 'application/vnd.github.v3+json' - }; - const fetchedUsername = await getGitHubUsername(header); - const data = await fetchPullRequests(session.accessToken, envConfig); - const filteredPRs = data.filter( - (pr: PullRequest) => pr.user.login === fetchedUsername && pr.labels.some((label) => label.name === 'skill' || label.name === 'knowledge') - ); - for (const pr of filteredPRs) { - pr.taxonomy = await fetchPrTaxonomy(session.accessToken, envConfig, pr.number); - } - - // Sort by date (newest first) - const sortedPRs = filteredPRs.sort((a: PullRequest, b: PullRequest) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); - setPullRequests(sortedPRs); - } catch (error) { - console.error('Failed to fetch pull requests.' + error); - addAlert('Error fetching pull requests.', 'danger'); - } - } - }, [addAlert, session?.accessToken, envConfig]); - - React.useEffect(() => { - fetchAndSetPullRequests().then(() => { - setIsLoading(false); - }); - - const intervalId = setInterval(fetchAndSetPullRequests, 60000); - - return () => clearInterval(intervalId); - }, [fetchAndSetPullRequests]); - - React.useEffect(() => { - // Fetch all the draft contributions and mark them submitted if present in the pull requests - const drafts = fetchDraftContributions().map((draft: DraftEditFormInfo) => ({ - ...draft, - isSubmitted: pullRequests.some((pr) => pr.head.ref === draft.branchName) - })); - - setDraftContributions(drafts); - }, [pullRequests]); - - const contributions: ContributionInfo[] = React.useMemo( - () => [ - ...draftContributions - .filter((draft) => !pullRequests.find((pr) => pr.head.ref === draft.branchName)) - .map((draft, index) => ({ - branchName: draft.branchName, - title: draft.title || `Untitled ${draft.isKnowledgeDraft ? 'knowledge' : 'skill'} contribution ${index + 1}`, - author: draft.author, - lastUpdated: new Date(draft.lastUpdated), - isDraft: true, - isKnowledge: draft.isKnowledgeDraft, - isSubmitted: draft.isSubmitted, - state: 'draft', - taxonomy: draft.taxonomy - })), - ...pullRequests.map((pr) => ({ - branchName: `${pr.head.ref}`, - title: pr.title, - author: '', - lastUpdated: new Date(pr.updated_at), - isDraft: !!draftContributions.find((draft) => draft.branchName == pr.head.ref), - isKnowledge: pr.labels.some((label) => label.name === 'knowledge'), - isSubmitted: true, - state: 'Available', - taxonomy: pr.taxonomy - })) - ], - [pullRequests, draftContributions] - ); - - const onUpdateContributions = () => { - fetchAndSetPullRequests(); - }; - - return ( - - ); -}; - -export { DashboardGithub }; diff --git a/src/components/GithubAccessPopup/index.tsx b/src/components/GithubAccessPopup/index.tsx deleted file mode 100644 index 04e2af3f..00000000 --- a/src/components/GithubAccessPopup/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -// src/app/dashboard/page.tsx -'use client'; - -import * as React from 'react'; -import { signOut } from 'next-auth/react'; -import { Modal, ModalVariant, Button, ModalBody, ModalFooter, ModalHeader } from '@patternfly/react-core'; -import { useEnvConfig } from '@/context/EnvConfigContext'; - -interface Props { - onAccept: () => void; -} -const GithubAccessPopup: React.FunctionComponent = ({ onAccept }) => { - const { - envConfig: { isGithubMode, isDevMode }, - loaded - } = useEnvConfig(); - const [isOpen, setIsOpen] = React.useState(false); - - React.useEffect(() => { - if (loaded && (!isGithubMode || isDevMode)) { - setIsOpen(false); - onAccept(); - } else { - setIsOpen(true); - } - }, [isDevMode, isGithubMode, loaded, onAccept]); - - const setDecisionAndClose = () => { - setIsOpen(false); - onAccept(); - }; - - if (!isOpen) { - return null; - } - - return ( - setIsOpen(false)} - aria-labelledby="github-access-warn-modal-title" - aria-describedby="github-access-warn-body-variant" - > - - -

- To allow InstructLab UI to manage your taxonomy submissions, you must grant read and write permissions to your GitHub account. InstructLab - UI will use your account to: -
-
-

  • - Pull PRs from the upstream{' '} - - InstructLab Taxonomy repo - {' '} - that you’ve opened to contribute skills and knowledge. -
  • -
  • - Fork the InstructLab Taxonomy repo to your GitHub account and create PRs from this fork, which can merge into the upstream Taxonomy repo. -
  • -
  • - Fork the{' '} - - taxonomy-knowledge-docs repo - {' '} - to your GitHub account and upload knowledge-related documents from your account. -
  • -
    - These permissions do not enable InstructLab UI to access your GitHub password. -
    -
    - To provide InstructLab UI with the permissions necessary for managing your taxonomy submissions, select accept. If you do not wish to - grant these permissions, select deny to sign out of InstructLab UI. -
    -

    -
    - - - - -
    - ); -}; - -export { GithubAccessPopup }; diff --git a/src/components/PathService/AddDirectoryModal.tsx b/src/components/PathService/AddDirectoryModal.tsx index bd8d69f6..9215c093 100644 --- a/src/components/PathService/AddDirectoryModal.tsx +++ b/src/components/PathService/AddDirectoryModal.tsx @@ -2,7 +2,6 @@ 'use client'; import React, { FormEvent } from 'react'; import { - Alert, Button, Content, Flex, @@ -19,12 +18,11 @@ import { } from '@patternfly/react-core'; interface Props { - isGithubMode: boolean; parentPath: string; onClose: (newDirectory?: string) => void; } -export const AddDirectoryModal: React.FunctionComponent = ({ isGithubMode, parentPath, onClose }) => { +export const AddDirectoryModal: React.FunctionComponent = ({ parentPath, onClose }) => { const [directoryPath, setDirectoryPath] = React.useState(''); const [validDirectoryPath, setValidDirectoryPath] = React.useState(ValidatedOptions.default); const [touched, setTouched] = React.useState(); @@ -68,17 +66,6 @@ export const AddDirectoryModal: React.FunctionComponent = ({ isGithubMode This will create a new folder in the taxonomy. - {isGithubMode ? ( - - - - ) : null}
    diff --git a/src/components/PathService/PathService.tsx b/src/components/PathService/PathService.tsx index 47ac4ea3..a1c7f192 100644 --- a/src/components/PathService/PathService.tsx +++ b/src/components/PathService/PathService.tsx @@ -68,13 +68,9 @@ interface PathServiceProps { path?: string; handlePathChange: (value: string) => void; helperText: string; - isGithubMode: boolean; } -const GITHUB_TAXONOMY_TREE_URL = '/api/github/tree'; -const NATIVE_TAXONOMY_TREE_URL = '/api/native/tree'; - -const PathService: React.FC = ({ fieldId, rootPath, path, handlePathChange, helperText, isGithubMode }) => { +const PathService: React.FC = ({ fieldId, rootPath, path, handlePathChange, helperText }) => { const [topLevelItems, setTopLevelItems] = React.useState([]); const [treeData, setTreeData] = React.useState([]); const [addDirectoryPath, setAddDirectoryPath] = React.useState(null); @@ -88,7 +84,7 @@ const PathService: React.FC = ({ fieldId, rootPath, path, hand const fetchChildren = React.useCallback( async (subpath: string = ''): Promise => { try { - const response = await fetch(isGithubMode ? GITHUB_TAXONOMY_TREE_URL : NATIVE_TAXONOMY_TREE_URL, { + const response = await fetch('/api/tree', { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -116,7 +112,7 @@ const PathService: React.FC = ({ fieldId, rootPath, path, hand return []; } }, - [rootPath, isGithubMode] + [rootPath] ); React.useEffect(() => { @@ -388,7 +384,6 @@ const PathService: React.FC = ({ fieldId, rootPath, path, hand {addDirectoryPath !== null ? ( { if (newDirectory) { diff --git a/src/context/EnvConfigContext.tsx b/src/context/EnvConfigContext.tsx index 288dda0a..c64fcbf8 100644 --- a/src/context/EnvConfigContext.tsx +++ b/src/context/EnvConfigContext.tsx @@ -31,7 +31,6 @@ export const EnvConfigProvider: React.FC<{ children: React.ReactNode }> = ({ chi React.useEffect(() => { fetchEnvConfig().then((newConfig) => { devLog(`======== Env Config ============`); - devLog(` isGithubMode: `, newConfig.isGithubMode); devLog(` isDevMode: `, newConfig.isDevMode); setCurrentState({ loaded: true, envConfig: newConfig }); diff --git a/src/types/index.ts b/src/types/index.ts index 8cb8d702..3253f99e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -203,7 +203,6 @@ export interface EnvConfigType { taxonomyRootDir: string; taxonomyKnowledgeDocumentRepo: string; apiServer: string; - isGithubMode: boolean; isDevMode: boolean; } diff --git a/src/utils/envConfigService.ts b/src/utils/envConfigService.ts index 711a2190..33746da0 100644 --- a/src/utils/envConfigService.ts +++ b/src/utils/envConfigService.ts @@ -15,7 +15,6 @@ export const fetchEnvConfig = async (): Promise => { taxonomyRootDir: envConfig.TAXONOMY_ROOT_DIR, taxonomyKnowledgeDocumentRepo: envConfig.TAXONOMY_KNOWLEDGE_DOCUMENT_REPO, apiServer: envConfig.API_SERVER, - isGithubMode: envConfig.DEPLOYMENT_TYPE !== 'native', isDevMode: envConfig.ENABLE_DEV_MODE === 'true' }; } catch (error) { @@ -30,7 +29,6 @@ export const fetchEnvConfig = async (): Promise => { taxonomyRootDir: '', taxonomyKnowledgeDocumentRepo: '', apiServer: '', - isGithubMode: false, isDevMode: false }; } diff --git a/src/utils/taxonomy.ts b/src/utils/taxonomy.ts index af6b9df5..d5395182 100644 --- a/src/utils/taxonomy.ts +++ b/src/utils/taxonomy.ts @@ -1,14 +1,10 @@ // src/utils/taxonomy.ts -const GITHUB_TAXONOMY_DOWNLOAD_URL = '/api/github/download'; -const NATIVE_TAXONOMY_DOWNLOAD_URL = '/api/native/download'; - interface TaxonomyDownloadProp { branchName: string; - isGithubMode: boolean; setIsDownloadDone: (isDownloadDone: boolean) => void; } -export async function handleTaxonomyDownload({ branchName, isGithubMode, setIsDownloadDone }: TaxonomyDownloadProp) { - const res = await fetch(isGithubMode ? GITHUB_TAXONOMY_DOWNLOAD_URL : NATIVE_TAXONOMY_DOWNLOAD_URL, { +export async function handleTaxonomyDownload({ branchName, setIsDownloadDone }: TaxonomyDownloadProp) { + const res = await fetch('/api/download', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({