Skip to content

Commit

Permalink
Merge pull request #683 from codeforpdx/issue-682/create-documents-route
Browse files Browse the repository at this point in the history
[Enhancement] - Issue-682/create documents route
  • Loading branch information
leekahung authored Nov 1, 2024
2 parents 2b8c5da + 27e8cbc commit 6f49b99
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 122 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "pass",
"homepage": ".",
"version": "1.0.1-alpha",
"version": "1.1.0-alpha",
"description": "",
"scripts": {
"start": "concurrently --kill-others \"npm run podserver\" \"npm run dev\"",
Expand Down
3 changes: 2 additions & 1 deletion src/AppRoutes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useSession } from '@hooks';
import { CIVIC_FORM_LIST, FormLayout } from '@components/CivicProfileForms';
import { MESSAGE_PAGES_LIST, MessagesLayout } from '@components/Messages';
// Page Imports
import { CivicProfile, Home, Contacts, Profile, Signup } from './pages';
import { CivicProfile, Documents, Home, Contacts, Profile, Signup } from './pages';

const ProtectedRoute = ({ isLoggedIn, children }) =>
isLoggedIn ? children ?? <Outlet /> : <Navigate to="/" replace />;
Expand Down Expand Up @@ -58,6 +58,7 @@ const AppRoutes = () => {
))}
</Route>
<Route path="/profile" element={<Profile />} />
<Route path="/documents" element={<Documents />} />
{/* TODO: Remove blank Civic Profile page, ensure it directs Basic Information instead */}
<Route path="/civic-profile" element={<CivicProfile />}>
{CIVIC_FORM_LIST.map((formProps) => (
Expand Down
40 changes: 24 additions & 16 deletions src/components/Documents/DocumentCard.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// React Imports
import React, { useState } from 'react';
import { useLocation } from 'react-router-dom';
// Material UI Imports
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
Expand Down Expand Up @@ -41,6 +42,9 @@ import { truncateText, getTypeText } from '@utils';
* @returns {React.JSX.Element} The DocumentCard component
*/
const DocumentCard = ({ document, onShare, onDelete, onPreview }) => {
const location = useLocation();
const profileWebId = decodeURIComponent(location.pathname.split('/')[2]);

const [anchorEl, setAnchorEl] = useState(null);
const [openMenu, setOpenMenu] = useState(null);

Expand Down Expand Up @@ -149,22 +153,26 @@ const DocumentCard = ({ document, onShare, onDelete, onPreview }) => {
>
Preview
</MenuItem>
<MenuItem
component={Button}
onClick={handleMenuItemClick(onShare, document)}
startIcon={<ShareIcon sx={iconSize} />}
sx={iconStyling}
>
Share
</MenuItem>
<MenuItem
component={Button}
onClick={handleMenuItemClick(onDelete, document)}
startIcon={<DeleteOutlineOutlinedIcon sx={iconSize} />}
sx={iconStyling}
>
Delete
</MenuItem>
{`${profileWebId}` === 'undefined' && (
<MenuItem
component={Button}
onClick={handleMenuItemClick(onShare, document)}
startIcon={<ShareIcon sx={iconSize} />}
sx={iconStyling}
>
Share
</MenuItem>
)}
{`${profileWebId}` === 'undefined' && (
<MenuItem
component={Button}
onClick={handleMenuItemClick(onDelete, document)}
startIcon={<DeleteOutlineOutlinedIcon sx={iconSize} />}
sx={iconStyling}
>
Delete
</MenuItem>
)}
</Menu>
</Card>
</Box>
Expand Down
9 changes: 9 additions & 0 deletions src/components/Documents/DocumentsDesktop.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// React Import
import React from 'react';
import { useLocation } from 'react-router-dom';
// Material UI Imports
import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined';
import FileOpenIcon from '@mui/icons-material/FileOpen';
Expand All @@ -13,6 +14,8 @@ import {
} from '@mui/x-data-grid';
// Util Imports
import { getTypeText } from '@utils';
// Hooks Imports
import { useSession } from '@hooks';
// Theme Imports
import theme from '../../theme';

Expand Down Expand Up @@ -65,6 +68,10 @@ const CustomToolbar = () => (
* @returns {React.JSX.Element} The DocumentsDesktop component
*/
const DocumentsDesktop = ({ documents, handlers }) => {
const { session } = useSession();
const location = useLocation();
const profileWebId = decodeURIComponent(location.pathname.split('/')[2]);

const columnTitlesArray = [
{ field: 'Name', minWidth: 120, flex: 1, headerAlign: 'center', align: 'center' },
{ field: 'Type', minWidth: 120, flex: 1, headerAlign: 'center', align: 'center' },
Expand Down Expand Up @@ -109,6 +116,7 @@ const DocumentsDesktop = ({ documents, handlers }) => {
onClick={() => handlers.onShare('document', name, type)}
label="Share"
data-testid={`share-button-${id}`}
disabled={session.info.webId !== profileWebId}
/>
);
}
Expand All @@ -130,6 +138,7 @@ const DocumentsDesktop = ({ documents, handlers }) => {
onClick={() => handlers.onDelete(document)}
label="Delete"
data-testid={`delete-button-${document.id}`}
disabled={session.info.webId !== profileWebId}
/>
);
}
Expand Down
13 changes: 13 additions & 0 deletions src/components/NavBar/NavMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ContactsIcon from '@mui/icons-material/Contacts';
import Divider from '@mui/material/Divider';
import Drawer from '@mui/material/Drawer';
import EmailIcon from '@mui/icons-material/Email';
import InventoryIcon from '@mui/icons-material/Inventory';
import LogoutIcon from '@mui/icons-material/Logout';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
Expand Down Expand Up @@ -139,6 +140,18 @@ const NavMenu = ({
Civic Profile
</MenuItem>
</Link>
<Link
to="/documents"
style={{ textDecoration: 'none', color: theme.palette.primary.main }}
>
<MenuItem
component={Button}
startIcon={<InventoryIcon sx={iconSize} />}
sx={iconStyling}
>
Documents
</MenuItem>
</Link>
<Divider sx={{ my: '5px' }} />
</div>
)}
Expand Down
3 changes: 2 additions & 1 deletion src/components/NavBar/NavbarLinks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const NavbarLinks = () => {
// Array of current nav links for menus
const routesArray = [
{ label: 'Contacts', path: '/contacts' },
{ label: 'Civic Profile', path: '/civic-profile/basic-info' }
{ label: 'Civic Profile', path: '/civic-profile/basic-info' },
{ label: 'Documents', path: '/documents' }
];

return (
Expand Down
4 changes: 3 additions & 1 deletion src/layouts/Layout.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// React Imports
import React from 'react';
import { useLocation } from 'react-router-dom';
// Inrupt Library Imports
import { useSession, useNotification } from '@hooks';
// Material UI Imports
Expand All @@ -14,6 +15,7 @@ import Main from './Main';
const Layout = ({ ariaLabel, children }) => {
const { session } = useSession();
const { state } = useNotification();
const location = useLocation();

return (
<Box
Expand All @@ -24,7 +26,7 @@ const Layout = ({ ariaLabel, children }) => {
>
<NavBarSkipLink />
<NavBar />
{session.info.isLoggedIn && <Breadcrumbs />}
{session.info.isLoggedIn && location.pathname !== '/profile' && <Breadcrumbs />}
<Main>{children}</Main>
{session.info.isLoggedIn && <InactivityMessage />}
<Footer />
Expand Down
150 changes: 150 additions & 0 deletions src/pages/Documents.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import React, { useContext, useEffect, useState } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import { useLocation } from 'react-router-dom';
import { useTheme } from '@emotion/react';
import { Container, useMediaQuery } from '@mui/material';
import { ConfirmationModal, SetAclPermissionsModal, UploadDocumentModal } from '@components/Modals';
import { useNotification, useSession } from '@hooks';
import { DocumentListContext } from '@contexts';
import { truncateText } from '@utils';
import { DocumentTable } from '@components/Documents';

/**
* Documents - Component that generates Documents Page for PASS
*
* @memberof Pages
* @name Documents
* @returns {React.JSX.Component} The Documents Page
*/
const Documents = () => {
// Route related states
const location = useLocation();
if (location.pathname.split('/')[1] === 'contacts') {
localStorage.setItem('restorePath', '/contacts');
} else {
localStorage.setItem('restorePath', '/profile');
}
const { setContact } = useContext(DocumentListContext);
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));

// Documents related states
const { session } = useSession();
const [showConfirmationModal, setShowConfirmationModal] = useState(false);
const [processing, setProcessing] = useState(false);
const { addNotification } = useNotification();
const { removeDocument } = useContext(DocumentListContext);
const [selectedDocToDelete, setSelectedDocToDelete] = useState(null);
const [showAddDocModal, setShowAddDocModal] = useState(false);
const [showAclPermissionModal, setShowAclPermissionModal] = useState(false);
const [dataset, setDataset] = useState({
modalType: '',
docName: '',
docType: ''
});

useEffect(() => {
setContact({
familyName: '',
givenName: '',
podUrl: session.info.webId?.split('profile')[0],
thingId: session.info.webId,
webId: session.info.webId
});
}, [session]);

const handleSelectDeleteDoc = (document) => {
setSelectedDocToDelete(document);
setShowConfirmationModal(true);
};

// Function for deleting documents
const handleDeleteDoc = async () => {
setProcessing(true);
try {
await removeDocument(selectedDocToDelete.name);
addNotification('success', `${selectedDocToDelete?.name} deleted from the pod.`);
} catch (e) {
addNotification('error', `Document deletion failed. Reason: ${e.message}`);
} finally {
setShowConfirmationModal(false);
setProcessing(false);
}
};

const handleAclPermissionsModal = (modalType, docName = '', docType = '') => {
setDataset({
modalType,
docName,
docType
});
setShowAclPermissionModal(true);
};

const truncatedText = selectedDocToDelete?.name ? truncateText(selectedDocToDelete.name) : '';

return (
<Container
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
width: '100%'
}}
>
<Box
sx={{
display: 'flex',
gap: 2,
flexDirection: 'row',
paddingLeft: '0px'
}}
>
<Button
variant="outlined"
color="primary"
size="small"
onClick={() => handleAclPermissionsModal('container')}
sx={{
width: isSmallScreen ? '165px' : '200px',
borderColor: 'primary.main',
padding: '6px 12px'
}}
>
Share Documents
</Button>
<Button
variant="contained"
color="primary"
size="small"
onClick={() => setShowAddDocModal(true)}
sx={{ width: isSmallScreen ? '140px' : '180px', padding: '6px 12px' }}
>
Add Document
</Button>
<UploadDocumentModal showModal={showAddDocModal} setShowModal={setShowAddDocModal} />
<SetAclPermissionsModal
showModal={showAclPermissionModal}
setShowModal={setShowAclPermissionModal}
dataset={dataset}
/>
<ConfirmationModal
showModal={showConfirmationModal}
setShowModal={setShowConfirmationModal}
title="Delete Document"
text={`You're about to delete "${truncatedText}" from the pod. Do you wish to continue?`}
onConfirm={handleDeleteDoc}
confirmButtonText="Delete"
processing={processing}
/>
</Box>
<DocumentTable
handleAclPermissionsModal={handleAclPermissionsModal}
handleSelectDeleteDoc={(document) => handleSelectDeleteDoc(document)}
/>
</Container>
);
};

export default Documents;
Loading

0 comments on commit 6f49b99

Please sign in to comment.