Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/461 publish collection #500

Merged
merged 25 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
26df4f4
feat: add publishCollection use case
ekraffmiller Sep 20, 2024
ecfd384
feat: add PublishDatasetModal component and Story
ekraffmiller Sep 21, 2024
b493f6e
test: add PublishCollectionModal.spec.tsx
ekraffmiller Sep 22, 2024
9fcf940
feat: add Publish Button to Collection page
ekraffmiller Sep 22, 2024
c1593e1
fix: add isReleased condition to display button
ekraffmiller Sep 22, 2024
c45a232
merge latest from develop
ekraffmiller Sep 23, 2024
76d4e39
fix: navigation to Collections page
ekraffmiller Sep 23, 2024
eeb3a8a
feat: add Published Alert to Collections page
ekraffmiller Sep 23, 2024
080b4f2
test: update component and e2e test
ekraffmiller Sep 23, 2024
df53089
fix: lint errors
ekraffmiller Sep 23, 2024
063b856
fix: json format
ekraffmiller Sep 23, 2024
f6f0882
test: add test that Publish Button doesn't exist
ekraffmiller Oct 1, 2024
13b56ee
format: add space
ekraffmiller Oct 1, 2024
5e2bd7c
fix: Publish button props
ekraffmiller Oct 1, 2024
47ac8bf
fix: render syntax
ekraffmiller Oct 1, 2024
6d1faab
feat: Add centered prop to Modal
ekraffmiller Oct 1, 2024
6732194
fix: Publish Modal appearance, Publish Button props, add translation
ekraffmiller Oct 1, 2024
b5e607f
Merge branch 'develop' into feat/461-publish-collection
ekraffmiller Oct 1, 2024
ac783ca
fix: translation label
ekraffmiller Oct 1, 2024
8694161
resolve merge conflicts
ekraffmiller Oct 3, 2024
43477b8
fix: collection.json formatting
ekraffmiller Oct 3, 2024
c20f84c
fix: Publish Button layout
ekraffmiller Oct 3, 2024
ae29873
test: add Unpublished to Collection.stories.tsx
ekraffmiller Oct 3, 2024
a9f576b
Update src/sections/collection/Collection.module.scss
ekraffmiller Oct 3, 2024
3ecad57
Merge branch 'develop' into feat/461-publish-collection
g-saracca Oct 10, 2024
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
5 changes: 3 additions & 2 deletions packages/design-system/src/lib/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import { ModalFooter } from './ModalFooter'
interface ModalProps {
show: boolean
onHide: () => void
centered?: boolean
size?: 'sm' | 'lg' | 'xl'
}

function Modal({ show, onHide, size, children }: PropsWithChildren<ModalProps>) {
function Modal({ show, onHide, centered, size, children }: PropsWithChildren<ModalProps>) {
return (
<BSModal show={show} onHide={onHide} size={size}>
<BSModal centered={centered} show={show} onHide={onHide} size={size}>
{children}
</BSModal>
)
Expand Down
9 changes: 8 additions & 1 deletion public/locales/en/collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,12 @@
"datasetFilterTypeLabel": "Datasets",
"fileFilterTypeLabel": "Files",
"searchThisCollectionPlaceholder": "Search this collection...",
"searchSubmitButtonLabel": "Search submit"
"searchSubmitButtonLabel": "Search submit",
"publish": {
"title": "Publish Collection",
"button": "Publish",
"question": "Are you sure you want to publish your collection? Once you do so it must remain published.",
"error": "There was an error publishing your collection."
},
"publishedAlert": "Your collection is now public."
}
4 changes: 3 additions & 1 deletion public/locales/en/shared.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"asterisksIndicateRequiredFields": "Asterisks indicate required fields",
"remove": "Remove",
"add": "Add"
"add": "Add",
"cancel": "Cancel",
"continue": "Continue"
}
1 change: 1 addition & 0 deletions src/collection/domain/repositories/CollectionRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export interface CollectionRepository {
paginationInfo: CollectionItemsPaginationInfo,
searchCriteria?: CollectionSearchCriteria
): Promise<CollectionItemSubset>
publish(collectionIdOrAlias: number | string): Promise<void>
}
10 changes: 10 additions & 0 deletions src/collection/domain/useCases/publishCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { CollectionRepository } from '../repositories/CollectionRepository'

export function publishCollection(
collectionRepository: CollectionRepository,
id: string
): Promise<void> {
return collectionRepository.publish(id).catch((error: Error) => {
throw new Error(error.message)
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
getCollection,
getCollectionFacets,
getCollectionUserPermissions,
getCollectionItems
getCollectionItems,
publishCollection
} from '@iqss/dataverse-client-javascript'
import { JSCollectionMapper } from '../mappers/JSCollectionMapper'
import { CollectionDTO } from '../../domain/useCases/DTOs/CollectionDTO'
Expand Down Expand Up @@ -57,4 +58,7 @@ export class CollectionJSDataverseRepository implements CollectionRepository {
}
})
}
publish(collectionIdOrAlias: number | string): Promise<void> {
return publishCollection.execute(collectionIdOrAlias)
}
}
3 changes: 2 additions & 1 deletion src/sections/Route.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ export enum QueryParamKey {
PERSISTENT_ID = 'persistentId',
QUERY = 'q',
COLLECTION_ITEM_TYPES = 'types',
PAGE = 'page'
PAGE = 'page',
COLLECTION_ID = 'collectionId'
}
6 changes: 6 additions & 0 deletions src/sections/collection/Collection.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
gap: 10px;
}

.action-buttons {
display: flex;
justify-content: flex-end;
margin-bottom: 1rem;
}

.subtext {
color: $dv-subtext-color;
}
23 changes: 21 additions & 2 deletions src/sections/collection/Collection.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next'
import { Col, Row } from '@iqss/dataverse-design-system'
import { Alert, Col, Row } from '@iqss/dataverse-design-system'
import { CollectionRepository } from '../../collection/domain/repositories/CollectionRepository'
import { useCollection } from './useCollection'
import { useScrollTop } from '../../shared/hooks/useScrollTop'
Expand All @@ -13,11 +13,14 @@ import { CollectionInfo } from './CollectionInfo'
import { CollectionSkeleton } from './CollectionSkeleton'
import { PageNotFound } from '../page-not-found/PageNotFound'
import { CreatedAlert } from './CreatedAlert'
import { PublishCollectionButton } from './publish-collection/PublishCollectionButton'
import styles from './Collection.module.scss'

interface CollectionProps {
collectionRepository: CollectionRepository
collectionId: string
created: boolean
published: boolean
collectionQueryParams: UseCollectionQueryParamsReturnType
infiniteScrollEnabled?: boolean
}
Expand All @@ -26,21 +29,24 @@ export function Collection({
collectionId,
collectionRepository,
created,
published,
collectionQueryParams
}: CollectionProps) {
useTranslation('collection')
useScrollTop()
const { user } = useSession()
const { collection, isLoading } = useCollection(collectionRepository, collectionId)
const { collection, isLoading } = useCollection(collectionRepository, collectionId, published)
const { collectionUserPermissions } = useGetCollectionUserPermissions({
collectionIdOrAlias: collectionId,
collectionRepository
})

const canUserAddCollection = Boolean(collectionUserPermissions?.canAddCollection)
const canUserAddDataset = Boolean(collectionUserPermissions?.canAddDataset)
const canUserPublishCollection = user && Boolean(collectionUserPermissions?.canPublishCollection)
ekraffmiller marked this conversation as resolved.
Show resolved Hide resolved

const showAddDataActions = Boolean(user && (canUserAddCollection || canUserAddDataset))
const { t } = useTranslation('collection')

if (!isLoading && !collection) {
return <PageNotFound />
Expand All @@ -56,6 +62,19 @@ export function Collection({
<BreadcrumbsGenerator hierarchy={collection.hierarchy} />
<CollectionInfo collection={collection} />
{created && <CreatedAlert />}
{published && (
<Alert variant="success" dismissible={false}>
{t('publishedAlert')}
</Alert>
)}
{!collection.isReleased && canUserPublishCollection && (
<div className={styles['action-buttons']}>
<PublishCollectionButton
repository={collectionRepository}
collectionId={collection.id}
/>
</div>
)}
</>
)}
<CollectionItemsPanel
Expand Down
4 changes: 3 additions & 1 deletion src/sections/collection/CollectionFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ function CollectionWithSearchParams() {
const collectionQueryParams = useGetCollectionQueryParams()
const { collectionId = 'root' } = useParams<{ collectionId: string }>()
const location = useLocation()
const state = location.state as { created: boolean } | undefined
const state = location.state as { published: boolean; created: boolean } | undefined
const created = state?.created ?? false
const published = state?.published ?? false

return (
<Collection
collectionRepository={collectionRepository}
collectionId={collectionId}
created={created}
collectionQueryParams={collectionQueryParams}
published={published}
infiniteScrollEnabled={INFINITE_SCROLL_ENABLED}
/>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useTranslation } from 'react-i18next'
import { useState } from 'react'
import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository'
import { PublishCollectionModal } from './PublishCollectionModal'
import { Button } from '@iqss/dataverse-design-system'
import { GlobeAmericas } from 'react-bootstrap-icons'
import styles from '../../shared/add-data-actions/AddDataActionsButton.module.scss'

interface PublishCollectionButtonProps {
repository: CollectionRepository
collectionId: string
}
export function PublishCollectionButton({
repository,
collectionId
}: PublishCollectionButtonProps) {
const { t } = useTranslation('collection')
const [showModal, setShowModal] = useState(false)

return (
<>
<PublishCollectionModal
show={showModal}
repository={repository}
collectionId={collectionId}
handleClose={() => setShowModal(false)}
/>
<Button
icon={<GlobeAmericas className={styles.icon} />}
variant="secondary"
onClick={() => setShowModal(true)}
type="button">
{t('publish.button')}
</Button>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module";

.errorText {
color: $dv-danger-color;
}

.warningText {
margin-bottom: 0;
color: $dv-warning-color;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useTranslation } from 'react-i18next'
import { Button, Modal, Stack } from '@iqss/dataverse-design-system'
import { usePublishCollection } from './usePublishCollection'

import styles from './PublishCollectionModal.module.scss'
import { useNavigate } from 'react-router-dom'
import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository'
import { SubmissionStatus } from '../../shared/form/DatasetMetadataForm/useSubmitDataset'
import { RouteWithParams } from '../../Route.enum'

interface PublishCollectionModalProps {
show: boolean
repository: CollectionRepository
collectionId: string
handleClose: () => void
}

export function PublishCollectionModal({
show,
repository,
collectionId,
handleClose
}: PublishCollectionModalProps) {
const { t: tShared } = useTranslation('shared')
const { t: tCollection } = useTranslation('collection')

const navigate = useNavigate()
const { submissionStatus, submitPublish, publishError } = usePublishCollection(
repository,
collectionId,
onPublishSucceed
)

function onPublishSucceed() {
navigate(RouteWithParams.COLLECTIONS(collectionId), {
state: { published: true }
})
handleClose()
}

return (
<Modal show={show} onHide={handleClose} size="lg">
<Modal.Header>
<Modal.Title>{tCollection('publish.title')}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Stack direction="vertical">
<p className={styles.warningText}>{tCollection('publish.question')}</p>
{submissionStatus === SubmissionStatus.Errored && (
<p className={styles.errorText}>
`${tCollection('publish.error')} ${publishError ? publishError : ''}`
</p>
)}
</Stack>
</Modal.Body>
<Modal.Footer>
<Button
variant="primary"
disabled={submissionStatus === SubmissionStatus.IsSubmitting}
onClick={submitPublish}
type="submit">
{tShared('continue')}
</Button>
<Button
withSpacing
variant="secondary"
type="button"
onClick={handleClose}
disabled={submissionStatus === SubmissionStatus.IsSubmitting}>
{tShared('cancel')}
</Button>
</Modal.Footer>
</Modal>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useState } from 'react'
import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository'
import { publishCollection } from '../../../collection/domain/useCases/publishCollection'

import { SubmissionStatus } from '../../shared/form/DatasetMetadataForm/useSubmitDataset'

type UsePublishCollectionReturnType =
| {
submissionStatus:
| SubmissionStatus.NotSubmitted
| SubmissionStatus.IsSubmitting
| SubmissionStatus.SubmitComplete
submitPublish: () => void
publishError: null
}
| {
submissionStatus: SubmissionStatus.Errored
submitPublish: () => void
publishError: string
}

export function usePublishCollection(
repository: CollectionRepository,
collectionId: string,
onPublishSucceed: () => void
): UsePublishCollectionReturnType {
const [submissionStatus, setSubmissionStatus] = useState<SubmissionStatus>(
SubmissionStatus.NotSubmitted
)
const [publishError, setPublishError] = useState<string | null>(null)

const submitPublish = (): void => {
setSubmissionStatus(SubmissionStatus.IsSubmitting)

publishCollection(repository, collectionId)
.then(() => {
setPublishError(null)
setSubmissionStatus(SubmissionStatus.SubmitComplete)
onPublishSucceed()
return
})
.catch((err) => {
const errorMessage = err instanceof Error && err.message ? err.message : 'Unknown Error' // TODO: i18n

setPublishError(errorMessage)
setSubmissionStatus(SubmissionStatus.Errored)
})
}

return {
submissionStatus,
submitPublish,
publishError
} as UsePublishCollectionReturnType
}
10 changes: 7 additions & 3 deletions src/sections/collection/useCollection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import { Collection } from '../../collection/domain/models/Collection'
import { useEffect, useState } from 'react'
import { getCollectionById } from '../../collection/domain/useCases/getCollectionById'

export function useCollection(collectionRepository: CollectionRepository, collectionId: string) {
export function useCollection(
collectionRepository: CollectionRepository,
collectionId: string,
published?: boolean
) {
const [isLoading, setIsLoading] = useState(true)
const [collection, setCollection] = useState<Collection>()

useEffect(() => {
setIsLoading(true)

setCollection(undefined)
getCollectionById(collectionRepository, collectionId)
.then((collection: Collection | undefined) => {
setCollection(collection)
Expand All @@ -20,7 +24,7 @@ export function useCollection(collectionRepository: CollectionRepository, collec
.finally(() => {
setIsLoading(false)
})
}, [collectionRepository, collectionId])
}, [collectionRepository, collectionId, published])

return { collection, isLoading }
}
Loading
Loading