@@ -117,12 +145,41 @@ export function CreateDataset({
/>
)}
+ {datasetTypes.length > 0 && selectedType && (
+ <>
+ {datasetTypes.length === 1 ? (
+
+
+
+
+
+
+
+
+ {selectedType.displayName}
+
+
{selectedType.description}
+
+
+
+
+ ) : (
+
+ )}
+ >
+ )}
+
>
diff --git a/src/sections/create-dataset/dataset-type-select/DatasetTypeSelect.module.scss b/src/sections/create-dataset/dataset-type-select/DatasetTypeSelect.module.scss
new file mode 100644
index 000000000..160336305
--- /dev/null
+++ b/src/sections/create-dataset/dataset-type-select/DatasetTypeSelect.module.scss
@@ -0,0 +1,110 @@
+@use 'sass:color';
+@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
+
+.dataset-type-select {
+ position: relative;
+
+ .toggle {
+ position: relative;
+ display: grid;
+ background-color: #fff;
+
+ // Chevron icon
+ &::after {
+ position: absolute;
+ top: 50%;
+ right: 1rem;
+ display: inline-block;
+ width: 0.45em;
+ height: 0.45em;
+ border-color: rgb(0 0 0 / 75%);
+ border-style: solid;
+ border-width: 2px 2px 0 0;
+ transform: translateY(-60%) rotate(-225deg);
+ content: '';
+ }
+
+ > input[type='button'] {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ grid-area: 1 / 1;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ padding: var(--toggle-padding);
+ background-color: transparent;
+ border: solid 1px $dv-secondary-color;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+
+ &::after {
+ display: none;
+ }
+
+ &:focus-within,
+ &:focus {
+ border-color: color.adjust($dv-primary-color, $alpha: -0.4);
+ outline: 0;
+ box-shadow: 0 0 0 0.25rem color.adjust($dv-primary-color, $alpha: -0.7);
+ }
+ }
+
+ > span {
+ position: relative;
+ z-index: 2;
+ grid-area: 1 / 1;
+ padding: var(--toggle-padding);
+ pointer-events: none;
+
+ .single-selected-option {
+ margin: 0;
+ }
+ }
+ }
+
+ .menu {
+ position: absolute;
+ top: calc(100% + 5px);
+ left: 0;
+ z-index: 120;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ width: 100%;
+ background: #fff;
+ max-width: 100%;
+ max-height: 300px;
+ padding: 0.5rem;
+ overflow-x: hidden;
+ overflow-y: auto;
+ border: 1px solid #ddd;
+ border-radius: 6px;
+ box-shadow: 0 5px 10px rgb(0 0 0 / 15%);
+ opacity: 0;
+ transform: translateY(-6px) scale(0.98);
+ pointer-events: none;
+
+ &.open {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ pointer-events: auto;
+ }
+
+ .type-option {
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover,
+ &.selected {
+ background: color.adjust($dv-primary-color, $alpha: -0.9);
+ border: solid 1px $dv-primary-color;
+ }
+
+ &:active {
+ background: color.adjust($dv-primary-color, $alpha: -0.6);
+ }
+ }
+ }
+}
diff --git a/src/sections/create-dataset/dataset-type-select/DatasetTypeSelect.tsx b/src/sections/create-dataset/dataset-type-select/DatasetTypeSelect.tsx
new file mode 100644
index 000000000..94a0def74
--- /dev/null
+++ b/src/sections/create-dataset/dataset-type-select/DatasetTypeSelect.tsx
@@ -0,0 +1,111 @@
+import { useState, useRef, useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
+import cn from 'classnames'
+import { Card, Col, Form } from '@iqss/dataverse-design-system'
+import { DatasetType } from '@/dataset/domain/models/DatasetType'
+import styles from './DatasetTypeSelect.module.scss'
+
+interface DatasetTypeSelectProps {
+ datasetTypes: DatasetType[]
+ onChange: (selectedTypeId: string) => void
+ selectedType: DatasetType
+ disabled?: boolean
+}
+
+export const DatasetTypeSelect = ({
+ datasetTypes,
+ onChange,
+ selectedType,
+ disabled = false
+}: DatasetTypeSelectProps) => {
+ const { t } = useTranslation('createDataset')
+ const [isOpen, setIsOpen] = useState(false)
+ const dropdownRef = useRef
(null)
+
+ const handleSelectType = (typeId: number) => {
+ onChange(typeId.toString())
+ setIsOpen(false)
+ }
+
+ const handleToggle = () => {
+ if (!disabled) {
+ setIsOpen((prev) => !prev)
+ }
+ }
+
+ const handleKeyDown = (event: KeyboardEvent, typeId: number) => {
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault()
+ handleSelectType(typeId)
+ } else if (event.key === 'Escape') {
+ setIsOpen(false)
+ }
+ }
+
+ // Close menu when clicking outside, focusing outside, or pressing Escape
+ useEffect(() => {
+ const handleClose = (event: MouseEvent | FocusEvent) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
+ setIsOpen(false)
+ }
+ }
+ const handleDocKeyDown = (event: KeyboardEvent) => {
+ if (event.key === 'Escape') setIsOpen(false)
+ }
+
+ document.addEventListener('mousedown', handleClose)
+ document.addEventListener('focusin', handleClose)
+ document.addEventListener('keydown', handleDocKeyDown)
+ return () => {
+ document.removeEventListener('mousedown', handleClose)
+ document.removeEventListener('focusin', handleClose)
+ document.removeEventListener('keydown', handleDocKeyDown)
+ }
+ }, [])
+
+ return (
+
+
+ {t('datasetType.label')}
+
+
+ {t('datasetType.helpText')}
+
+
+
+
+ {selectedType.displayName}
+
+
+
+ {datasetTypes.map((dt) => (
+ !disabled && handleSelectType(dt.id)}
+ onKeyDown={(e) => !disabled && handleKeyDown(e as unknown as KeyboardEvent, dt.id)}
+ tabIndex={disabled ? -1 : 0}
+ role="menuitem"
+ key={dt.id}>
+
+
+ {dt.displayName}
+
+ {dt.description}
+
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/src/sections/dataset/Dataset.tsx b/src/sections/dataset/Dataset.tsx
index 68d5dfaa4..20b4b679c 100644
--- a/src/sections/dataset/Dataset.tsx
+++ b/src/sections/dataset/Dataset.tsx
@@ -140,6 +140,7 @@ export function Dataset({
diff --git a/src/sections/dataset/dataset-summary/DatasetSummary.tsx b/src/sections/dataset/dataset-summary/DatasetSummary.tsx
index 5d9bf73cb..b29e287a5 100644
--- a/src/sections/dataset/dataset-summary/DatasetSummary.tsx
+++ b/src/sections/dataset/dataset-summary/DatasetSummary.tsx
@@ -1,11 +1,14 @@
+import { Row, Col } from '@iqss/dataverse-design-system'
import { SummaryFields } from './SummaryFields'
import { DatasetLicense, DatasetMetadataBlock } from '../../../dataset/domain/models/Dataset'
import { SummaryLicense } from './SummaryLicense'
import { MetadataBlockInfoRepository } from '../../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { useTranslation } from 'react-i18next'
interface DatasetSummaryProps {
summaryFields: DatasetMetadataBlock[]
license?: DatasetLicense
+ datasetType?: string
metadataBlockInfoRepository: MetadataBlockInfoRepository
onCustomTermsClick: () => void
}
@@ -13,9 +16,12 @@ interface DatasetSummaryProps {
export function DatasetSummary({
summaryFields,
license,
+ datasetType,
metadataBlockInfoRepository,
onCustomTermsClick
}: DatasetSummaryProps) {
+ const { t } = useTranslation('dataset')
+
return (
<>
+ {datasetType && (
+
+
+ {t('datasetType.label')}
+
+
+ {datasetType}
+
+
+ )}
>
)
}
diff --git a/src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx b/src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx
index 80f78a7e5..15d7b20ff 100644
--- a/src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx
+++ b/src/sections/edit-dataset-metadata/EditDatasetMetadata.tsx
@@ -1,6 +1,6 @@
import { useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Alert, Tabs } from '@iqss/dataverse-design-system'
+import { Alert, Col, Form, Tabs } from '@iqss/dataverse-design-system'
import { MetadataBlockInfoRepository } from '../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
import { useDataset } from '../dataset/DatasetContext'
import { useLoading } from '../../shared/contexts/loading/LoadingContext'
@@ -54,6 +54,18 @@ export const EditDatasetMetadata = ({ metadataBlockInfoRepository }: EditDataset
{t('infoAlert.text')}
+
+
+ {t('datasetType.label')}
+
+
+
+
+
@@ -65,6 +77,7 @@ export const EditDatasetMetadata = ({ metadataBlockInfoRepository }: EditDataset
datasetPersistentID={dataset.persistentId}
datasetMetadaBlocksCurrentValues={dataset.metadataBlocks}
datasetLastUpdateTime={dataset.version.lastUpdateTime}
+ datasetTypeName={dataset.datasetType}
/>
diff --git a/src/sections/shared/form/DatasetMetadataForm/MetadataForm/index.tsx b/src/sections/shared/form/DatasetMetadataForm/MetadataForm/index.tsx
index 60d9413cd..13d147854 100644
--- a/src/sections/shared/form/DatasetMetadataForm/MetadataForm/index.tsx
+++ b/src/sections/shared/form/DatasetMetadataForm/MetadataForm/index.tsx
@@ -15,6 +15,7 @@ import { SeparationLine } from '@/sections/shared/layout/SeparationLine/Separati
import { usePrefillFieldsWithUserData } from './usePrefillFieldsWithUserData'
import { DatasetTemplateInstruction } from '@/templates/domain/models/Template'
import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider'
+import { DatasetType } from '@/dataset/domain/models/DatasetType'
import styles from './index.module.scss'
interface FormProps {
@@ -25,6 +26,7 @@ interface FormProps {
datasetPersistentID?: string
datasetLastUpdateTime?: string
datasetTemplateInstructions?: DatasetTemplateInstruction[]
+ datasetTypeName?: DatasetType['name']
}
export const MetadataForm = ({
@@ -34,7 +36,8 @@ export const MetadataForm = ({
metadataBlocksInfo,
datasetPersistentID,
datasetLastUpdateTime,
- datasetTemplateInstructions
+ datasetTemplateInstructions,
+ datasetTypeName
}: FormProps) => {
const { datasetRepository } = useDatasetRepositories()
const { user } = useSession()
@@ -56,7 +59,8 @@ export const MetadataForm = ({
datasetRepository,
onSubmitDatasetError,
datasetPersistentID,
- datasetLastUpdateTime
+ datasetLastUpdateTime,
+ datasetTypeName
)
usePrefillFieldsWithUserData({ mode, user, formDefaultValues, setValue })
diff --git a/src/sections/shared/form/DatasetMetadataForm/index.tsx b/src/sections/shared/form/DatasetMetadataForm/index.tsx
index 71ff59017..913c0d338 100644
--- a/src/sections/shared/form/DatasetMetadataForm/index.tsx
+++ b/src/sections/shared/form/DatasetMetadataForm/index.tsx
@@ -8,6 +8,7 @@ import { MetadataForm } from './MetadataForm'
import { DatasetMetadataBlocks } from '../../../../dataset/domain/models/Dataset'
import { Alert } from '@iqss/dataverse-design-system'
import { Template } from '@/templates/domain/models/Template'
+import { DatasetType } from '@/dataset/domain/models/DatasetType'
type DatasetMetadataFormProps =
| {
@@ -18,6 +19,7 @@ type DatasetMetadataFormProps =
datasetMetadaBlocksCurrentValues?: never
datasetLastUpdateTime?: never
datasetTemplate?: Template
+ datasetTypeName?: DatasetType['name']
}
| {
mode: 'edit'
@@ -27,6 +29,7 @@ type DatasetMetadataFormProps =
datasetMetadaBlocksCurrentValues: DatasetMetadataBlocks
datasetLastUpdateTime?: string
datasetTemplate?: never
+ datasetTypeName?: DatasetType['name']
}
export type DatasetMetadataFormMode = 'create' | 'edit'
@@ -38,7 +41,8 @@ export const DatasetMetadataForm = ({
metadataBlockInfoRepository,
datasetMetadaBlocksCurrentValues,
datasetLastUpdateTime,
- datasetTemplate
+ datasetTemplate,
+ datasetTypeName
}: DatasetMetadataFormProps) => {
const { setIsLoading } = useLoading()
@@ -49,7 +53,8 @@ export const DatasetMetadataForm = ({
} = useGetMetadataBlocksInfo({
mode: 'create',
collectionId,
- metadataBlockInfoRepository
+ metadataBlockInfoRepository,
+ datasetTypeName
})
const {
@@ -59,7 +64,8 @@ export const DatasetMetadataForm = ({
} = useGetMetadataBlocksInfo({
mode: 'edit',
collectionId,
- metadataBlockInfoRepository
+ metadataBlockInfoRepository,
+ datasetTypeName
})
const isLoadingData =
@@ -104,6 +110,7 @@ export const DatasetMetadataForm = ({
datasetPersistentID={datasetPersistentID}
datasetLastUpdateTime={datasetLastUpdateTime}
datasetTemplateInstructions={datasetTemplate?.instructions}
+ datasetTypeName={datasetTypeName}
/>
)
}
diff --git a/src/sections/shared/form/DatasetMetadataForm/useGetMetadataBlocksInfo.tsx b/src/sections/shared/form/DatasetMetadataForm/useGetMetadataBlocksInfo.tsx
index 27e5ce567..a38b8e86f 100644
--- a/src/sections/shared/form/DatasetMetadataForm/useGetMetadataBlocksInfo.tsx
+++ b/src/sections/shared/form/DatasetMetadataForm/useGetMetadataBlocksInfo.tsx
@@ -3,12 +3,14 @@ import { getMetadataBlockInfoByCollectionId } from '../../../../metadata-block-i
import { getDisplayedOnCreateMetadataBlockInfoByCollectionId } from '../../../../metadata-block-info/domain/useCases/getDisplayedOnCreateMetadataBlockInfoByCollectionId'
import { MetadataBlockInfoRepository } from '../../../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
import { MetadataBlockInfo } from '../../../../metadata-block-info/domain/models/MetadataBlockInfo'
+import { DatasetType } from '@/dataset/domain/models/DatasetType'
import { DatasetMetadataFormMode } from '.'
interface Props {
mode: DatasetMetadataFormMode
collectionId: string
metadataBlockInfoRepository: MetadataBlockInfoRepository
+ datasetTypeName?: DatasetType['name']
}
interface UseGetMetadataBlocksInfoReturn {
@@ -20,7 +22,8 @@ interface UseGetMetadataBlocksInfoReturn {
export const useGetMetadataBlocksInfo = ({
mode,
collectionId,
- metadataBlockInfoRepository
+ metadataBlockInfoRepository,
+ datasetTypeName
}: Props): UseGetMetadataBlocksInfoReturn => {
const [metadataBlocksInfo, setMetadataBlocksInfo] = useState