Skip to content

Commit

Permalink
address #368, #369, #370, #374
Browse files Browse the repository at this point in the history
  • Loading branch information
sanghoonio committed Aug 26, 2024
1 parent 126ce7e commit 5a7739c
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 141 deletions.
7 changes: 5 additions & 2 deletions web/src/components/forms/blank-project-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ sample_table: samples.csv
<label className="fw-bold text-sm">Tag</label>
</div>
<div className="namespace-name-tag-container fs-4 w-full">
<div className="d-flex flex-row align-items-center justify-content-between w-full ">
<div className="d-flex flex-row align-items-center justify-content-between w-full">
<select
id="blank-namespace-select"
className="form-select"
Expand All @@ -142,7 +142,7 @@ sample_table: samples.csv
</select>
<span className="mx-1 mb-1">/</span>
</div>
<div className="d-flex flex-row align-items-center justify-content-between w-full ">
<div className="d-flex flex-row align-items-center justify-content-between w-full">
<input
// dont allow any whitespace
{...register('project_name', {
Expand All @@ -161,6 +161,9 @@ sample_table: samples.csv
</div>
<input {...register('tag')} id="blank_tag" type="text" className="form-control" placeholder="default" />
</div>
<p className='text-xs'>
* Namespace and Project Name are required. A tag value of "default" will be supplied if the Tag input is left empty.
</p>
<ErrorMessage errors={errors} name="project_name" render={({ message }) => <p>{message}</p>} />
<textarea
id="blank_description"
Expand Down
3 changes: 3 additions & 0 deletions web/src/components/forms/create-schema-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ export const CreateSchemaForm = (props: Props) => {
/>
</div>
</div>
<p className='text-xs'>
* Namespace and Schema Name are required.
</p>
<div className="my-1">
<label className="fw-bold text-sm" htmlFor="schema-description">
Description
Expand Down
3 changes: 3 additions & 0 deletions web/src/components/forms/pop-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ export const PopForm: FC<Props> = ({ onHide, defaultNamespace }) => {
<input {...register('tag')} id="blank_tag" type="text" className="form-control" placeholder="default" />
</div>
</div>
<p className='text-xs'>
* Namespace and Project Name are required. A tag value of "default" will be supplied if the Tag input is left empty.
</p>
<ErrorMessage errors={errors} name="project_name" render={({ message }) => <p>{message}</p>} />
<textarea
id="blank_description"
Expand Down
3 changes: 3 additions & 0 deletions web/src/components/forms/project-upload-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ export const ProjectUploadForm = ({ onHide, defaultNamespace }: Props) => {
<input id="tag" type="text" className="form-control" placeholder="default" {...register('tag')} />
</div>
</div>
<p className='text-xs'>
* Namespace and Project Name are required. A tag value of "default" will be supplied if the Tag input is left empty.
</p>
<ErrorMessage errors={errors} name="name" render={({ message }) => <p>{message}</p>} />
<textarea
id="description"
Expand Down
3 changes: 3 additions & 0 deletions web/src/components/forms/upload-schema-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ export const SchemaUploadForm = (props: Props) => {
/>
</div>
</div>
<p className='text-xs'>
* Namespace and Schema Name are required.
</p>
<ErrorMessage errors={errors} name="name" render={({ message }) => <p>{message}</p>} />
<textarea
id="description"
Expand Down
268 changes: 148 additions & 120 deletions web/src/components/modals/add-view-options.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ErrorMessage } from '@hookform/error-message';
import { FormEvent, useState } from 'react';
import { Modal } from 'react-bootstrap';
import { Modal, Tab, Tabs } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import ReactSelect from 'react-select';
Expand Down Expand Up @@ -98,130 +98,158 @@ export const ViewOptionsModal = (props: Props) => {
<h1 className="modal-title fs-5">Manage Views</h1>
</Modal.Header>
<Modal.Body>
{filteredSamples.length > 0 ? (
<div className="">
<h6 className="mb-1">Save View</h6>
<p className="mb-3">
Save the current filtered sample table state as a view by providing a name (required) and description
(optional) for the view.
</p>
<form>
<div className="pb-2">
<label htmlFor="view-description" className="form-label fw-bold">
Name
</label>
<input
{...register('name', {
required: {
value: true,
message: 'View Name must not be empty.',
},
pattern: {
value: /^[a-zA-Z0-9_-]+$/,
message: "View Name must contain only alphanumeric characters, '-', or '_'.",
},
})}
type="text"
className="form-control"
id="view-name"
aria-describedby="view-name-help"
placeholder="Name..."
/>
</div>
<div className="mt-1">
<label htmlFor="view-description" className="form-label fw-bold">
Description
</label>
<textarea
{...register('description')}
className="form-control"
id="view-description"
aria-describedby="view-description-help"
placeholder="Description..."
/>
</div>
<ErrorMessage
errors={errors}
name="name"
render={({ message }) => (message ? <p className="text-danger pt-1 mb-0">{message}</p> : null)}
/>
<button
disabled={
filteredSamples.length === 0 ||
!isValid ||
!!errors.name?.message ||
viewMutations.addViewMutation.isPending
}
type="button"
className="btn btn-success px-2 mt-3"
onClick={() => {
onSubmit();
resetForm();
}}
>
<i className="bi bi-plus-circle me-1"></i>
{viewMutations.addViewMutation.isPending ? 'Creating...' : 'Create'}
</button>
</form>
<hr />
</div>
) : null}
<div className="">
<h6 className="mb-1">Remove View</h6>
<p className="mb-3">Remove an existing view by selecting it from the dropdown menu.</p>
<ReactSelect
styles={{
control: (provided) => ({
...provided,
borderRadius: '0.333333em', // Left radii set to 0, right radii kept at 4px
}),
}}
className="top-z w-100 ms-auto"
options={
projectViews?.views.map((view) => ({
view: view.name,
description: view.description || 'No description',
value: view.name,
label: `${view.name} | ${view.description || 'No description'}`,
})) || []
<Tabs defaultActiveKey="blank" id="uncontrolled-tab">
<Tab
eventKey="blank"
title={
<span>
<i className="bi bi-save me-1"></i>
Save View
</span>
}
onChange={(selectedOption) => {
if (selectedOption === null || projectViews?.views.length === 0) {
setSelectedViewDelete(null);
setDeleteState(true);
} else {
setSelectedViewDelete(selectedOption);
setDeleteState(false);
>
<div className="border border-top-0 p-2">
<p className="mb-3">
Save the current filtered sample table state as a view by providing a name (required) and description
(optional) for the view.
</p>
{filteredSamples.length > 0 ? (
null
) :
<p className='text-xs text-danger'>
You currently do not have any table filters applied.
To apply one, click on the dropdown arrow next to your column header of interest, and then filter by value.
To clear the current filter, click "Select all".
</p>
}
}}
isDisabled={projectViews?.views.length === 0 || projectViewsIsLoading}
isClearable
placeholder={
projectViewsIsLoading
? 'Loading views...'
: projectViews?.views.length === 0
? 'No views available'
: 'Select a view'
}
value={
selectedViewDelete === null
? null
: {
view: selectedViewDelete.view,
description: selectedViewDelete.description,
value: selectedViewDelete.value,
label: selectedViewDelete.label,
<form>
<div className="pb-2">
<label htmlFor="view-description" className="form-label fw-bold">
Name
</label>
<input
{...register('name', {
required: {
value: true,
message: 'View Name must not be empty.',
},
pattern: {
value: /^[a-zA-Z0-9_-]+$/,
message: "View Name must contain only alphanumeric characters, '-', or '_'.",
},
})}
type="text"
className="form-control"
id="view-name"
aria-describedby="view-name-help"
placeholder="Name..."
disabled={filteredSamples.length === 0}
/>
</div>
<div className="mt-1">
<label htmlFor="view-description" className="form-label fw-bold">
Description
</label>
<textarea
{...register('description')}
className="form-control"
id="view-description"
aria-describedby="view-description-help"
placeholder="Description..."
disabled={filteredSamples.length === 0}
/>
</div>
<ErrorMessage
errors={errors}
name="name"
render={({ message }) => (message ? <p className="text-danger pt-1 mb-0">{message}</p> : null)}
/>
<button
disabled={
filteredSamples.length === 0 ||
!isValid ||
!!errors.name?.message ||
viewMutations.addViewMutation.isPending
}
type="button"
className="btn btn-success px-2 mt-3"
onClick={() => {
onSubmit();
resetForm();
}}
>
<i className="bi bi-plus-circle me-1"></i>
{viewMutations.addViewMutation.isPending ? 'Creating...' : 'Create'}
</button>
</form>
</div>
</Tab>
<Tab
eventKey="from-file"
title={
<span>
<i className="bi bi-trash me-1"></i>
Remove View
</span>
}
/>
<button
disabled={deleteState || viewMutations.removeViewMutation.isPending || selectedViewDelete === null}
onClick={handleDeleteView}
className="btn btn-danger px-2 mt-3"
>
<i className="bi bi-trash"></i> {viewMutations.removeViewMutation.isPending ? 'Removing...' : 'Remove'}
</button>
</div>
<div className="border border-top-0 p-2">
<p className="mb-3">Remove an existing view by selecting it from the dropdown menu.</p>
<ReactSelect
styles={{
control: (provided) => ({
...provided,
borderRadius: '0.333333em',
}),
}}
className="top-z w-100 ms-auto"
options={
projectViews?.views.map((view) => ({
view: view.name,
description: view.description || 'No description',
value: view.name,
label: `${view.name} | ${view.description || 'No description'}`,
})) || []
}
onChange={(selectedOption) => {
if (selectedOption === null || projectViews?.views.length === 0) {
setSelectedViewDelete(null);
setDeleteState(true);
} else {
setSelectedViewDelete(selectedOption);
setDeleteState(false);
}
}}
isDisabled={projectViews?.views.length === 0 || projectViewsIsLoading}
isClearable
placeholder={
projectViewsIsLoading
? 'Loading views...'
: projectViews?.views.length === 0
? 'No views available'
: 'Select a view'
}
value={
selectedViewDelete === null
? null
: {
view: selectedViewDelete.view,
description: selectedViewDelete.description,
value: selectedViewDelete.value,
label: selectedViewDelete.label,
}
}
/>
<button
disabled={deleteState || viewMutations.removeViewMutation.isPending || selectedViewDelete === null}
onClick={handleDeleteView}
className="btn btn-danger px-2 mt-3"
>
<i className="bi bi-trash"></i> {viewMutations.removeViewMutation.isPending ? 'Removing...' : 'Remove'}
</button>
</div>
</Tab>
</Tabs>
</Modal.Body>
</Modal>
);
Expand Down
Loading

0 comments on commit 5a7739c

Please sign in to comment.