diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b5c50aa88..dc2acf7c22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [RDMO 2.2.2](https://github.com/rdmorganiser/rdmo/compare/2.2.1...2.2.2) (Oct 18, 2024) + +* Fix projects interface when using RDMO with a path (#1152) +* Fix missing (unavailable) catalogs projects interface + ## [RDMO 2.2.1](https://github.com/rdmorganiser/rdmo/compare/2.2.0...2.2.1) (Sep 13, 2024) * Fix import error when allauth is not used (#1145) diff --git a/rdmo/__init__.py b/rdmo/__init__.py index b19ee4b77e..ba51cedfc0 100644 --- a/rdmo/__init__.py +++ b/rdmo/__init__.py @@ -1 +1 @@ -__version__ = "2.2.1" +__version__ = "2.2.2" diff --git a/rdmo/core/assets/js/api/BaseApi.js b/rdmo/core/assets/js/api/BaseApi.js index c2d81e9473..b993faaf08 100644 --- a/rdmo/core/assets/js/api/BaseApi.js +++ b/rdmo/core/assets/js/api/BaseApi.js @@ -15,6 +15,10 @@ function ValidationError(errors) { this.errors = errors } +function BadRequestError(errors) { + this.errors = errors +} + class BaseApi { static get(url) { @@ -23,12 +27,17 @@ class BaseApi { }).then(response => { if (response.ok) { return response.json() + } else if (response.status === 400) { + return response.json().then(errors => { + throw new BadRequestError(errors) + }) } else { throw new ApiError(response.statusText, response.status) } }) } + static post(url, data) { return fetch(baseUrl + url, { method: 'POST', diff --git a/rdmo/core/assets/js/components/UploadDropZone.js b/rdmo/core/assets/js/components/UploadDropZone.js index d4f335f52b..f9be740aac 100644 --- a/rdmo/core/assets/js/components/UploadDropZone.js +++ b/rdmo/core/assets/js/components/UploadDropZone.js @@ -5,7 +5,7 @@ import { useDropzone } from 'react-dropzone' const UploadDropZone = ({ acceptedTypes, onImportFile }) => { const [errorMessage, setErrorMessage] = useState('') - const { getRootProps, getInputProps } = useDropzone({ + const { getRootProps, getInputProps, isDragActive } = useDropzone({ accept: acceptedTypes, onDropAccepted: acceptedFiles => { if (acceptedFiles.length > 0) { @@ -14,7 +14,6 @@ const UploadDropZone = ({ acceptedTypes, onImportFile }) => { } }, onDropRejected: rejectedFiles => { - console.log(rejectedFiles) setErrorMessage(interpolate(gettext('%s has unsupported file type'), [rejectedFiles[0].path])) } }) @@ -23,9 +22,17 @@ const UploadDropZone = ({ acceptedTypes, onImportFile }) => {
-

- {gettext('Drag and drop a file here or click to select a file')} -

+ { + isDragActive ? ( +
+ {gettext('Drop the file here ...')} +
+ ) : ( +
+ {gettext('Drag and drop a file here or click to select a file')} +
+ ) + } {errorMessage &&
{errorMessage}
}
diff --git a/rdmo/core/static/core/css/base.scss b/rdmo/core/static/core/css/base.scss index ecf2056c51..e3519e18fb 100644 --- a/rdmo/core/static/core/css/base.scss +++ b/rdmo/core/static/core/css/base.scss @@ -214,7 +214,8 @@ metadata { .page h2:nth-child(2) { margin-top: 0; } -.sidebar h2:first-child { +.sidebar h2:first-child, +.sidebar-mt { margin-top: 70px; } diff --git a/rdmo/locale/de/LC_MESSAGES/djangojs.mo b/rdmo/locale/de/LC_MESSAGES/djangojs.mo index 352312c054..f75f9f9831 100644 Binary files a/rdmo/locale/de/LC_MESSAGES/djangojs.mo and b/rdmo/locale/de/LC_MESSAGES/djangojs.mo differ diff --git a/rdmo/locale/de/LC_MESSAGES/djangojs.po b/rdmo/locale/de/LC_MESSAGES/djangojs.po index fb95e1f41a..f05583a2c3 100644 --- a/rdmo/locale/de/LC_MESSAGES/djangojs.po +++ b/rdmo/locale/de/LC_MESSAGES/djangojs.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: RDMO\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-13 12:34+0000\n" -"PO-Revision-Date: 2024-07-23 11:18+0200\n" +"POT-Creation-Date: 2024-10-18 15:56+0200\n" +"PO-Revision-Date: 2024-10-18 15:56+0200\n" "Last-Translator: Jochen Klar \n" "Language-Team: RDMO \n" "Language: de\n" @@ -28,12 +28,16 @@ msgstr "Speichern" msgid "Search" msgstr "Suchen" -#: core/assets/js/components/UploadDropZone.js:18 +#: core/assets/js/components/UploadDropZone.js:17 #, javascript-format msgid "%s has unsupported file type" msgstr "%s hat einen nicht unterstützten Dateityp" -#: core/assets/js/components/UploadDropZone.js:27 +#: core/assets/js/components/UploadDropZone.js:28 +msgid "Drop the file here ..." +msgstr "Legen Sie die Datei hier ab ..." + +#: core/assets/js/components/UploadDropZone.js:32 msgid "Drag and drop a file here or click to select a file" msgstr "Datei hierher ziehen oder klicken um Datei auszuwählen" @@ -136,8 +140,8 @@ msgstr "Neues Attribut erstellen" #: management/assets/js/components/edit/EditAttribute.js:53 #, javascript-format msgid "" -"This attribute will be added to the attribute %s." +"This attribute will be added to the attribute " +"%s." msgstr "" "Dieses Attribut wird dem Attribut %s " "hinzugefügt." @@ -154,8 +158,8 @@ msgstr "" #: management/assets/js/components/edit/EditAttribute.js:68 #, javascript-format msgid "" -"This attribute will be added to the question set %s." +"This attribute will be added to the question set %s." msgstr "" "Dieses Attribut wird dem Fragenset %s " "hinzugefügt." @@ -163,8 +167,8 @@ msgstr "" #: management/assets/js/components/edit/EditAttribute.js:75 #, javascript-format msgid "" -"This attribute will be added to the question %s." +"This attribute will be added to the question " +"%s." msgstr "" "Dieses Attribut wird der Frage %s " "hinzugefügt." @@ -172,8 +176,8 @@ msgstr "" #: management/assets/js/components/edit/EditAttribute.js:82 #, javascript-format msgid "" -"This attribute will be added to the condition %s." +"This attribute will be added to the condition %s." msgstr "" "Dieses Attribut wird der Bedingung %s " "hinzugefügt." @@ -219,8 +223,8 @@ msgstr "Neue Bedingung erstellen" #: management/assets/js/components/edit/EditCondition.js:57 #, javascript-format msgid "" -"This condition will be added to the option set %s." +"This condition will be added to the option set " +"%s." msgstr "" "Diese Bedingung wird dem Optionenset %s " "hinzugefügt." @@ -237,8 +241,8 @@ msgstr "" #: management/assets/js/components/edit/EditCondition.js:71 #, javascript-format msgid "" -"This condition will be added to the question set %s." +"This condition will be added to the question set %s." msgstr "" "Diese Bedingung wird dem Fragenset %s " "hinzugefügt." @@ -246,8 +250,8 @@ msgstr "" #: management/assets/js/components/edit/EditCondition.js:78 #, javascript-format msgid "" -"This condition will be added to the question %s." +"This condition will be added to the question " +"%s." msgstr "" "Diese Bedingung wird der Frage %s " "hinzugefügt." @@ -307,8 +311,8 @@ msgstr "Neues Optionenset erstellen" #: management/assets/js/components/edit/EditOptionSet.js:61 #, javascript-format msgid "" -"This option set will be added to the question %s." +"This option set will be added to the question " +"%s." msgstr "" "Dieser Optionenset wird der Frage %s " "hinzugefügt." @@ -415,8 +419,8 @@ msgstr "" #: management/assets/js/components/edit/EditQuestion.js:74 #, javascript-format msgid "" -"This question will be added to the question set %s." +"This question will be added to the question set %s." msgstr "" "Diese Frage wird dem Fragenset %s " "hinzugefügt." @@ -439,8 +443,8 @@ msgstr "Fragenset erstellen" #: management/assets/js/components/edit/EditQuestionSet.js:96 #, javascript-format msgid "" -"This question set will be added to the page %s." +"This question set will be added to the page " +"%s." msgstr "" "Dieser Fragenset wird der Seite %s " "hinzugefügt." @@ -1444,42 +1448,42 @@ msgstr "Projekt direkt importieren" msgid "Import from file" msgstr "Importieren aus Datei" -#: projects/assets/js/components/main/Projects.js:22 +#: projects/assets/js/components/main/Projects.js:25 #: projects/assets/js/utils/translations.js:13 msgid "Pending invitations" msgstr "Ausstehende Einladungen" -#: projects/assets/js/components/main/Projects.js:28 +#: projects/assets/js/components/main/Projects.js:31 #: projects/assets/js/utils/translations.js:7 msgid "Import project" msgstr "Projekt importieren" -#: projects/assets/js/components/main/Projects.js:33 +#: projects/assets/js/components/main/Projects.js:36 #, javascript-format msgid "%s of %s projects are displayed" msgstr "%s von %s Projekten werden angezeigt" -#: projects/assets/js/components/main/Projects.js:36 +#: projects/assets/js/components/main/Projects.js:39 #, javascript-format msgid "%s of %s" msgstr "%s von %s" -#: projects/assets/js/components/main/Projects.js:47 +#: projects/assets/js/components/main/Projects.js:50 #: projects/assets/js/utils/translations.js:18 msgid "View all projects" msgstr "Alle Projekte ansehen" -#: projects/assets/js/components/main/Projects.js:47 +#: projects/assets/js/components/main/Projects.js:50 #: projects/assets/js/utils/translations.js:19 msgid "View my projects" msgstr "Meine Projekte ansehen" -#: projects/assets/js/components/main/Projects.js:48 +#: projects/assets/js/components/main/Projects.js:51 #: projects/assets/js/utils/translations.js:11 msgid "My projects" msgstr "Meine Projekte" -#: projects/assets/js/components/main/Projects.js:48 +#: projects/assets/js/components/main/Projects.js:51 #: projects/assets/js/utils/translations.js:3 msgid "All projects" msgstr "Alle Projekte" diff --git a/rdmo/projects/assets/js/api/ProjectsApi.js b/rdmo/projects/assets/js/api/ProjectsApi.js index 5fc35590ad..359f2c81e2 100644 --- a/rdmo/projects/assets/js/api/ProjectsApi.js +++ b/rdmo/projects/assets/js/api/ProjectsApi.js @@ -3,83 +3,27 @@ import BaseApi from 'rdmo/core/assets/js/api/BaseApi' import { encodeParams } from 'rdmo/core/assets/js/utils/api' import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' -function BadRequestError(errors) { - this.errors = errors -} - class ProjectsApi extends BaseApi { - static fetchProjects(params, fetchParams = {}) { - return fetch('/api/v1/projects/projects/?' + encodeParams(params), fetchParams).then(response => { - if (response.ok) { - return response.json() - } else if (response.status == 400) { - return response.json().then(errors => { - throw new BadRequestError(errors) - }) - } else { - throw new Error(response.statusText) - } - }) + static fetchProjects(params) { + const url = '/api/v1/projects/projects/?' + encodeParams(params) + return this.get(url) } static fetchCatalogs() { - return fetch('/api/v1/projects/catalogs/').then(response => { - if (response.ok) { - return response.json() - } else { - throw new Error(response.statusText) - } - }) + return this.get('/api/v1/projects/catalogs/') } static fetchAllowedFileTypes() { - return fetch('/api/v1/projects/projects/upload-accept/') - .then(response => { - if (response.ok) { - return response.text() - .then(text => { - try { - // Attempt to parse the text as JSON - const jsonData = JSON.parse(text) - // Check if the parsed data is an array - if (Array.isArray(jsonData)) { - return jsonData - } - } catch (error) { - // If JSON.parse fails, handle text as plain string below - } - - // If it's not a JSON array, process it as a string - const cleanedText = text.replace(/^"|"$/g, '') - return cleanedText ? cleanedText.split(',') : [] - }) - } else { - throw new Error(response.statusText) - } - }) + return this.get('/api/v1/projects/projects/upload-accept/') } static fetchDirectImportUrls() { - return fetch('/api/v1/projects/projects/imports/') - .then(response => { - if (response.ok) { - return response.json() - } else { - throw new Error(response.statusText) - } - }) + return this.get('/api/v1/projects/projects/imports/') } static fetchInvites() { - return fetch('/api/v1/projects/invites/user') - .then(response => { - if (response.ok) { - return response.json() - } else { - throw new Error(response.statusText) - } - }) + return this.get('/api/v1/projects/invites/user/') } static uploadProject(url, file) { diff --git a/rdmo/projects/assets/js/components/helper/PendingInvitations.js b/rdmo/projects/assets/js/components/helper/PendingInvitations.js index 9290ccb32c..e2c13fcfff 100644 --- a/rdmo/projects/assets/js/components/helper/PendingInvitations.js +++ b/rdmo/projects/assets/js/components/helper/PendingInvitations.js @@ -1,5 +1,8 @@ import React from 'react' import PropTypes from 'prop-types' + +import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' + import { ROLE_LABELS } from '../../utils' const PendingInvitations = ({ invitations }) => { @@ -14,8 +17,14 @@ const PendingInvitations = ({ invitations }) => { {ROLE_LABELS[item.role]}
- - + +
)) diff --git a/rdmo/projects/assets/js/components/helper/ProjectFilters.js b/rdmo/projects/assets/js/components/helper/ProjectFilters.js index 5b317224f6..0ce94c1a23 100644 --- a/rdmo/projects/assets/js/components/helper/ProjectFilters.js +++ b/rdmo/projects/assets/js/components/helper/ProjectFilters.js @@ -25,7 +25,6 @@ const ProjectFilters = ({ catalogs, config, configActions, isManager, projectsAc configActions.deleteConfig('params.catalog') configActions.deleteConfig('params.created_after') setStartDate('created', null) - console.log('dateRange', dateRange) configActions.deleteConfig('params.created_before') setEndDate('created', null) configActions.deleteConfig('params.last_changed_after') @@ -36,7 +35,8 @@ const ProjectFilters = ({ catalogs, config, configActions, isManager, projectsAc projectsActions.fetchProjects() } - const catalogOptions = catalogs?.map(catalog => ({ value: catalog.id.toString(), label: catalog.title })) + const catalogOptions = catalogs?.filter(catalog => catalog.available) + .map(catalog => ({ value: catalog.id.toString(), label: catalog.title })) const selectedCatalog = get(config, 'params.catalog', '') const updateCatalogFilter = (value) => { value ? configActions.updateConfig('params.catalog', value) : configActions.deleteConfig('params.catalog') diff --git a/rdmo/projects/assets/js/components/main/Projects.js b/rdmo/projects/assets/js/components/main/Projects.js index aac7337816..585b9eb354 100644 --- a/rdmo/projects/assets/js/components/main/Projects.js +++ b/rdmo/projects/assets/js/components/main/Projects.js @@ -1,11 +1,14 @@ import React from 'react' import PropTypes from 'prop-types' -import { PendingInvitations, ProjectFilters, ProjectImport, Table } from '../helper' +import { get, isEmpty } from 'lodash' + import { Link, Modal, SearchField } from 'rdmo/core/assets/js/components' import { useFormattedDateTime, useModal, useScrollToTop } from 'rdmo/core/assets/js/hooks' import { language } from 'rdmo/core/assets/js/utils' +import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' + +import { PendingInvitations, ProjectFilters, ProjectImport, Table } from '../helper' import { getTitlePath, getUserRoles, userIsManager, HEADER_FORMATTERS, SORTABLE_COLUMNS } from '../../utils' -import { get, isEmpty } from 'lodash' const Projects = ({ config, configActions, currentUserObject, projectsActions, projectsObject }) => { const { allowedTypes, catalogs, importUrls, invites, projects, projectsCount, hasNext } = projectsObject @@ -54,10 +57,12 @@ const Projects = ({ config, configActions, currentUserObject, projectsActions, p } const handleNew = () => { - window.location.href = '/projects/create' + window.location.href = `${baseUrl}/projects/create/` } - const handleImport = (file) => { projectsActions.uploadProject('/projects/import/', file) } + const handleImport = (file) => { + projectsActions.uploadProject('/projects/import/', file) + } const renderTitle = (title, row) => { const pathArray = getTitlePath(projects, title, row).split(' / ') @@ -67,7 +72,7 @@ const Projects = ({ config, configActions, currentUserObject, projectsActions, p return (
- + {pathArray.map((path, index) => ( {path} / ))} @@ -129,25 +134,26 @@ const Projects = ({ config, configActions, currentUserObject, projectsActions, p created: content => useFormattedDateTime(content, language), last_changed: content => useFormattedDateTime(content, language), actions: (_content, row) => { - const rowUrl = `/projects/${row.id}` - const path = `?next=${window.location.pathname}` + const rowUrl = `${baseUrl}/projects/${row.id}` + const params = `?next=${window.location.pathname}` const { isProjectManager, isProjectOwner } = getUserRoles(row, currentUserId, ['managers', 'owners']) + return (
{(isProjectManager || isProjectOwner || isManager) && window.location.href = `${rowUrl}/update/${path}`} + onClick={() => window.location.href = `${rowUrl}/update/${params}`} /> } {(isProjectOwner || isManager) && window.location.href = `${rowUrl}/delete/${path}`} + onClick={() => window.location.href = `${rowUrl}/delete/${params}`} /> }
@@ -217,10 +223,13 @@ const Projects = ({ config, configActions, currentUserObject, projectsActions, p sortableColumns={SORTABLE_COLUMNS} visibleColumns={visibleColumns} /> + {renderLoadButtons()} + + {% trans 'Snapshot' %}: {{ snapshot.title }} {% endif %}