Skip to content

Commit

Permalink
feat: default values and enhanced identifier/alias field
Browse files Browse the repository at this point in the history
  • Loading branch information
g-saracca committed Jul 10, 2024
1 parent c3340a7 commit 371be44
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 78 deletions.
7 changes: 4 additions & 3 deletions public/locales/en/createCollection.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
"required": "Host Collection is required"
},
"name": {
"label": "Dataverse Name",
"label": "Collection Name",
"description": "The project, department, university, professor, or journal this collection will contain data for.",
"required": "Dataverse Name is required"
"required": "Collection Name is required"
},
"affiliation": {
"label": "Affiliation",
Expand All @@ -22,7 +22,8 @@
"invalid": {
"format": "Identifier is not a valid identifier. Valid characters are a-Z, 0-9, '_', and '-'.",
"maxLength": "Identifier must be at most {{maxLength}} characters."
}
},
"suggestion": "Psst... try this"
},
"storage": {
"label": "Storage",
Expand Down
30 changes: 15 additions & 15 deletions src/sections/create-collection/CreateCollection.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useCollection } from '../collection/useCollection'
import { CollectionRepository } from '../../collection/domain/repositories/CollectionRepository'
import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator'
import { useTranslation } from 'react-i18next'
import { RequiredFieldText } from '../shared/form/RequiredFieldText/RequiredFieldText'
import { CollectionForm, CollectionFormProps } from './collection-form'
import { useEffect } from 'react'
import { useLoading } from '../loading/LoadingContext'
import { useSession } from '../session/SessionContext'
import { RequiredFieldText } from '../shared/form/RequiredFieldText/RequiredFieldText'
import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator'
import { CollectionForm, CollectionFormData } from './collection-form'

interface CreateCollectionProps {
ownerCollectionId: string
Expand All @@ -18,14 +19,13 @@ export function CreateCollection({
}: CreateCollectionProps) {
const { t } = useTranslation('createCollection')
const { isLoading, setIsLoading } = useLoading()
const { user } = useSession()

const { collection, isLoading: isLoadingCollection } = useCollection(
collectionRepository,
ownerCollectionId
)

console.log({ collection, isLoadingCollection })

// TODO:ME If is not loading and collection is not found, show a message and navigate to the root collection
// One good thing would be add toastify to show messages on top of the page for this kind of things

Expand All @@ -44,15 +44,15 @@ export function CreateCollection({
return <p>Loading...</p>
}
// TODO:ME name = user name, affiliation = user affiliation, first email = user email
const formDefaultValues: CollectionFormProps['defaultValues'] = {
parentCollectionName: collection.name,
name: 'The Nameeeee',
alias: 'Some Alias',
type: 'Department',
contacts: [{ value: '' }],
affiliation: 'Some Affi',
const formDefaultValues: Partial<CollectionFormData> = {
hostCollection: collection.name,
name: user?.displayName ? `${user?.displayName} Collection` : '',
alias: '',
type: undefined,
contacts: [{ value: user?.email ?? '' }],
affiliation: user?.affiliation ?? '',
storage: 'Local (Default)',
description: 'Some Description'
description: ''
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,35 @@
:global .input-group-text {
font-size: 14px;
}

.suggestion-container {
display: flex;
gap: 0.5rem;
align-items: center;
width: 100%;
margin-top: -0.75rem;
margin-bottom: 1rem;
padding-top: 0.25rem;

:global .form-text {
margin-top: 0;
}

.apply-suggestion-btn {
display: grid;
place-items: center;
padding: 3px;
border-radius: 50%;
}
}
}

.contact-row {
margin-bottom: 1rem;

&:last-child {
margin-bottom: 0;
}
}

.dynamic-fields-button-container {
Expand Down
30 changes: 16 additions & 14 deletions src/sections/create-collection/collection-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import { useRef } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import { TopFieldsSection } from './top-fields-section'
import {
CollectionType,
CollectionStorage
} from '../../../collection/domain/useCases/DTOs/CollectionDTO'
import { SeparationLine } from '../../shared/layout/SeparationLine/SeparationLine'
import { TopFieldsSection } from './top-fields-section'

export interface CollectionFormProps {
defaultValues: {
parentCollectionName: string
name: string
affiliation?: string
alias: string
storage?: CollectionStorage
type: CollectionType
description?: string
contacts: { value: string }[]
}
defaultValues: Partial<CollectionFormData>
}

export type CollectionFormData = {
hostCollection: string
name: string
affiliation?: string
alias: string
storage?: CollectionStorage
type: CollectionType
description?: string
contacts: { value: string }[]
}

export const CollectionForm = ({ defaultValues }: CollectionFormProps) => {
const formContainerRef = useRef<HTMLDivElement>(null)

const form = useForm({
const form = useForm<CollectionFormData>({
mode: 'onChange',
defaultValues
})
Expand All @@ -38,8 +40,8 @@ export const CollectionForm = ({ defaultValues }: CollectionFormProps) => {
if (!isButton && !isButtonTypeSubmit) e.preventDefault()
}

const submitForm = (formValues: any) => {
// console.log({ formValues })
const submitForm = (formValues: CollectionFormData) => {
console.log({ formValues })
}

function onSubmittedCollectionError() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const ContactsField = ({ rules }: ContactsFieldProps) => {
</Form.Group.Label>

{(fieldsArray as { id: string; value: string }[]).map((field, index) => (
<Row className="mb-3" key={field.id}>
<Row className={styles['contact-row']} key={field.id}>
<Controller
name={builtFieldNameWithIndex(index)}
control={control}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Controller, UseControllerProps, useFormContext, useWatch } from 'react-hook-form'
import { Button, Col, Form } from '@iqss/dataverse-design-system'
import { CheckLg } from 'react-bootstrap-icons'
import styles from '../CollectionForm.module.scss'

interface IdentifierFieldProps {
rules: UseControllerProps['rules']
}

export const IdentifierField = ({ rules }: IdentifierFieldProps) => {
const { t } = useTranslation('createCollection')
const { control, setValue } = useFormContext()
const nameFieldValue = useWatch({ name: 'name' }) as string

const collectionNameToAlias = (name: string) => {
if (!name) return ''

return name
.toLowerCase() // Convert to lowercase
.trim() // Remove leading/trailing whitespace
.replace(/[^\w\s-]/g, '') // Remove non-alphanumeric characters except for spaces and hyphens
.replace(/[\s_-]+/g, '-') // Replace spaces and underscores with hyphens
.replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
}

const aliasSuggestion = useMemo(() => collectionNameToAlias(nameFieldValue), [nameFieldValue])

const applyAliasSuggestion = () =>
setValue('alias', aliasSuggestion, { shouldValidate: true, shouldDirty: true })

return (
<Form.Group controlId="identifier" as={Col} md={6} className={styles['identifier-field-group']}>
<Form.Group.Label message={t('fields.alias.description')} required={true}>
{t('fields.alias.label')}
</Form.Group.Label>

<Controller
name="alias"
control={control}
rules={rules}
render={({ field: { onChange, ref, value }, fieldState: { invalid, error } }) => (
<Col>
<Form.InputGroup hasValidation>
<Form.InputGroup.Text>{window.location.origin}/spa/collections/</Form.InputGroup.Text>
<Form.Group.Input
type="text"
aria-label="identifier"
value={value as string}
onChange={onChange}
isInvalid={invalid}
aria-required={true}
ref={ref}
/>
<Form.Group.Feedback type="invalid">{error?.message}</Form.Group.Feedback>
</Form.InputGroup>

{aliasSuggestion !== '' && value !== aliasSuggestion && !invalid && (
<div className={styles['suggestion-container']}>
<Form.Group.Text>
{t('fields.alias.suggestion')} 👉 <strong>{aliasSuggestion}</strong>
</Form.Group.Text>

<Button
type="button"
size="sm"
icon={<CheckLg />}
variant="secondary"
onClick={applyAliasSuggestion}
className={styles['apply-suggestion-btn']}
aria-label="Apply suggestion"
/>
</div>
)}
</Col>
)}
/>
</Form.Group>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
collectionTypeOptions,
collectionStorageOptions
} from '../../../../collection/domain/useCases/DTOs/CollectionDTO'
import { ContactsField } from './ContactsField'
import styles from '../CollectionForm.module.scss'
import { Validator } from '../../../../shared/helpers/Validator'
import { ContactsField } from './ContactsField'
import { IdentifierField } from './IdentifierField'

export const TopFieldsSection = () => {
const { t } = useTranslation('createCollection')
Expand Down Expand Up @@ -69,9 +69,6 @@ export const TopFieldsSection = () => {
aria-required={true}
ref={ref}
disabled
// readOnly
// plaintext
// TODO:ME Check this, modify ds component?
/>
<Form.Group.Feedback type="invalid">{error?.message}</Form.Group.Feedback>
</Col>
Expand Down Expand Up @@ -112,7 +109,6 @@ export const TopFieldsSection = () => {
<Controller
name="affiliation"
control={control}
rules={affiliationRules}
render={({ field: { onChange, ref, value }, fieldState: { invalid, error } }) => (
<Col>
<Form.Group.Input
Expand All @@ -131,48 +127,14 @@ export const TopFieldsSection = () => {

{/* Identifier(alias) & Storage */}
<Row>
<Form.Group
controlId="identifier"
as={Col}
md={6}
className={styles['identifier-field-group']}>
<Form.Group.Label message={t('fields.alias.description')} required={true}>
{t('fields.alias.label')}
</Form.Group.Label>
<Controller
name="alias"
control={control}
rules={aliasRules}
render={({ field: { onChange, ref, value }, fieldState: { invalid, error } }) => (
<Col>
<Form.InputGroup hasValidation>
<Form.InputGroup.Text>
{window.location.origin}/spa/collections/
</Form.InputGroup.Text>
<Form.Group.Input
type="text"
placeholder={t('fields.alias.label')}
aria-label="identifier"
value={value as string}
onChange={onChange}
isInvalid={invalid}
aria-required={true}
ref={ref}
/>
<Form.Group.Feedback type="invalid">{error?.message}</Form.Group.Feedback>
</Form.InputGroup>
</Col>
)}
/>
</Form.Group>
<IdentifierField rules={aliasRules} />
<Form.Group controlId="storage" as={Col} md={6}>
<Form.Group.Label message={t('fields.storage.description')}>
{t('fields.storage.label')}
</Form.Group.Label>
<Controller
name="storage"
control={control}
rules={storageRules}
render={({ field: { onChange, ref, value }, fieldState: { invalid, error } }) => (
<Col>
<Form.Group.Select
Expand All @@ -198,7 +160,7 @@ export const TopFieldsSection = () => {
{/* Category (type) & Description */}
<Row>
<Form.Group controlId="type" as={Col} md={6}>
<Form.Group.Label message={t('fields.type.description')}>
<Form.Group.Label message={t('fields.type.description')} required>
{t('fields.type.label')}
</Form.Group.Label>
<Controller
Expand All @@ -210,6 +172,7 @@ export const TopFieldsSection = () => {
<Form.Group.Select
onChange={onChange}
value={value as string}
aria-required={true}
isInvalid={invalid}
ref={ref}>
<option value="">Select...</option>
Expand All @@ -226,20 +189,18 @@ export const TopFieldsSection = () => {
</Form.Group>

<Form.Group controlId="description" as={Col} md={6}>
<Form.Group.Label message={t('fields.description.description')} required={true}>
<Form.Group.Label message={t('fields.description.description')}>
{t('fields.description.label')}
</Form.Group.Label>
<Controller
name="description"
control={control}
rules={descriptionRules}
render={({ field: { onChange, ref, value }, fieldState: { invalid, error } }) => (
<Col>
<Form.Group.TextArea
value={value as string}
onChange={onChange}
isInvalid={invalid}
aria-required={true}
ref={ref}
/>
<Form.Group.Feedback type="invalid">{error?.message}</Form.Group.Feedback>
Expand Down

0 comments on commit 371be44

Please sign in to comment.