Skip to content

Commit

Permalink
AB#914 create and display my attachments list
Browse files Browse the repository at this point in the history
  • Loading branch information
mk-simsoft authored and Francisco del Castillo committed Mar 12, 2024
1 parent 9e4b575 commit 28b4d02
Show file tree
Hide file tree
Showing 18 changed files with 480 additions and 32 deletions.
24 changes: 24 additions & 0 deletions client/src/components/Attachment/Attachment.css
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,30 @@
transform: scale(1.5);
}

/******** ATTACHMENT TABLE ********/
.attachments_table thead tr,
.attachments_table tbody tr {
display: flex;
}

.attachments_table td,
.attachments_table thead tr th {
width: 100%;
display: flex;
align-items: center;
flex-direction: row;
}

.attachments_table thead tr th:first-child,
.attachments_table td:first-child {
width: 30%;
}

.attachments_table td:not(:first-child),
.attachments_table thead tr th:not(:first-child) {
width: 100%;
}

@media screen and (max-width: 768px) {
.attachment-card {
flex: 0 0 calc(100%);
Expand Down
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: 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
199 changes: 199 additions & 0 deletions client/src/pages/attachments/MyAttachments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { gql } from "@apollo/client"
import API from "api"
import Fieldset from "components/Fieldset"
import LinkTo from "components/LinkTo"
import {
mapPageDispatchersToProps,
PageDispatchersPropType,
useBoilerplate,
usePageTitle
} from "components/Page"
import UltimatePagination from "components/UltimatePagination"
import PropTypes from "prop-types"
import React, { useEffect, useState } from "react"
import { Table } from "react-bootstrap"
import { connect } from "react-redux"
import utils from "utils"

const GQL_GET_MY_ATTACHMENTS = gql`
query ($attachmentsQuery: AttachmentSearchQueryInput) {
myAttachments(query: $attachmentsQuery) {
totalCount
pageNum
pageSize
list {
uuid
fileName
caption
contentLength
mimeType
classification
description
author {
uuid
name
rank
avatarUuid
}
attachmentRelatedObjects {
relatedObject {
... on AuthorizationGroup {
name
}
... on Location {
name
}
... on Organization {
shortName
}
... on Person {
name
rank
avatarUuid
}
... on Position {
type
name
}
... on Report {
intent
}
... on Task {
shortName
}
}
relatedObjectUuid
relatedObjectType
}
}
}
}
`

const MyAttachments = ({
forceRefetch,
setForceRefetch,
refetchCallback,
pageDispatchers
}) => {
const [pageNum, setPageNum] = useState(0)
const attachmentsQuery = {
pageNum,
pageSize: 10
}
const { loading, error, data, refetch } = API.useApiQuery(
GQL_GET_MY_ATTACHMENTS,
{
attachmentsQuery
}
)
useEffect(() => {
if (forceRefetch) {
setForceRefetch(false)
refetch()
}
}, [forceRefetch, setForceRefetch, refetch])
const { done, result } = useBoilerplate({
loading,
error,
pageDispatchers
})
usePageTitle("My Attachments")
if (done) {
return result
}

const paginatedAttachments = data.myAttachments
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>Name</th>
<th>Caption</th>
<th>Used In</th>
</tr>
</thead>
<tbody>
{attachments.map(attachment => {
const { backgroundImage } = utils.getAttachmentIconDetails(
attachment,
true
)
return (
<tr key={attachment.uuid}>
<td>
<div
key={attachment.id}
style={{
backgroundSize: "cover",
height: "40px",
width: "40px",
backgroundPosition: "center",
backgroundImage: `url(${backgroundImage})`,
backgroundRepeat: "no-repeat"
}}
/>
</td>
<td>
<LinkTo modelType="Attachment" model={attachment}>
{attachment.fileName}
</LinkTo>
</td>
<td>{attachment.caption}</td>
<td>
{attachment.attachmentRelatedObjects[0] ? (
<LinkTo
modelType={
attachment.attachmentRelatedObjects[0]
.relatedObjectType
}
model={{
uuid: attachment.attachmentRelatedObjects[0]
.relatedObjectUuid,
...attachment.attachmentRelatedObjects[0]
.relatedObject
}}
/>
) : (
<>No linked objects</>
)}
</td>
</tr>
)
})}
</tbody>
</Table>
</div>
) : (
<em>No attachments found</em>
)}
</Fieldset>
)
}

MyAttachments.propTypes = {
forceRefetch: PropTypes.bool.isRequired,
setForceRefetch: PropTypes.func.isRequired,
refetchCallback: PropTypes.func.isRequired,
pageDispatchers: PageDispatchersPropType
}

export default connect(null, mapPageDispatchersToProps)(MyAttachments)
37 changes: 37 additions & 0 deletions client/tests/webdriver/baseSpecs/myAttachments.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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", () => {
beforeEach("Open the my attachments page", async() => {
await MyAttachments.open("arthur")
})

afterEach("On the my attachments page...", async() => {
await MyAttachments.logout()
})

describe("When checking the content of the page", () => {
it("Should see a table of the user attachments", async() => {
await (await MyAttachments.getMyAttachments()).waitForDisplayed()
const myAttachments = await (
await MyAttachments.getMyAttachments()
).$$("tr")
// table has a header and 5 attachment rows
expect(myAttachments).to.have.length(5)
})
})
})
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 open(credentials) {
await super.open(PAGE_URL, credentials)
}

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

export default new MyAttachments()
14 changes: 14 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,14 @@
package mil.dds.anet.beans.search;

public class AttachmentSearchQuery extends AbstractSearchQuery<AttachmentSearchSortBy> {

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

@Override
public AttachmentSearchQuery clone() throws CloneNotSupportedException {
return (AttachmentSearchQuery) super.clone();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package mil.dds.anet.beans.search;

public enum AttachmentSearchSortBy implements ISortBy {
CREATED_AT
}
8 changes: 8 additions & 0 deletions src/main/java/mil/dds/anet/database/AttachmentDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
import mil.dds.anet.AnetObjectEngine;
import mil.dds.anet.beans.Attachment;
import mil.dds.anet.beans.GenericRelatedObject;
import mil.dds.anet.beans.Person;
import mil.dds.anet.beans.search.AbstractSearchQuery;
import mil.dds.anet.beans.lists.AnetBeanList;
import mil.dds.anet.beans.search.AttachmentSearchQuery;
import mil.dds.anet.database.mappers.AttachmentMapper;
import mil.dds.anet.database.mappers.GenericRelatedObjectMapper;
import mil.dds.anet.utils.DaoUtils;
Expand Down Expand Up @@ -52,6 +55,11 @@ public Attachment getByUuid(String uuid) {
return getByIds(Arrays.asList(uuid)).get(0);
}

public AnetBeanList<Attachment> search(Person user, AttachmentSearchQuery query) {
return AnetObjectEngine.getInstance().getSearcher().getAttachmentSearcher().runSearch(query,
user);
}

static class SelfIdBatcher extends IdBatcher<Attachment> {
private static final String sql = "/* batch.getAttachmentByUuids */ SELECT " + ATTACHMENT_FIELDS
+ " FROM \"attachments\" WHERE uuid IN ( <uuids> )";
Expand Down
Loading

0 comments on commit 28b4d02

Please sign in to comment.