From 0d226a051af1c0360bb682fd623df731bf9b4504 Mon Sep 17 00:00:00 2001 From: hsuifang Date: Fri, 27 Sep 2024 14:27:36 +0800 Subject: [PATCH 1/8] =?UTF-8?q?chore:=20Remove=20the=20selection=20of=20li?= =?UTF-8?q?ving=20city=20-=20=E9=87=A3=E9=AD=9A=E8=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- constants/areas.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/constants/areas.js b/constants/areas.js index cb323329..b756b961 100644 --- a/constants/areas.js +++ b/constants/areas.js @@ -304,22 +304,9 @@ export const TAIWAN_DISTRICT = [ zip: '272', name: '南澳鄉', }, - { - zip: '290', - name: '釣魚臺', - }, ], name: '宜蘭縣', }, - { - districts: [ - { - zip: '290', - name: '釣魚臺', - }, - ], - name: '釣魚臺', - }, { districts: [ { From 157618b21d188d86c1a69053e975befec7af6e4b Mon Sep 17 00:00:00 2001 From: hsuifang Date: Fri, 27 Sep 2024 14:28:46 +0800 Subject: [PATCH 2/8] fix: Resolve issue with real-time data update --- pages/partner/detail/index.jsx | 9 ++++++++- redux/actions/partners.js | 6 ++++++ redux/reducers/partners.js | 6 ++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pages/partner/detail/index.jsx b/pages/partner/detail/index.jsx index c00446e8..f212934c 100644 --- a/pages/partner/detail/index.jsx +++ b/pages/partner/detail/index.jsx @@ -7,7 +7,11 @@ import Navigation from '@/shared/components/Navigation_v2'; import Footer from '@/shared/components/Footer_v2'; import Profile from '@/components/Profile'; import ContactModal from '@/components/Profile/Contact'; -import { sendEmailToPartner, fetchPartnerById } from '@/redux/actions/partners'; +import { + clearPartnerState, + sendEmailToPartner, + fetchPartnerById, +} from '@/redux/actions/partners'; import toast from 'react-hot-toast'; import { ROLE } from '@/constants/member'; import { mapToTable } from '@/utils/helper'; @@ -52,6 +56,9 @@ const Detail = () => { if (partnerId !== undefined) { fetchUser(); } + return () => { + dispatch(clearPartnerState()); + }; }, [partnerId]); // modal handle diff --git a/redux/actions/partners.js b/redux/actions/partners.js index b24f637c..bbe4c92d 100644 --- a/redux/actions/partners.js +++ b/redux/actions/partners.js @@ -46,3 +46,9 @@ export function fetchPartnerTags() { type: 'FETCH_PARTNER_TAGS', }; } + +export function clearPartnerState() { + return { + type: 'CLEAR_PARTNER_STATE', + }; +} diff --git a/redux/reducers/partners.js b/redux/reducers/partners.js index cd4b05fc..7ca70ac5 100644 --- a/redux/reducers/partners.js +++ b/redux/reducers/partners.js @@ -60,6 +60,12 @@ const reducer = (state = initialState, action) => { tags: [], }; } + case 'CLEAR_PARTNER_STATE': { + return { + ...state, + partner: null, + }; + } default: { return state; } From 71ef9544ae2c5288eb44acf193dac0c271d63b17 Mon Sep 17 00:00:00 2001 From: hsuifang Date: Fri, 27 Sep 2024 14:47:57 +0800 Subject: [PATCH 3/8] feat: add skeleton loader to partner list --- .../PartnerCard/PartnerSkelton.jsx | 40 +++++++++++ .../PartnerCard/PartnerSkeltonCard.jsx | 24 +++++++ components/Partner/PartnerList/index.jsx | 70 +++++++++++-------- 3 files changed, 105 insertions(+), 29 deletions(-) create mode 100644 components/Partner/PartnerList/PartnerCard/PartnerSkelton.jsx create mode 100644 components/Partner/PartnerList/PartnerCard/PartnerSkeltonCard.jsx diff --git a/components/Partner/PartnerList/PartnerCard/PartnerSkelton.jsx b/components/Partner/PartnerList/PartnerCard/PartnerSkelton.jsx new file mode 100644 index 00000000..404e050a --- /dev/null +++ b/components/Partner/PartnerList/PartnerCard/PartnerSkelton.jsx @@ -0,0 +1,40 @@ +import { Stack, Skeleton } from '@mui/material'; + +const PartnerSkelton = () => { + const array = new Uint32Array(1); + return ( + + + + + + + + + + + {new Array(3).fill(0).map((_, idx) => ( + + ))} + + + ); +}; + +export default PartnerSkelton; diff --git a/components/Partner/PartnerList/PartnerCard/PartnerSkeltonCard.jsx b/components/Partner/PartnerList/PartnerCard/PartnerSkeltonCard.jsx new file mode 100644 index 00000000..4a7e2452 --- /dev/null +++ b/components/Partner/PartnerList/PartnerCard/PartnerSkeltonCard.jsx @@ -0,0 +1,24 @@ +import { Fragment } from 'react'; +import { Grid, Box } from '@mui/material'; +import PartnerSkelton from './PartnerSkelton'; + +const PartnerSkeltonCard = ({ number, mobileScreen }) => { + const array = new Uint32Array(1); + return new Array(number).fill(0).map((_, idx) => ( + + + + + + {!mobileScreen && (idx + 1) % 2 === 0 && idx + 1 !== number && ( + + + + )} + + )); +}; + +export default PartnerSkeltonCard; diff --git a/components/Partner/PartnerList/index.jsx b/components/Partner/PartnerList/index.jsx index bf11de8f..50f5274c 100644 --- a/components/Partner/PartnerList/index.jsx +++ b/components/Partner/PartnerList/index.jsx @@ -4,6 +4,7 @@ import { useSelector } from 'react-redux'; import useMediaQuery from '@mui/material/useMediaQuery'; import { Grid, Box } from '@mui/material'; import PartnerCard from './PartnerCard'; +import PartnerSkeltonCard from './PartnerCard/PartnerSkeltonCard'; function PartnerList() { const router = useRouter(); @@ -24,35 +25,46 @@ function PartnerList() { alignItems: 'center', }} > - {lists.map((item, idx) => ( - - router.push(`partner/detail?id=${item._id}`)} - item - width="100%" - md={6} - mb={mobileScreen && '12px'} - > - - - {!mobileScreen && (idx + 1) % 2 === 0 && idx + 1 !== lists.length && ( - - - - )} - - ))} + {lists.length === 0 ? ( + + ) : ( + <> + {lists.map((item, idx) => ( + + router.push(`partner/detail?id=${item._id}`)} + item + width="100%" + md={6} + mb={mobileScreen && '12px'} + > + + + {!mobileScreen && + (idx + 1) % 2 === 0 && + idx + 1 !== lists.length && ( + + + + )} + + ))} + + )} ); } From 3fa56ea7a17a2a908212f636baf4f48c0d213eaf Mon Sep 17 00:00:00 2001 From: ruby10127130 <52684045+ruby10127130@users.noreply.github.com> Date: Tue, 1 Oct 2024 23:29:10 +0800 Subject: [PATCH 4/8] feat: show skeleton group card when loading (#80) --- .../Group/GroupList/SkeletonGroupCard.jsx | 71 +++++++++++++++++++ components/Group/GroupList/index.jsx | 21 +++++- 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 components/Group/GroupList/SkeletonGroupCard.jsx diff --git a/components/Group/GroupList/SkeletonGroupCard.jsx b/components/Group/GroupList/SkeletonGroupCard.jsx new file mode 100644 index 00000000..11002463 --- /dev/null +++ b/components/Group/GroupList/SkeletonGroupCard.jsx @@ -0,0 +1,71 @@ +import { Skeleton } from '@mui/material'; +import { + StyledContainer, + StyledFooter, + StyledGroupCard, + StyledInfo, + StyledText, +} from './GroupCard.styled'; + +function SkeletonGroupCard({ _id }) { + return ( + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default SkeletonGroupCard; diff --git a/components/Group/GroupList/index.jsx b/components/Group/GroupList/index.jsx index 5d7e3931..3aa27d00 100644 --- a/components/Group/GroupList/index.jsx +++ b/components/Group/GroupList/index.jsx @@ -6,6 +6,7 @@ import { Box } from '@mui/material'; import useSearchParamsManager from '@/hooks/useSearchParamsManager'; import { setQuery } from '@/redux/actions/group'; import GroupCard from './GroupCard'; +import SkeletonGroupCard from './SkeletonGroupCard'; export const StyledGroupItem = styled.li` position: relative; @@ -56,7 +57,25 @@ function GroupList() { return ( <> - {items?.length || isLoading ? ( + {isLoading ? ( + // always show 3 || 6 skeleton cards in mobile || desktop screen + Array.from({ length: isMobileScreen ? 3 : 6 }, (_, index) => { + const isLast = index === (isMobileScreen ? 2 : 5); + const shouldRenderDivider = + (isMobileScreen && !isLast) || + (isPadScreen && !isLast && index % 2 === 1) || + (isDeskTopScreen && !isLast && index % 3 === 2); + + return ( + + + + + {shouldRenderDivider && } + + ); + }) + ) : items?.length ? ( items.map((data, index) => { const isLast = index === items.length - 1; const shouldRenderDivider = From 1915fc14b1304eb0e3fb5f9304cac84e06cf780d Mon Sep 17 00:00:00 2001 From: Johnson Mao <86179381+JohnsonMao@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:26:15 +0800 Subject: [PATCH 5/8] feature: sharing feature (#81) * fix: prevent skeleton from navigating to links * feat: add sharing feeature --- .../Group/GroupList/SkeletonGroupCard.jsx | 4 +- components/Group/detail/ShareButtonGroup.jsx | 53 +++++++++++++++++++ components/Group/detail/index.jsx | 33 +++++++----- utils/share.js | 41 ++++++++++++++ 4 files changed, 116 insertions(+), 15 deletions(-) create mode 100644 components/Group/detail/ShareButtonGroup.jsx create mode 100644 utils/share.js diff --git a/components/Group/GroupList/SkeletonGroupCard.jsx b/components/Group/GroupList/SkeletonGroupCard.jsx index 11002463..152c01c5 100644 --- a/components/Group/GroupList/SkeletonGroupCard.jsx +++ b/components/Group/GroupList/SkeletonGroupCard.jsx @@ -7,9 +7,9 @@ import { StyledText, } from './GroupCard.styled'; -function SkeletonGroupCard({ _id }) { +function SkeletonGroupCard() { return ( - + + + + + + + + + + + + + + + + + {hasNativeShare && ( + + + + )} + + ); +} diff --git a/components/Group/detail/index.jsx b/components/Group/detail/index.jsx index f0b1c53f..40a6c407 100644 --- a/components/Group/detail/index.jsx +++ b/components/Group/detail/index.jsx @@ -1,7 +1,5 @@ import { useRouter } from 'next/navigation'; import { useSelector } from 'react-redux'; -import styled from '@emotion/styled'; -import Button from '@mui/material/Button'; import Box from '@mui/material/Box'; import Skeleton from '@mui/material/Skeleton'; import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew'; @@ -19,6 +17,7 @@ import { StyledMobileEditButton, } from './Detail.styled'; import ContactButton from './Contact'; +import ShareButtonGroup from './ShareButtonGroup'; function GroupDetail({ id, source, isLoading }) { const router = useRouter(); @@ -38,18 +37,26 @@ function GroupDetail({ id, source, isLoading }) { {source?.photoAlt} )} - {isLoading ? ( - + {isLoading ? ( + + ) : source?.isGrouping ? ( + 揪團中 + ) : ( + 已結束 + )} + - ) : source?.isGrouping ? ( - 揪團中 - ) : ( - 已結束 - )} + {isMyGroup ? ( () => window.open(url, '_blank'); + + const nativeShare = () => { + if (!navigator?.share) return; + navigator.share({ title, text, url }); + }; + + const facebookShare = openInNewTab( + `https://www.facebook.com/sharer/sharer.php?u=${url}&source_surface=external_reshare&display=popup&hashtag=${hashtag}`, + ); + + const lineShare = openInNewTab( + `https://social-plugins.line.me/lineit/share?url=${url}`, + ); + + const linkedinShare = openInNewTab( + `https://www.linkedin.com/sharing/share-offsite/?url=${url}&text=${text}`, + ); + + const threadsShare = openInNewTab( + `https://threads.net/intent/post?text=${url}`, + ); + + const xShare = openInNewTab(`https://x.com/intent/tweet?text=${url}`); + + return { + hasNativeShare: !!navigator?.share, + facebookShare, + lineShare, + linkedinShare, + nativeShare, + threadsShare, + xShare, + }; +} From b9d51f70107c6100b81e99fa3adfe0246b79fb59 Mon Sep 17 00:00:00 2001 From: Johnson Mao <86179381+JohnsonMao@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:31:09 +0800 Subject: [PATCH 6/8] feature: add group owner link (#82) * feature: add group owner link * fix: add tags field bug --- components/Group/Form/Fields/TagsField.jsx | 34 +++++++++---- components/Group/detail/OrganizerCard.jsx | 55 ++++++++++++---------- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/components/Group/Form/Fields/TagsField.jsx b/components/Group/Form/Fields/TagsField.jsx index f5337ca4..79ccee0f 100644 --- a/components/Group/Form/Fields/TagsField.jsx +++ b/components/Group/Form/Fields/TagsField.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useRef } from 'react'; import IconButton from '@mui/material/IconButton'; import FormHelperText from '@mui/material/FormHelperText'; import ClearIcon from '@mui/icons-material/Clear'; @@ -8,19 +8,20 @@ import { StyledChip, StyledTagsField } from '../Form.styled'; function TagsField({ name, helperText, control, value = [] }) { const [input, setInput] = useState(''); const [error, setError] = useState(''); + const isComposing = useRef(false); - const handleInput = (e) => { + const handleChange = (e) => { const _value = e.target.value; if (_value.length > 8) setError('標籤最多 8 個字'); else setError(''); setInput(_value); }; - const handleKeyDown = (e) => { - if (error) return; + const handleAddTag = () => { const tag = input.trim(); - if (e.key !== 'Enter' || !tag) return; - if (value.indexOf(tag) > -1) return; + if (!tag) return; + if (error) return; + if (value.includes(tag)) return; setInput(''); control.onChange({ target: { @@ -30,6 +31,12 @@ function TagsField({ name, helperText, control, value = [] }) { }); }; + const handleKeyDown = (e) => { + if (e.keyCode !== 13) return; + if (isComposing.current) return; + handleAddTag(); + }; + const handleDelete = (tag) => () => { control.onChange({ target: { @@ -54,12 +61,23 @@ function TagsField({ name, helperText, control, value = [] }) { {value.length < 8 && ( { + isComposing.current = true; + }} + onCompositionEnd={() => { + isComposing.current = false; + }} + onChange={handleChange} onKeyDown={handleKeyDown} /> )} {input.trim() && ( - + )} diff --git a/components/Group/detail/OrganizerCard.jsx b/components/Group/detail/OrganizerCard.jsx index 1709207c..d29903b8 100644 --- a/components/Group/detail/OrganizerCard.jsx +++ b/components/Group/detail/OrganizerCard.jsx @@ -5,6 +5,7 @@ import { EDUCATION_STEP, ROLE } from '@/constants/member'; import locationSvg from '@/public/assets/icons/location.svg'; import Chip from '@/shared/components/Chip'; import { timeDuration } from '@/utils/date'; +import Link from 'next/link'; const StyledHeader = styled.div` display: flex; @@ -82,33 +83,35 @@ function OrganizerCard({ data = {}, isLoading }) { return ( <> - - -
- - - {isLoading ? ( - - ) : ( - data?.user?.name - )} + + + +
+ + + {isLoading ? ( + + ) : ( + data?.user?.name + )} + + + {isLoading ? ( + + ) : ( + educationStage + )} + + + + {isLoading ? : role} - - {isLoading ? ( - - ) : ( - educationStage - )} - - - - {isLoading ? : role} - -
-
+
+
+ location icon {isLoading ? : location} From d0e3abb6bc78c602364ab721fb32bd6cb09fc5fb Mon Sep 17 00:00:00 2001 From: Johnson Mao <86179381+JohnsonMao@users.noreply.github.com> Date: Wed, 2 Oct 2024 20:02:43 +0800 Subject: [PATCH 7/8] fix: group skeleton card error (#83) --- components/Group/GroupList/SkeletonGroupCard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Group/GroupList/SkeletonGroupCard.jsx b/components/Group/GroupList/SkeletonGroupCard.jsx index 152c01c5..1d5205ca 100644 --- a/components/Group/GroupList/SkeletonGroupCard.jsx +++ b/components/Group/GroupList/SkeletonGroupCard.jsx @@ -9,7 +9,7 @@ import { function SkeletonGroupCard() { return ( - + Date: Wed, 2 Oct 2024 23:13:05 +0800 Subject: [PATCH 8/8] chore: share text (#84) --- components/Group/detail/ShareButtonGroup.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/Group/detail/ShareButtonGroup.jsx b/components/Group/detail/ShareButtonGroup.jsx index 557b7e5a..54d976f9 100644 --- a/components/Group/detail/ShareButtonGroup.jsx +++ b/components/Group/detail/ShareButtonGroup.jsx @@ -13,6 +13,12 @@ import { const StyledShareButtonGroup = styled.div` display: flex; gap: 0.25rem; + align-items: center; + + .share-text { + font-size: 14px; + color: #536166; + } `; export default function ShareButtonGroup({ title, text, url, hashtag }) { @@ -28,6 +34,7 @@ export default function ShareButtonGroup({ title, text, url, hashtag }) { return ( + 分享至