Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c848f6c
temp updates
germanurrus Aug 19, 2022
004a626
updates
germanurrus Aug 19, 2022
63df4f2
update
germanurrus Aug 19, 2022
48aeaed
remove comment
germanurrus Aug 19, 2022
21d16d7
remove output
germanurrus Aug 19, 2022
d79e914
updates
germanurrus Aug 22, 2022
a26c929
install package
germanurrus Aug 22, 2022
2835960
updates
germanurrus Aug 23, 2022
a65c810
update
germanurrus Aug 23, 2022
455c7d1
create yup helper
germanurrus Aug 23, 2022
ef0c070
remove output
germanurrus Aug 23, 2022
56cad6e
create option component
germanurrus Aug 23, 2022
9378fb4
updates
germanurrus Aug 23, 2022
54f3ef9
updates
germanurrus Aug 23, 2022
74a4b27
Merge branch 'main' into germanurrustarazu/cas-440
germanurrus Aug 25, 2022
ce6da10
updates
germanurrus Aug 26, 2022
6232530
Merge branch 'main' into germanurrustarazu/cas-440
germanurrus Aug 29, 2022
bc537ed
updates
germanurrus Aug 29, 2022
222add0
update
germanurrus Aug 29, 2022
6d3d35a
update validation
germanurrus Aug 30, 2022
5bbb771
updates
germanurrus Aug 30, 2022
fe47c82
add initial state back
germanurrus Aug 30, 2022
badec5b
updates for choices
germanurrus Aug 30, 2022
ddd60b7
updates
germanurrus Aug 30, 2022
2d31f46
remove outpu
germanurrus Aug 30, 2022
9652104
updates
germanurrus Aug 31, 2022
76dd549
update field name to match backend field
germanurrus Aug 31, 2022
8894c34
fix strategy payload
germanurrus Aug 31, 2022
8fc1366
remove output
germanurrus Aug 31, 2022
e222574
update component
germanurrus Aug 31, 2022
8474a9c
update component
germanurrus Aug 31, 2022
f003fc2
remove import
germanurrus Aug 31, 2022
ff06745
add clear error
germanurrus Aug 31, 2022
40dbdcc
update error message
germanurrus Aug 31, 2022
ce1ecfe
add Error message
germanurrus Aug 31, 2022
141b62b
remove error
germanurrus Aug 31, 2022
16e09f6
clear error to fix dropdown styles
germanurrus Aug 31, 2022
e60b526
set focus on address
germanurrus Aug 31, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"date-fns": "^2.28.0",
"draft-js": "^0.11.7",
"draft-js-export-html": "^1.4.1",
"draft-js-import-html": "^1.4.1",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"immutable": "^4.0.0",
Expand Down
11 changes: 7 additions & 4 deletions frontend/packages/client/src/App.sass
Original file line number Diff line number Diff line change
Expand Up @@ -695,9 +695,12 @@ span[data-tooltip]
background-size: contain
border-color: transparent
background-repeat: no-repeat

@keyframes star
from
transform: scale(0)
from
transform: scale(0)
to
transform: scale(1)
transform: scale(1)

.rdw-editor-toolbar
border-radius: 8px !important
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import React, { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { useWebContext } from 'contexts/Web3';
import { ActionButton } from 'components';
import { ThresholdForm } from 'components/Community/ProposalThresholdEditor';
Expand All @@ -24,7 +24,7 @@ export default function StepThree({

const { isValidFlowAddress } = useWebContext();

const { control, register, handleSubmit, formState } = useForm({
const { control, register, handleSubmit, formState, setFocus } = useForm({
resolver: yupResolver(Schema(isValidFlowAddress)),
defaultValues: {
proposalThreshold,
Expand All @@ -42,6 +42,15 @@ export default function StepThree({

const { isDirty, isSubmitting, errors, isValid } = formState;

const dropdownValue = useWatch({ control, name: 'contractType' });
const contractAddressValue = useWatch({ control, name: 'contractAddress' });

useEffect(() => {
if (dropdownValue && contractAddressValue === '') {
setFocus('contractAddress');
}
}, [dropdownValue, contractAddressValue, setFocus]);

return (
<ThresholdForm
handleSubmit={handleSubmit(onSubmit)}
Expand Down
4 changes: 3 additions & 1 deletion frontend/packages/client/src/components/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ const DropDown = forwardRef(
const [isOpen, setIsOpen] = useState(false);
const [innerValue, setInnerValue] = useState(defaultValue ?? { label });

const openCloseDropdown = () => {
const openCloseDropdown = (e) => {
e.preventDefault();
e.stopPropagation();
setIsOpen((status) => !status);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import yup from 'helpers/validation';

const formFieldsStepOne = ['name', 'strategy', 'body', 'choices', 'tabOption'];
const formFieldsStepTwo = ['startDate', 'endDate', 'startTime', 'endTime'];

const StepOneSchema = yup.object().shape({
name: yup
.string()
.trim()
.required('Please enter a proposal title')
.max(150, 'The maximum length for title is 128 characters'),
strategy: yup.string().required('Please select a strategy'),
body: yup.string().required('Please enter a proposal description'),
tabOption: yup.string().oneOf(['text-based', 'visual']),
choices: yup
.array()
.of(
yup.object({
value: yup.string().required('Please enter option value'),
choiceImgUrl: yup.string().nullable(),
})
)
.when('tabOption', {
is: 'visual',
then: yup.array().of(
yup.object({
value: yup.string().required('Please enter option value'),
choiceImgUrl: yup
.string()
.trim()
.url('Image option is not valid')
.required('Please upload an image'),
})
),
})
.min(2, 'Please add a choice, minimun amout is two')
.unique('value', 'Invalid duplicated option'),
maxWeight: yup
.string()
.trim()
.matches(
/\s+$|^$|(^[0-9]+$)/,
'Proposal maximun weight must be a valid number'
),
minBalance: yup
.string()
.trim()
.matches(
/\s+$|^$|(^[0-9]+$)/,
'Proposal minimun balance must be a valid number'
),
});

const StepTwoSchema = yup.object().shape({
startDate: yup.date().required('Please provide a start date'),
startTime: yup.date().required('Please provide a start time'),
endDate: yup.date().required('Please provide an end date'),
endTime: yup.date().required('Please provide an end time'),
});

const initialValues = (fields = []) =>
Object.assign({}, ...fields.map((key) => ({ [key]: undefined })));

const stepOne = {
Schema: StepOneSchema,
initialValues: initialValues(formFieldsStepOne),
formFields: formFieldsStepOne,
};

const stepTwo = {
Schema: StepTwoSchema,
initialValues: initialValues(formFieldsStepTwo),
formFields: formFieldsStepTwo,
};

export { stepOne, stepTwo };
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import { useWatch } from 'react-hook-form';
import ImageChoices from './ImageChoices';
import TextBasedChoices from './TextBasedChoices';

export default function ChoiceOptionCreator({
setValue = () => {},
error = [],
register,
fieldName,
control,
clearErrors,
} = {}) {
const tabOption = useWatch({ control, name: 'tabOption' });

// tabOption value is saved on form
const setTab = (option) => (e) => {
e.preventDefault();
e.stopPropagation();
setValue('tabOption', option);
};

return (
<>
<div className="tabs choice-option is-toggle mt-2 mb-4">
<ul>
<li>
<button
className={`button left ${
tabOption === 'text-based' ? 'is-black' : 'outlined'
}`}
onClick={setTab('text-based')}
>
<span>Text-based</span>
</button>
</li>
<li>
<button
className={`button right ${
tabOption === 'visual' ? 'is-black' : 'outlined'
}`}
onClick={setTab('visual')}
>
<span>Visual</span>
</button>
</li>
</ul>
</div>
{tabOption === 'text-based' && (
<TextBasedChoices
error={error}
register={register}
fieldName={fieldName}
control={control}
clearErrors={clearErrors}
/>
)}
{tabOption === 'visual' && (
<ImageChoices control={control} error={error} />
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { Loader } from 'components';
import { FadeIn, Loader } from 'components';
import { Bin, Upload } from 'components/Svg';
import { useFileUploader } from 'hooks';
import { MAX_FILE_SIZE } from 'const';
Expand All @@ -13,6 +13,13 @@ const IMAGE_STATUS = {
toBeDeleted: 'to-be-deleted',
};

const initialState = {
imageUrl: null,
uploadStatus: null,
file: null,
text: '',
};

const UploadArea = ({ getRootProps, getInputProps, errorMessage }) => {
return (
<>
Expand Down Expand Up @@ -46,32 +53,23 @@ const UploadArea = ({ getRootProps, getInputProps, errorMessage }) => {
);
};

// initial state when no image has been uploaded
const initialState = {
imageUrl: null,
uploadStatus: null,
file: null,
text: '',
};

export default function ImageChoiceUploader({
onImageUpdate,
image: imageParam,
letterLabel,
error: errorParam,
} = {}) {
const [errorMessage, setErrorMessage] = useState(null);
// existing image and component receives props

const { imageUrl, text } = imageParam;
const existingImage = {
imageUrl,
uploadStatus: IMAGE_STATUS.uploaded,

const [image, setImage] = useState({
imageUrl: imageUrl === '' ? null : imageUrl,
uploadStatus: imageUrl === '' ? null : IMAGE_STATUS.uploaded,
file: null,
text,
};

const [image, setImage] = useState(
imageParam.imageUrl === '' ? initialState : existingImage
);
});

const { uploadFile, loading, error } = useFileUploader({
useModalNotifications: false,
Expand Down Expand Up @@ -183,11 +181,22 @@ export default function ImageChoiceUploader({
return (
<div>
{!image.imageUrl && (
<UploadArea
getRootProps={getRootProps}
getInputProps={getInputProps}
errorMessage={errorMessage}
/>
<>
<UploadArea
getRootProps={getRootProps}
getInputProps={getInputProps}
errorMessage={errorMessage}
/>
{errorParam?.choiceImgUrl?.message && (
<FadeIn>
<div className="pl-1 pt-2">
<p className="smaller-text has-text-red">
{errorParam.choiceImgUrl.message}
</p>
</div>
</FadeIn>
)}
</>
)}
{(image?.uploadStatus === IMAGE_STATUS.notStarted ||
image?.uploadStatus === IMAGE_STATUS.uploading) && (
Expand Down Expand Up @@ -253,6 +262,15 @@ export default function ImageChoiceUploader({
}
style={{ width: '100%' }}
/>
{errorParam?.value?.message && (
<FadeIn>
<div className="pl-1 pt-2">
<p className="smaller-text has-text-red">
{errorParam.value.message}
</p>
</div>
</FadeIn>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import React, { useEffect } from 'react';
import { getProposalType } from 'utils';
import { useFieldArray } from 'react-hook-form';
import ImageChoiceUploader from './ImageChoiceUploader';

const ImageChoices = ({ choices = [], onChoiceChange, initChoices } = {}) => {
const ImageChoices = ({ error, control } = {}) => {
const [errorOptOne, errorOptTwo] = Array.isArray(error) ? error : [];

const {
fields: choices,
update,
append,
} = useFieldArray({
control,
name: 'choices',
focusAppend: true,
});

useEffect(() => {
if (getProposalType(choices) !== 'image') {
initChoices([
{
id: 1,
value: '',
choiceImgUrl: '',
},
{
id: 2,
value: '',
choiceImgUrl: '',
},
]);
if (choices.length < 2) {
const size = 2 - choices.length;
const toAdd = new Array(size).fill({ value: '', choiceImgUrl: '' });
append(toAdd);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [choices, append]);

const onImageUpdate = (index) => (image) => {
onChoiceChange({ value: image.text, choiceImgUrl: image.imageUrl }, index);
update(index, { value: image.text, choiceImgUrl: image.imageUrl });
};

const [choiceA, choiceB] = choices;
Expand All @@ -44,6 +46,7 @@ const ImageChoices = ({ choices = [], onChoiceChange, initChoices } = {}) => {
}}
letterLabel="A"
onImageUpdate={onImageUpdate(0)}
error={errorOptOne}
/>
</div>
<div className="column">
Expand All @@ -54,6 +57,7 @@ const ImageChoices = ({ choices = [], onChoiceChange, initChoices } = {}) => {
}}
letterLabel="B"
onImageUpdate={onImageUpdate(1)}
error={errorOptTwo}
/>
</div>
</div>
Expand Down
Loading