-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #67 from daodaoedu/dev
v1.1.0
- Loading branch information
Showing
161 changed files
with
13,034 additions
and
7,072 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
NEXT_PUBLIC_API_URL= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { useCallback, useMemo } from 'react'; | ||
import styled from '@emotion/styled'; | ||
import { AREAS } from '@/constants/areas'; | ||
import useSearchParamsManager from '@/hooks/useSearchParamsManager'; | ||
import Chip from '@/shared/components/Chip'; | ||
|
||
const StyledAreaChips = styled.ul` | ||
display: flex; | ||
flex-wrap: wrap; | ||
margin-bottom: 16px; | ||
gap: 12px 0; | ||
`; | ||
|
||
const AreaChips = () => { | ||
const [getSearchParams, pushState] = useSearchParamsManager(); | ||
|
||
const currentArea = useMemo( | ||
() => | ||
getSearchParams('area').filter((area) => | ||
AREAS.find(({ name }) => name === area), | ||
), | ||
[getSearchParams], | ||
); | ||
|
||
const handleClickArea = useCallback( | ||
(event) => { | ||
const targetArea = event.target.parentNode.textContent; | ||
const areas = currentArea.filter((area) => area !== targetArea); | ||
|
||
pushState('area', areas.toString()); | ||
}, | ||
[pushState, currentArea], | ||
); | ||
|
||
return ( | ||
currentArea.length > 0 && ( | ||
<StyledAreaChips> | ||
{currentArea.map((name) => ( | ||
<li key={name}> | ||
<Chip value={name} onDelete={handleClickArea} /> | ||
</li> | ||
))} | ||
</StyledAreaChips> | ||
) | ||
); | ||
}; | ||
|
||
export default AreaChips; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { useRouter } from 'next/router'; | ||
import styled from '@emotion/styled'; | ||
import Button from '@/shared/components/Button'; | ||
import groupBannerImg from '@/public/assets/group-banner.png'; | ||
import Image from '@/shared/components/Image'; | ||
|
||
const StyledBanner = styled.div` | ||
position: relative; | ||
picture { | ||
position: absolute; | ||
width: 100%; | ||
top: 0; | ||
height: 398px; | ||
img { | ||
height: inherit; | ||
} | ||
} | ||
h1 { | ||
margin-bottom: 8px; | ||
font-weight: 700; | ||
font-size: 36px; | ||
line-height: 140%; | ||
color: #536166; | ||
} | ||
p { | ||
font-weight: 400; | ||
font-size: 14px; | ||
line-height: 140%; | ||
color: #536166; | ||
} | ||
> div { | ||
position: relative; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
padding-top: 100px; | ||
} | ||
`; | ||
|
||
const Banner = () => { | ||
const router = useRouter(); | ||
|
||
return ( | ||
<StyledBanner> | ||
<picture> | ||
<Image | ||
src={groupBannerImg.src} | ||
alt="揪團封面" | ||
height="inherit" | ||
background="linear-gradient(#fcfefe 10%, #e0f1f2 40%)" | ||
borderRadius="0" | ||
/> | ||
</picture> | ||
<div> | ||
<h1>揪團</h1> | ||
<p>想一起組織有趣的活動或學習小組嗎?</p> | ||
<p>註冊並加入我們,然後創建你的活動,讓更多人一起參加!</p> | ||
<Button onClick={() => router.push('/group/create')}>我想揪團</Button> | ||
</div> | ||
</StyledBanner> | ||
); | ||
}; | ||
|
||
export default Banner; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { useEffect, useState } from 'react'; | ||
import Box from '@mui/material/Box'; | ||
import FormControlLabel from '@mui/material/FormControlLabel'; | ||
import Checkbox from '@mui/material/Checkbox'; | ||
import Select from './Select'; | ||
|
||
export default function AreaCheckbox({ | ||
options, | ||
itemLabel, | ||
itemValue, | ||
name, | ||
value, | ||
control, | ||
}) { | ||
const [isPhysicalArea, setIsPhysicalArea] = useState(false); | ||
|
||
const getPhysicalArea = (data) => | ||
options.find((option) => data.includes(option.name)); | ||
|
||
const handleChange = (val) => | ||
control.onChange({ target: { name, value: val } }); | ||
|
||
const physicalAreaValue = getPhysicalArea(value)?.name || ''; | ||
|
||
const toggleIsPhysicalArea = () => { | ||
const updatedValue = value.filter((v) => !getPhysicalArea([v])); | ||
handleChange(updatedValue); | ||
setIsPhysicalArea((pre) => !pre); | ||
}; | ||
|
||
const handleCheckboxChange = (_value) => { | ||
const updatedValue = value.includes(_value) | ||
? value.filter((v) => v !== _value) | ||
: [...value, _value]; | ||
handleChange(updatedValue); | ||
}; | ||
|
||
const handlePhysicalAreaChange = ({ target }) => { | ||
const updatedValue = value | ||
.filter((v) => !getPhysicalArea([v])) | ||
.concat(target.value); | ||
handleChange(updatedValue); | ||
}; | ||
|
||
const physicalAreaControl = { | ||
onChange: handlePhysicalAreaChange, | ||
onBlur: handlePhysicalAreaChange, | ||
}; | ||
|
||
useEffect(() => { | ||
if (value.find((v) => getPhysicalArea([v]))) setIsPhysicalArea(true); | ||
}, [value]); | ||
|
||
return ( | ||
<> | ||
<Box sx={{ display: 'flex', label: { whiteSpace: 'nowrap' } }}> | ||
<FormControlLabel | ||
control={<Checkbox onClick={toggleIsPhysicalArea} />} | ||
label="實體活動" | ||
checked={isPhysicalArea} | ||
/> | ||
<Select | ||
name={name} | ||
options={options} | ||
placeholder="地點" | ||
value={physicalAreaValue} | ||
itemLabel={itemLabel} | ||
itemValue={itemValue} | ||
control={physicalAreaControl} | ||
/> | ||
</Box> | ||
<div> | ||
<FormControlLabel | ||
control={<Checkbox onClick={() => handleCheckboxChange('線上')} />} | ||
label="線上" | ||
checked={value.includes('線上')} | ||
/> | ||
</div> | ||
<div> | ||
<FormControlLabel | ||
control={<Checkbox onClick={() => handleCheckboxChange('待討論')} />} | ||
label="待討論" | ||
checked={value.includes('待討論')} | ||
/> | ||
</div> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { useState } from 'react'; | ||
import FormControl from '@mui/material/FormControl'; | ||
import MuiSelect from '@mui/material/Select'; | ||
import MenuItem from '@mui/material/MenuItem'; | ||
|
||
export default function Select({ | ||
id, | ||
name, | ||
placeholder, | ||
options = [], | ||
itemLabel = 'label', | ||
fullWidth = true, | ||
multiple, | ||
sx, | ||
disabled, | ||
control, | ||
value, | ||
error, | ||
}) { | ||
const getValue = (any, key) => (typeof any === 'object' ? any[key] : any); | ||
const renderValue = (selected) => { | ||
if (selected.length === 0) return placeholder; | ||
if (Array.isArray(selected)) return selected.join('、'); | ||
return selected; | ||
}; | ||
|
||
return ( | ||
<FormControl size="small" fullWidth> | ||
<MuiSelect | ||
displayEmpty | ||
multiple={multiple} | ||
fullWidth={fullWidth} | ||
renderValue={renderValue} | ||
id={id} | ||
name={name} | ||
sx={{ | ||
color: value.length ? '#000' : '#92989A', | ||
'& legend': { display: 'none' }, | ||
'& fieldset': { top: 0 }, | ||
...sx, | ||
}} | ||
value={value} | ||
disabled={disabled} | ||
{...control} | ||
> | ||
{placeholder && ( | ||
<MenuItem disabled value="" sx={{ fontSize: 14 }}> | ||
{placeholder} | ||
</MenuItem> | ||
)} | ||
{options.map((item) => ( | ||
<MenuItem | ||
key={getValue(item, itemLabel)} | ||
value={getValue(item, itemLabel)} | ||
> | ||
{getValue(item, itemLabel)} | ||
</MenuItem> | ||
))} | ||
</MuiSelect> | ||
<span className="error-message">{error}</span> | ||
</FormControl> | ||
); | ||
} |
Oops, something went wrong.