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

club detail page selected tab 로직 분리 #82

Open
wants to merge 11 commits into
base: frontend/origin
Choose a base branch
from
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"editor.tabSize": 2,
"editor.detectIndentation": false
}
18 changes: 5 additions & 13 deletions server/models/Club.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,13 @@ const clubSchema = mongoose.Schema({
views: {
type: Number,
default: 0
}
// managers: [{
// type: userSchema,
// ref: "User"
// }],
// useCertificate: {
// type: Boolean,
// },
// alumni: {
// type: User[],
// },
},
recruits: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Recruit'
}]
})

// add gender ratio?

const Image = mongoose.model('Image', imageSchema);
const Club = mongoose.model('Club', clubSchema)
module.exports = {
Expand Down
30 changes: 30 additions & 0 deletions server/models/Recruit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const mongoose = require("mongoose");

const recruitSchema = mongoose.Schema({
club: {
type: mongoose.Schema.Types.ObjectId,
ref: "Club",
},
title: {
type: String,
required: true,
},
startDate: {
type: Date,
},
endDate: {
type: Date,
},
contact: {
type: String,
required: true,
},
description: {
type: String,
},
});

const Recruit = mongoose.model("Recruit", recruitSchema);
module.exports = {
Recruit,
};
51 changes: 43 additions & 8 deletions server/routes/clubs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const express = require("express");
const router = express.Router();
const { Club, Image } = require("../models/Club");
const { User } = require("../models/User");
const { Recruit } = require("../models/Recruit");

// use for image upload
const fs = require("fs");
Expand Down Expand Up @@ -39,7 +40,6 @@ router.post("/", upload_club.array("photos"), async (req, res) => {
status: req.body.status,
});

// console.log(req.files);
for (let image of req.files) {
let obj = {
img: {
Expand All @@ -48,18 +48,19 @@ router.post("/", upload_club.array("photos"), async (req, res) => {
),
contentType: "image/png",
},
club: club,
club: club._id,
};

const newImage = new Image(obj);
newImage.save();
club.photos.push(newImage);
}
club.save((err, club) => {
club.save((err, _) => {
// success response로 club까지 리턴하면 너무 오래 지연되어 server error 발생
if (err) return res.json({ success: false, err });
return res.status(200).json({
success: true,
club,
// club,
});
});
});
Expand Down Expand Up @@ -174,15 +175,15 @@ router.post("/:clubId/like/:userId", async (req, res) => {
const club = await Club.findById(req.params.clubId);
const user = await User.findById(req.params.userId);
if (user.likedClubs.some((cid) => cid.toString() === club.id)) {
console.log("already exists...");
// console.log('already exists...')
user.likedClubs = user.likedClubs.filter(
(cid) => cid.toString() !== club.id
);
club.likedUsers = club.likedUsers.filter(
(uid) => uid.toString() !== user._id.toString()
);
} else {
console.log("new like!");
// console.log('new like!')
user.likedClubs.push(club);
club.likedUsers.push(user);
}
Expand All @@ -195,8 +196,42 @@ router.post("/:clubId/like/:userId", async (req, res) => {
}
});

router.get("/info", function (req, res) {
res.send("clubs info");
// 모집공고 등록
router.post("/:clubId/recruit", async (req, res) => {
try {
const recruit = new Recruit({
...req.body,
club: req.params.clubId,
});
const club = await Club.findById(req.params.clubId);

recruit.save((err, recruit) => {
if (err) return res.json({ success: false, err });
club.recruits.push(recruit);
club.save();
return res.status(200).json({
success: true,
recruit,
});
});
} catch (err) {
res.status(400).json({ success: false, err });
}
});

router.get("/:clubId/recruit", async (req, res) => {
try {
Club.findById(req.params.clubId)
.populate("recruits")
.exec((err, recruits) => {
res.status(200).json({
success: true,
recruits,
});
});
} catch (err) {
res.status(400).json({ success: false, err });
}
});

module.exports = router;
Binary file added server/routes/uploads/clubs/1613037738938.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613038594091.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613038594092.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613038717884.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613038717886.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613038789087.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613038789089.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613038955810.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613038955812.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613039423199.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613039423201.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613039712296.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613039913858.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613039913860.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613040091890.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613040091892.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613111523032.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613111877035.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613111877038.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613112211156.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613112211159.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613112337695.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613112337697.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613112738141.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613112838302.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added server/routes/uploads/clubs/1613113258360.jpg
Binary file added server/routes/uploads/clubs/1613113258362.jpg
Binary file added server/routes/uploads/clubs/1613113332405.jpg
Binary file added server/routes/uploads/clubs/1613113871594.jpg
Binary file added server/routes/uploads/clubs/1613114809468.jpg
Binary file added server/routes/uploads/clubs/1613115431893.jpg
Binary file added server/routes/uploads/clubs/1613115451502.jpg
Binary file added server/routes/uploads/clubs/1613115551795.jpg
Binary file added server/routes/uploads/clubs/1613115551797.jpg
Binary file added server/routes/uploads/clubs/1613115590118.jpg
Binary file added server/routes/uploads/clubs/1613115590122.jpg
8 changes: 4 additions & 4 deletions src/components/Config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const USER_SERVER = '/api/users';
export const CLUB_SERVER = '/api/clubs';
export const SEARCH_SERVER = '/api/search';
export const COMMENT_SERVER = '/api/comment';
export const USER_SERVER = "/api/users";
export const CLUB_SERVER = "/api/clubs";
export const SEARCH_SERVER = "/api/search";
export const COMMENT_SERVER = "/api/comment";
150 changes: 76 additions & 74 deletions src/components/pages/ClubDetailPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { AuthResponse } from 'api/auth';
import CommentList from '../../templates/CommentList';
import StoryList from 'components/templates/StoryList';
import ClubIntro from 'components/templates/ClubIntro';
import RecruitNotice from 'components/templates/RecruitNotice';

const Root = styled.div`
margin: 36px 144px;
Expand Down Expand Up @@ -96,12 +97,7 @@ interface ClubInfoRouterProps {
}

const ClubDetailPage: FC<Props & RouteComponentProps<ClubInfoRouterProps>> = ({ match, user, history }) => {
const [selectedTab, setSelectedTab] = useState<T.TabItem>(
match.params.tab === 'story' ? 'STORY' as T.TabItem :
match.params.tab === 'qna'? 'QNA' as T.TabItem :
match.params.tab === 'recruitment'? 'RECRUIT_NOTICE' as T.TabItem :
'CLUB_INTRO' as T.TabItem
);
const [selectedTab, setSelectedTab] = useState<T.TabItem>('CLUB_INTRO');
const [likeImg, setLikeImg] = useState<boolean>(false);
const [likeCount, setLikeCount] = useState<number>(0);

Expand All @@ -110,12 +106,19 @@ const ClubDetailPage: FC<Props & RouteComponentProps<ClubInfoRouterProps>> = ({

useEffect(() => {
dispatch(fetchClub.request({ id: match.params.id }));
console.log(match.params.tab)
}, [match.params.id]);
const tabItem = Object.entries(T.ClubDetailTab).find(([key, value]) => {
if (value.path === match.params.tab) return key;
});
tabItem && setSelectedTab(tabItem[0] as T.TabItem);
}, [match.params]);

useEffect(() => {
console.log(selectedTab);
}, [selectedTab])

useEffect(() => {
if(fetchedData && user) {
const club = fetchedData!.club;
const club = fetchedData.club;
if (user.likedClubs && user.likedClubs.some(cid => cid === club._id)) {
setLikeImg(true);
}
Expand All @@ -124,76 +127,75 @@ const ClubDetailPage: FC<Props & RouteComponentProps<ClubInfoRouterProps>> = ({
}
}, [user, fetchedData])

if (fetchedData === null || user === null) {
return <Loading />
} else {
const club = fetchedData!.club;
const handleTabClick: (type: keyof typeof T.ClubDetailTab, path: string) => void = (type, path) => {
setSelectedTab(type);
history.push(`/club/${club._id}${path}`)
};

const isSelectedTab: (type: T.TabItem) => boolean = (type) => {
return selectedTab === type;
}
const club = fetchedData?.club;

const handleTabClick: (type: keyof typeof T.ClubDetailTab, path: string) => void = (type, path) => {
if (!club) return;
setSelectedTab(type);
history.push(`/club/${club._id}/${path}`)
};

const handleLike: () => void = async () => {
if(user!.isAuth) {
dispatch(likeClub.request({ cludId: club._id, userId: user!._id, setLikeImg, likeImg, setLikeCount, likeCount}));
} else {
alert('로그인해주세요')
}
const isSelectedTab: (type: T.TabItem) => boolean = (type) => {
return selectedTab === type;
}

const handleLike: () => void = async () => {
if (!user || !club) return;
if (user.isAuth) {
dispatch(likeClub.request({ cludId: club._id, userId: user._id, setLikeImg, likeImg, setLikeCount, likeCount}));
} else {
alert('로그인해주세요')
}
}

const menuItems: ReactNode =
Object.entries(T.ClubDetailTab).map(([key, value]) => {
return (
<ClubDetailMenuItem
key={key}
isSelected={isSelectedTab(key as T.TabItem)}
onClick={() => handleTabClick(key as T.TabItem, value.path) }
>
{value.name}
</ClubDetailMenuItem>
);
})

return club ? (
<Root>
<BoldLargeText>{club.name}</BoldLargeText>
<TopWrapper>
<IconTagWrapper>
<IconCountWrapper>
<Icon src={likeImg ? likeFilledSvg : likeEmptySvg} onClick={() => handleLike()} />
<IconCount>{likeCount}</IconCount>
<Icon src={eyesSvg} />
<IconCount>{club.views}</IconCount>
</IconCountWrapper>
<TagWrapper>
{/* array.map으로 전환 필요*/}
<TagText>#학회</TagText>
<TagText>#생명과학</TagText>
<TagText>#화학</TagText>
<TagText>#논문읽기</TagText>
</TagWrapper>
</IconTagWrapper>
<ClubDetailMenuWrapper>
{menuItems}
</ClubDetailMenuWrapper>
</TopWrapper>
{ selectedTab === 'CLUB_INTRO' as T.TabItem &&
<ClubIntro club={club}/>
}
{ selectedTab === 'QNA' as T.TabItem &&
<CommentList user={user} clubId={club._id}/>
}
{ selectedTab === 'STORY' as T.TabItem &&
<StoryList clubId={club._id}/>
}
</Root>
) : null;
const menuItems: ReactNode =
Object.entries(T.ClubDetailTab).map(([key, value]) => {
return (
<ClubDetailMenuItem
key={key}
isSelected={isSelectedTab(key as T.TabItem)}
onClick={() => handleTabClick(key as T.TabItem, value.path) }
>
{value.name}
</ClubDetailMenuItem>
);
})

const detailMenuPage: () => ReactNode = () => {
if (!club) return null;

if (selectedTab === 'CLUB_INTRO') return (<ClubIntro club={club}/>)
else if (selectedTab === 'RECRUIT_NOTICE') return (<RecruitNotice />)
else if (selectedTab === 'STORY') return (<StoryList clubId={club._id}/>)
else if (selectedTab === 'QNA') return (<CommentList user={user} clubId={club._id}/>)
}

return (!user || !club) ? <Loading /> : (
<Root>
<BoldLargeText>{club.name}</BoldLargeText>
<TopWrapper>
<IconTagWrapper>
<IconCountWrapper>
<Icon src={likeImg ? likeFilledSvg : likeEmptySvg} onClick={() => handleLike()} />
<IconCount>{likeCount}</IconCount>
<Icon src={eyesSvg} />
<IconCount>{club.views}</IconCount>
</IconCountWrapper>
<TagWrapper>
{/* array.map으로 전환 필요*/}
<TagText>#학회</TagText>
<TagText>#생명과학</TagText>
<TagText>#화학</TagText>
<TagText>#논문읽기</TagText>
</TagWrapper>
</IconTagWrapper>
<ClubDetailMenuWrapper>
{menuItems}
</ClubDetailMenuWrapper>
</TopWrapper>
{ detailMenuPage() }
</Root>
)
}

export default withRouter(ClubDetailPage);
1 change: 0 additions & 1 deletion src/components/templates/ClubRegisterContents/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ const ClubRegisterContents: FC<Props & RouteComponentProps> = ({ history }) => {

const requiredAlert: (type: any, text: string) => void = (type, text) => {
alert(`[${text}]은/는 필수 항목입니다.`);
return;
}

const isRequiredEmpty: (input: string) => boolean = (input) => {
Expand Down
Loading