From dca89367a51ba1d074b1fe30b7ba48b525c47b69 Mon Sep 17 00:00:00 2001 From: hsuifang Date: Sun, 14 Jan 2024 17:24:23 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat(user):=20=20=E6=96=B0=E5=A2=9E=20user?= =?UTF-8?q?=20=E7=99=BB=E5=85=A5=20google=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Profile/Accountsetting/index.jsx | 54 +- components/Profile/Edit/Edit.styled.jsx | 143 +++ components/Profile/Edit/index.jsx | 1125 +++++------------ components/Profile/Edit/useEditProfile.jsx | 100 ++ constants/member.js | 37 +- pages/_app.jsx | 9 +- pages/login/index.jsx | 43 +- pages/signin/index.jsx | 211 +--- pages/signin/interest/index.jsx | 107 +- pages/signin/signin.styled.jsx | 43 + redux/actions/user.js | 24 + redux/reducers/user.js | 28 +- redux/sagas/user/index.js | 43 + redux/store/index.js | 20 +- .../MainNav/SubList/UserAvatar/index.jsx | 16 +- 15 files changed, 937 insertions(+), 1066 deletions(-) create mode 100644 components/Profile/Edit/Edit.styled.jsx create mode 100644 components/Profile/Edit/useEditProfile.jsx create mode 100644 pages/signin/signin.styled.jsx diff --git a/components/Profile/Accountsetting/index.jsx b/components/Profile/Accountsetting/index.jsx index cdddf5f1..103d9c56 100644 --- a/components/Profile/Accountsetting/index.jsx +++ b/components/Profile/Accountsetting/index.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import { useState, useEffect } from 'react'; import { Box, Typography, @@ -8,7 +8,8 @@ import { FormControlLabel, } from '@mui/material'; import { useRouter } from 'next/router'; -import useFirebase from '../../../hooks/useFirebase'; +import { useDispatch, useSelector } from 'react-redux'; +import { updateUser, userLogout } from '@/redux/actions/user'; const TypographyStyle = { fontFamily: 'Noto Sans TC', @@ -20,15 +21,35 @@ const TypographyStyle = { }; const AccountSetting = () => { - const { push } = useRouter(); - const { auth, user, signInWithFacebook, signOutWithGoogle } = useFirebase(); + const dispatch = useDispatch(); + const router = useRouter(); + + const [isSubscribeEmail, setIsSubscribeEmail] = useState(false); + const user = useSelector((state) => state.user); + + const onUpdateUser = () => { + const payload = { + email: user.email, + isSubscribeEmail, + }; + dispatch(updateUser(payload)); + }; + + const logout = () => { + dispatch(userLogout()); + router.push('/'); + }; + + useEffect(() => { + setIsSubscribeEmail(user?.isSubscribeEmail || false); + }, [user]); + return ( { disabled sx={{ width: '100%', margin: '8px 0 30px 0' }} > - daodao@gmail.com + {user.email} - + {/* 電話驗證 - + */} 電子報 } + control={ + // eslint-disable-next-line react/jsx-wrap-multilines + { + setIsSubscribeEmail(event.target.checked); + // onUpdateUser();//待處理取消訂閱 + }} + /> + } label="訂閱電子報與島島阿學的新資訊" /> @@ -92,10 +121,7 @@ const AccountSetting = () => { margin: '24px 0 30px 0', backgroundColor: 'white', }} - onClick={() => { - signOutWithGoogle(); - push('/'); - }} + onClick={logout} > 登出 diff --git a/components/Profile/Edit/Edit.styled.jsx b/components/Profile/Edit/Edit.styled.jsx new file mode 100644 index 00000000..08507ebf --- /dev/null +++ b/components/Profile/Edit/Edit.styled.jsx @@ -0,0 +1,143 @@ +import styled from '@emotion/styled'; +import { Box, Typography, Button } from '@mui/material'; + +export const HomePageWrapper = styled.div` + --section-height: calc(100vh - 80px); + --section-height-offset: 80px; +`; + +export const ContentWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + border-radius: 16px; + margin: 0 auto; + padding: 40px 10px; + width: 672px; + @media (max-width: 767px) { + width: 80%; + .title { + text-overflow: ellipsis; + width: 100%; + } + } +`; + +export const StyledTitleWrap = styled(Box)` + background-color: #ffffff; + padding: 5%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + border-radius: 16px; + h2 { + font-weight: 700; + font-size: 22px; + line-height: 140%; + text-align: center; + color: #536166; + } + p { + font-weight: 700; + font-size: 14px; + line-height: 140%; + text-align: center; + color: #536166; + margin-top: 8px; + } +`; + +export const StyledSection = styled(Box)` + background-color: #ffffff; + padding: 40px; + margin-top: 16px; + width: 100%; + border-radius: 16px; +`; + +export const StyledGroup = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + margin-top: ${({ mt = '20' }) => `${mt}px`}; + input { + padding: 17px 16px 12px; + } +`; + +export const StyledSelectWrapper = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + width: 100%; + margin-top: 10px; + @media (maxwidth: 767px) { + flex-direction: column; + } +`; + +export const StyledSelectText = styled(Typography)` + margin: auto; + font-weight: ${({ isselected }) => + isselected === 'true' ? '700' : 'normal'}; +`; + +export const StyledSelectBox = styled(Box)` + border: 1px solid #dbdbdb; + border-radius: 8px; + padding: 10px; + width: ${({ col = '3' }) => `calc(calc(100% - 16px) / ${col})`}; + display: flex; + justify-items: center; + align-items: center; + cursor: pointer; + background-color: ${({ isselected }) => + isselected === 'true' ? '#DEF5F5' : 'initial'}; + border: ${({ isselected }) => + isselected === 'true' ? '1px solid #16B9B3' : '1px solid #DBDBDB'}; + margin-bottom: 12px; + + @media (maxwidth: 767px) { + width: 100%; + margin: 10px; + } +`; + +export const StyledToggleWrapper = styled(Box)` + border: 1px solid #dbdbdb; + border-radius: 8px; + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 16px; + padding: 13px 16px; +`; + +export const StyledToggleText = styled(Typography)` + font-weight: 500; + font-size: 16px; + line-height: 140%; + color: #293a3d; +`; + +export const StyledButtonGroup = styled(Box)` + margin-top: 24px; + width: 100%; + display: flex; +`; + +export const StyledButton = styled(Button)(({ variant = 'contained' }) => ({ + ...(variant === 'contained' && { + color: '#ffffff', + backgroundColor: '#16b9b3', + }), + width: '100%', + height: '40px', + borderRadius: '20px', + marginRight: '8px', +})); diff --git a/components/Profile/Edit/index.jsx b/components/Profile/Edit/index.jsx index 5934cda4..6f69703f 100644 --- a/components/Profile/Edit/index.jsx +++ b/components/Profile/Edit/index.jsx @@ -1,177 +1,70 @@ import React, { useMemo, useState, useEffect } from 'react'; -import styled from '@emotion/styled'; import { useRouter } from 'next/router'; -import Script from 'next/script'; +import { useSelector } from 'react-redux'; +import COUNTIES from '@/constants/countries.json'; +import { + GENDER, + ROLE, + EDUCATION_STAGE, + WANT_TO_DO_WITH_PARTNER, +} from '@/constants/member'; + import { Box, Typography, - Button, Skeleton, TextField, - Divider, Switch, TextareaAutosize, MenuItem, Select, + Grid, } from '@mui/material'; import { LazyLoadImage } from 'react-lazy-load-image-component'; -import toast from 'react-hot-toast'; -import { useAuthState } from 'react-firebase-hooks/auth'; -import { getAuth, updateProfile } from 'firebase/auth'; import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker'; -import { - getFirestore, - collection, - getDocs, - doc, - getDoc, - setDoc, - addDoc, - updateDoc, -} from 'firebase/firestore'; -import dayjs from 'dayjs'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import SEOConfig from '../../../shared/components/SEO'; -import Navigation from '../../../shared/components/Navigation_v2'; -import Footer from '../../../shared/components/Footer_v2'; -import { - GENDER, - ROLE, - EDUCATION_STEP, - WANT_TO_DO_WITH_PARTNER, - CATEGORIES, -} from '../../../constants/member'; -import COUNTIES from '../../../constants/countries.json'; - -const HomePageWrapper = styled.div` - --section-height: calc(100vh - 80px); - --section-height-offset: 80px; -`; +import SEOConfig from '@/shared/components/SEO'; -const ContentWrapper = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - background: white; - border-radius: 16px; - margin: 0 auto; - padding: 40px 10px; - width: 672px; - @media (max-width: 767px) { - width: 80%; - .title { - text-overflow: ellipsis; - width: 100%; - } - } -`; +import useEditProfile from './useEditProfile'; +import { + StyledGroup, + StyledSelectWrapper, + StyledSelectBox, + StyledSelectText, + StyledToggleWrapper, + StyledToggleText, + HomePageWrapper, + ContentWrapper, + StyledTitleWrap, + StyledSection, + StyledButtonGroup, + StyledButton, +} from './Edit.styled'; function EditPage() { const router = useRouter(); - const auth = getAuth(); - const [user, isLoading] = useAuthState(auth); - const [userName, setUserName] = useState(''); - const [photoURL, setPhotoURL] = useState(''); - const [birthDay, setBirthDay] = useState(dayjs()); - const [gender, setGender] = useState(''); - const [roleList, setRoleList] = useState([]); - const [wantToLearnList, setWantToLearnList] = useState([]); - const [interestAreaList, setInterestAreaList] = useState([]); - const [educationStep, setEducationStep] = useState('-1'); - const [location, setLocation] = useState('tw'); - const [url, setUrl] = useState(''); - const [description, setDescription] = useState(''); - const [isOpenLocation, setIsOpenLocation] = useState(false); - const [isOpenProfile, setIsOpenProfile] = useState(false); + + const { userState, onChangeHandler, onSubmit } = useEditProfile(); + const user = useSelector((state) => state.user); + const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); - console.log('user', user); + useEffect(() => { - if (!isLoading) { - const db = getFirestore(); - if (user?.uid) { - const docRef = doc(db, 'partnerlist', user?.uid); - getDoc(docRef).then((docSnap) => { - const data = docSnap.data(); - console.log('data', data); - setUserName(data?.userName || user?.displayName || ''); - setPhotoURL(data?.photoURL || user?.photoURL || ''); - setBirthDay(dayjs(data?.birthDay) || dayjs()); - setGender(data?.gender || ''); - setRoleList(data?.roleList || []); - setWantToLearnList(data?.wantToLearnList || []); - setInterestAreaList(data?.interestAreaList || []); - setEducationStep(data?.educationStep); - setLocation(data?.location || ''); - setUrl(data?.url || ''); - setDescription(data?.description || ''); - setIsOpenLocation(data?.isOpenLocation || false); - setIsOpenProfile(data?.isOpenProfile || false); - }); - } + if (user._id) { + Object.entries(user).forEach(([key, value]) => { + onChangeHandler({ key, value }); + }); + } else { + router.push('/'); } - }, [user, isLoading]); - - const onUpdateUser = (successCallback) => { - const payload = { - userName, - photoURL, - birthDay: birthDay.toISOString(), - gender, - roleList, - wantToLearnList, - interestAreaList, - educationStep, - location, - url, - description, - isOpenLocation, - isOpenProfile, - lastUpdateDate: dayjs().toISOString(), - }; + }, [user]); - const db = getFirestore(); - - const docRef = doc(db, 'partnerlist', user?.uid); - const partnerlistDocRef = doc(db, 'partnerlist', user?.uid); - getDoc(docRef).then((docSnap) => { - setIsLoadingSubmit(true); - if (isOpenProfile) { - toast - .promise( - Promise.allSettled([ - updateDoc(docRef, payload), - setDoc(partnerlistDocRef, payload), - ]).then(() => { - setIsLoadingSubmit(false); - }), - { - success: '更新成功!', - error: '更新失敗', - loading: '更新中...', - }, - ) - .then(() => { - successCallback(); - }); - } else { - toast - .promise( - updateDoc(docRef, payload).then(() => { - setIsLoadingSubmit(false); - }), - { - success: '更新成功!', - error: '更新失敗', - loading: '更新中...', - }, - ) - .then(() => { - successCallback(); - }); - } - }); + const onUpdateUser = async (successCallback) => { + setIsLoadingSubmit(true); + await onSubmit({ id: user._id, email: user.email }); + setIsLoadingSubmit(false); + successCallback(); }; const SEOData = useMemo( @@ -197,657 +90,335 @@ function EditPage() { background: 'linear-gradient(0deg, #f3fcfc, #f3fcfc), #f7f8fa', }} > - - - + +

編輯個人頁面

+

填寫完整資訊可以幫助其他夥伴更了解你哦!

+ - - 編輯個人頁面 - - - 填寫完整資訊可以幫助其他夥伴更了解你哦! - - - } - /> - - - - 您的名稱 * - setUserName(event.target.value)} - /> - - - 生日 * - setBirthDay(date)} - renderInput={(params) => ( - - )} - /> - - - 性別 * - - {GENDER.map(({ label, value }) => ( - { - setGender(value); - }} - sx={{ - border: '1px solid #DBDBDB', - borderRadius: '8px', - padding: '10px', - width: 'calc(calc(100% - 16px) / 3)', - display: 'flex', - justifyItems: 'center', - alignItems: 'center', - cursor: 'pointer', - ...(gender === value - ? { - backgroundColor: '#DEF5F5', - border: '1px solid #16B9B3', - } - : {}), - }} - > - {label} - - ))} - - - - 身份 * - - {ROLE.slice(0, 3).map(({ label, value }) => ( - { - if (roleList.includes(value)) { - setRoleList((state) => - state.filter((data) => data !== value), - ); - } else { - setRoleList((state) => [...state, value]); - } - }} - sx={{ - border: '1px solid #DBDBDB', - borderRadius: '8px', - padding: '10px', - width: 'calc(calc(100% - 16px) / 3)', - display: 'flex', - justifyItems: 'center', - alignItems: 'center', - cursor: 'pointer', - ...(roleList.includes(value) - ? { - backgroundColor: '#DEF5F5', - border: '1px solid #16B9B3', - } - : {}), - '@media (maxWidth: 767px)': { - width: '100%', - margin: '10px', - }, - }} + variant="circular" + animation="wave" + /> + } + /> + + + + 名稱 * + + onChangeHandler({ key: 'name', value: event.target.value }) + } + /> + + + 生日 * + + onChangeHandler({ key: 'birthDay', value: date }) + } + renderInput={(params) => ( + + )} + /> + + + 性別 * + + {GENDER.map(({ label, value }) => ( + { + onChangeHandler({ key: 'gender', value }); + }} + > + - - {label} - - - ))} - - - {ROLE.slice(3).map(({ label, value }) => ( - { - if (roleList.includes(value)) { - setRoleList((state) => - state.filter((data) => data !== value), - ); - } else { - setRoleList((state) => [...state, value]); - } - }} - sx={{ - border: '1px solid #DBDBDB', - borderRadius: '8px', - padding: '10px', - width: 'calc(calc(100% - 16px) / 3)', - display: 'flex', - justifyItems: 'center', - alignItems: 'center', - cursor: 'pointer', - ...(roleList.includes(value) - ? { - backgroundColor: '#DEF5F5', - border: '1px solid #16B9B3', - } - : {}), - '@media (maxWidth: 767px)': { - width: '100%', - margin: '10px', - }, - }} + {label} + + + ))} + + + + 身份 * + + {ROLE.map(({ label, value }) => ( + + onChangeHandler({ + key: 'roleList', + value, + isMultiple: true, + }) + } + > + - - {label} - - - ))} - - - + {label} + + + ))} + + - - + + + + 教育階段 + { - setEducationStep(event.target.value); - }} - // placeholder="請選擇您或孩子目前的教育階段" - sx={{ width: '100%' }} - > - - 請選擇您或孩子目前的教育階段 + + 請選擇您或孩子目前的教育階段 + + {EDUCATION_STAGE.map(({ label, value }) => ( + + {label} - {EDUCATION_STEP.map(({ label, value }) => ( - - {label} - - ))} - - - + + + 居住地 + { - setLocation(event.target.value); - }} - // placeholder="請選擇您或孩子目前的教育階段" - sx={{ width: '100%' }} - > - - 請選擇居住地 + + 請選擇居住地 + + {COUNTIES.map(({ name, alpha2 }) => ( + + {name} - {COUNTIES.map(({ name, alpha2 }) => ( - - {name} - - ))} - - {/* { - setLocation(event.target.value); - }} - /> */} - - + + + + {/* 聯絡方式 */} + + {/* TODO: 新增 Social */} + + 聯絡方式 + - 想和夥伴一起 - - - {WANT_TO_DO_WITH_PARTNER.slice(0, 3).map( - ({ label, value }) => ( - { - if (wantToLearnList.includes(value)) { - setWantToLearnList((state) => - state.filter((data) => data !== value), - ); - } else { - setWantToLearnList((state) => [...state, value]); - } - }} - sx={{ - border: '1px solid #DBDBDB', - borderRadius: '8px', - padding: '10px', - width: 'calc(calc(100% - 16px) / 3)', - display: 'flex', - justifyItems: 'center', - alignItems: 'center', - cursor: 'pointer', - ...(wantToLearnList.includes(value) - ? { - backgroundColor: '#DEF5F5', - border: '1px solid #16B9B3', - } - : {}), - }} - > - - {label} - - - ), - )} - - + + + + + Instagram + + + + + + Discord + + + + + + Line + + + + + + Facebook + + + + + + + + + 想和夥伴一起 + + {WANT_TO_DO_WITH_PARTNER.map(({ label, value }) => ( + { + onChangeHandler({ + key: 'wantToDoList', + value, + isMultiple: true, + }); }} > - {WANT_TO_DO_WITH_PARTNER.slice(3).map( - ({ label, value }) => ( - { - if (wantToLearnList.includes(value)) { - setWantToLearnList((state) => - state.filter((data) => data !== value), - ); - } else { - setWantToLearnList((state) => [...state, value]); - } - }} - sx={{ - border: '1px solid #DBDBDB', - borderRadius: '8px', - padding: '10px', - width: 'calc(calc(100% - 16px) / 3)', - display: 'flex', - justifyItems: 'center', - alignItems: 'center', - cursor: 'pointer', - ...(wantToLearnList.includes(value) - ? { - backgroundColor: '#DEF5F5', - border: '1px solid #16B9B3', - } - : {}), - }} - > - - {label} - - - ), - )} - - -
- + {label} + + + ))} + + + + 可以和夥伴分享的事物 + { + onChangeHandler({ key: 'share', value: e.target.value }); }} - > - 可以和夥伴分享的事物 - - - {/* + + {/* TODO: 新增 TAG */} + + 標籤 + { + onChangeHandler({ + tagList: event.target.value, + isMultiple: true, + key: 'tagList', + }); }} + /> + - 標籤 - - - 可以是學習領域、興趣等等的標籤,例如:音樂創作、程式語言、電繪、社會議題。 - - */} - + + + + 個人簡介 + - 個人網站或社群 - { - setUrl(event.target.value); - }} - /> - - { + onChangeHandler({ + key: 'selfIntroduction', + value: event.target.value, + }); }} - > - 個人簡介 - { - setDescription(event.target.value); - }} - /> - -
- - + + + + + + 公開顯示居住地 + { + onChangeHandler({ + key: 'isOpenLocation', + value, + }); }} - > - - 公開顯示居住地 - - { - setIsOpenLocation(value); - }} - /> - - + + + 公開個人頁面尋找夥伴 + { + onChangeHandler({ + key: 'isOpenProfile', + value, + }); }} - > - - 公開個人頁面尋找夥伴 - - { - setIsOpenProfile(value); - }} - /> - - - + + + + + { + router.push('/profile/myprofile'); }} > - - - - - + 查看我的頁面 + + { + onUpdateUser(() => router.push('/profile')); + }} + > + 儲存資料 + + + ); diff --git a/components/Profile/Edit/useEditProfile.jsx b/components/Profile/Edit/useEditProfile.jsx new file mode 100644 index 00000000..91bd919b --- /dev/null +++ b/components/Profile/Edit/useEditProfile.jsx @@ -0,0 +1,100 @@ +import dayjs from 'dayjs'; +import { useReducer } from 'react'; +import { useDispatch } from 'react-redux'; +import { updateUser } from '@/redux/actions/user'; + +const initialState = { + name: '', + photoURL: '', + birthDay: dayjs(), + gender: '', + roleList: [], + wantToDoList: [], + // interestList: [], + educationStage: '-1', + location: 'tw', + contactInformationList: [], + tagList: [], + selfIntroduction: '', + share: '', + isOpenLocation: false, + isOpenProfile: false, + isLoadingSubmit: false, +}; + +const userReducer = (state, payload) => { + const { key, value, isMultiple = false } = payload; + if (isMultiple) { + return { + ...state, + [key]: state[key].includes(value) + ? state[key].filter((role) => role !== value) + : [...state[key], value], + }; + } else if (state && state[key] !== undefined) { + return { + ...state, + [key]: value, + }; + } + return state; +}; + +const useEditProfile = () => { + const reduxDispatch = useDispatch(); + + const [userState, stateDispatch] = useReducer(userReducer, initialState); + + // TODO ErrorMap + + const onChangeHandler = ({ key, value }) => { + stateDispatch({ key, value }); + }; + + const onSubmit = async ({ id, email }) => { + if (!id || !email) return; + const { + name, + birthDay, + gender, + roleList, + educationStage, + location, + wantToDoList, + share, + isOpenLocation, + isOpenProfile, + contactInformationList, + tagList, + selfIntroduction, + } = userState; + + const payload = { + id, + email, + contactInformationList, + name, + birthDay, + gender, + roleList, + wantToDoList, + educationStage, + location, + tagList, + selfIntroduction, + share, + isOpenLocation, + isOpenProfile, + }; + + reduxDispatch(updateUser(payload)); + }; + + return { + userState, + onChangeHandler, + onSubmit, + }; +}; + +export default useEditProfile; diff --git a/constants/member.js b/constants/member.js index 97938e23..f585b8e8 100644 --- a/constants/member.js +++ b/constants/member.js @@ -106,6 +106,41 @@ export const EDUCATION_STEP = [ }, ]; +export const EDUCATION_STAGE = [ + { + label: '學齡前', + value: 'preschool', + }, + { + label: '國小低年級', + value: 'elementary-junior', + }, + { + label: '國小中年級', + value: 'elementary-middle', + }, + { + label: '國小高年級', + value: 'elementary-senior', + }, + { + label: '國中', + value: 'junior-high', + }, + { + label: '高中', + value: 'high', + }, + { + label: '大學', + value: 'university', + }, + { + label: '其他', + value: 'other', + }, +]; + export const WANT_TO_DO_WITH_PARTNER = [ { label: '交朋友', @@ -128,7 +163,7 @@ export const WANT_TO_DO_WITH_PARTNER = [ value: 'make-group-class', }, { - label: '做專案', + label: '做專案/競賽', key: 'do-project', value: 'do-project', }, diff --git a/pages/_app.jsx b/pages/_app.jsx index 4c00e61b..224e1f0f 100644 --- a/pages/_app.jsx +++ b/pages/_app.jsx @@ -14,7 +14,12 @@ import { initGA, logPageView } from '../utils/analytics'; import Mode from '../shared/components/Mode'; import 'regenerator-runtime/runtime'; // Speech.js +import { persistStore } from 'redux-persist'; +import { PersistGate } from 'redux-persist/integration/react'; + const store = storeFactory(); +let persistor = persistStore(store); + const firebaseConfig = { apiKey: 'AIzaSyBJK-FKcGHwDy1TMcoJcBdEqbTYpEquUi4', authDomain: 'daodaoedu-4ae8f.firebaseapp.com', @@ -93,7 +98,9 @@ const App = ({ Component, pageProps }) => { /> - + + + ); diff --git a/pages/login/index.jsx b/pages/login/index.jsx index d0a4f949..7a1cba70 100644 --- a/pages/login/index.jsx +++ b/pages/login/index.jsx @@ -1,4 +1,5 @@ -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; import styled from '@emotion/styled'; import Router, { useRouter } from 'next/router'; import Script from 'next/script'; @@ -39,8 +40,8 @@ const ContentWrapper = styled.div` `; const LoginPage = () => { - const provider = new GoogleAuthProvider(); const router = useRouter(); + const SEOData = useMemo( () => ({ title: '登入島島|島島阿學', @@ -56,42 +57,8 @@ const LoginPage = () => { ); const onLogin = () => { - const auth = getAuth(); - - signInWithPopup(auth, provider) - .then((result) => { - // This gives you a Google Access Token. You can use it to access the Google API. - // const credential = GoogleAuthProvider.credentialFromResult(result); - // const token = credential.accessToken; - // The signed-in user info. - // console.log('result', result); - const { displayName } = result.user; - // sendDataToChromeExtension( - // 'locidnghejlnnlnbglelhaflehebblei', - // result.user, - // ); - const db = getFirestore(); - const docRef = doc(db, 'partnerlist', result?.user?.uid); - getDoc(docRef).then((docSnap) => { - // const isNewUser = Object.keys(docSnap.data() || {}).length === 0; - // if (isNewUser) { - toast.success(`歡迎登入! ${displayName}`); - router.push('/signin'); - // } else { - // toast.success(`歡迎回來! ${displayName}`); - // router.push('/'); - // } - }); - console.log(result); - }) - .catch((error) => { - console.log('error', error); - toast.error('登入失敗', { - style: { - marginTop: '70px', - }, - }); - }); + // toast.success(`歡迎登入! ${displayName}`); + window.open('https://daodao-server.onrender.com/auth/google', '_target'); }; return ( diff --git a/pages/signin/index.jsx b/pages/signin/index.jsx index 3a87431c..b06d9448 100644 --- a/pages/signin/index.jsx +++ b/pages/signin/index.jsx @@ -1,130 +1,80 @@ -import React, { useMemo, useState, useEffect } from 'react'; -import styled from '@emotion/styled'; +import { useMemo, useState, useEffect } from 'react'; import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { - Box, - Typography, - Button, - Skeleton, - TextField, - Divider, - Switch, - TextareaAutosize, - MenuItem, - Select, -} from '@mui/material'; +import { useSelector, useDispatch } from 'react-redux'; +import { fetchUserById, updateUser } from '@/redux/actions/user'; +import { GENDER, ROLE } from '@/constants/member'; +import dayjs from 'dayjs'; + +import { Box, Typography, Button, Skeleton, TextField } from '@mui/material'; import { LazyLoadImage } from 'react-lazy-load-image-component'; -import toast from 'react-hot-toast'; -import { useAuthState } from 'react-firebase-hooks/auth'; -import { getAuth, updateProfile } from 'firebase/auth'; import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker'; -import { - getFirestore, - collection, - getDocs, - doc, - getDoc, - setDoc, - addDoc, -} from 'firebase/firestore'; -import dayjs from 'dayjs'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; -import SEOConfig from '../../shared/components/SEO'; -import Navigation from '../../shared/components/Navigation_v2'; -import Footer from '../../shared/components/Footer_v2'; +import SEOConfig from '@/shared/components/SEO'; +import Navigation from '@/shared/components/Navigation_v2'; +import Footer from '@/shared/components/Footer_v2'; import { - GENDER, - ROLE, - EDUCATION_STEP, - WANT_TO_DO_WITH_PARTNER, - CATEGORIES, -} from '../../constants/member'; -import COUNTIES from '../../constants/countries.json'; - -const HomePageWrapper = styled.div` - --section-height: calc(100vh - 80px); - --section-height-offset: 80px; - background: linear-gradient(0deg, #f3fcfc, #f3fcfc), #f7f8fa; -`; - -const ContentWrapper = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - background-color: #fff; - border-radius: 16px; - margin: 60px auto; - max-width: 50%; - width: 100%; - @media (max-width: 767px) { - max-width: 80%; - .title { - text-overflow: ellipsis; - width: 100%; - } - } -`; + HomePageWrapper, + StyledContentWrapper, + StyledQuestionInput, +} from './signin.styled'; function EditPage() { const router = useRouter(); - const auth = getAuth(); - const [user, isLoading] = useAuthState(auth); + const dispatch = useDispatch(); + + const { + birthDay: userBirthDay, + gender: userGender, + roleList: userRoleList, + isSubscribeEmail: userIsSubscribeEmail, + email: userEmail, + createdDate, + updatedDate, + } = useSelector((state) => state?.user); + const { id } = router.query; + const [isSubscribeEmail, setIsSubscribeEmail] = useState(false); - const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); const [birthDay, setBirthDay] = useState(dayjs()); const [gender, setGender] = useState(''); const [roleList, setRoleList] = useState([]); + + const fetchUser = async () => { + dispatch(fetchUserById(id)); + }; + useEffect(() => { - if (!isLoading) { - const db = getFirestore(); - if (user?.uid) { - // console.log('auth.currentUser', auth.currentUser); - const docRef = doc(db, 'partnerlist', user?.uid); - getDoc(docRef).then((docSnap) => { - const data = docSnap.data(); - setBirthDay(dayjs(data?.birthDay) || dayjs()); - setGender(data?.gender || ''); - setRoleList(data?.roleList || []); - }); - } + if (id) { + fetchUser(); + } + }, [id]); + + useEffect(() => { + setBirthDay(userBirthDay ? dayjs(userBirthDay) : dayjs()); + setGender(userGender || ''); + setRoleList(userRoleList || []); + setIsSubscribeEmail(userIsSubscribeEmail || false); + + if (createdDate !== updatedDate) { + router.push('/profile'); } - }, [user, isLoading]); + }, [userEmail]); - const onUpdateUser = (successCallback) => { + const onUpdateUser = () => { const payload = { + id, + email: userEmail, birthDay: birthDay.toISOString(), gender, roleList, - lastUpdateDate: dayjs().toISOString(), isSubscribeEmail, }; - - const db = getFirestore(); - - const docRef = doc(db, 'partnerlist', user?.uid); - getDoc(docRef).then(() => { - setIsLoadingSubmit(true); - toast - .promise( - setDoc(docRef, payload).then(() => { - setIsLoadingSubmit(false); - }), - { - success: '更新成功!', - error: '更新失敗', - loading: '更新中...', - }, - ) - .then(() => { - successCallback(); - }); - }); + dispatch(updateUser(payload)); + router.push(`/signin/interest?id=${id}`); }; + const SEOData = useMemo( () => ({ title: '編輯我的島島資料|島島阿學', @@ -146,29 +96,10 @@ function EditPage() { - - - 基本資料 - + +

基本資料

- + 生日 * )} /> - - + + 性別 * ))} - - + + 身份 * ))} - + { - onUpdateUser(() => router.push('/signin/interest')); - }} + onClick={onUpdateUser} > 下一步 -
+
diff --git a/pages/signin/interest/index.jsx b/pages/signin/interest/index.jsx index 0d341cf1..3e2b1da0 100644 --- a/pages/signin/interest/index.jsx +++ b/pages/signin/interest/index.jsx @@ -1,41 +1,19 @@ import React, { useMemo, useState, useEffect } from 'react'; import styled from '@emotion/styled'; import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { - Box, - Typography, - Button, - Skeleton, - Modal, - TextField, - Divider, - Switch, - TextareaAutosize, - MenuItem, - Select, -} from '@mui/material'; +import { useSelector, useDispatch } from 'react-redux'; +import { fetchUserById, updateUser } from '@/redux/actions/user'; + +import { Box, Typography, Button, Skeleton } from '@mui/material'; import { LazyLoadImage } from 'react-lazy-load-image-component'; import toast from 'react-hot-toast'; -import { useAuthState } from 'react-firebase-hooks/auth'; -import { getAuth, updateProfile } from 'firebase/auth'; import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker'; -import { - getFirestore, - collection, - getDocs, - doc, - getDoc, - updateDoc, - setDoc, - addDoc, -} from 'firebase/firestore'; import dayjs from 'dayjs'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import SEOConfig from '../../../shared/components/SEO'; -import Navigation from '../../../shared/components/Navigation_v2'; -import Footer from '../../../shared/components/Footer_v2'; +import SEOConfig from '@/shared/components/SEO'; +import Navigation from '@/shared/components/Navigation_v2'; +import Footer from '@/shared/components/Footer_v2'; import { GENDER, ROLE, @@ -73,52 +51,35 @@ const ContentWrapper = styled.div` function EditPage() { const router = useRouter(); - const auth = getAuth(); - const [user, isLoading] = useAuthState(auth); - const [interestAreaList, setInterestAreaList] = useState([]); - const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); + const { id } = router.query; + const dispatch = useDispatch(); + + const { + _id: userId, + interestList: userInterestList, + email: userEmail, + } = useSelector((state) => state?.user); + + const [interestList, setInterestList] = useState([]); const [open, setOpen] = useState(false); useEffect(() => { - if (!isLoading) { - const db = getFirestore(); - if (user?.uid) { - // console.log('auth.currentUser', auth.currentUser); - const docRef = doc(db, 'partnerlist', user?.uid); - getDoc(docRef).then((docSnap) => { - const data = docSnap.data(); - setInterestAreaList(data?.interestAreaList || []); - }); - } + if (userId) { + setInterestList(userInterestList); + } + if (id) { + dispatch(fetchUserById(id)); } - }, [user, isLoading]); + }, [userId, id]); const onUpdateUser = (successCallback) => { const payload = { - interestAreaList, - lastUpdateDate: dayjs().toISOString(), + id: userId, + interestList, + email: userEmail, }; - - const db = getFirestore(); - - const docRef = doc(db, 'partnerlist', user?.uid); - getDoc(docRef).then(() => { - setIsLoadingSubmit(true); - toast - .promise( - updateDoc(docRef, payload).then(() => { - setIsLoadingSubmit(false); - }), - { - success: '更新成功!', - error: '更新失敗', - loading: '更新中...', - }, - ) - .then(() => { - successCallback(); - }); - }); + dispatch(updateUser(payload)); + successCallback(); }; const SEOData = useMemo( @@ -139,7 +100,6 @@ function EditPage() { { setOpen(false); router.push('/'); @@ -202,12 +162,12 @@ function EditPage() { { - if (interestAreaList.includes(value)) { - setInterestAreaList((state) => + if (interestList.includes(value)) { + setInterestList((state) => state.filter((data) => data !== value), ); } else { - setInterestAreaList((state) => [...state, value]); + setInterestList((state) => [...state, value]); } }} sx={{ @@ -221,7 +181,7 @@ function EditPage() { justifyItems: 'center', alignItems: 'center', cursor: 'pointer', - ...(interestAreaList.includes(value) + ...(interestList.includes(value) ? { backgroundColor: '#DEF5F5', border: '1px solid #16B9B3', @@ -267,7 +227,7 @@ function EditPage() { { router.back(); }} diff --git a/pages/signin/signin.styled.jsx b/pages/signin/signin.styled.jsx new file mode 100644 index 00000000..676433e6 --- /dev/null +++ b/pages/signin/signin.styled.jsx @@ -0,0 +1,43 @@ +import styled from '@emotion/styled'; + +export const HomePageWrapper = styled.div` + --section-height: calc(100vh - 80px); + --section-height-offset: 80px; + background: linear-gradient(0deg, #f3fcfc, #f3fcfc), #f7f8fa; +`; + +export const StyledContentWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #fff; + border-radius: 16px; + margin: 60px auto; + max-width: 50%; + width: 100%; + @media (max-width: 767px) { + max-width: 80%; + .title { + text-overflow: ellipsis; + width: 100%; + } + } + + h2 { + font-weight: 700; + font-size: 22px; + line-height: 140%; + text-align: center; + color: #536166; + margin-top: 40px; + } +`; + +export const StyledQuestionInput = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + margin-top: 20px; +`; diff --git a/redux/actions/user.js b/redux/actions/user.js index 12b05ec5..9dd45fac 100644 --- a/redux/actions/user.js +++ b/redux/actions/user.js @@ -4,6 +4,12 @@ export function userLogin() { }; } +export function userLogout() { + return { + type: 'USER_LOGOUT', + }; +} + export function checkUserAccount() { return { type: 'CHECK_USER_ACCOUNT', @@ -16,6 +22,15 @@ export function fetchAllUsers() { }; } +export function fetchUserById(id) { + return { + type: 'FETCH_USER_BY_ID', + payload: { + id, + }, + }; +} + export function addResourceToCollection(resourceId) { return { type: 'ADD_RESOURCE_TO_COLLECTION', @@ -33,3 +48,12 @@ export function removeResourceFromCollection(resourceId) { }, }; } + +export function updateUser(user) { + return { + type: 'UPDATE_USER_PROFILE', + payload: { + user, + }, + }; +} diff --git a/redux/reducers/user.js b/redux/reducers/user.js index 5539ab2a..126e9f46 100644 --- a/redux/reducers/user.js +++ b/redux/reducers/user.js @@ -1,10 +1,6 @@ // import toast from 'react-hot-toast'; -const initialState = { - name: '', - email: '', - photoURL: '', -}; +const initialState = {}; const reducer = (state = initialState, action) => { switch (action.type) { @@ -20,6 +16,11 @@ const reducer = (state = initialState, action) => { ...action.payload, }; } + case 'USER_LOGOUT': { + return { + ...initialState, + }; + } case 'ADD_RESOURCE_TO_COLLECTION_SUCCESS': { return { ...state, @@ -32,6 +33,23 @@ const reducer = (state = initialState, action) => { ...action.payload, }; } + case 'FETCH_USER_BY_ID_SUCCESS': { + return { + ...action.payload, + }; + } + + case 'FETCH_USER_BY_ID_FAILURE': { + return { + ...state, + }; + } + case 'UPDATE_USER_PROFILE_SUCCESS': { + return { + ...state, + ...action.payload, + }; + } default: { return state; } diff --git a/redux/sagas/user/index.js b/redux/sagas/user/index.js index dd5995c2..9dee9e08 100644 --- a/redux/sagas/user/index.js +++ b/redux/sagas/user/index.js @@ -47,10 +47,53 @@ function* fetchAllUsers() { } } +function* updateUserProfile(action) { + const { user } = action.payload; + try { + const baseUri = + process.env.NEXT_PUBLIC_API_URL || 'https://daodao-server.onrender.com'; + const URL = `${baseUri}/user/${user.id}`; + + const result = yield fetch(URL, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...user, + }), + }).then((res) => res.json()); + + yield put({ type: 'UPDATE_USER_PROFILE_SUCCESS', payload: result.data }); + } catch (error) { + yield put({ type: 'UPDATE_USER_PROFILE_FAILURE' }); + } +} + +function* fetchUserById(action) { + const { id } = action.payload; + try { + const baseUrl = + process.env.NEXT_PUBLIC_API_URL || 'https://daodao-server.onrender.com'; + const URL = `${baseUrl}/user/${id}`; + const result = yield fetch(URL).then((res) => res.json()); + console.log(result); + yield put({ + type: 'FETCH_USER_BY_ID_SUCCESS', + payload: result.data && result.data[0], + }); + } catch (error) { + console.log(error); + yield put({ type: 'FETCH_USER_BY_ID_FAILURE' }); + } +} + function* userSaga() { yield takeEvery('CHECK_USER_ACCOUNT', checkUserStatus); yield takeEvery('USER_LOGIN', userLogin); yield takeEvery('FETCH_ALL_USERS', fetchAllUsers); + yield takeEvery('UPDATE_USER_PROFILE', updateUserProfile); + yield takeEvery('FETCH_USER_BY_ID', fetchUserById); } export default userSaga; diff --git a/redux/store/index.js b/redux/store/index.js index 2ebd9c17..889e4926 100644 --- a/redux/store/index.js +++ b/redux/store/index.js @@ -1,11 +1,29 @@ import { createStore, applyMiddleware } from 'redux'; +import storage from 'redux-persist/lib/storage'; import logger from 'redux-logger'; import createSagaMiddleware from 'redux-saga'; import { configureStore } from '@reduxjs/toolkit'; +import { + persistReducer, + FLUSH, + REHYDRATE, + PAUSE, + PERSIST, + PURGE, + REGISTER, +} from 'redux-persist'; + +const persistConfig = { + key: 'root', + storage, + whitelist: ['user'], +}; import rootReducer from '../reducers'; import rootSaga from '../sagas'; +const persistedReducer = persistReducer(persistConfig, rootReducer); + // create a makeStore function const storeFactory = (preloadedState) => { const enableLog = @@ -13,7 +31,7 @@ const storeFactory = (preloadedState) => { const sagaMiddleware = createSagaMiddleware(); const middlewares = enableLog ? [logger, sagaMiddleware] : [sagaMiddleware]; const store = configureStore({ - reducer: rootReducer, + reducer: persistedReducer, preloadedState, middleware: [...middlewares], }); diff --git a/shared/components/Navigation_v2/MainNav/SubList/UserAvatar/index.jsx b/shared/components/Navigation_v2/MainNav/SubList/UserAvatar/index.jsx index 767b7975..d23a0106 100644 --- a/shared/components/Navigation_v2/MainNav/SubList/UserAvatar/index.jsx +++ b/shared/components/Navigation_v2/MainNav/SubList/UserAvatar/index.jsx @@ -1,16 +1,20 @@ import React, { useState } from 'react'; -import styled from '@emotion/styled'; -import Link from 'next/link'; +import { useSelector } from 'react-redux'; import { Avatar, Box, IconButton, Menu, MenuItem } from '@mui/material'; import { Group } from '@mui/icons-material'; import { useRouter } from 'next/router'; -import useFirebase from '../../../../../../hooks/useFirebase'; const UserAvatar = () => { const { push } = useRouter(); - const { auth, user, signInWithFacebook, signOutWithGoogle } = useFirebase(); + const user = useSelector((state) => state.user); + const [isOpenMenu, setIsOpenMenu] = useState(null); - if (!user) { + + const handleSignOut = () => { + console.log('handleSignOut'); + }; + + if (!user._id) { return ( { { - signOutWithGoogle(); + handleSignOut(); push('/'); setIsOpenMenu(false); }} From d807fe92d1ea06adfbb78b9f7cca2da535df04d3 Mon Sep 17 00:00:00 2001 From: hsuifang Date: Sun, 14 Jan 2024 22:51:00 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=E4=B8=B2=E6=8E=A5=E8=81=AF?= =?UTF-8?q?=E7=B9=AB=E5=A4=A5=E4=BC=B4=EF=BC=8C=E4=B8=A6=E5=AF=84=E9=80=81?= =?UTF-8?q?=20Email?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Profile/Contact/index.jsx | 113 ++++++++++++++---------- components/Profile/Edit/Edit.styled.jsx | 1 - components/Profile/UserTabs/index.jsx | 6 +- components/Profile/index.jsx | 66 +++++++++----- pages/partner/detail/index.jsx | 7 +- pages/profile/index.jsx | 30 ++++++- pages/profile/myprofile/index.jsx | 5 +- redux/actions/partners.js | 17 ++++ redux/reducers/partners.js | 6 ++ redux/sagas/partnersSaga.js | 22 +++++ redux/sagas/user/index.js | 1 - 11 files changed, 196 insertions(+), 78 deletions(-) diff --git a/components/Profile/Contact/index.jsx b/components/Profile/Contact/index.jsx index a8c38f5c..801b557d 100644 --- a/components/Profile/Contact/index.jsx +++ b/components/Profile/Contact/index.jsx @@ -7,8 +7,49 @@ import { TextareaAutosize, Avatar, } from '@mui/material'; +import styled from '@emotion/styled'; + +const StyledGroup = styled(Box)` + margin-bottom: 16px; +`; + +const StyledTitle = styled(Typography)` + color: var(--black-white-gray-dark, #293a3d); + /* desktop/body-M-Medium */ + font-family: Noto Sans TC; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 140%; /* 22.4px */ + margin-bottom: 11px; +`; +const StyledTextArea = styled(TextareaAutosize)` + border-radius: 8px; + border: 1px solid var(--black-white-gray-very-light, #dbdbdb); + background: var(--black-white-white, #fff); + padding: 12px 16px; + width: 100%; + min-height: 128px; +`; + +function ContactModal({ + title, + descipt, + avatar, + onClose, + onOk, + isLoadingSubmit, + open, +}) { + const [message, setMessage] = useState(''); + const [contact, setContact] = useState(''); + + const handleSubmit = () => { + onOk({ message, contact }); + setMessage(''); + setContact(''); + }; -function ContactModal({ onClose, onOk, isLoadingSubmit, open }) { return ( - + - 黃芊宇 + {title} - 實驗教育學生 + {descipt} - - 邀請訊息 - - - - 聯繫資訊 - - + + 邀請訊息 + setMessage(e.target.value)} + placeholder="想要和新夥伴交流什麼呢?可以簡單的自我介紹,寫下想認識夥伴的原因。" + /> + + + + 聯繫資訊 + + setContact(e.target.value)} + placeholder="ex. 自學申請、學習方法、學習資源,或各種學習領域的知識" + /> + handleSubmit({ message, contact })} > 送出 diff --git a/components/Profile/Edit/Edit.styled.jsx b/components/Profile/Edit/Edit.styled.jsx index 08507ebf..4e125605 100644 --- a/components/Profile/Edit/Edit.styled.jsx +++ b/components/Profile/Edit/Edit.styled.jsx @@ -13,7 +13,6 @@ export const ContentWrapper = styled.div` align-items: center; border-radius: 16px; margin: 0 auto; - padding: 40px 10px; width: 672px; @media (max-width: 767px) { width: 80%; diff --git a/components/Profile/UserTabs/index.jsx b/components/Profile/UserTabs/index.jsx index 2bbd7d68..2be4d1bb 100644 --- a/components/Profile/UserTabs/index.jsx +++ b/components/Profile/UserTabs/index.jsx @@ -41,17 +41,17 @@ const UserTabs = ({ description = '', wantToDoList = [], share = '' }) => { sx={{ borderBottom: '1px solid #F3F3F3', paddingBottom: '6px' }} >

可分享

- {share || '無'} + {share || '尚未填寫'}

想一起

- {wantToDoList || '無'} + {wantToDoList || '尚未填寫'}

簡介

- {description || '無'} + {description || '尚未填寫'}
diff --git a/components/Profile/index.jsx b/components/Profile/index.jsx index 1ccb9a3e..11d1623b 100644 --- a/components/Profile/index.jsx +++ b/components/Profile/index.jsx @@ -1,5 +1,6 @@ import { useMemo, useState } from 'react'; import { useRouter } from 'next/router'; +import { useDispatch } from 'react-redux'; import { Box, Button } from '@mui/material'; import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import { @@ -12,6 +13,8 @@ import SEOConfig from '@/shared/components/SEO'; import UserCard from './UserCard'; import UserTabs from './UserTabs'; import ContactModal from './Contact'; +import { sendEmailToPartner } from '@/redux/actions/partners'; +import toast from 'react-hot-toast'; const BottonBack = { color: '#536166', @@ -33,6 +36,7 @@ const EDUCATION_STEP_TABLE = mapToTable(EDUCATION_STEP); const Profile = ({ name, + email, photoURL, tagList = [], roleList = [], @@ -41,7 +45,10 @@ const Profile = ({ wantToDoList = [], location, share, + enableContactBtn = false, + sendEmail, }) => { + const dispatch = useDispatch(); const router = useRouter(); const [isLoading] = useState(false); const [open, setOpen] = useState(false); @@ -66,6 +73,21 @@ const Profile = ({ [router?.asPath, name], ); + const handleOnOk = ({ message, contact }) => { + dispatch( + sendEmailToPartner({ + to: email, + name, + roleList: roleList.length ? roleList : [], + photoURL, + text: message, + information: [sendEmail, contact], + }), + ); + setOpen(false); + toast.success('寄送成功'); + }; + return ( { setOpen(false); - // router.push('/'); - // router.push('/partner'); - }} - onOk={() => { - setOpen(false); - // router.push('/profile'); - // router.push('/profile/edit'); }} + onOk={handleOnOk} /> - - + {email !== sendEmail && ( + + )} ); }; diff --git a/pages/partner/detail/index.jsx b/pages/partner/detail/index.jsx index 8f192ea2..4f1c2517 100644 --- a/pages/partner/detail/index.jsx +++ b/pages/partner/detail/index.jsx @@ -17,6 +17,7 @@ const HomePageWrapper = styled.div` const Detail = () => { const dispatch = useDispatch(); const { partner } = useSelector((state) => state?.partners); + const { email: loginUserEmail } = useSelector((state) => state?.user); const router = useRouter(); const { id } = router.query; @@ -33,7 +34,11 @@ const Detail = () => { return ( - +