From c1f167cd596a71939575298c2b35092c63029ffa Mon Sep 17 00:00:00 2001 From: yhaeul Date: Tue, 11 Aug 2026 19:44:38 +0900 Subject: [PATCH 1/5] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=B0=94=EC=9D=BC=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=ED=8F=89=EC=A0=90=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mobile/src/entities/club.ts | 2 -- apps/mobile/src/repositories/review.ts | 3 --- 2 files changed, 5 deletions(-) diff --git a/apps/mobile/src/entities/club.ts b/apps/mobile/src/entities/club.ts index 3a9e7034..5062c3e5 100644 --- a/apps/mobile/src/entities/club.ts +++ b/apps/mobile/src/entities/club.ts @@ -59,8 +59,6 @@ export type Club = { isOfficialVerified?: boolean; officialVerificationStatus?: OfficialVerificationStatus; verifiedAt?: string | null; - // v240121 - avgRating: number; reviewKeywords: ReviewKeyword[]; // v240218 totalReviews: number; diff --git a/apps/mobile/src/repositories/review.ts b/apps/mobile/src/repositories/review.ts index c1579c26..ee5e232d 100644 --- a/apps/mobile/src/repositories/review.ts +++ b/apps/mobile/src/repositories/review.ts @@ -4,7 +4,6 @@ import { apiConnector } from "@/shared/utils/api"; export type CreateClubReviewsRequest = { uuid: Club["uuid"]; - rating?: number; // unused reviewKeywordIds: ReviewKeyword["id"][]; }; @@ -18,7 +17,6 @@ export type GetMyClubReviewRequest = { }; export type GetMyClubReviewResponse = { - rating: number; reviewKeywordIds: ReviewKeyword["id"][]; createdAt: string; updatedAt: string; @@ -35,7 +33,6 @@ export type ReviewRepository = { export const getReviewRepository = (): ReviewRepository => ({ createClubReviews: async (req) => { await apiConnector.post(`/v2/clubs/${req.uuid}/reviews`, { - rating: req.rating, reviewKeywordIds: req.reviewKeywordIds, }); }, From 96d7e1d388f21e1e8b533d3464a30ec0bef37e8c Mon Sep 17 00:00:00 2001 From: yhaeul Date: Tue, 11 Aug 2026 19:59:42 +0900 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20=EB=AA=A8=EC=A7=91=EA=B3=B5?= =?UTF-8?q?=EA=B3=A0=20=EC=88=98=EC=A0=95=20=EC=8B=9C=EA=B0=81=EC=9D=84=20?= =?UTF-8?q?recruitment=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mobile/src/entities/club.ts | 1 - .../src/features/club/components/ClubDetail/RecruitTab.tsx | 5 +++-- apps/mobile/src/repositories/recruitment.ts | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/mobile/src/entities/club.ts b/apps/mobile/src/entities/club.ts index 5062c3e5..da685936 100644 --- a/apps/mobile/src/entities/club.ts +++ b/apps/mobile/src/entities/club.ts @@ -49,7 +49,6 @@ export type Club = { imageUri: string; snsUrls?: string[]; article: string; - articleUploadedAt: string | null; activeMemberCount?: number; foundedAt?: string | null; activityImageUrls?: string[]; diff --git a/apps/mobile/src/features/club/components/ClubDetail/RecruitTab.tsx b/apps/mobile/src/features/club/components/ClubDetail/RecruitTab.tsx index 599c9c17..3f079c63 100644 --- a/apps/mobile/src/features/club/components/ClubDetail/RecruitTab.tsx +++ b/apps/mobile/src/features/club/components/ClubDetail/RecruitTab.tsx @@ -57,8 +57,9 @@ const RecruitTab = ({ (representativeId !== null && detailQuery.isLoading); const isError = representativeQuery.isError || detailQuery.isError; const content = detailQuery.data?.content; - const updatedAt = club.articleUploadedAt - ? dayjs(club.articleUploadedAt) + const representativeUpdatedAt = representativeQuery.data?.updatedAt; + const updatedAt = representativeUpdatedAt + ? dayjs(representativeUpdatedAt) : null; return ( diff --git a/apps/mobile/src/repositories/recruitment.ts b/apps/mobile/src/repositories/recruitment.ts index b8123a3b..721f0e9e 100644 --- a/apps/mobile/src/repositories/recruitment.ts +++ b/apps/mobile/src/repositories/recruitment.ts @@ -33,6 +33,7 @@ type ListClubRecruitmentsApiResponse = { type RepresentativeRecruitmentApiPayload = { id: number | string; + updatedAt: string; }; type GetRepresentativeRecruitmentApiResponse = @@ -59,6 +60,7 @@ export type GetRepresentativeRecruitmentRequest = { export type GetRepresentativeRecruitmentResponse = { id: number; + updatedAt: string; } | null; export type CreateRecruitmentRequest = { @@ -187,7 +189,9 @@ export const getRecruitmentRepository = (): RecruitmentRepository => ({ if (!representativeRecruitment) return null; const id = Number(representativeRecruitment.id); - return Number.isNaN(id) ? null : { id }; + return Number.isNaN(id) + ? null + : { id, updatedAt: representativeRecruitment.updatedAt }; }, createRecruitment: async (req) => { From 545e66f4fea9a31dd08770bb7c172e74d3019715 Mon Sep 17 00:00:00 2001 From: yhaeul Date: Tue, 11 Aug 2026 20:24:28 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=B0=94=EC=9D=BC=20a?= =?UTF-8?q?ctivityCycle=20=EC=9D=98=EC=A1=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mobile/src/entities/club.ts | 1 - .../club/screens/ClubInfoEditScreen/index.tsx | 108 +++++++++--------- .../screens/ClubDetailsScreen/index.tsx | 65 ++++++----- .../RegisterClubScreenContainer/index.tsx | 2 +- .../src/features/register-club/types.ts | 4 +- apps/mobile/src/shared/utils/activityCycle.ts | 26 ----- .../src/shared/utils/minActivityPeriod.ts | 26 +++++ 7 files changed, 120 insertions(+), 112 deletions(-) delete mode 100644 apps/mobile/src/shared/utils/activityCycle.ts create mode 100644 apps/mobile/src/shared/utils/minActivityPeriod.ts diff --git a/apps/mobile/src/entities/club.ts b/apps/mobile/src/entities/club.ts index da685936..cfccdaf1 100644 --- a/apps/mobile/src/entities/club.ts +++ b/apps/mobile/src/entities/club.ts @@ -43,7 +43,6 @@ export type Club = { hasDongbang: boolean; dongbangLocation?: string | null; minActivityPeriod?: number | null; - activityCycle: string; membershipFee: string; tags: string[]; imageUri: string; diff --git a/apps/mobile/src/features/club/screens/ClubInfoEditScreen/index.tsx b/apps/mobile/src/features/club/screens/ClubInfoEditScreen/index.tsx index 8d541062..6e0756a7 100644 --- a/apps/mobile/src/features/club/screens/ClubInfoEditScreen/index.tsx +++ b/apps/mobile/src/features/club/screens/ClubInfoEditScreen/index.tsx @@ -49,17 +49,17 @@ import type { SCREEN_TYPE, StackParamList } from "@/shared/constants/screen"; import { typography } from "@/shared/constants/typography"; import { serviceContext } from "@/shared/contexts/serviceContext"; import type { EditableImage, ImageFile } from "@/shared/types/image"; -import { - type ActivityCycleMode, - decrementActivityCycleValue, - incrementActivityCycleValue, -} from "@/shared/utils/activityCycle"; import { areValidSnsUrls, getClubSnsUrls, getSnsRequestFields, normalizeSnsUrls, } from "@/shared/utils/clubSns"; +import { + decrementMinActivityPeriodValue, + incrementMinActivityPeriodValue, + type MinActivityPeriodMode, +} from "@/shared/utils/minActivityPeriod"; import { navigation } from "@/shared/utils/navigation"; import { ms, s, vs } from "@/shared/utils/scale"; @@ -72,7 +72,7 @@ type ClubInfoEditFormData = { department: string; shortIntro: string; recruitType: string; - activityCycle: string; + minActivityPeriodInput: string; hasDongbang: boolean; dongbangLocation: string; clubSNSUrls: string[]; @@ -108,7 +108,7 @@ const initialFormData: ClubInfoEditFormData = { department: "", shortIntro: "", recruitType: "", - activityCycle: "", + minActivityPeriodInput: "", hasDongbang: false, dongbangLocation: "", clubSNSUrls: [""], @@ -139,12 +139,12 @@ const mapClubToFormData = (club: ManagedClubDetail): ClubInfoEditFormData => { recruitType: CLUB_RECRUIT_TYPES.includes(club.recruitType) ? club.recruitType : "", - activityCycle: - club.minActivityPeriod !== null && club.minActivityPeriod !== undefined - ? club.minActivityPeriod > 0 - ? String(club.minActivityPeriod) - : "" - : club.activityCycle || "", + minActivityPeriodInput: + club.minActivityPeriod !== null && + club.minActivityPeriod !== undefined && + club.minActivityPeriod > 0 + ? String(club.minActivityPeriod) + : "", hasDongbang: club.hasDongbang ?? false, dongbangLocation: club.dongbangLocation ?? "", clubSNSUrls: snsUrls.length > 0 ? snsUrls : [""], @@ -156,19 +156,14 @@ const mapClubToFormData = (club: ManagedClubDetail): ClubInfoEditFormData => { }; }; -const getInitialActivityCycleMode = ( +const getInitialMinActivityPeriodMode = ( club: ManagedClubDetail, -): ActivityCycleMode => { - const minActivityPeriod = club.minActivityPeriod; - if (minActivityPeriod !== null && minActivityPeriod !== undefined) { - return minActivityPeriod > 0 ? "number" : "none"; - } - return club.activityCycle ? "number" : "none"; -}; +): MinActivityPeriodMode => + (club.minActivityPeriod ?? 0) > 0 ? "number" : "none"; const normalizeComparableClubInfo = ( data: ClubInfoEditFormData, - activityCycleMode: ActivityCycleMode, + minActivityPeriodMode: MinActivityPeriodMode, ): ComparableClubInfo => ({ name: data.clubName.trim(), category: data.category, @@ -176,7 +171,9 @@ const normalizeComparableClubInfo = ( shortDescription: data.shortIntro.trim(), recruitType: data.recruitType, minActivityPeriod: - activityCycleMode === "number" ? parseInt(data.activityCycle, 10) || 0 : 0, + minActivityPeriodMode === "number" + ? parseInt(data.minActivityPeriodInput, 10) || 0 + : 0, hasDongbang: data.hasDongbang, dongbangLocation: data.hasDongbang ? data.dongbangLocation.trim() : "", snsUrls: normalizeSnsUrls(data.clubSNSUrls), @@ -344,9 +341,9 @@ const ClubRecruitTypeSection = memo( ClubRecruitTypeSection.displayName = "ClubRecruitTypeSection"; type ClubActivityPeriodSectionProps = { - mode: ActivityCycleMode; + mode: MinActivityPeriodMode; semesters: number; - onModeChange: (mode: ActivityCycleMode) => void; + onModeChange: (mode: MinActivityPeriodMode) => void; onDecrement: () => void; onIncrement: () => void; }; @@ -729,8 +726,8 @@ const ClubInfoEditScreen = () => { const [formData, setFormData] = useState(initialFormData); - const [activityCycleMode, setActivityCycleMode] = - useState("none"); + const [minActivityPeriodMode, setMinActivityPeriodMode] = + useState("none"); const [showDepartmentDropdown, setShowDepartmentDropdown] = useState(false); const [departmentSearch, setDepartmentSearch] = useState(""); const [imageFile, setImageFile] = useState(null); @@ -749,7 +746,7 @@ const ClubInfoEditScreen = () => { useEffect(() => { if (club) { setFormData(mapClubToFormData(club)); - setActivityCycleMode(getInitialActivityCycleMode(club)); + setMinActivityPeriodMode(getInitialMinActivityPeriodMode(club)); setImageFile(null); } }, [club]); @@ -764,7 +761,8 @@ const ClubInfoEditScreen = () => { [], ); - const activitySemesters = parseInt(formData.activityCycle, 10) || 0; + const minActivityPeriodSemesters = + parseInt(formData.minActivityPeriodInput, 10) || 0; const filteredDepartmentOptions = useMemo(() => { const keyword = departmentSearch.trim(); if (!keyword) { @@ -824,25 +822,25 @@ const ClubInfoEditScreen = () => { }); }, [setFormField]); - const handleActivityCycleModeChange = useCallback( - (mode: ActivityCycleMode) => { - setActivityCycleMode(mode); - setFormField("activityCycle", mode === "number" ? "1" : ""); + const handleMinActivityPeriodModeChange = useCallback( + (mode: MinActivityPeriodMode) => { + setMinActivityPeriodMode(mode); + setFormField("minActivityPeriodInput", mode === "number" ? "1" : ""); }, [setFormField], ); - const incrementActivityCycle = useCallback(() => { - const next = incrementActivityCycleValue(activitySemesters); - setActivityCycleMode(next.mode); - setFormField("activityCycle", next.value); - }, [activitySemesters, setFormField]); + const incrementMinActivityPeriod = useCallback(() => { + const next = incrementMinActivityPeriodValue(minActivityPeriodSemesters); + setMinActivityPeriodMode(next.mode); + setFormField("minActivityPeriodInput", next.value); + }, [minActivityPeriodSemesters, setFormField]); - const decrementActivityCycle = useCallback(() => { - const next = decrementActivityCycleValue(activitySemesters); - setActivityCycleMode(next.mode); - setFormField("activityCycle", next.value); - }, [activitySemesters, setFormField]); + const decrementMinActivityPeriod = useCallback(() => { + const next = decrementMinActivityPeriodValue(minActivityPeriodSemesters); + setMinActivityPeriodMode(next.mode); + setFormField("minActivityPeriodInput", next.value); + }, [minActivityPeriodSemesters, setFormField]); const handleSetHasDongbang = useCallback((value: boolean) => { setFormData((prev) => ({ @@ -897,13 +895,16 @@ const ClubInfoEditScreen = () => { return null; } const initialData = mapClubToFormData(club); - const initialActivityCycleMode = getInitialActivityCycleMode(club); - return normalizeComparableClubInfo(initialData, initialActivityCycleMode); + const initialMinActivityPeriodMode = getInitialMinActivityPeriodMode(club); + return normalizeComparableClubInfo( + initialData, + initialMinActivityPeriodMode, + ); }, [club]); const currentComparable = useMemo( - () => normalizeComparableClubInfo(formData, activityCycleMode), - [activityCycleMode, formData], + () => normalizeComparableClubInfo(formData, minActivityPeriodMode), + [minActivityPeriodMode, formData], ); const isSNSComplete = @@ -920,7 +921,8 @@ const ClubInfoEditScreen = () => { !!formData.recruitType.trim() && isSNSComplete && !!formData.clubDescription.trim() && - (activityCycleMode === "none" || !!formData.activityCycle.trim()); + (minActivityPeriodMode === "none" || + !!formData.minActivityPeriodInput.trim()); const updateRequest = useMemo(() => { if (!initialComparable) { @@ -1076,11 +1078,11 @@ const ClubInfoEditScreen = () => { /> { - const [activityCycleMode, setActivityCycleMode] = useState( - formData.activityCycle ? "number" : "none", - ); + const [minActivityPeriodMode, setMinActivityPeriodMode] = + useState( + formData.minActivityPeriodInput ? "number" : "none", + ); - const activitySemesters = parseInt(formData.activityCycle, 10) || 0; + const minActivityPeriodSemesters = + parseInt(formData.minActivityPeriodInput, 10) || 0; const setHasDongbang = (value: boolean) => { onFormDataChange( @@ -55,28 +57,31 @@ export const ClubDetailsScreen = ({ ); }; - const handleActivityCycleModeChange = (mode: ActivityCycleMode) => { - setActivityCycleMode(mode); - onFormDataChange({ activityCycle: mode === "number" ? "1" : "" }); + const handleMinActivityPeriodModeChange = (mode: MinActivityPeriodMode) => { + setMinActivityPeriodMode(mode); + onFormDataChange({ + minActivityPeriodInput: mode === "number" ? "1" : "", + }); }; - const incrementActivityCycle = () => { - const next = incrementActivityCycleValue(activitySemesters); - setActivityCycleMode(next.mode); - onFormDataChange({ activityCycle: next.value }); + const incrementMinActivityPeriod = () => { + const next = incrementMinActivityPeriodValue(minActivityPeriodSemesters); + setMinActivityPeriodMode(next.mode); + onFormDataChange({ minActivityPeriodInput: next.value }); }; - const decrementActivityCycle = () => { - const next = decrementActivityCycleValue(activitySemesters); - setActivityCycleMode(next.mode); - onFormDataChange({ activityCycle: next.value }); + const decrementMinActivityPeriod = () => { + const next = decrementMinActivityPeriodValue(minActivityPeriodSemesters); + setMinActivityPeriodMode(next.mode); + onFormDataChange({ minActivityPeriodInput: next.value }); }; const isComplete = formData.recruitType.trim() && areValidSnsUrls(formData.clubSNSUrls) && formData.clubDescription.trim() && - (activityCycleMode === "none" || formData.activityCycle.trim()); + (minActivityPeriodMode === "none" || + formData.minActivityPeriodInput.trim()); return ( @@ -130,14 +135,15 @@ export const ClubDetailsScreen = ({ handleActivityCycleModeChange("none")} + onPress={() => handleMinActivityPeriodModeChange("none")} > @@ -147,14 +153,15 @@ export const ClubDetailsScreen = ({ handleActivityCycleModeChange("number")} + onPress={() => handleMinActivityPeriodModeChange("number")} > @@ -165,12 +172,12 @@ export const ClubDetailsScreen = ({ - {activitySemesters}학기 + {minActivityPeriodSemesters}학기 ({ - mode: "number", - value: String(currentSemesters + 1), -}); - -export const decrementActivityCycleValue = ( - currentSemesters: number, -): ActivityCycleState => - currentSemesters > 1 - ? { - mode: "number", - value: String(currentSemesters - 1), - } - : { - mode: "none", - value: "", - }; diff --git a/apps/mobile/src/shared/utils/minActivityPeriod.ts b/apps/mobile/src/shared/utils/minActivityPeriod.ts new file mode 100644 index 00000000..9c79dbc9 --- /dev/null +++ b/apps/mobile/src/shared/utils/minActivityPeriod.ts @@ -0,0 +1,26 @@ +export type MinActivityPeriodMode = "none" | "number"; + +type MinActivityPeriodState = { + mode: MinActivityPeriodMode; + value: string; +}; + +export const incrementMinActivityPeriodValue = ( + currentSemesters: number, +): MinActivityPeriodState => ({ + mode: "number", + value: String(currentSemesters + 1), +}); + +export const decrementMinActivityPeriodValue = ( + currentSemesters: number, +): MinActivityPeriodState => + currentSemesters > 1 + ? { + mode: "number", + value: String(currentSemesters - 1), + } + : { + mode: "none", + value: "", + }; From 7b1371a5ff9419ea7d01c083ae7c85d71c4fed12 Mon Sep 17 00:00:00 2001 From: yhaeul Date: Tue, 11 Aug 2026 21:20:07 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=EB=8F=99=EC=95=84=EB=A6=AC=20?= =?UTF-8?q?=EC=86=8C=EC=86=8D=C2=B7=EC=9A=94=EC=95=BD=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=8B=A0=EA=B7=9C=20=ED=95=84=EB=93=9C=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mobile/src/entities/club.ts | 1 - .../club/components/ClubList/ClubCard.tsx | 3 +- .../club/screens/ClubDetailScreen/index.tsx | 16 +------ .../screens/ClubManagementScreen/index.tsx | 18 ++----- .../ManageClubRegistrationScreen/index.tsx | 3 +- .../mypage/screens/MyPageScreen/index.tsx | 7 +-- .../shared/components/HorizontalCarousel.tsx | 3 +- apps/mobile/src/shared/utils/club.test.ts | 48 +++++++++++++++++++ apps/mobile/src/shared/utils/club.ts | 20 ++++++++ .../service/search/search-query.service.ts | 1 + 10 files changed, 82 insertions(+), 38 deletions(-) create mode 100644 apps/mobile/src/shared/utils/club.test.ts diff --git a/apps/mobile/src/entities/club.ts b/apps/mobile/src/entities/club.ts index cfccdaf1..a51119fc 100644 --- a/apps/mobile/src/entities/club.ts +++ b/apps/mobile/src/entities/club.ts @@ -34,7 +34,6 @@ export type Club = { type: string; affiliationType?: string; category: Category["name"]; - college: string; collegeMajorId?: number; collegeMajor?: ClubCollegeMajor | null; recruitType: string; diff --git a/apps/mobile/src/features/club/components/ClubList/ClubCard.tsx b/apps/mobile/src/features/club/components/ClubList/ClubCard.tsx index 4f13b01a..3f551a19 100644 --- a/apps/mobile/src/features/club/components/ClubList/ClubCard.tsx +++ b/apps/mobile/src/features/club/components/ClubList/ClubCard.tsx @@ -7,6 +7,7 @@ import { CategoryMap } from "@/shared/constants/category"; import { Colors } from "@/shared/constants/colors"; import { typography } from "@/shared/constants/typography"; import useSaveClub from "@/shared/hooks/useSaveClub"; +import { getClubSummaryWithAffiliation } from "@/shared/utils/club"; import { ms, s, vs } from "@/shared/utils/scale"; type Props = { @@ -39,7 +40,7 @@ const ClubCard = ({ club, category, onPress }: Props) => { {club.name} - {club.description} + {getClubSummaryWithAffiliation(club)} diff --git a/apps/mobile/src/features/club/screens/ClubDetailScreen/index.tsx b/apps/mobile/src/features/club/screens/ClubDetailScreen/index.tsx index 316120c8..ac10f3b4 100644 --- a/apps/mobile/src/features/club/screens/ClubDetailScreen/index.tsx +++ b/apps/mobile/src/features/club/screens/ClubDetailScreen/index.tsx @@ -52,6 +52,7 @@ import WithViewEventLog from "@/shared/hocs/WithViewEventLog"; import useClickEventLog from "@/shared/hooks/useClickEventLog"; import useRequireLogin from "@/shared/hooks/useRequireLogin"; import useSaveClub from "@/shared/hooks/useSaveClub"; +import { getClubSummaryWithAffiliation } from "@/shared/utils/club"; import { getClubSnsUrls, getSnsIcon, @@ -165,20 +166,7 @@ const ClubDetailScreen = ({ route, navigation }: Props) => { if (!currentCategory) return null; const categoryDetail = CategoryMap[currentCategory]; - const affiliation = - club?.collegeMajor?.major || - club?.collegeMajor?.college || - club?.college || - club?.affiliationType || - ""; - const heroDescription = club - ? [ - affiliation ? `${affiliation} 소속` : "", - club.shortDescription || club.description, - ] - .filter(Boolean) - .join(" ") - : ""; + const heroDescription = club ? getClubSummaryWithAffiliation(club) : ""; const snsUrls = club ? getClubSnsUrls(club) : []; const handleBackButton = () => navigation.goBack(); diff --git a/apps/mobile/src/features/club/screens/ClubManagementScreen/index.tsx b/apps/mobile/src/features/club/screens/ClubManagementScreen/index.tsx index 32799878..ea697611 100644 --- a/apps/mobile/src/features/club/screens/ClubManagementScreen/index.tsx +++ b/apps/mobile/src/features/club/screens/ClubManagementScreen/index.tsx @@ -33,7 +33,7 @@ import EditPencilButton from "@/shared/components/EditPencilButton"; import { Colors } from "@/shared/constants/colors"; import { SCREEN_TYPE, type StackParamList } from "@/shared/constants/screen"; import { serviceContext } from "@/shared/contexts/serviceContext"; -import { getClubAffiliationLabel } from "@/shared/utils/club"; +import { getClubSummaryWithAffiliation } from "@/shared/utils/club"; import { navigation } from "@/shared/utils/navigation"; import { ms, s, vs } from "@/shared/utils/scale"; @@ -173,8 +173,7 @@ const ClubManagementScreen = () => { : recruitments; const hasMore = previousRecruitments.length > VISIBLE_COUNT; const isRecruitmentsLoading = isLoading || isRepresentativeLoading; - const clubAffiliation = club ? getClubAffiliationLabel(club) : ""; - const shortIntro = club?.shortDescription || ""; + const summary = club ? getClubSummaryWithAffiliation(club) : ""; const officialVerificationStatus = club?.officialVerification?.status ?? (club?.isOfficialVerified ? "VERIFIED" : undefined); @@ -239,11 +238,8 @@ const ClubManagementScreen = () => { size={MANAGEMENT_VERIFICATION_MARK_SIZE} /> - - {clubAffiliation} - - {shortIntro} + {summary} @@ -805,14 +801,6 @@ const styles = StyleSheet.create({ color: "#000000", flexShrink: 1, }, - clubCollege: { - fontFamily: "Pretendard", - fontWeight: "500", - fontSize: ms(14), - lineHeight: ms(17), - letterSpacing: -0.02 * 14, - color: "#757474", - }, clubDesc: { fontFamily: "Pretendard", fontWeight: "500", diff --git a/apps/mobile/src/features/mypage/screens/ManageClubRegistrationScreen/index.tsx b/apps/mobile/src/features/mypage/screens/ManageClubRegistrationScreen/index.tsx index dec66c9f..59b78534 100644 --- a/apps/mobile/src/features/mypage/screens/ManageClubRegistrationScreen/index.tsx +++ b/apps/mobile/src/features/mypage/screens/ManageClubRegistrationScreen/index.tsx @@ -27,6 +27,7 @@ import { SCREEN_TYPE, type StackParamList } from "@/shared/constants/screen"; import { typography } from "@/shared/constants/typography"; import { useProfile } from "@/shared/contexts/profileContext"; import { serviceContext } from "@/shared/contexts/serviceContext"; +import { getClubSummaryWithAffiliation } from "@/shared/utils/club"; import { formatPhoneNumberInput, formatStudentIdInput, @@ -586,7 +587,7 @@ const ManageClubRegistrationScreen = () => { {club.name} - {club.description} + {getClubSummaryWithAffiliation(club)} { - {getClubAffiliationLabel(club)} - - - {club.shortDescription} + {getClubSummaryWithAffiliation(club)} diff --git a/apps/mobile/src/shared/components/HorizontalCarousel.tsx b/apps/mobile/src/shared/components/HorizontalCarousel.tsx index 18f58f85..b62fd8ae 100644 --- a/apps/mobile/src/shared/components/HorizontalCarousel.tsx +++ b/apps/mobile/src/shared/components/HorizontalCarousel.tsx @@ -4,6 +4,7 @@ import Animated from "react-native-reanimated"; import type { Club } from "@/entities/club"; import ClubPreviewCard from "@/shared/components/ClubPreviewCard"; import useAutoScroll from "@/shared/hooks/useAutoScroll"; +import { getClubSummary } from "@/shared/utils/club"; import { s } from "@/shared/utils/scale"; export const HORIZONTAL_CAROUSEL_BOTTOM_PADDING = s(2); @@ -58,7 +59,7 @@ const HorizontalCarousel = ({ clubs, onPressClub }: Props) => { renderItem={({ item }) => ( onPressClub(item)} /> diff --git a/apps/mobile/src/shared/utils/club.test.ts b/apps/mobile/src/shared/utils/club.test.ts new file mode 100644 index 00000000..66755cc3 --- /dev/null +++ b/apps/mobile/src/shared/utils/club.test.ts @@ -0,0 +1,48 @@ +import { getClubSummary, getClubSummaryWithAffiliation } from "./club"; + +describe("club summary presentation", () => { + it("shows a summary without affiliation for carousel cards", () => { + expect( + getClubSummary({ + description: "기존 소개", + shortDescription: "짧은 소개", + }), + ).toBe("짧은 소개"); + }); + + it("shows affiliation before the short description", () => { + expect( + getClubSummaryWithAffiliation({ + affiliationType: "소속동아리", + collegeMajor: { + id: 1, + college: "공과대학", + major: "컴퓨터공학부", + }, + description: "기존 소개", + shortDescription: "짧은 소개", + }), + ).toBe("컴퓨터공학부 소속 짧은 소개"); + }); + + it("shows the legacy description without affiliation when short description is absent", () => { + expect( + getClubSummaryWithAffiliation({ + affiliationType: "중앙동아리", + collegeMajor: null, + description: "기존 소개", + }), + ).toBe("기존 소개"); + }); + + it("shows only the short description when affiliation is 기타", () => { + expect( + getClubSummaryWithAffiliation({ + affiliationType: "기타", + collegeMajor: null, + description: "기존 소개", + shortDescription: "짧은 소개", + }), + ).toBe("짧은 소개"); + }); +}); diff --git a/apps/mobile/src/shared/utils/club.ts b/apps/mobile/src/shared/utils/club.ts index 1aa6bf90..706b6c76 100644 --- a/apps/mobile/src/shared/utils/club.ts +++ b/apps/mobile/src/shared/utils/club.ts @@ -1,6 +1,7 @@ import type { Club } from "@/entities/club"; type ClubAffiliation = Pick; +type ClubSummary = Pick; export const getClubAffiliationLabel = (club: ClubAffiliation): string => { if (club.affiliationType === "소속동아리") { @@ -9,3 +10,22 @@ export const getClubAffiliationLabel = (club: ClubAffiliation): string => { return club.affiliationType || "기타"; }; + +export const getClubSummary = (club: ClubSummary): string => + club.shortDescription?.trim() || club.description?.trim() || ""; + +export const getClubSummaryWithAffiliation = ( + club: ClubAffiliation & ClubSummary, +): string => { + const shortDescription = club.shortDescription?.trim(); + if (!shortDescription) { + return club.description?.trim() || ""; + } + + const affiliation = getClubAffiliationLabel(club); + if (affiliation === "기타") { + return shortDescription; + } + + return `${affiliation} 소속 ${shortDescription}`; +}; diff --git a/apps/web/server/service/search/search-query.service.ts b/apps/web/server/service/search/search-query.service.ts index d073e2c5..56537d2c 100644 --- a/apps/web/server/service/search/search-query.service.ts +++ b/apps/web/server/service/search/search-query.service.ts @@ -68,6 +68,7 @@ export class SearchQueryService { private buildBaseQuery(): SelectQueryBuilder { return this.clubRepository .createQueryBuilder('club') + .leftJoinAndSelect('club.collegeMajor', 'collegeMajor') .where('club.status = :status', { status: PUBLIC_CLUB_STATUS }) .andWhere('club.deleted_at IS NULL') } From 37240e2e9c0b2e17f27c5de3e579ba651d5a714f Mon Sep 17 00:00:00 2001 From: yhaeul Date: Tue, 11 Aug 2026 21:32:38 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=B0=94=EC=9D=BC=20?= =?UTF-8?q?=EB=AF=B8=EC=82=AC=EC=9A=A9=20=EB=A0=88=EA=B1=B0=EC=8B=9C=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=ED=83=80=EC=9E=85=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mobile/src/entities/club.ts | 6 ------ apps/mobile/src/entities/review.ts | 4 +--- apps/mobile/src/entities/user.ts | 5 ----- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/apps/mobile/src/entities/club.ts b/apps/mobile/src/entities/club.ts index a51119fc..23f69bee 100644 --- a/apps/mobile/src/entities/club.ts +++ b/apps/mobile/src/entities/club.ts @@ -27,7 +27,6 @@ export type Club = { id: string; uuid: string; // id와 동일한 필드. 기존의 id는 number 타입이었으나, string 타입으로 변경되었다. name: string; - fullName: string; description: string; shortDescription?: string; introduction: string; @@ -37,20 +36,16 @@ export type Club = { collegeMajorId?: number; collegeMajor?: ClubCollegeMajor | null; recruitType: string; - isPopular: boolean; hasManager: boolean; hasDongbang: boolean; dongbangLocation?: string | null; minActivityPeriod?: number | null; - membershipFee: string; - tags: string[]; imageUri: string; snsUrls?: string[]; article: string; activeMemberCount?: number; foundedAt?: string | null; activityImageUrls?: string[]; - blurHash?: string | null; status?: string; rejectReason?: string | null; isOfficialVerified?: boolean; @@ -76,7 +71,6 @@ export type ManagedClubListItem = Club & { type ReviewKeyword = { id: string; title: string; - color: string; iconUri: string; totalUpvotes: number; }; diff --git a/apps/mobile/src/entities/review.ts b/apps/mobile/src/entities/review.ts index 0b03b569..f57a99f9 100644 --- a/apps/mobile/src/entities/review.ts +++ b/apps/mobile/src/entities/review.ts @@ -1,13 +1,11 @@ export type ReviewKeywordCategory = { id: number; title: string; - color: string; // hex keywords: ReviewKeyword[]; }; export type ReviewKeyword = { id: string; title: string; - color: string; // hex - iconUri: string; // unused + iconUri: string; }; diff --git a/apps/mobile/src/entities/user.ts b/apps/mobile/src/entities/user.ts index c3c2a955..f255e2d2 100644 --- a/apps/mobile/src/entities/user.ts +++ b/apps/mobile/src/entities/user.ts @@ -4,14 +4,9 @@ export type User = { name: string; phone: string; email: string; - gender: string; - birthDate: string | null; - birthYear: string; collegeMajor: CollegeMajor | null; // 학번 admissionClass: number | null; - // 학년 - grade: number | null; }; export type CollegeMajor = {