Skip to content
This repository was archived by the owner on Feb 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/app/contribute/knowledge/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// src/app/contribute/knowledge/page.tsx
'use client';
import { useEffect, useState } from 'react';
import { AppLayout } from '@/components/AppLayout';
import KnowledgeFormGithub from '@/components/Contribute/Knowledge/Github';
import KnowledgeFormNative from '@/components/Contribute/Knowledge/Native';
import { useEffect, useState } from 'react';

const KnowledgeFormPage: React.FunctionComponent = () => {
const [deploymentType, setDeploymentType] = useState<string | undefined>();
Expand All @@ -17,7 +17,7 @@ const KnowledgeFormPage: React.FunctionComponent = () => {
getEnvVariables();
}, []);

return <AppLayout>{deploymentType === 'native' ? <KnowledgeFormNative /> : <KnowledgeFormGithub />}</AppLayout>;
return <AppLayout className="contribute-page">{deploymentType === 'native' ? <KnowledgeFormNative /> : <KnowledgeFormGithub />}</AppLayout>;
};

export default KnowledgeFormPage;
168 changes: 76 additions & 92 deletions src/components/Contribute/AuthorInformation.tsx
Original file line number Diff line number Diff line change
@@ -1,120 +1,104 @@
import React, { useEffect, useState } from 'react';
import { checkSkillFormCompletion } from './Skill/validation';
import { checkKnowledgeFormCompletion } from './Knowledge/validation';
import { ValidatedOptions, FormGroup, TextInput, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core';
import React from 'react';
import {
ValidatedOptions,
FormGroup,
TextInput,
FormHelperText,
HelperText,
HelperTextItem,
Form,
Flex,
FlexItem,
Content
} from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons';

export enum FormType {
Knowledge,
Skill
}

interface Props {
formType: FormType;
reset: boolean;
formData: object;
setDisableAction: React.Dispatch<React.SetStateAction<boolean>>;
email: string;
setEmail: React.Dispatch<React.SetStateAction<string>>;
setEmail: (val: string) => void;
name: string;
setName: React.Dispatch<React.SetStateAction<string>>;
setName: (val: string) => void;
}
const AuthorInformation: React.FC<Props> = ({ formType, reset, formData, setDisableAction, email, setEmail, name, setName }) => {
const [validEmail, setValidEmail] = useState<ValidatedOptions>();
const [validName, setValidName] = useState<ValidatedOptions>();
const [validEmailError, setValidEmailError] = useState('Required Field');
const AuthorInformation: React.FC<Props> = ({ email, setEmail, name, setName }) => {
const [validEmail, setValidEmail] = React.useState<ValidatedOptions>(ValidatedOptions.default);
const [validName, setValidName] = React.useState<ValidatedOptions>(ValidatedOptions.default);
const [validEmailError, setValidEmailError] = React.useState('Required Field');
const touchedRef = React.useRef<boolean>();

const validateEmail = (emailStr: string) => {
const email = emailStr.trim();
const re = /\S+@\S+\.\S+/;
if (re.test(email)) {
setValidEmail(ValidatedOptions.success);
setValidEmailError('');
if (formType === FormType.Knowledge) {
setDisableAction(!checkKnowledgeFormCompletion(formData));
return;
}
setDisableAction(!checkSkillFormCompletion(formData));
return;
}
const errMsg = email ? 'Please enter a valid email address.' : 'Required field';
setDisableAction(true);
setValidEmail(ValidatedOptions.error);
setValidEmailError(errMsg);
return;
};

const validateName = (nameStr: string) => {
const name = nameStr.trim();
if (name.length > 0) {
setValidName(ValidatedOptions.success);
if (formType === FormType.Knowledge) {
setDisableAction(!checkKnowledgeFormCompletion(formData));
return;
}
setDisableAction(!checkSkillFormCompletion(formData));
return;
}
setDisableAction(true);
setValidName(ValidatedOptions.error);
return;
setValidName(name.length > 0 ? ValidatedOptions.success : ValidatedOptions.error);
};

useEffect(() => {
setValidEmail(ValidatedOptions.default);
setValidName(ValidatedOptions.default);
}, [reset]);

return (
<>
<h2>
<strong>Author Information </strong>
<span style={{ color: 'red' }}>*</span>
</h2>
<p>Provide your information required for a GitHub DCO sign-off.</p>
<FormGroup isRequired key={'author-info-details-email'} label="Email address">
<TextInput
isRequired
type="email"
aria-label="email"
placeholder="Enter your email address"
value={email}
validated={validEmail}
onChange={(_event, value) => setEmail(value)}
onBlur={() => validateEmail(email)}
/>
{validEmail === ValidatedOptions.error && (
<FormHelperText>
<HelperText>
<HelperTextItem icon={<ExclamationCircleIcon />} variant={validEmail}>
{validEmailError}
</HelperTextItem>
</HelperText>
</FormHelperText>
)}
</FormGroup>
<FormGroup isRequired key={'author-info-details-name'} label="Full name">
<TextInput
isRequired
type="text"
aria-label="name"
placeholder="Enter your full name"
value={name}
validated={validName}
onChange={(_event, value) => setName(value)}
onBlur={() => validateName(name)}
/>
{validName === ValidatedOptions.error && (
<FormHelperText>
<HelperText>
<HelperTextItem icon={<ExclamationCircleIcon />} variant={validName}>
Required field
</HelperTextItem>
</HelperText>
</FormHelperText>
)}
</FormGroup>
</>
<Flex gap={{ default: 'gapMd' }} direction={{ default: 'column' }}>
<FlexItem>
<Content component="h4">Author Information</Content>
<Content component="p">Provide your information required for a GitHub DCO sign-off.</Content>
</FlexItem>
<FlexItem>
<Form>
<FormGroup isRequired key={'author-info-details-email'} label="Email address">
<TextInput
isRequired
type="email"
aria-label="email"
placeholder="Enter your email address"
value={email}
validated={validEmail}
onChange={(_event, value) => {
touchedRef.current = true;
setEmail(value);
}}
onBlur={() => validateEmail(email)}
/>
{validEmail === ValidatedOptions.error && (
<FormHelperText>
<HelperText>
<HelperTextItem icon={<ExclamationCircleIcon />} variant={validEmail}>
{validEmailError}
</HelperTextItem>
</HelperText>
</FormHelperText>
)}
</FormGroup>
<FormGroup isRequired key={'author-info-details-name'} label="Full name">
<TextInput
isRequired
type="text"
aria-label="name"
placeholder="Enter your full name"
value={name}
validated={validName}
onChange={(_event, value) => setName(value)}
onBlur={() => validateName(name)}
/>
{validName === ValidatedOptions.error && (
<FormHelperText>
<HelperText>
<HelperTextItem icon={<ExclamationCircleIcon />} variant={validName}>
Required field
</HelperTextItem>
</HelperText>
</FormHelperText>
)}
</FormGroup>
</Form>
</FlexItem>
</Flex>
);
};

Expand Down
44 changes: 44 additions & 0 deletions src/components/Contribute/ContributeAlertGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// src/components/Contribute/Native/Knowledge/index.tsx
'use client';
import React from 'react';
import { AlertGroup, Alert, AlertActionCloseButton, Spinner } from '@patternfly/react-core';
import { ActionGroupAlertContent } from '@/components/Contribute/types';

export interface ContributeAlertGroupProps {
actionGroupAlertContent?: ActionGroupAlertContent;
onCloseActionGroupAlert: () => void;
}

export const ContributeAlertGroup: React.FunctionComponent<ContributeAlertGroupProps> = ({ actionGroupAlertContent, onCloseActionGroupAlert }) => {
if (!actionGroupAlertContent) {
return null;
}

return (
<AlertGroup isToast isLiveRegion>
<Alert
variant={actionGroupAlertContent.waitAlert ? 'info' : actionGroupAlertContent.success ? 'success' : 'danger'}
title={actionGroupAlertContent.title}
timeout={actionGroupAlertContent.timeout === false ? false : actionGroupAlertContent.timeout}
onTimeout={onCloseActionGroupAlert}
actionClose={<AlertActionCloseButton onClose={onCloseActionGroupAlert} />}
>
<p>
{actionGroupAlertContent.waitAlert && <Spinner size="md" />}
{actionGroupAlertContent.message}
<br />
{!actionGroupAlertContent.waitAlert &&
actionGroupAlertContent.success &&
actionGroupAlertContent.url &&
actionGroupAlertContent.url.trim().length > 0 && (
<a href={actionGroupAlertContent.url} rel="noreferrer">
View your new branch
</a>
)}
</p>
</Alert>
</AlertGroup>
);
};

export default ContributeAlertGroup;
Loading