Skip to content

Commit

Permalink
Merge pull request #67 from daodaoedu/dev
Browse files Browse the repository at this point in the history
v1.1.0
  • Loading branch information
JohnsonMao authored Jul 21, 2024
2 parents a19061f + e174b42 commit 896b983
Show file tree
Hide file tree
Showing 161 changed files with 13,034 additions and 7,072 deletions.
22 changes: 21 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,25 @@
}
]
],
"plugins": ["@emotion/babel-plugin"]
"plugins": [
[
"babel-plugin-import",
{
"libraryName": "@mui/material",
"libraryDirectory": "",
"camel2DashComponentName": false
},
"core"
],
[
"babel-plugin-import",
{
"libraryName": "@mui/icons-material",
"libraryDirectory": "",
"camel2DashComponentName": false
},
"icons"
],
"@emotion/babel-plugin"
]
}
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_API_URL=
16 changes: 9 additions & 7 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ module.exports = {
node: true,
},
ignorePatterns: ['.eslintrc.js'],
// settings: {
// 'import/resolver': {
// node: {
// extensions: ['.js', '.jsx', '.ts', '.tsx'],
// },
// },
// },
settings: {
'import/resolver': {
alias: {
extensions: ['.js', '.jsx'],
map: [['@', '.']],
},
},
},
rules: {
'react/no-unescaped-entities': 'off',
'@next/next/no-page-custom-font': 'off',
Expand All @@ -41,6 +42,7 @@ module.exports = {
'operator-linebreak': 0,
'function-paren-newline': 0,
'jsx-a11y/click-events-have-key-events': 0,
'jsx-a11y/control-has-associated-label': 0,
'jsx-a11y/no-noninteractive-element-interactions': 0,
'react/jsx-one-expression-per-line': 0,
'no-confusing-arrow': 0,
Expand Down
Binary file added .yarn/install-state.gz
Binary file not shown.
48 changes: 48 additions & 0 deletions components/Group/AreaChips.jsx
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;
69 changes: 69 additions & 0 deletions components/Group/Banner.jsx
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;
88 changes: 88 additions & 0 deletions components/Group/Form/Fields/AreaCheckbox.jsx
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>
</>
);
}
63 changes: 63 additions & 0 deletions components/Group/Form/Fields/Select.jsx
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>
);
}
Loading

0 comments on commit 896b983

Please sign in to comment.