Skip to content

Commit

Permalink
Updated Document Upload and details (#3643)
Browse files Browse the repository at this point in the history
* Added drag and drop component

* Updated Documents UI

* lint fixes

* PR fixes

* Data test id fixes

* Updated ui
  • Loading branch information
FuriousLlama authored Dec 14, 2023
1 parent b6f33c1 commit 750c95c
Show file tree
Hide file tree
Showing 22 changed files with 833 additions and 765 deletions.
11 changes: 10 additions & 1 deletion source/frontend/src/components/common/GenericModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface ModalContent {
handleCancel?: Function;
/** Optional function to control behaviour of ok button. Default is to reload the app. */
handleOk?: Function;
handleOkDisabled?: boolean;
/** Optional text to display on the cancel button. Default is Cancel. */
cancelButtonText?: string;
/** Optional variant that will override the default variant of warning. */
Expand Down Expand Up @@ -98,6 +99,7 @@ export const GenericModal = (props: Omit<BsModalProps, 'onHide'> & ModalProps) =
display,
setDisplay,
handleOk,
handleOkDisabled,
handleCancel,
title,
message,
Expand Down Expand Up @@ -215,12 +217,19 @@ export const GenericModal = (props: Omit<BsModalProps, 'onHide'> & ModalProps) =
title="cancel-modal"
variant={cancelButtonVariant ?? 'secondary'}
onClick={close}
data-testid="cancel-modal-button"
>
{cancelButtonText}
</Button>
)}

<Button title="ok-modal" variant={okButtonVariant ?? 'primary'} onClick={ok}>
<Button
title="ok-modal"
variant={okButtonVariant ?? 'primary'}
onClick={ok}
disabled={handleOkDisabled}
data-testid="ok-modal-button"
>
{okButtonText ?? 'Ok'}
</Button>
</div>
Expand Down
19 changes: 15 additions & 4 deletions source/frontend/src/components/common/Section/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,29 @@ interface SectionProps {
header?: React.ReactNode;
isCollapsable?: boolean;
initiallyExpanded?: boolean;
noPadding?: boolean;
}

export const Section: React.FC<
React.PropsWithChildren<SectionProps & React.HTMLAttributes<HTMLDivElement>> & {
'data-testid'?: string;
}
> = ({ header, children, title, isCollapsable, initiallyExpanded, className, ...rest }) => {
> = ({
header,
children,
title,
isCollapsable,
initiallyExpanded,
noPadding,
className,
...rest
}) => {
const [isCollapsed, setIsCollapsed] = useState<boolean>(!initiallyExpanded && true);
return (
<StyledFormSection
className={clsx('form-section', className)}
data-testid={rest['data-testid']}
noPadding={noPadding}
>
{header && (
<StyledSectionHeader>
Expand Down Expand Up @@ -66,9 +77,9 @@ const StyledSectionHeader = styled.h2`
margin-bottom: 2rem;
`;

const StyledFormSection = styled.div`
margin: 1.5rem;
padding: 1.5rem;
const StyledFormSection = styled.div<{ noPadding?: boolean }>`
margin: ${props => (props.noPadding === true ? '' : '1.5rem')};
padding: ${props => (props.noPadding === true ? '' : '1.5rem')};
background-color: white;
text-align: left;
border-radius: 0.5rem;
Expand Down
152 changes: 152 additions & 0 deletions source/frontend/src/components/common/form/FileDragAndDrop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { ChangeEvent, FunctionComponent, useState } from 'react';
import styled from 'styled-components';

import { SectionField } from '../Section/SectionField';

interface IFileDragAndDropProps {
onSelectFile: (file: File | null) => void;
selectedFile: File | null;
validExtensions: string[];
}

const FileDragAndDrop: FunctionComponent<React.PropsWithChildren<IFileDragAndDropProps>> = ({
onSelectFile,
selectedFile,
validExtensions,
}) => {
const validDocumentExtensions: string = validExtensions.map(x => `.${x}`).join(',');

const [isDragging, setIsDragging] = useState(false);

const handleFileInput = (changeEvent: ChangeEvent<HTMLInputElement>) => {
// handle validations
if (changeEvent.target !== null) {
var target = changeEvent.target;
if (target.files !== null && target.files.length > 0) {
onSelectFile(target.files[0]);
}
}
};

const handleDragEnter = (event: React.DragEvent) => {
event.preventDefault();
event.stopPropagation();

setIsDragging(true);
};

const handleDragLeave = (event: React.DragEvent) => {
event.preventDefault();
event.stopPropagation();

setIsDragging(false);
};

const handleDragOver = (event: React.DragEvent) => {
event.preventDefault();
event.stopPropagation();

setIsDragging(true);
};

const handleDrop = (event: React.DragEvent) => {
event.preventDefault();
event.stopPropagation();

let files = [...event.dataTransfer.files];

if (files && files.length > 0) {
onSelectFile(files[0]);
event.dataTransfer.clearData();
}

setIsDragging(false);
};

const shortenString = (text: string, maxLength: number, terminator: string = '...'): string => {
if (text.length > maxLength) {
return text.substring(0, maxLength - terminator.length) + terminator;
}

return text;
};

return (
<div>
<DragDropZone
onDrop={handleDrop}
onDragOver={handleDragOver}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
isDragging={isDragging}
>
<StyledContent>
Drag files here to attach or{' '}
<StyledUploadLabel>
Browse
<StyledFileInput
data-testid="upload-input"
id="uploadInput"
type="file"
name="documentFile"
accept={validDocumentExtensions}
onChange={handleFileInput}
className=""
/>
</StyledUploadLabel>
</StyledContent>
</DragDropZone>
{selectedFile !== null && (
<StyledSelectedFile>
<SectionField label="File to add" labelWidth="3">
{shortenString(selectedFile.name || '', 20)}
</SectionField>
</StyledSelectedFile>
)}
</div>
);
};

export default FileDragAndDrop;

const DragDropZone = styled.div<{ isDragging: boolean }>`
border: 1px solid ${({ theme }) => theme.css.lightVariantColor};
border-style: ${({ isDragging }) => (isDragging ? 'solid' : 'dashed')};
border: ${props => (props.isDragging ? `1px solid ${props.theme.css.draftColor}` : '')};
background-color: ${props => (props.isDragging ? props.theme.css.filterBoxColor : '')};
height: 10rem;
line-height: 10rem;
text-align: center;
`;

const StyledContent = styled.div`
width: 100%;
display: inline-block;
vertical-align: middle;
line-height: normal;
`;

const StyledUploadLabel = styled.label`
display: inline-block;
color: ${({ theme }) => theme.css.linkColor};
cursor: pointer;
&:hover {
color: ${({ theme }) => theme.css.linkHoverColor};
text-decoration: underline;
}
`;

const StyledFileInput = styled.input`
display: none;
`;

const StyledSelectedFile = styled.div`
padding-top: 2rem;
overflow: hide;
word-wrap: break-word;
`;
6 changes: 6 additions & 0 deletions source/frontend/src/features/documents/ComposedDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ export class DocumentUploadFormData {
public documentTypeId: string;
public documentStatusCode: string;
public documentMetadata: Record<string, string>;
public isDocumentTypeChanged: boolean = false;
public isSelectedFile: boolean = false;

public constructor(
initialStatus: string,
Expand Down Expand Up @@ -125,6 +127,7 @@ export class DocumentUpdateFormData {
public mayanDocumentId: number;
public documentStatusCode: string;
public documentMetadata: Record<string, string>;
public documentTypeId: string = '';

public static fromApi(
composedDocument: ComposedDocument,
Expand All @@ -144,6 +147,9 @@ export class DocumentUpdateFormData {
model.documentMetadata[metaType.metadata_type?.id?.toString() || '-'] =
foundMetadata?.value ?? '';
});
const documentTypeLabel = composedDocument.pimsDocumentRelationship?.document?.documentType?.id;

model.documentTypeId = documentTypeLabel?.toString() || '';
return model;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ export const getDocumentMetadataYupSchema = (
}
}

return Yup.object().shape({ documentMetadata: Yup.object().shape(metadataSchema) });
return Yup.object().shape({
documentMetadata: Yup.object().shape(metadataSchema),
documentTypeId: Yup.string().required('Document type is required'),
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Col, Row } from 'react-bootstrap';
import { Button } from '@/components/common/buttons/Button';
import { Select } from '@/components/common/form';
import LoadingBackdrop from '@/components/common/LoadingBackdrop';
import { Section } from '@/components/common/Section/Section';
import { SectionField } from '@/components/common/Section/SectionField';
import TooltipIcon from '@/components/common/TooltipIcon';
import * as API from '@/constants/API';
Expand All @@ -13,13 +14,7 @@ import useLookupCodeHelpers from '@/hooks/useLookupCodeHelpers';
import { Api_DocumentUpdateRequest } from '@/models/api/Document';
import { Api_Storage_DocumentTypeMetadataType } from '@/models/api/DocumentStorage';

import {
StyledGreySection,
StyledH2,
StyledH3,
StyledHeader,
StyledScrollable,
} from '../commonStyles';
import { StyledH3, StyledScrollable } from '../commonStyles';
import { ComposedDocument, DocumentUpdateFormData } from '../ComposedDocument';
import { DocumentMetadataView } from '../DocumentMetadataView';
import { getDocumentMetadataYupSchema } from '../DocumentMetadataYupSchema';
Expand Down Expand Up @@ -53,20 +48,20 @@ export const DocumentDetailForm: React.FunctionComponent<
{hasClaim(Claims.DOCUMENT_VIEW) && (
<>
<DocumentDetailHeader document={props.document} />
<StyledGreySection>
<Row className="pb-3">
<Col className="text-left">
<StyledHeader>
<StyledH2>Document information</StyledH2>
<TooltipIcon
toolTipId={'documentInfoToolTip'}
innerClassName={'documentInfoToolTip'}
toolTip="Information you provided here will be searchable"
></TooltipIcon>
</StyledHeader>
</Col>
</Row>

<Section
noPadding
header={
<>
Document information
<TooltipIcon
toolTipId="documentInfoToolTip"
innerClassName="documentInfoToolTip"
toolTip="Information you provided here will be searchable"
/>{' '}
</>
}
>
<StyledScrollable>
<Formik<DocumentUpdateFormData>
innerRef={props.formikRef}
Expand Down Expand Up @@ -95,23 +90,30 @@ export const DocumentDetailForm: React.FunctionComponent<
mayanMetadata={props.mayanMetadataTypes}
formikProps={formikProps}
/>
<div className="pt-5">Do you want to proceed?</div>
<hr />
<Row className="justify-content-end pt-4">
<Col xs="auto">
<Button variant="secondary" type="button" onClick={props.onCancel}>
Cancel
<Button
variant="secondary"
type="button"
onClick={props.onCancel}
className="px-5"
>
No
</Button>
</Col>
<Col xs="auto">
<Button type="submit" onClick={formikProps.submitForm}>
Save
<Button type="submit" onClick={formikProps.submitForm} className="px-5">
Yes
</Button>
</Col>
</Row>
</>
)}
</Formik>
</StyledScrollable>
</StyledGreySection>
</Section>
</>
)}
</StyledContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const DocumentDetailHeader: React.FunctionComponent<
const mayanDocumentId = props.document.pimsDocumentRelationship?.document?.mayanDocumentId || -1;

return (
<>
<div className="pb-4">
<SectionField
data-testid="document-type"
label="Document type"
Expand All @@ -39,7 +39,7 @@ const DocumentDetailHeader: React.FunctionComponent<
</Col>
</Row>
</SectionField>
</>
</div>
);
};

Expand Down
Loading

0 comments on commit 750c95c

Please sign in to comment.