diff --git a/doc/release/RELEASE-NOTES.md b/doc/release/RELEASE-NOTES.md
index 3795cc1b5..8ab7ae025 100644
--- a/doc/release/RELEASE-NOTES.md
+++ b/doc/release/RELEASE-NOTES.md
@@ -10,7 +10,6 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
* Release date: January 25, 2025
### Database changes
-
#### Migrations:
#### Scheme changes
@@ -23,12 +22,12 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
### Bugfix
### What's new
+* [OSDEV-40](https://opensupplyhub.atlassian.net/browse/OSDEV-40) - Created new page for `/contribute` to choose between multiple & single location upload. Replaced current multiple list upload to `/contribute/multiple-locations`. Changed `Upload Data` to `Add Data` text.
### Release instructions:
* Ensure that the following commands are included in the `post_deployment` command:
* `migrate`
* `reindex_database`
-* Run `[Release] Deploy` pipeline for the target environment with the flag `Clear the custom OpenSearch indexes and templates` set to true - to refresh the index mappings for the `moderation-events` index after disabling dynamic mapping for the new fields that don't have an explicit mapping defined. The `production-locations` will also be affected since it will clean all of our custom indexes and templates within the OpenSearch cluster.
## Release 1.27.0
diff --git a/src/react/src/Routes.jsx b/src/react/src/Routes.jsx
index 36990e1ed..1a22f3435 100644
--- a/src/react/src/Routes.jsx
+++ b/src/react/src/Routes.jsx
@@ -14,6 +14,7 @@ import RegisterForm from './components/RegisterForm';
import ResetPasswordForm from './components/ResetPasswordForm';
import LoginForm from './components/LoginForm';
import Contribute from './components/Contribute';
+import AddLocationData from './components/AddLocationData';
import Homepage from './components/Homepage';
import FacilityLists from './components/FacilityLists';
import FacilityListItems from './components/FacilityListItems';
@@ -46,6 +47,7 @@ import {
authResetPasswordFormRoute,
authConfirmRegistrationRoute,
contributeRoute,
+ multipleLocationRoute,
listsRoute,
facilityListItemsRoute,
facilitiesRoute,
@@ -142,6 +144,10 @@ class Routes extends Component {
+ {
+ const mockAuthorizedState = {
+ auth: {
+ user: { user: { isAnon: false } },
+ session: { fetching: false },
+ },
+ };
+
+ const mockNotAuthorizedState = {
+ auth: {
+ user: { user: { isAnon: true } },
+ session: { fetching: false },
+ },
+ };
+
+ const renderComponent = (preloadedState = {}) => {
+ const theme = createMuiTheme({
+ palette: {
+ action: { main: '#000', dark: '#333' }, // Define a valid palette
+ getContrastText: jest.fn(() => '#fff'), // Mock the method to return a contrast color
+ },
+ });
+ return renderWithProviders(
+
+
+
+
+ ,
+ { preloadedState }
+ );
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders for the authorized user', () => {
+ const { getByText } = renderComponent(mockAuthorizedState);
+ expect(getByText('Add production location data to OS Hub')).toBeInTheDocument();
+ });
+
+ it('renders for the unauthorized user', () => {
+ const { getByText } = renderComponent(mockNotAuthorizedState);
+ expect(getByText('Log in to contribute to Open Supply Hub')).toBeInTheDocument();
+ });
+});
diff --git a/src/react/src/components/AddLocationData.jsx b/src/react/src/components/AddLocationData.jsx
new file mode 100644
index 000000000..f5fef9327
--- /dev/null
+++ b/src/react/src/components/AddLocationData.jsx
@@ -0,0 +1,200 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { Link } from 'react-router-dom';
+import PropTypes from 'prop-types';
+import { withStyles, withTheme } from '@material-ui/core/styles';
+import Paper from '@material-ui/core/Paper';
+import Button from '@material-ui/core/Button';
+import Typography from '@material-ui/core/Typography';
+import Grid from '@material-ui/core/Grid';
+import CircularProgress from '@material-ui/core/CircularProgress';
+
+import AppGrid from './AppGrid';
+
+import { openInNewTab } from '../util/util';
+import {
+ authLoginFormRoute,
+ InfoLink,
+ InfoPaths,
+ contributeProductionLocationRoute,
+ multipleLocationRoute,
+} from '../util/constants';
+import { makeAddLocationStyles } from '../util/styles';
+
+import MessyIcon from './MessyIcon';
+import PlaylistIcon from './PlaylistIcon';
+import PinDropIcon from './PinDropIcon';
+import RectangleCardFigure from './RectangleCardFigure';
+import SliceCardFigure from './SliceCardFigure';
+import SliceMessyFigure from './SliceMessyFigure';
+import SliceMessyDuoFigure from './SliceMessyDuoFigure';
+
+function AddLocationData({ classes, userHasSignedIn, fetchingSessionSignIn }) {
+ if (fetchingSessionSignIn) {
+ return (
+
+
+
+
+
+
+
+ );
+ }
+
+ if (!userHasSignedIn) {
+ return (
+
+
+
+
+ Log in to contribute to Open Supply Hub
+
+
+
+
+ );
+ }
+
+ return (
+
+
+ Add production location data to OS Hub
+
+
+ Contribute your data here to help build the world’s most
+ complete, open and accessible map of global production:
+
+
+
+
+
+
+
+
+
+ Upload a dataset with multiple production locations
+ using a{' '}
+
+ spreadsheet.
+
+
+
+ This option is best if you have a large number of
+ production locations to contribute.
+
+
+
+
+
+
+ Have messy data?
+
+
+ We can get it ready for you. All you need to
+ do is upload your data and we’ll take care
+ of the rest.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Add data for a
+
+
+ single production location.
+
+
+
+ This option is best if you want to register your
+ production location or contribute data for one
+ production location at a time.
+
+
+