Skip to content

Commit

Permalink
feat: permet d'enregistrer des métadonnées de type revue sur un corpu…
Browse files Browse the repository at this point in the history
…s (au format JSON)
  • Loading branch information
ggrossetie committed Nov 5, 2024
1 parent 8424b72 commit f2099a4
Show file tree
Hide file tree
Showing 17 changed files with 287 additions and 75 deletions.
21 changes: 9 additions & 12 deletions front/src/components/Form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import Form, { getDefaultRegistry } from '@rjsf/core'
import validator from '@rjsf/validator-ajv8'
import { set } from 'object-path-immutable'
import { Translation } from 'react-i18next'
import basicUiSchema from '../schemas/ui-schema-basic-override.json'
import defaultUiSchema from '../schemas/ui-schema-editor.json'
import defaultSchema from '../schemas/data-schema.json'

// REMIND: use a custom SelectWidget to support "ui:emptyValue"
// remove once fixed in https://github.com/rjsf-team/react-jsonschema-form/issues/1041
Expand Down Expand Up @@ -183,7 +180,7 @@ function ObjectFieldTemplate (properties) {
const element = properties.properties.find((element) => element.name === field)

if (!element) {
console.error('Field configuration not found for "%s" in \'ui:groups\' "%s" — part of %o', field, title, fields)
console.error('Field configuration not found for "%s" in \'ui:groups\' "%s" — part of %o', field, title || '', fields)
}

return [field, element]
Expand Down Expand Up @@ -237,14 +234,16 @@ const customFields = {
/**
*
* @param initialFormData
* @param basicMode
* @param schema
* @param uiSchema
* @param {(any) => void} onChange
* @return {Element}
* @constructor
*/
export default function SchemaForm ({
formData: initialFormData,
basicMode,
schema,
uiSchema,
onChange = () => {
},
}) {
Expand All @@ -261,10 +260,6 @@ export default function SchemaForm ({
},
}), [onChange, setFormData])

const effectiveUiSchema = useMemo(
() => (basicMode ? { ...defaultUiSchema, ...basicUiSchema } : defaultUiSchema),
[basicMode]
)

const customWidgets = {
SelectWidget: CustomSelectWidget,
Expand All @@ -289,12 +284,12 @@ export default function SchemaForm ({
<Form
className={styles.form}
formContext={formContext}
schema={defaultSchema}
schema={schema}
name="Metadata"
templates={customTemplates}
widgets={customWidgets}
fields={customFields}
uiSchema={effectiveUiSchema}
uiSchema={uiSchema}
formData={formData}
onChange={handleUpdate}
onError={setErrors}
Expand All @@ -307,6 +302,8 @@ export default function SchemaForm ({

SchemaForm.propTypes = {
formData: PropTypes.object,
schema: PropTypes.object,
uiSchema: PropTypes.object,
basicMode: PropTypes.bool,
onChange: PropTypes.func
}
9 changes: 2 additions & 7 deletions front/src/components/Form.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@ import React from 'react'
import Component from './Form.jsx'

describe('Form', () => {
test('renders in basic mode with an empty form data', () => {
const { getByRole } = render(<Component formData={{}} basicMode={true} />)

expect(getByRole('form')).toBeInTheDocument()
})
test('renders in advanced mode with an empty form data', () => {
const { getByRole } = render(<Component formData={{}} basicMode={false} />)
test('renders a form with an empty form data and empty schema/uiSchema', () => {
const { getByRole } = render(<Component formData={{}} schema={{}} uiSchema={{}} />)

expect(getByRole('form')).toBeInTheDocument()
})
Expand Down
8 changes: 7 additions & 1 deletion front/src/components/Select.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import fieldStyles from './field.module.scss'
import { clsx } from 'clsx'

const Select = forwardRef((props, forwardedRef) => {
const alignLabel = props.alignLabel !== false
return (<div className={clsx(fieldStyles.field, 'control-field')} ref={forwardedRef}>
{props.label && <label htmlFor={props.id}>{props.label}</label>}
{props.label && <label htmlFor={props.id} style={
alignLabel ? {
flexBasis: "10rem",
textAlign: "end"
} : {}
}>{props.label}</label>}
<div className={styles.selectContainer}>
<select className={props.className || styles.select} {...props}>{props.children}</select>
</div>
Expand Down
15 changes: 11 additions & 4 deletions front/src/components/Write/yamleditor/YamlEditor.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import React, { useCallback } from 'react'
import React, { useCallback, useMemo } from 'react'
import PropTypes from 'prop-types'
import YAML from 'js-yaml'
import Form from '../../Form'
import { toYaml } from "../metadata/yaml.js";
import { convertLegacyValues } from "../../metadata/MetadataValues.js";
import { toYaml } from "../metadata/yaml.js"
import { convertLegacyValues } from "../../metadata/MetadataValues.js"
import defaultUiSchema from "../../../schemas/ui-schema-editor.json"
import basicUiSchema from "../../../schemas/ui-schema-basic-override.json"
import defaultSchema from '../../../schemas/data-schema.json'

export default function YamlEditor({ yaml = '', basicMode = false, onChange = () => {} }) {
const effectiveUiSchema = useMemo(
() => (basicMode ? { ...defaultUiSchema, ...basicUiSchema } : defaultUiSchema),
[basicMode]
)
const [parsed = {}] = YAML.loadAll(yaml)
const formData = convertLegacyValues(parsed)
const handleChange = useCallback((newFormData) => onChange(toYaml(newFormData)), [onChange])
return <Form formData={formData} basicMode={basicMode} onChange={handleChange}/>
return <Form formData={formData} schema={defaultSchema} uiSchema={effectiveUiSchema} onChange={handleChange}/>
}

YamlEditor.propTypes = {
Expand Down
2 changes: 1 addition & 1 deletion front/src/components/corpus/Corpus.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ mutation deleteCorpus($corpusId: ID!) {
}
}

mutation updateMetadata($corpusId: ID!, $metadata: String!) {
mutation updateMetadata($corpusId: ID!, $metadata: JSON!) {
corpus(corpusId: $corpusId) {
updateMetadata(metadata: $metadata) {
_id
Expand Down
4 changes: 4 additions & 0 deletions front/src/components/corpus/CorpusItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import CorpusUpdate from './CorpusUpdate.jsx'

import { deleteCorpus } from './Corpus.graphql'
import styles from './corpusItem.module.scss'
import CorpusMetadataModal from "./CorpusMetadataModal.jsx";


export default function CorpusItem({ corpus }) {
Expand Down Expand Up @@ -90,6 +91,8 @@ export default function CorpusItem({ corpus }) {
<Settings/>
</Button>

<CorpusMetadataModal corpusId={corpusId} initialValue={corpus.metadata}/>

<Button title="Delete" icon={true} onClick={(event) => {
event.preventDefault()
setDeleteCorpusVisible(true)
Expand Down Expand Up @@ -147,6 +150,7 @@ CorpusItem.propTypes = {
_id: PropTypes.string.isRequired,
name: PropTypes.string,
description: PropTypes.string,
metadata: PropTypes.object,
creator: PropTypes.shape({
displayName: PropTypes.string,
username: PropTypes.string
Expand Down
72 changes: 72 additions & 0 deletions front/src/components/corpus/CorpusMetadataModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import PropTypes from "prop-types"
import React, { useCallback, useState } from "react"
import { Modal as GeistModal, useModal } from "@geist-ui/core"
import { useDispatch } from "react-redux"
import { useTranslation } from "react-i18next"

import { updateMetadata } from './Corpus.graphql'
import { useGraphQL } from "../../helpers/graphQL.js"
import MetadataForm from '../metadata/MetadataForm.jsx'
import corpusMetadataSchema from '../../schemas/corpus-journal-metadata.schema.json'
import corpusUiSchema from '../../schemas/corpus-journal-ui-schema.json'
import Button from "../Button.jsx";
import { List } from "react-feather";
import Select from "../Select.jsx";


export default function CorpusMetadataModal({ corpusId, initialValue }) {
const { t } = useTranslation()
const dispatch = useDispatch()
const runQuery = useGraphQL()
const [corpusMetadata, setCorpusMetadata] = useState(initialValue)
const {
visible: editMetadataVisible,
setVisible: setEditMetadataVisible,
bindings: editMetadataBindings,
} = useModal()

const handleUpdateMetadata = useCallback(async () => {
await runQuery({
query: updateMetadata,
variables: {
corpusId: corpusId,
metadata: corpusMetadata
}
})
dispatch({ type: 'SET_LATEST_CORPUS_UPDATED', data: { corpusId, date: new Date() } })
setEditMetadataVisible(false)
}, [corpusId, dispatch, runQuery, updateMetadata, corpusId, corpusMetadata])

const handleMetadataUpdated = useCallback((metadata) => {
setCorpusMetadata(metadata)
}, [setCorpusMetadata])

return (
<>
<Button title="Metadata" icon={true} onClick={() => setEditMetadataVisible(true)}>
<List/>
</Button>
<GeistModal width="40rem" visible={editMetadataVisible} {...editMetadataBindings}>
<h2>{t('corpus.metadataModal.title')}</h2>
<GeistModal.Content>
<Select alignLabel={false} id="corpus-type" label="Type">
<option value="journal">{t('corpus.type.journal')}</option>
</Select>
<MetadataForm
data={initialValue}
schema={corpusMetadataSchema}
uiSchema={corpusUiSchema}
onChange={handleMetadataUpdated}
/>
</GeistModal.Content>
<GeistModal.Action passive
onClick={() => setEditMetadataVisible(false)}>{t('modal.close.text')}</GeistModal.Action>
<GeistModal.Action onClick={handleUpdateMetadata}>{t('modal.saveButton.text')}</GeistModal.Action>
</GeistModal>
</>)
}

CorpusMetadataModal.propTypes = {
corpusId: PropTypes.string.isRequired,
initialValue: PropTypes.object.isRequired
}
2 changes: 0 additions & 2 deletions front/src/components/field.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
flex-shrink: 0;
margin-right: 1rem;
padding: calc(0.5em - 1px) 0;
flex-basis: 10rem;
text-align: end;

&[for] {
cursor: pointer;
Expand Down
75 changes: 39 additions & 36 deletions front/src/components/form.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

.selectContainer {
@include selectFieldContainer;

select {
@include actionField;
@include selectField;
Expand All @@ -29,41 +30,7 @@
.field-object > label {
font-weight: bold;
}
}

label {
padding-right: 0.5rem;
}

textarea {
width: 100%;
height: 150px;
}
}

.verticalForm {
:global {
.control-field {
flex-direction: column;
margin-bottom: 1rem;

label {
flex-basis: 1em;
}
}
}
}

.fieldset {
border: none;
margin: 0 0 0.75rem;
padding: 0;

legend {
@include legend;
}

:global {
[role='combobox'] {
position: relative;

Expand Down Expand Up @@ -98,10 +65,11 @@
padding-bottom: 0.25rem;
font-weight: bold;
}

> * {
width: 100%;
}

.field-object > label {
display: none;
}
Expand All @@ -123,6 +91,7 @@
.field-string {
flex: 1;
border-bottom: none;

> input {
width: 100%;
}
Expand Down Expand Up @@ -200,20 +169,54 @@

/** Array of objects field */
.array-item.can-add-remove {
/*padding: 0 0.5rem;*/
/*padding: 0 0.5rem;*/

> div {
padding-right: 0;
}

> button {
border: 1px solid black;
}

> .field-string {
display: unset;
}
}
}
}

label {
padding-right: 0.5rem;
}

textarea {
width: 100%;
height: 150px;
}
}

.verticalForm {
:global {
.control-field {
flex-direction: column;
margin-bottom: 1rem;

label {
flex-basis: 1em;
}
}
}
}

.fieldset {
border: none;
margin: 0 0 0.75rem;
padding: 0;

legend {
@include legend;
}
}

.comboboxReadonlyField {
Expand Down
Loading

0 comments on commit f2099a4

Please sign in to comment.