-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
task/WG-74: Create Map React Modal #193
Changes from 1 commit
55eb4e7
61fa55c
a59ebe1
01da5e3
1e6067d
11c90ab
dce9921
8129ec1
a857ea6
a1492b0
4dfe23a
4b0328c
fb11b80
a9c9e7f
2e2378a
36c7e9e
0170c08
b636ca2
b39c3ec
6f4e2e0
b7fae15
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React, { useState } from 'react'; | ||
import { Button } from 'reactstrap'; | ||
import MapModal from './MapModal'; | ||
|
||
const ModalTestPage: React.FC = () => { | ||
const [isModalOpen, setIsModalOpen] = useState(false); | ||
|
||
const toggleModal = () => setIsModalOpen(!isModalOpen); | ||
|
||
// Dummy onSubmit function for testing | ||
const dummyOnSubmit = () => { | ||
// No operation (noop) | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Button color="secondary" onClick={toggleModal}> | ||
Open Map Modal | ||
</Button> | ||
<MapModal | ||
isOpen={isModalOpen} | ||
toggle={toggleModal} | ||
onSubmit={dummyOnSubmit} | ||
isCreating={false} | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ModalTestPage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import React from 'react'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: what about making the name more specific? something like or NewMapModal? or CreateMapModal |
||
import { | ||
Button, | ||
Modal, | ||
ModalHeader, | ||
ModalBody, | ||
ModalFooter, | ||
FormGroup, | ||
Label, | ||
Input, | ||
} from 'reactstrap'; | ||
import { Formik, Form, Field, ErrorMessage } from 'formik'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should consider using the form-related components in core-components. Originally from CEP (but probably improved via TUP) the `react/src/core-components/Form has all the labels and required label etc for the formik fields. what about we create a follow-on ticket for using components from core-components/Form in this modal? |
||
import * as Yup from 'yup'; | ||
|
||
type MapModalProps = { | ||
isOpen: boolean; | ||
toggle: () => void; | ||
onSubmit: (values: any) => void; | ||
isCreating: boolean; | ||
}; | ||
|
||
// Yup validation schema | ||
const validationSchema = Yup.object({ | ||
name: Yup.string().required('Name is required'), | ||
description: Yup.string().required('Description is required'), | ||
system_file: Yup.string() | ||
.matches( | ||
/^[A-Za-z0-9-_]+$/, | ||
'Only letters, numbers, hyphens, and underscores are allowed' | ||
) | ||
.required('Custom file name is required'), | ||
}); | ||
|
||
const MapModal: React.FC<MapModalProps> = ({ | ||
isOpen, | ||
toggle, | ||
onSubmit, | ||
isCreating, | ||
}) => { | ||
return ( | ||
<Modal isOpen={isOpen} toggle={toggle}> | ||
tjgrafft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<ModalHeader toggle={toggle}>Create a New Map</ModalHeader> | ||
<ModalBody> | ||
<Formik | ||
initialValues={{ | ||
name: '', | ||
description: '', | ||
system_file: '', | ||
syncFolder: false, | ||
}} | ||
validationSchema={validationSchema} | ||
onSubmit={(values, actions) => { | ||
console.log('Form data', values); | ||
onSubmit(values); | ||
actions.setSubmitting(false); | ||
}} | ||
> | ||
{({ errors, touched, values, handleChange }) => ( | ||
<Form> | ||
<FormGroup> | ||
<Label for="name">Name</Label> | ||
<Field | ||
name="name" | ||
as={Input} | ||
invalid={touched.name && !!errors.name} | ||
/> | ||
<ErrorMessage | ||
name="name" | ||
component="div" | ||
className="invalid-feedback" | ||
/> | ||
</FormGroup> | ||
<FormGroup> | ||
<Label for="description">Description</Label> | ||
<Field | ||
name="description" | ||
as={Input} | ||
invalid={touched.description && !!errors.description} | ||
/> | ||
<ErrorMessage | ||
name="description" | ||
component="div" | ||
className="invalid-feedback" | ||
/> | ||
</FormGroup> | ||
<FormGroup> | ||
<Label for="system_file">Custom File Name</Label> | ||
<div className="input-group"> | ||
<Field | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we match the behavior on the current app where the user inputs the map name and the file initially matches the map name? Lets add a follow on Jira issue |
||
name="system_file" | ||
as={Input} | ||
className="col-sm-8" | ||
invalid={touched.system_file && !!errors.system_file} | ||
/> | ||
<span className="input-group-text col-sm-4">.hazmapper</span> | ||
</div> | ||
{/* Alt solution to render error message bc input group was causing text to not display properly */} | ||
tjgrafft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{touched.system_file && errors.system_file && ( | ||
<div | ||
className="custom-error-message" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is custom-error-message a class somewhere? or is that the desired class to contain the margin-top, font-size and color? We should put some custom style in a MapModul.module.css file like:
Then
|
||
style={{ | ||
marginTop: '0.25rem', | ||
fontSize: '0.875em', | ||
color: 'var(--bs-danger)', | ||
}} | ||
> | ||
{errors.system_file} | ||
</div> | ||
)} | ||
</FormGroup> | ||
<FormGroup className="row align-items-center"> | ||
<Label className="col-sm-4">Save Location:</Label> | ||
<div className="col-sm-8 text-primary">/tgrafft</div> | ||
</FormGroup> | ||
<FormGroup className="row mb-2"> | ||
<Label className="col-sm-4 col-form-label pt-0"> | ||
Sync Folder: | ||
</Label> | ||
<div className="col-sm-8"> | ||
<div className="form-check"> | ||
<Field | ||
type="checkbox" | ||
name="syncFolder" | ||
id="syncFolder" | ||
className="form-check-input" | ||
checked={values.syncFolder} | ||
onChange={handleChange} | ||
/> | ||
</div> | ||
<Label | ||
className="form-check-label" | ||
for="syncFolder" | ||
style={{ fontStyle: 'italic' }} | ||
> | ||
When enabled, files in this folder are automatically synced | ||
into the map periodically. | ||
</Label> | ||
</div> | ||
</FormGroup> | ||
<ModalFooter className="justify-content-start"> | ||
<Button color="warning" type="button" onClick={toggle}> | ||
Close | ||
</Button> | ||
<Button color="primary" type="submit" disabled={isCreating}> | ||
{isCreating ? 'Creating...' : 'Create'} | ||
</Button> | ||
</ModalFooter> | ||
</Form> | ||
)} | ||
</Formik> | ||
</ModalBody> | ||
</Modal> | ||
); | ||
}; | ||
|
||
export default MapModal; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './MapModal'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { usePost } from '../../requests'; | ||
import { Project } from '../../types'; | ||
|
||
type ProjectData = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about renaming to CreateProjectData? we can always refactor again when we complete TACC-Cloud/geoapi#173 Should this be moved into |
||
// TO-DO: Make sure project data structure is correct for API | ||
name: string; | ||
description: string; | ||
system_file: string; | ||
}; | ||
|
||
// type ProjectData = { | ||
// id: number; | ||
// name: string; | ||
// description: string; | ||
// public: boolean; | ||
// uuid: string; | ||
// system_file: string; | ||
// system_id: string; | ||
// system_path: string; | ||
// deletable: boolean; | ||
// }; | ||
|
||
const useCreateProject = () => { | ||
const baseUrl = 'https://agave.designsafe-ci.org/geo/v2'; | ||
const endpoint = '/projects/'; | ||
|
||
return usePost<ProjectData, Project>({ | ||
endpoint, | ||
baseUrl, | ||
}); | ||
}; | ||
|
||
export default useCreateProject; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,37 @@ | ||
import React from 'react'; | ||
import React, { useState } from 'react'; | ||
import 'bootstrap/dist/css/bootstrap.min.css'; | ||
import { | ||
LoadingSpinner, | ||
InlineMessage, | ||
SectionHeader, | ||
} from '../../core-components'; | ||
import { useProjects } from '../../hooks'; | ||
import MapModal from '../../components/MapModal'; | ||
import { Button } from 'reactstrap'; | ||
import useCreateProject from '../../hooks/projects/useCreateProject'; | ||
|
||
function MainMenu() { | ||
const { data, isLoading, error } = useProjects(); | ||
const { mutate: createProject, isLoading: isCreatingProject } = | ||
useCreateProject(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could line 14 and the handleCreateProject (20-33) be moved into the MapModal? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
const [isModalOpen, setIsModalOpen] = useState(false); | ||
|
||
const toggleModal = () => setIsModalOpen(!isModalOpen); | ||
|
||
const handleCreateProject = (projectData) => { | ||
createProject(projectData, { | ||
onSuccess: () => { | ||
// TO-ADD: Handle success (e.g., show success message, refetch projects, etc.) | ||
console.log('Data after submit', projectData); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we want to navigate to the new map as now we know the new uuid:
this /project/uuid route is already supported in |
||
toggleModal(); | ||
}, | ||
onError: (err) => { | ||
// Handle error (e.g., show error message) | ||
console.error('Error creating project:', err); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need to not let the modal close and then show an error in the modal for users to see. we should match the current behavior where we show the error message or the text " That folder is already syncing with a different map!" |
||
}, | ||
}); | ||
}; | ||
|
||
if (isLoading) { | ||
return ( | ||
<> | ||
|
@@ -27,6 +51,19 @@ function MainMenu() { | |
return ( | ||
<> | ||
<SectionHeader isNestedHeader>Main Menu</SectionHeader> | ||
<Button | ||
color="primary" | ||
onClick={toggleModal} | ||
disabled={isCreatingProject} | ||
> | ||
Create Map | ||
</Button> | ||
<MapModal | ||
isOpen={isModalOpen} | ||
toggle={toggleModal} | ||
onSubmit={handleCreateProject} | ||
isCreating={isCreatingProject} | ||
/> | ||
|
||
<table> | ||
<thead>Projects</thead> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can bootstrap package be removed now that we are using the cdn?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it should still work with just the cdn. Let me test that though