Skip to content
Closed
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
15 changes: 15 additions & 0 deletions src/app/documents/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// src/app/dashboard/page.tsx
'use client';

import React from 'react';
import '@patternfly/react-core/dist/styles/base.css';
import { AppLayout } from '@/components/AppLayout';
import Documents from '@/components/Documents/Documents';

const DocumentsPage: React.FunctionComponent = () => (
<AppLayout className="documents-page">
<Documents />
</AppLayout>
);

export default DocumentsPage;
1 change: 1 addition & 0 deletions src/components/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const AppLayout: React.FunctionComponent<IAppLayout> = ({ children, className })

const routes = [
{ path: '/dashboard', altPaths: ['/contribute'], label: 'My contributions' },
{ path: '/documents', label: 'Documents' },
...(playgroundFeaturesEnabled
? [
{
Expand Down
9 changes: 5 additions & 4 deletions src/components/CardView/CardView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type Props<DataType> = {
data: DataType[];
cardRenderer: (data: DataType, rowIndex: number) => React.ReactNode;
enablePagination?: boolean | 'compact';
showBottomPagination?: boolean;
toolbarContent?: React.ReactElement<typeof ToolbarItem | typeof ToolbarGroup>;
onClearFilters?: () => void;
emptyTableView?: React.ReactNode;
Expand All @@ -31,12 +32,12 @@ const CardView = <T,>({
data,
cardRenderer,
enablePagination,
showBottomPagination = false,
perPageOptions = defaultPerPageOptions,
toolbarContent,
onClearFilters,
emptyTableView
}: Props<T>): React.ReactElement => {
const showPagination = enablePagination;
const [page, setPage] = React.useState(1);
const [pageSize, setPageSize] = React.useState(10);

Expand Down Expand Up @@ -90,7 +91,7 @@ const CardView = <T,>({

return (
<Flex direction={{ default: 'column' }} style={{ height: '100%' }}>
{(toolbarContent || showPagination) && (
{(toolbarContent || enablePagination) && (
<FlexItem>
<Toolbar
inset={{ default: 'insetNone' }}
Expand All @@ -100,7 +101,7 @@ const CardView = <T,>({
>
<ToolbarContent>
{toolbarContent}
{showPagination && (
{enablePagination && (
<ToolbarItem variant="pagination" align={{ default: 'alignEnd' }} className="pf-v6-u-pr-lg">
{pagination('top')}
</ToolbarItem>
Expand All @@ -117,7 +118,7 @@ const CardView = <T,>({
<div style={{ padding: 'var(--pf-global--spacer--2xl) 0', textAlign: 'center' }}>{emptyTableView}</div>
</FlexItem>
) : null}
{showPagination ? <FlexItem>{pagination('bottom')}</FlexItem> : null}
{enablePagination && showBottomPagination ? <FlexItem>{pagination('bottom')}</FlexItem> : null}
</Flex>
);
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/Dashboard/ContributionActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ const ContributionActions: React.FC<Props> = ({ contribution, onUpdateContributi
console.error(errorMessage);
addAlert(errorMessage, 'danger');
} else {
console.error('Unknown error deleting the contribution ${branchName}');
addAlert('Unknown error deleting the contribution ${branchName}', 'danger');
console.error(`Unknown error deleting the contribution ${branchName}`);
addAlert(`Unknown error deleting the contribution ${branchName}`, 'danger');
}
}
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ import {
import { AngleDownIcon, GithubIcon, SearchIcon } from '@patternfly/react-icons';
import { ContributionInfo } from '@/types';
import { useFeatureFlags } from '@/context/FeatureFlagsContext';
import Table from '@/components/Table/Table';
import { useEnvConfig } from '@/context/EnvConfigContext';
import Table from '@/components/Table/Table';
import CardView from '@/components/CardView/CardView';
import XsExternalLinkAltIcon from '@/components/Common/XsExternalLinkAltIcon';
import ClearDraftDataButton from '@/components/Contribute/ClearDraftDataButton';
import {
Expand All @@ -45,7 +46,6 @@ import ContributionTableRow from '@/components/Dashboard/ContributionTableRow';
import ContributionCard from '@/components/Dashboard/ContributionCard';

import './Dashboard.scss';
import CardView from '@/components/CardView/CardView';

const InstructLabLogo: React.FC = () => <Image src="/InstructLab-LogoFile-RGB-FullColor.svg" alt="InstructLab Logo" width={256} height={256} />;

Expand Down
69 changes: 69 additions & 0 deletions src/components/Documents/DeleteDocumentModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client';

import React from 'react';
import { Button, Content, Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant, TextInput } from '@patternfly/react-core';
import { KnowledgeFile } from '@/types';

interface Props {
document: KnowledgeFile;
onClose: (deleteDocument: boolean) => void;
}

const DeleteDocumentModal: React.FC<Props> = ({ document, onClose }) => {
const [deleteDocumentConfirm, setDeleteDocumentConfirm] = React.useState<string>('');

return (
<Modal
variant={ModalVariant.medium}
isOpen
onClose={() => onClose(false)}
aria-labelledby="confirm-delete-document-title"
aria-describedby="confirm-delete-document-description"
>
<ModalHeader
titleIconVariant="warning"
title={`Permanently delete this document?`}
labelId="confirm-delete-document-title"
descriptorId="confirm-delete-document-description"
description={
<>
Are you sure you want to delete the file <strong>{document.filename}</strong>?
</>
}
/>
<ModalBody>
<Content component="p">
Type <strong>DELETE</strong> to confirm deletion:
<span style={{ color: 'var(--pf-t--global--color--status--danger--default' }}> *</span>
</Content>
<TextInput
isRequired
type="text"
id="deleteDocumentConfirm"
name="deleteDocumentConfirm"
value={deleteDocumentConfirm}
onChange={(_, value) => setDeleteDocumentConfirm(value)}
/>
</ModalBody>
<ModalFooter>
<Button
key="confirm"
variant="danger"
isDisabled={deleteDocumentConfirm !== 'DELETE'}
onClick={() => {
if (deleteDocumentConfirm === 'DELETE') {
onClose(true);
}
}}
>
Delete
</Button>
<Button key="cancel" variant="secondary" onClick={() => onClose(false)}>
Cancel
</Button>
</ModalFooter>
</Modal>
);
};

export default DeleteDocumentModal;
52 changes: 52 additions & 0 deletions src/components/Documents/DocumentActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as React from 'react';
import { Dropdown, DropdownItem, DropdownList, MenuToggle, MenuToggleElement } from '@patternfly/react-core';
import { EllipsisVIcon } from '@patternfly/react-icons';
import { KnowledgeFile } from '@/types';
import DeleteDocumentModal from '@/components/Documents/DeleteDocumentModal';

interface Props {
document: KnowledgeFile;
onRemove: () => void;
}

const DocumentActions: React.FC<Props> = ({ document, onRemove }) => {
const [isActionMenuOpen, setIsActionMenuOpen] = React.useState<boolean>(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState(false);

const handleDeleteDocumentConfirm = async (doDelete: boolean) => {
setIsDeleteModalOpen(false);
if (doDelete) {
onRemove();
}
};

return (
<>
<Dropdown
onSelect={() => setIsActionMenuOpen(false)}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
isExpanded={isActionMenuOpen}
onClick={() => setIsActionMenuOpen((prev) => !prev)}
variant="plain"
aria-label="document action menu"
icon={<EllipsisVIcon aria-hidden="true" />}
/>
)}
isOpen={isActionMenuOpen}
onOpenChange={(isOpen: boolean) => setIsActionMenuOpen(isOpen)}
popperProps={{ position: 'end' }}
>
<DropdownList>
<DropdownItem className="destructive-action-item" key="delete-document" onClick={() => setIsDeleteModalOpen(true)}>
Delete document
</DropdownItem>
</DropdownList>
</Dropdown>
{isDeleteModalOpen ? <DeleteDocumentModal document={document} onClose={handleDeleteDocumentConfirm} /> : null}
</>
);
};

export default DocumentActions;
70 changes: 70 additions & 0 deletions src/components/Documents/DocumentCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// src/components/Documents/DocumentCard.tsx
import * as React from 'react';
import {
Card,
CardBody,
Flex,
FlexItem,
CardHeader,
CardTitle,
Button,
DescriptionList,
DescriptionListGroup,
DescriptionListTerm,
DescriptionListDescription,
GalleryItem
} from '@patternfly/react-core';
import { KnowledgeFile } from '@/types';
import TruncatedText from '@/components/Common/TruncatedText';
import TableRowTitleDescription from '@/components/Table/TableRowTitleDescription';
import { getFormattedLastUpdatedDate } from '@/components/Documents/const';
import DocumentActions from '@/components/Documents/DocumentActions';
import MarkdownFileViewer from '@/components/Documents/MarkdownFileViewer';

interface Props {
document: KnowledgeFile;
onRemove: () => void;
}

const DocumentCard: React.FC<Props> = ({ document, onRemove }) => {
const [showDocumentViewer, setShowDocumentViewer] = React.useState<boolean>(false);

return (
<GalleryItem key={document.filename}>
<Card className="document-card">
<CardHeader
actions={{
actions: <DocumentActions document={document} onRemove={onRemove} />
}}
>
<CardTitle>
<TableRowTitleDescription
title={
<Button component="a" variant="link" isInline onClick={() => setShowDocumentViewer(true)}>
<TruncatedText maxLines={2} content={document.filename} />
</Button>
}
/>
</CardTitle>
</CardHeader>
<CardBody>
<Flex direction={{ default: 'column' }} gap={{ default: 'gapSm' }}>
<FlexItem>
<DescriptionList isCompact>
<DescriptionListGroup>
<DescriptionListTerm>Last updated</DescriptionListTerm>
<DescriptionListDescription>
{getFormattedLastUpdatedDate(new Date(document.commitDate ? Date.parse(document.commitDate) : Date.now()))}
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</FlexItem>
</Flex>
</CardBody>
</Card>
{showDocumentViewer ? <MarkdownFileViewer markdownFile={document} handleCloseModal={() => setShowDocumentViewer(false)} /> : null}
</GalleryItem>
);
};

export default DocumentCard;
39 changes: 39 additions & 0 deletions src/components/Documents/DocumentTableRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { Button } from '@patternfly/react-core';
import { Tr, Td } from '@patternfly/react-table';
import { KnowledgeFile } from '@/types';
import TruncatedText from '@/components/Common/TruncatedText';
import TableRowTitleDescription from '@/components/Table/TableRowTitleDescription';
import { getFormattedLastUpdatedDate } from '@/components/Documents/const';
import DocumentActions from '@/components/Documents/DocumentActions';
import MarkdownFileViewer from '@/components/Documents/MarkdownFileViewer';

interface Props {
document: KnowledgeFile;
onRemove: () => void;
}

const DocumentTableRow: React.FC<Props> = ({ document, onRemove }) => {
const [showDocumentViewer, setShowDocumentViewer] = React.useState<boolean>(false);

return (
<Tr>
<Td modifier="truncate" dataLabel="File name">
<TableRowTitleDescription
title={
<Button variant="link" isInline onClick={() => setShowDocumentViewer(true)}>
<TruncatedText maxLines={2} content={document.filename} />
</Button>
}
/>
</Td>
<Td dataLabel="Last updated">{getFormattedLastUpdatedDate(new Date(document.commitDate ? Date.parse(document.commitDate) : Date.now()))}</Td>
<Td isActionCell>
<DocumentActions document={document} onRemove={onRemove} />
</Td>
{showDocumentViewer ? <MarkdownFileViewer markdownFile={document} handleCloseModal={() => setShowDocumentViewer(false)} /> : null}
</Tr>
);
};

export default DocumentTableRow;
Loading