Skip to content
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

Merge to prod #198

Merged
merged 22 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
616623c
feat: adjust stepper bar sticky top position in marathon form
ruby10127130 Dec 28, 2024
a894408
chore: rename saveBar to stepperBar for clear meaning
ruby10127130 Dec 28, 2024
4490f74
feat: move showPromotionBar, setShowPromotionBar, headerHeight into N…
ruby10127130 Dec 28, 2024
89855e0
fix: use param showPromotionBar from NavigationContext to fix broken …
ruby10127130 Dec 28, 2024
9bf34cc
chore: remove no use function and correct text indent
ruby10127130 Dec 28, 2024
eac97c2
refactor: rename validateRules to marathonDataMap, add dispatchType a…
ruby10127130 Dec 28, 2024
ae40822
refactor: refactor handleValidate and handleOnChange function, reduce…
ruby10127130 Dec 28, 2024
c71cd76
feat: validate all when user click nextStep on marathonForm
ruby10127130 Dec 28, 2024
ea1f7d9
chore: fix ESLint errors
ruby10127130 Dec 28, 2024
ab19ef4
chore: remove no use state hasError
ruby10127130 Dec 28, 2024
7b46354
feat: modify MarathonForm motivation tags content
ruby10127130 Dec 29, 2024
93243ef
feat: modify MarathonForm strategies tags content
ruby10127130 Dec 29, 2024
d19932c
feat: modify MarathonForm outcomes tags content
ruby10127130 Dec 29, 2024
dc2ba5b
chore: code prettify
ruby10127130 Dec 29, 2024
6f4b6a5
refactor: seperate showPromotionBar from NavigationContext, create Pr…
ruby10127130 Dec 29, 2024
f91c37a
feat: modify PromotionProvider scope in default layout
ruby10127130 Dec 29, 2024
f5bde33
fix: use param showPromotionBar from PromotionContext to fix broken s…
ruby10127130 Dec 29, 2024
c8cc7b5
Merge pull request #196 from ruby10127130/feature/marathon/stepperBar
JohnsonMao Dec 29, 2024
ce0ec34
fix(group): container style
JohnsonMao Dec 29, 2024
51dc0e9
refactor: rename Navigation_v2 to Navigation
JohnsonMao Dec 29, 2024
de2219f
remove: unused util and pages
JohnsonMao Dec 29, 2024
57caab8
Merge pull request #197 from daodaoedu/fix/web-style
JohnsonMao Dec 29, 2024
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
2 changes: 1 addition & 1 deletion components/Group/Form/Form.styled.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const StyledContainer = styled.main`
font-size: 14px;
}

@media (max-width: 760px) {
@media (max-width: 767px) {
padding: 20px;
}
`;
Expand Down
6 changes: 3 additions & 3 deletions components/Group/Form/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function GroupForm({
return (
<ProtectedComponent>
<StyledContainer>
<StyledPaper className="p-10 mb-4 md:w-[720px]">
<StyledPaper className="p-10 mb-4 w-full md:w-[720px]">
<StyledHeading>
{isCreateMode ? '發起揪團' : '編輯揪團'}
</StyledHeading>
Expand Down Expand Up @@ -145,7 +145,7 @@ export default function GroupForm({
placeholder="希望在什麼時間舉行?"
/>
</StyledPaper>
<StyledPaper className="p-10 mb-4 md:w-[720px]">
<StyledPaper className="p-10 mb-4 w-full md:w-[720px]">
<Fields.TextField
label="想找的夥伴"
name="partnerStyle"
Expand Down Expand Up @@ -215,7 +215,7 @@ export default function GroupForm({
helperText="標籤填寫完成後,會用 Hashtag 的形式呈現,例如: #一起學日文"
/>
</StyledPaper>
<StyledPaper className="md:w-[720px]">
<StyledPaper className="w-full md:w-[720px]">
<Fields.DateRadio
label="揪團期限"
name="deadline"
Expand Down
141 changes: 99 additions & 42 deletions components/Marathon/SignUp/MarathonForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ export default function MarathonForm({
const reduxDispatch = useDispatch();
const [hasLoaded, setHasLoaded] = useState(false);
const [errors, setErrors] = useState({});
const [hasErrors, setHasErrors] = useState(false);
const marathonState = useSelector((state) => { return state.marathon; });
const localStorgeStored = window.localStorage.getItem('newMarathon');
const editingMarathon = localStorgeStored ? JSON.parse(localStorgeStored) : null;
Expand All @@ -87,20 +86,6 @@ export default function MarathonForm({
};
const [newMarathon, setNewMarathon] = useReducer(marathonFormReducer, initialState());

const onNextStep = () => {
if (hasErrors) {
toast.error('請修正錯誤');
} else {
reduxDispatch(updateNewMarathon(newMarathon));
setCurrentStep(currentStep + 1);
}
};

const onPrevStep = () => {
reduxDispatch(updateNewMarathon(newMarathon));
setCurrentStep(currentStep - 1);
};

const validators = {
required: (value) => {
return value.trim().length > 0;
Expand All @@ -110,50 +95,74 @@ export default function MarathonForm({
return names.length === milestonesLength;
}
};
const validateRules = {

const marathonDataMap = {
title: {
dispatchType: 'UPDATE_FIELD',
dispatchKey: 'title',
validate: validators.required,
message: '請填寫表格',
},
description: {
dispatchType: 'UPDATE_FIELD',
dispatchKey: 'description',
validate: validators.required,
message: '請填寫計畫敘述',
},
motivationDescription: {
dispatchType: 'UPDATE_MOTIVATION_FIELD',
dispatchKey: 'description',
validate: validators.required,
message: '請填寫學習動機',
},
outcomesDescription: {
dispatchType: 'UPDATE_OUTCOMES_FIELD',
dispatchKey: 'description',
validate: validators.required,
message: '請填寫學習成果',
},
goals: {
dispatchType: 'UPDATE_FIELD',
dispatchKey: 'goals',
validate: validators.required,
message: '請填寫學習目標',
},
content: {
dispatchType: 'UPDATE_FIELD',
dispatchKey: 'content',
validate: validators.required,
message: '請填寫學習內容',
},
milestonesName: {
dispatchType: 'UPDATE_FIELD',
dispatchKey: 'milestones',
validate: (value) => {
return validators.allMilestonesNameRequired(value, newMarathon.milestones?.length);
},
message: '請填寫每週/隔週里程碑目標',
},
strategiesDescription: {
dispatchType: 'UPDATE_STRATEGIES_FIELD',
dispatchKey: 'description',
validate: validators.required,
message: '請填寫學習策略',
},
resources: {
dispatchType: 'UPDATE_FIELD',
dispatchKey: 'resources',
validate: validators.required,
message: '請填寫學習資源'
}
};

/**
* @param {string} name - The name of the field to validate.
* @param {*} input - The input value to validate.
* @returns {boolean} - Returns true if the input value passes validation, otherwise false.
*/
const handleValidate = (name, input) => {
const validateResult = validateRules[name]?.validate(input);
const errorMessage = validateRules[name]?.message;
const validateResult = marathonDataMap[name]?.validate(input);
const errorMessage = marathonDataMap[name]?.message;
if (validateResult) {
setErrors((prevErrors) =>
Object.fromEntries(Object.entries(prevErrors).filter(([key]) => key !== name))
Expand All @@ -168,22 +177,75 @@ export default function MarathonForm({
}
return validateResult;
};
const handleValidateAll = () => {
const newErrors = {};
let isValid = true;
Object.entries(marathonDataMap).forEach(([name, fieldData]) => {
const { validate, message } = fieldData;
let input;
if (validate) {
switch (name) {
case 'milestonesName':
input = newMarathon.milestones;
break;
case 'motivationDescription':
input = newMarathon.motivation?.description;
break;
case 'strategiesDescription':
input = newMarathon.strategies?.description;
break;
case 'outcomesDescription':
input = newMarathon.outcomes?.description;
break;
default:
input = newMarathon[name];
break;
}
const validationPassed = validate(input);

if (!validationPassed) {
newErrors[name] = { message: message || "驗證失敗" };
isValid = false;
}
}
});
setErrors(newErrors);
return isValid;
};
const handleOnChange = (
stateDispatchType,
stateDispatchKey,
name,
value,
validateName,
) => {
if (stateDispatchType && stateDispatchKey) {
const type = marathonDataMap[name]?.dispatchType;
const key = marathonDataMap[name]?.dispatchKey;

if (type && key) {
setNewMarathon({
type: stateDispatchType,
payload: { key: stateDispatchKey, value }
type,
payload: { key, value }
});
}
if (validateName) {
handleValidate(validateName, value);

if (name) {
handleValidate(name, value);
}
};

const onNextStep = () => {
const isValid = handleValidateAll();
if (!isValid) {
toast.error('請修正錯誤');
} else {
reduxDispatch(updateNewMarathon(newMarathon));
setCurrentStep(currentStep + 1);
}
};

const onPrevStep = () => {
reduxDispatch(updateNewMarathon(newMarathon));
setCurrentStep(currentStep - 1);
};

useEffect(() => {
setHasLoaded(true);
const storagedErrors = getMarathonErrorsStorage().get();
Expand All @@ -200,11 +262,6 @@ export default function MarathonForm({

useEffect(() => {
getMarathonErrorsStorage().set(errors);
if (Object.keys(errors).length) {
setHasErrors(true);
} else {
setHasErrors(false);
}
}, [errors]);

return (
Expand Down Expand Up @@ -245,7 +302,7 @@ export default function MarathonForm({
title="學習主題名稱"
value={newMarathon.title || ''}
onChange={(e) => {
handleOnChange('UPDATE_FIELD', 'title', e.target.value, 'title');
handleOnChange('title', e.target.value);
}}
sx={{
mb: '8px',
Expand Down Expand Up @@ -278,7 +335,7 @@ export default function MarathonForm({
<StyledTextareaAutosize
value={newMarathon.description || ''}
onChange={(e) => {
handleOnChange('UPDATE_FIELD', 'description', e.target.value, 'description');
handleOnChange('description', e.target.value);
}}
placeholder="範例:因為對剪影片和當 Youtuber 有興趣,我預計會研究搞笑型 Youtuber 的影片腳本與剪輯方式、拍攝我日常生活及練習剪輯,並建立 Youtube 頻道上傳影片。希望能藉此了解如何當一位 Youtuber。"
className={errors.description ? 'error' : ''}
Expand Down Expand Up @@ -323,15 +380,15 @@ export default function MarathonForm({
'生活發生變化',
'影響社會',
'受群體影響',
'其他:請在下方補上其他原因,並詳細說明動機'
'其他:請於下方撰寫'
]}
type="UPDATE_MOTIVATION_FIELD"
onChange={setNewMarathon}
selectedItems={newMarathon?.motivation?.tags || []}
/>
<StyledTextareaAutosize
onChange={(e) => {
handleOnChange('UPDATE_MOTIVATION_FIELD', 'description', e.target.value, 'motivationDescription');
handleOnChange('motivationDescription', e.target.value);
}}
className={errors.motivationDescription ? 'error' : ''}
value={newMarathon?.motivation?.description || ''}
Expand Down Expand Up @@ -360,7 +417,7 @@ export default function MarathonForm({
</Typography>
<StyledTextareaAutosize
onChange={(e) => {
handleOnChange('UPDATE_FIELD', 'goals', e.target.value, 'goals');
handleOnChange('goals', e.target.value);
}}
value={newMarathon.goals || ''}
placeholder="範例:
Expand Down Expand Up @@ -391,7 +448,7 @@ export default function MarathonForm({
</Typography>
<StyledTextareaAutosize
onChange={(e) => {
handleOnChange('UPDATE_FIELD', 'content', e.target.value, 'content');
handleOnChange('content', e.target.value);
}}
value={newMarathon.content || ''}
placeholder="範例:
Expand Down Expand Up @@ -442,15 +499,15 @@ export default function MarathonForm({
"田野調查",
"訪談",
"問卷調查",
"其他:請在下方補上其他原因,並詳細說明動機"
"其他:請於下方撰寫"
]}
type="UPDATE_STRATEGIES_FIELD"
onChange={setNewMarathon}
selectedItems={newMarathon?.strategies?.tags || []}
/>
<StyledTextareaAutosize
onChange={(e) => {
handleOnChange('UPDATE_STRATEGIES_FIELD', 'description', e.target.value, 'strategiesDescription');
handleOnChange('strategiesDescription', e.target.value);
}}
value={newMarathon?.strategies?.description || ''}
placeholder="範例:我預計會研究影片腳本、拍攝與剪輯方式,接著了解拍攝、剪輯與Youtube頻道經營,並同時練習拍攝與剪輯,開始經營頻道。我會用notion整理我收集到的資料以及筆記。"
Expand Down Expand Up @@ -483,7 +540,7 @@ export default function MarathonForm({
placeholder="範例:YouTube 創作者的實用資源"
value={newMarathon.resources || ''}
onChange={(e) => {
handleOnChange('UPDATE_FIELD', 'resources', e.target.value, 'resources');
handleOnChange('resources', e.target.value);
}}
className={errors.resources ? 'error' : 'warning'}
endAdornment={errors.resources ? <ClearIcon sx={{ color: '#EF5364' }} /> : null}
Expand Down Expand Up @@ -543,15 +600,15 @@ export default function MarathonForm({
"舉辦活動",
"開課",
"參與競賽",
"其他:請在下方補上其他原因,並詳細說明動機"
"其他:請於下方撰寫"
]}
type="UPDATE_OUTCOMES_FIELD"
onChange={setNewMarathon}
selectedItems={newMarathon?.outcomes?.tags || []}
/>
<StyledTextareaAutosize
onChange={(e) => {
handleOnChange('UPDATE_OUTCOMES_FIELD', 'description', e.target.value, 'outcomesDescription');
handleOnChange('outcomesDescription', e.target.value);
}}
value={newMarathon?.outcomes?.description || ''}
placeholder="範例:我預計會架設一個Youtube頻道,並上傳至少5支影片,並整理觀眾回饋與相關數據。"
Expand Down
6 changes: 3 additions & 3 deletions components/Marathon/SignUp/MilestoneGroup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default function MilestoneGroup({
// if change frequency, clear all data
setFrequency(e.target.value);
const changedMilestones = calculateMilestones(startDate, e.target.value, []);
onChangeHandler('UPDATE_FIELD', 'milestones', changedMilestones, 'milestonesName');
onChangeHandler('milestonesName', changedMilestones);
};

const handleEndDate = (/** fakeDate */) => {
Expand All @@ -115,7 +115,7 @@ export default function MilestoneGroup({
const changedMilestones = milestones.map((item) => {
return (item._tempId === newMilestone._tempId ? newMilestone : item);
});
onChangeHandler('UPDATE_FIELD', 'milestones', changedMilestones, 'milestonesName');
onChangeHandler('milestonesName', changedMilestones);
};
useEffect(() => {
const weeklyMilestonesLength = 22;
Expand All @@ -132,7 +132,7 @@ export default function MilestoneGroup({
}

if (!isDisabled) {
onChangeHandler('UPDATE_FIELD', 'milestones', initMilestones, 'milestonesName');
onChangeHandler('milestonesName', initMilestones);
}
}, []);

Expand Down
Loading
Loading