diff --git a/.env.example b/.env.example index 02fa3123..af228663 100644 --- a/.env.example +++ b/.env.example @@ -12,3 +12,5 @@ GITHUB_TOKEN=<TOKEN FOR OAUTH INSTRUCTLAB MEMBER LOOKUP> TAXONOMY_REPO_OWNER=<GITHUB_ACCOUNT> TAXONOMY_REPO=<REPO_NAME> TAXONOMY_DOCUMENTS_REPO=github.com/<USER_ID>/<REPO_NAME> +NEXT_PUBLIC_TAXONOMY_REPO_OWNER=<GITHUB_ACCOUNT> +NEXT_PUBLIC_TAXONOMY_REPO=<REPO_NAME> diff --git a/src/app/api/pr/knowledge/route.ts b/src/app/api/pr/knowledge/route.ts index 6508e612..66b24abc 100644 --- a/src/app/api/pr/knowledge/route.ts +++ b/src/app/api/pr/knowledge/route.ts @@ -5,8 +5,8 @@ import { NextRequest } from 'next/server'; import yaml from 'js-yaml'; const GITHUB_API_URL = 'https://api.github.com'; -const UPSTREAM_REPO_OWNER = process.env.TAXONOMY_REPO_OWNER!; -const UPSTREAM_REPO_NAME = process.env.TAXONOMY_REPO!; +const UPSTREAM_REPO_OWNER = process.env.NEXT_PUBLIC_TAXONOMY_REPO_OWNER!; +const UPSTREAM_REPO_NAME = process.env.NEXT_PUBLIC_TAXONOMY_REPO!; const BASE_BRANCH = 'main'; export async function POST(req: NextRequest) { diff --git a/src/app/api/pr/skill/route.ts b/src/app/api/pr/skill/route.ts index beaa47cc..d691b9f9 100644 --- a/src/app/api/pr/skill/route.ts +++ b/src/app/api/pr/skill/route.ts @@ -5,8 +5,8 @@ import { NextRequest } from 'next/server'; import yaml from 'js-yaml'; const GITHUB_API_URL = 'https://api.github.com'; -const UPSTREAM_REPO_OWNER = process.env.TAXONOMY_REPO_OWNER!; -const UPSTREAM_REPO_NAME = process.env.TAXONOMY_REPO!; +const UPSTREAM_REPO_OWNER = process.env.NEXT_PUBLIC_TAXONOMY_REPO_OWNER!; +const UPSTREAM_REPO_NAME = process.env.NEXT_PUBLIC_TAXONOMY_REPO!; const BASE_BRANCH = 'main'; export async function POST(req: NextRequest) { diff --git a/src/app/edit-submission/knowledge/[id]/page.tsx b/src/app/edit-submission/knowledge/[id]/page.tsx index c414706c..e8b4ad75 100644 --- a/src/app/edit-submission/knowledge/[id]/page.tsx +++ b/src/app/edit-submission/knowledge/[id]/page.tsx @@ -317,7 +317,7 @@ Creator names: ${creators} <AppLayout> <PageSection> <Title headingLevel="h1" size="lg"> - Edit Pull Request + Edit Knowledge Submission </Title> {error && <Alert variant="danger" title={error} />} <Form> @@ -389,7 +389,7 @@ Creator names: ${creators} header={ <FormFieldGroupHeader titleText={{ text: 'Knowledge', id: 'contrib-knowledge-id' }} - titleDescription="Contribute new knowledge to the taxonomy repository." + titleDescription="Contribute knowledge to the taxonomy repository (shift+enter for a new line)." /> } > diff --git a/src/app/edit-submission/skill/[id]/page.tsx b/src/app/edit-submission/skill/[id]/page.tsx index b7c354eb..84677dbf 100644 --- a/src/app/edit-submission/skill/[id]/page.tsx +++ b/src/app/edit-submission/skill/[id]/page.tsx @@ -258,7 +258,7 @@ Creator names: ${creators} <AppLayout> <PageSection> <Title headingLevel="h1" size="lg"> - Edit Pull Request + Edit Skill Submission </Title> {error && <Alert variant="danger" title={error} />} <Form> @@ -319,7 +319,7 @@ Creator names: ${creators} header={ <FormFieldGroupHeader titleText={{ text: 'Skill', id: 'contrib-skill-id' }} - titleDescription="Contribute new skill to the taxonomy repository." + titleDescription="Contribute skill to the taxonomy repository (shift+enter for a new line)." /> } > diff --git a/src/components/Contribute/Knowledge/index.tsx b/src/components/Contribute/Knowledge/index.tsx index 48210711..45e2a5b6 100644 --- a/src/components/Contribute/Knowledge/index.tsx +++ b/src/components/Contribute/Knowledge/index.tsx @@ -1,6 +1,6 @@ // src/components/Contribute/Knowledge/index.tsx 'use client'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import './knowledge.css'; import { Alert, AlertActionLink, AlertActionCloseButton } from '@patternfly/react-core/dist/dynamic/components/Alert'; import { ActionGroup, FormFieldGroupExpandable, FormFieldGroupHeader } from '@patternfly/react-core/dist/dynamic/components/Form'; @@ -10,13 +10,33 @@ import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextIn import { Form } from '@patternfly/react-core/dist/dynamic/components/Form'; import { FormGroup } from '@patternfly/react-core/dist/dynamic/components/Form'; import { TextArea } from '@patternfly/react-core/dist/dynamic/components/TextArea'; -import { PlusIcon, MinusCircleIcon } from '@patternfly/react-icons/dist/dynamic/icons/'; +import { PlusIcon, MinusCircleIcon, CodeIcon } from '@patternfly/react-icons/dist/dynamic/icons/'; import yaml from 'js-yaml'; import { validateFields, validateEmail, validateUniqueItems } from '../../../utils/validation'; +import { getGitHubUsername } from '../../../utils/github'; +import { useSession } from 'next-auth/react'; +import YamlCodeModal from '../../YamlCodeModal'; import { UploadFile } from './UploadFile'; export const KnowledgeForm: React.FunctionComponent = () => { - // Define the initial state and type + const { data: session } = useSession(); + const [githubUsername, setGithubUsername] = useState<string | null>(null); + + useEffect(() => { + const fetchUsername = async () => { + if (session?.accessToken) { + try { + const fetchedUsername = await getGitHubUsername(session.accessToken); + setGithubUsername(fetchedUsername); + } catch (error) { + console.error('Failed to fetch GitHub username:', error); + } + } + }; + + fetchUsername(); + }, [session?.accessToken]); + const [email, setEmail] = useState(''); const [name, setName] = useState(''); const [task_description, setTaskDescription] = useState(''); @@ -48,6 +68,9 @@ export const KnowledgeForm: React.FunctionComponent = () => { const [useFileUpload, setUseFileUpload] = useState(false); const [uploadedFiles, setUploadedFiles] = useState<File[]>([]); + const [isModalOpen, setIsModalOpen] = useState(false); + const [yamlContent, setYamlContent] = useState(''); + const handleInputChange = (index: number, type: string, value: string) => { switch (type) { case 'question': @@ -308,7 +331,7 @@ export const KnowledgeForm: React.FunctionComponent = () => { } const yamlData = { - created_by: email, + created_by: githubUsername, domain: domain, task_description: task_description, seed_examples: questions.map( @@ -363,8 +386,36 @@ Creator names: ${creators} document.body.removeChild(a); }; + const handleViewYaml = () => { + const yamlData = { + created_by: githubUsername, + domain: domain, + task_description: task_description, + seed_examples: questions.map((question, index) => ({ + question, + answer: answers[index] + })), + document: { + repo: repo, + commit: commit, + patterns: patterns.split(',').map((pattern) => pattern.trim()) + } + }; + + const yamlString = yaml.dump(yamlData, { lineWidth: -1 }); + setYamlContent(yamlString); + setIsModalOpen(true); + }; + return ( <Form className="form-k"> + <YamlCodeModal isModalOpen={isModalOpen} handleModalToggle={() => setIsModalOpen(!isModalOpen)} yamlContent={yamlContent} /> + <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> + <FormFieldGroupHeader titleText={{ text: 'Knowledge Contribution Form', id: 'knowledge-contribution-form-id' }} /> + <Button variant="plain" onClick={handleViewYaml} aria-label="View YAML"> + <CodeIcon /> View YAML + </Button> + </div> <FormFieldGroupExpandable isExpanded toggleAriaLabel="Details" @@ -438,7 +489,7 @@ Creator names: ${creators} header={ <FormFieldGroupHeader titleText={{ text: 'Knowledge', id: 'contrib-knowledge-id' }} - titleDescription="Contribute new knowledge to the taxonomy repository." + titleDescription="Contribute knowledge to the taxonomy repository (shift+enter for a new line)." /> } > diff --git a/src/components/Contribute/Skill/index.tsx b/src/components/Contribute/Skill/index.tsx index 1c69be8e..fbb0ed43 100644 --- a/src/components/Contribute/Skill/index.tsx +++ b/src/components/Contribute/Skill/index.tsx @@ -1,6 +1,6 @@ // src/components/Contribute/Skill/index.tsx 'use client'; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import './skill.css'; import { Alert, AlertActionLink, AlertActionCloseButton } from '@patternfly/react-core/dist/dynamic/components/Alert'; import { ActionGroup, FormFieldGroupExpandable, FormFieldGroupHeader } from '@patternfly/react-core/dist/dynamic/components/Form'; @@ -10,11 +10,32 @@ import { TextInput } from '@patternfly/react-core/dist/dynamic/components/TextIn import { Form } from '@patternfly/react-core/dist/dynamic/components/Form'; import { FormGroup } from '@patternfly/react-core/dist/dynamic/components/Form'; import { TextArea } from '@patternfly/react-core/dist/dynamic/components/TextArea'; -import { PlusIcon, MinusCircleIcon } from '@patternfly/react-icons/dist/dynamic/icons/'; +import { PlusIcon, MinusCircleIcon, CodeIcon } from '@patternfly/react-icons/dist/dynamic/icons/'; import { validateFields, validateEmail, validateUniqueItems } from '../../../utils/validation'; import yaml from 'js-yaml'; +import { getGitHubUsername } from '../../../utils/github'; +import { useSession } from 'next-auth/react'; +import YamlCodeModal from '../../YamlCodeModal'; export const SkillForm: React.FunctionComponent = () => { + const { data: session } = useSession(); + const [githubUsername, setGithubUsername] = useState<string | null>(null); + + useEffect(() => { + const fetchUsername = async () => { + if (session?.accessToken) { + try { + const fetchedUsername = await getGitHubUsername(session.accessToken); + setGithubUsername(fetchedUsername); + } catch (error) { + console.error('Failed to fetch GitHub username:', error); + } + } + }; + + fetchUsername(); + }, [session?.accessToken]); + const [email, setEmail] = useState(''); const [name, setName] = useState(''); const [task_description, setTaskDescription] = useState(''); @@ -37,6 +58,9 @@ export const SkillForm: React.FunctionComponent = () => { const [success_alert_title, setSuccessAlertTitle] = useState(''); const [success_alert_message, setSuccessAlertMessage] = useState(''); + const [isModalOpen, setIsModalOpen] = useState(false); + const [yamlContent, setYamlContent] = useState(''); + const handleInputChange = (index: number, type: string, value: string) => { switch (type) { case 'question': @@ -186,6 +210,22 @@ export const SkillForm: React.FunctionComponent = () => { } }; + const handleViewYaml = () => { + const yamlData = { + created_by: githubUsername, + task_description: task_description, + seed_examples: questions.map((question, index) => ({ + question, + answer: answers[index], + context: contexts[index] || '' + })) + }; + + const yamlString = yaml.dump(yamlData, { lineWidth: -1 }); + setYamlContent(yamlString); + setIsModalOpen(true); + }; + const handleDownloadYaml = () => { const infoFields = { email, name, task_description, submission_summary }; const attributionFields = { title_work, link_work: '-', revision: '-', license_work, creators }; @@ -237,7 +277,7 @@ export const SkillForm: React.FunctionComponent = () => { } const yamlData = { - created_by: email, + created_by: githubUsername, task_description: task_description, seed_examples: questions.map((question, index) => { const example: SeedExample = { @@ -274,11 +314,11 @@ export const SkillForm: React.FunctionComponent = () => { } const attributionContent = `Title of work: ${title_work} -Link to work: - -Revision: - -License of the work: ${license_work} -Creator names: ${creators} -`; + Link to work: - + Revision: - + License of the work: ${license_work} + Creator names: ${creators} + `; const blob = new Blob([attributionContent], { type: 'text/plain' }); const url = URL.createObjectURL(blob); @@ -292,6 +332,13 @@ Creator names: ${creators} return ( <Form className="form"> + <YamlCodeModal isModalOpen={isModalOpen} handleModalToggle={() => setIsModalOpen(!isModalOpen)} yamlContent={yamlContent} /> + <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> + <FormFieldGroupHeader titleText={{ text: 'Skill Contribution Form', id: 'skill-contribution-form-id' }} /> + <Button variant="plain" onClick={handleViewYaml} aria-label="View YAML"> + <CodeIcon /> View YAML + </Button> + </div> <FormFieldGroupExpandable isExpanded toggleAriaLabel="Details" @@ -354,7 +401,7 @@ Creator names: ${creators} header={ <FormFieldGroupHeader titleText={{ text: 'Skill', id: 'contrib-skill-id' }} - titleDescription="Contribute new skill to the taxonomy repository." + titleDescription="Contribute skill to the taxonomy repository (shift+enter for a new line)." /> } > diff --git a/src/components/YamlCodeModal/index.tsx b/src/components/YamlCodeModal/index.tsx new file mode 100644 index 00000000..808832ba --- /dev/null +++ b/src/components/YamlCodeModal/index.tsx @@ -0,0 +1,38 @@ +// src/components/YamlCodeModal.tsx +'use client'; +import React from 'react'; +import { Modal } from '@patternfly/react-core/components'; +import { ModalVariant } from '@patternfly/react-core/dist/dynamic/next/components/Modal'; +import { Button } from '@patternfly/react-core/dist/dynamic/components/Button'; +import { CodeBlock, CodeBlockCode } from '@patternfly/react-core/dist/dynamic/components/CodeBlock'; +import CopyToClipboardButton from '../../components/CopyToClipboardButton'; + +interface YamlCodeModalProps { + isModalOpen: boolean; + handleModalToggle: () => void; + yamlContent: string; +} + +export const YamlCodeModal: React.FC<YamlCodeModalProps> = ({ isModalOpen, handleModalToggle, yamlContent }) => { + return ( + <Modal + variant={ModalVariant.medium} + title="Current YAML" + isOpen={isModalOpen} + onClose={handleModalToggle} + aria-label="YAML Code Modal" + actions={[ + <Button key="close" variant="primary" onClick={handleModalToggle}> + Close + </Button>, + <CopyToClipboardButton key="copy" text={yamlContent} /> + ]} + > + <CodeBlock> + <CodeBlockCode>{yamlContent}</CodeBlockCode> + </CodeBlock> + </Modal> + ); +}; + +export default YamlCodeModal;