Skip to content

Commit

Permalink
Merge pull request #4463 from NCI-Agency/AB-914-Create-and-display-my…
Browse files Browse the repository at this point in the history
…-Attachments-List

Show a list of My Attachments
  • Loading branch information
gjvoosten authored Mar 18, 2024
2 parents 626893d + e265e15 commit a2ab176
Show file tree
Hide file tree
Showing 21 changed files with 452 additions and 42 deletions.
44 changes: 44 additions & 0 deletions client/src/components/Attachment/AttachmentImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import classNames from "classnames"
import PropTypes from "prop-types"
import React from "react"

const AttachmentImage = ({
uuid,
backgroundSize,
backgroundImage,
contentMissing
}) => {
const image = (
<div
className="image-preview info-show card-image attachment-image"
style={{
backgroundSize,
backgroundImage: `url(${backgroundImage})`
}}
/>
)
return (
<div
className={classNames("img-container", {
"img-hover-zoom": !contentMissing
})}
>
{contentMissing ? (
<>{image}</>
) : (
<a href={`/api/attachment/view/${uuid}`} className="d-flex h-100">
{image}
</a>
)}
</div>
)
}

AttachmentImage.propTypes = {
uuid: PropTypes.string.isRequired,
backgroundSize: PropTypes.string.isRequired,
backgroundImage: PropTypes.string.isRequired,
contentMissing: PropTypes.bool.isRequired
}

export default AttachmentImage
7 changes: 7 additions & 0 deletions client/src/components/Nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,13 @@ const Navigation = ({ allOrganizations, resetPages, clearSearchQuery }) => {
<small>{myOrg.shortName}</small>
</SidebarLink>
)}
<SidebarLink
id="my-attachments-nav"
linkTo={{ pathname: "/attachments/mine" }}
handleOnClick={resetPages}
>
My Attachments
</SidebarLink>
<SidebarLink
linkTo={{ pathname: "/subscriptions/mine" }}
handleOnClick={resetPages}
Expand Down
2 changes: 1 addition & 1 deletion client/src/models/Attachment.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ export default class Attachment extends Model {
}

toString() {
return this.fileName || this.description
return this.caption || this.fileName || this.description
}
}
2 changes: 2 additions & 0 deletions client/src/pages/Routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import UserActivitiesOverTime from "pages/admin/useractivities/UserActivitiesOve
import UserActivitiesPerPeriod from "pages/admin/useractivities/UserActivitiesPerPeriod"
import UsersPendingVerification from "pages/admin/UsersPendingVerification"
import AttachmentEdit from "pages/attachments/Edit"
import MyAttachments from "pages/attachments/MyAttachments"
import AttachmentShow from "pages/attachments/Show"
import AuthorizationGroupEdit from "pages/authorizationGroups/Edit"
import MyAuthorizationGroups from "pages/authorizationGroups/MyAuthorizationGroups"
Expand Down Expand Up @@ -84,6 +85,7 @@ const Routing = () => {
</Route>
{attachmentsEnabled && (
<Route path={PAGE_URLS.ATTACHMENTS}>
<Route path="mine" element={<MyAttachments />} />
<Route path=":uuid">
<Route index element={<AttachmentShow />} />
{attachmentEditEnabled && (
Expand Down
162 changes: 162 additions & 0 deletions client/src/pages/attachments/MyAttachments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { gql } from "@apollo/client"
import API from "api"
import AppContext from "components/AppContext"
import AttachmentImage from "components/Attachment/AttachmentImage"
import AttachmentRelatedObjectsTable from "components/Attachment/AttachmentRelatedObjectsTable"
import Fieldset from "components/Fieldset"
import LinkTo from "components/LinkTo"
import {
mapPageDispatchersToProps,
PageDispatchersPropType,
useBoilerplate,
usePageTitle
} from "components/Page"
import UltimatePagination from "components/UltimatePagination"
import { Attachment } from "models"
import React, { useContext, useState } from "react"
import { Table } from "react-bootstrap"
import { connect } from "react-redux"
import utils from "utils"

const GQL_GET_ATTACHMENT_LIST = gql`
query ($attachmentQuery: AttachmentSearchQueryInput) {
attachmentList(query: $attachmentQuery) {
totalCount
pageNum
pageSize
list {
${Attachment.basicFieldsQuery}
author {
uuid
name
rank
avatarUuid
}
attachmentRelatedObjects {
relatedObject {
... on AuthorizationGroup {
name
}
... on Location {
name
}
... on Organization {
shortName
longName
identificationCode
}
... on Person {
name
rank
avatarUuid
}
... on Position {
type
name
}
... on Report {
intent
}
... on Task {
shortName
longName
}
}
relatedObjectUuid
relatedObjectType
}
}
}
}
`

const MyAttachments = ({ pageDispatchers }) => {
const { currentUser } = useContext(AppContext)
const [pageNum, setPageNum] = useState(0)
const attachmentQuery = {
pageNum,
pageSize: 10,
authorUuid: currentUser?.uuid
}
const { loading, error, data } = API.useApiQuery(GQL_GET_ATTACHMENT_LIST, {
attachmentQuery
})
const { done, result } = useBoilerplate({
loading,
error,
pageDispatchers
})
usePageTitle("My Attachments")
if (done) {
return result
}

const paginatedAttachments = data.attachmentList
const attachments = paginatedAttachments ? paginatedAttachments.list : []
const { pageSize, totalCount } = paginatedAttachments
const attachmentsExist = totalCount > 0

return (
<Fieldset id="my-attachments" title="My Attachments">
{attachmentsExist ? (
<div>
<UltimatePagination
Component="header"
componentClassName="searchPagination"
className="float-end"
pageNum={pageNum}
pageSize={pageSize}
totalCount={totalCount}
goToPage={setPageNum}
/>

<Table striped hover responsive className="attachments_table">
<thead>
<tr>
<th>Content</th>
<th>Caption</th>
<th>Used In</th>
</tr>
</thead>
<tbody>
{attachments.map(attachment => {
const { backgroundSize, backgroundImage, contentMissing } =
utils.getAttachmentIconDetails(attachment, true)
return (
<tr key={attachment.uuid}>
<td>
<div style={{ width: "50px", height: "50px" }}>
<AttachmentImage
uuid={attachment.uuid}
contentMissing={contentMissing}
backgroundSize={backgroundSize}
backgroundImage={backgroundImage}
/>
</div>
</td>
<td>
<LinkTo modelType="Attachment" model={attachment} />
</td>
<td>
<AttachmentRelatedObjectsTable
relatedObjects={attachment.attachmentRelatedObjects}
/>
</td>
</tr>
)
})}
</tbody>
</Table>
</div>
) : (
<em>No attachments found</em>
)}
</Fieldset>
)
}

MyAttachments.propTypes = {
pageDispatchers: PageDispatchersPropType
}

export default connect(null, mapPageDispatchersToProps)(MyAttachments)
41 changes: 1 addition & 40 deletions client/src/pages/attachments/Show.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { gql } from "@apollo/client"
import { DEFAULT_PAGE_PROPS, DEFAULT_SEARCH_PROPS } from "actions"
import API from "api"
import classNames from "classnames"
import AppContext from "components/AppContext"
import "components/Attachment/Attachment.css"
import AttachmentImage from "components/Attachment/AttachmentImage"
import AttachmentRelatedObjectsTable from "components/Attachment/AttachmentRelatedObjectsTable"
import DictionaryField from "components/DictionaryField"
import * as FieldHelper from "components/FieldHelper"
Expand All @@ -19,7 +19,6 @@ import {
import RichTextEditor from "components/RichTextEditor"
import { Field, Form, Formik } from "formik"
import { Attachment } from "models"
import PropTypes from "prop-types"
import React, { useContext } from "react"
import { Button, Col } from "react-bootstrap"
import { connect } from "react-redux"
Expand Down Expand Up @@ -74,44 +73,6 @@ const GQL_GET_ATTACHMENT = gql`
}
`

const AttachmentImage = ({
uuid,
backgroundSize,
backgroundImage,
contentMissing
}) => {
const image = (
<div
className="image-preview info-show card-image attachment-image"
style={{
backgroundSize,
backgroundImage: `url(${backgroundImage})`
}}
/>
)
return (
<div
className={classNames("img-container", {
"img-hover-zoom": !contentMissing
})}
>
{contentMissing ? (
<>{image}</>
) : (
<a href={`/api/attachment/view/${uuid}`} className="d-flex h-100">
{image}
</a>
)}
</div>
)
}
AttachmentImage.propTypes = {
uuid: PropTypes.string.isRequired,
backgroundSize: PropTypes.string.isRequired,
backgroundImage: PropTypes.string.isRequired,
contentMissing: PropTypes.bool.isRequired
}

const AttachmentShow = ({ pageDispatchers }) => {
const { currentUser } = useContext(AppContext)
const { uuid } = useParams()
Expand Down
31 changes: 31 additions & 0 deletions client/tests/webdriver/baseSpecs/myAttachments.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { expect } from "chai"
import Home from "../pages/home.page"
import MyAttachments from "../pages/myAttachments.page"

describe("Home page", () => {
describe("When checking the navigation items", () => {
it("Should see a link to my attachments page", async() => {
await Home.open()
await (await Home.getLinksMenuButton()).click()
await (await Home.getMyAttachmentsLink()).waitForDisplayed()
// eslint-disable-next-line no-unused-expressions
expect(await (await Home.getMyAttachmentsLink()).isExisting()).to.be.true
await Home.logout()
})
})
})

describe("My Attachments page", () => {
describe("When checking the content of the page", () => {
it("Should see a table of arthur's attachments", async() => {
await MyAttachments.openAsAdminUser()
await (await MyAttachments.getMyAttachments()).waitForDisplayed()
const myAttachments = await (
await MyAttachments.getMyAttachments()
).$$("table.attachments_table > tbody > tr")
// table has a header and 4 attachment rows
expect(myAttachments).to.have.length(4)
await MyAttachments.logout()
})
})
})
4 changes: 4 additions & 0 deletions client/tests/webdriver/pages/home.page.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ class Home extends Page {
return browser.$('//a//span[text()="My Counterparts"]')
}

async getMyAttachmentsLink() {
return browser.$('//a//span[text()="My Attachments"]')
}

async getMyCounterpartsNotifications() {
return (await this.getMyCounterpartsLink()).$("span.badge")
}
Expand Down
15 changes: 15 additions & 0 deletions client/tests/webdriver/pages/myAttachments.page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Page from "./page"

const PAGE_URL = "/attachments/mine"

class MyAttachments extends Page {
async openAsAdminUser() {
await super.openAsAdminUser(PAGE_URL)
}

async getMyAttachments() {
return browser.$("#my-attachments")
}
}

export default new MyAttachments()
29 changes: 29 additions & 0 deletions src/main/java/mil/dds/anet/beans/search/AttachmentSearchQuery.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package mil.dds.anet.beans.search;

import io.leangen.graphql.annotations.GraphQLInputField;
import io.leangen.graphql.annotations.GraphQLQuery;

public class AttachmentSearchQuery extends AbstractSearchQuery<AttachmentSearchSortBy> {

@GraphQLQuery
@GraphQLInputField
private String authorUuid;

public AttachmentSearchQuery() {
super(AttachmentSearchSortBy.CREATED_AT);
this.setSortOrder(SortOrder.DESC);
}

public String getAuthorUuid() {
return authorUuid;
}

public void setAuthorUuid(String authorUuid) {
this.authorUuid = authorUuid;
}

@Override
public AttachmentSearchQuery clone() throws CloneNotSupportedException {
return (AttachmentSearchQuery) super.clone();
}
}
Loading

0 comments on commit a2ab176

Please sign in to comment.