Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 75 additions & 26 deletions frontend/src/constants/eventName.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,88 @@
export const USER_EVENT = {
CATEGORY_BUTTON_CLICKED: 'CategoryButton Clicked' as const,
SEARCH_BOX_CLICKED: 'SearchBox Clicked' as const,
CATEGORY_BUTTON_CLICKED: 'CategoryButton Clicked',
SEARCH_BOX_CLICKED: 'SearchBox Clicked',

// 네비게이션
BACK_BUTTON_CLICKED: 'Back Button Clicked' as const,
HOME_BUTTON_CLICKED: 'Home Button Clicked' as const,
MOBILE_HOME_BUTTON_CLICKED: 'Mobile Home Button Clicked' as const,
MOBILE_MENU_BUTTON_CLICKED: 'Mobile Menu Button Clicked' as const,
MOBILE_MENU_DELETE_BUTTON_CLICKED:
'Mobile Menubar delete Button Clicked' as const,
BACK_BUTTON_CLICKED: 'Back Button Clicked',
HOME_BUTTON_CLICKED: 'Home Button Clicked',
MOBILE_HOME_BUTTON_CLICKED: 'Mobile Home Button Clicked',
MOBILE_MENU_BUTTON_CLICKED: 'Mobile Menu Button Clicked',
MOBILE_MENU_DELETE_BUTTON_CLICKED: 'Mobile Menubar delete Button Clicked',

// 탭 & 섹션
TAB_CLICKED: 'Tab Clicked' as const,
PHOTO_NAVIGATION_CLICKED: 'Photo Navigation' as const,
CLUB_CARD_CLICKED: 'ClubCard Clicked' as const,
TAB_CLICKED: 'Tab Clicked',
PHOTO_NAVIGATION_CLICKED: 'Photo Navigation',
CLUB_CARD_CLICKED: 'ClubCard Clicked',

// 동아리 지원
CLUB_APPLY_BUTTON_CLICKED: 'Club Apply Button Clicked' as const,
RECOMMENDED_CLUB_CLICKED: 'Recommended Club Clicked' as const,
CLUB_UNION_BUTTON_CLICKED: 'Club Union Button Clicked' as const,
CLUB_APPLY_BUTTON_CLICKED: 'Club Apply Button Clicked',
RECOMMENDED_CLUB_CLICKED: 'Recommended Club Clicked',
CLUB_UNION_BUTTON_CLICKED: 'Club Union Button Clicked',

// 공유 버튼
SHARE_BUTTON_CLICKED: 'Share Button Clicked' as const,
SNS_LINK_CLICKED: 'SNS Link Button Clicked' as const,
SHARE_BUTTON_CLICKED: 'Share Button Clicked',
SNS_LINK_CLICKED: 'SNS Link Button Clicked',

STATUS_RADIO_BUTTON_CLICKED: 'StatusRadioButton Clicked' as const,
INTRODUCE_BUTTON_CLICKED: 'Introduce Button Clicked' as const,
APPLICATION_FORM_SUBMITTED: 'Application Form Submitted' as const,
PATCH_NOTE_BUTTON_CLICKED: 'Patch Note Button Clicked' as const,
STATUS_RADIO_BUTTON_CLICKED: 'StatusRadioButton Clicked',
INTRODUCE_BUTTON_CLICKED: 'Introduce Button Clicked',
APPLICATION_FORM_SUBMITTED: 'Application Form Submitted',
PATCH_NOTE_BUTTON_CLICKED: 'Patch Note Button Clicked',
} as const;

export const ADMIN_EVENT = {
// 로그인 페이지
LOGIN_BUTTON_CLICKED: '로그인 버튼클릭',
SIGNUP_BUTTON_CLICKED: '회원가입 버튼클릭',
FORGOT_ID_BUTTON_CLICKED: '아이디 찾기 버튼클릭',
FORGOT_PASSWORD_BUTTON_CLICKED: '비밀번호 찾기 버튼클릭',

// 사이드바
CLUB_LOGO_UPLOAD_BUTTON_CLICKED: '동아리 로고 업로드 버튼클릭',
CLUB_LOGO_EDIT_BUTTON_CLICKED: '동아리 로고 수정 버튼클릭',
CLUB_LOGO_RESET_BUTTON_CLICKED: '동아리 로고 초기화 버튼클릭',
TAB_CLICKED: '사이드바 탭 클릭',
LOGOUT_BUTTON_CLICKED: '로그아웃 버튼클릭',

// 기본 정보 수정
UPDATE_CLUB_BUTTON_CLICKED: '동아리 기본 정보 수정 버튼클릭',
CLUB_NAME_CLEAR_BUTTON_CLICKED: '동아리 명 입력 초기화 버튼클릭',
CLUB_PRESIDENT_CLEAR_BUTTON_CLICKED: '회장 정보 입력 초기화 버튼클릭',
TELEPHONE_NUMBER_CLEAR_BUTTON_CLICKED: '전화번호 입력 초기화 버튼클릭',
CLUB_INTRODUCTION_CLEAR_BUTTON_CLICKED: '한줄소개 입력 초기화 버튼클릭',
CLUB_TAG_SELECT_BUTTON_CLICKED: '분류/분과/자유태그 선택 버튼클릭',
CLUB_TAG_CLEAR_BUTTON_CLICKED: '자유태그 입력 초기화 버튼클릭',
CLUB_SNS_LINK_CLEAR_BUTTON_CLICKED: 'SNS 링크 입력 초기화 버튼클릭',

// 모집 정보 수정
UPDATE_RECRUIT_BUTTON_CLICKED: '동아리 모집 정보 수정 버튼클릭',
ALWAYS_RECRUIT_BUTTON_CLICKED: '상시모집 버튼클릭',
RECRUITMENT_START_CHANGED: '모집 시작 날짜 변경',
RECRUITMENT_END_CHANGED: '모집 종료 날짜 변경',
RECRUITMENT_TARGET_CLEAR_BUTTON_CLICKED: '모집 대상 입력 초기화 버튼클릭',
MARKDOWN_EDITOR_PREVIEW_BUTTON_CLICKED: '소개글 미리보기/편집 버튼클릭',

// 활동 사진 수정
IMAGE_UPLOAD_BUTTON_CLICKED: '활동 사진 업로드 버튼클릭',
IMAGE_DELETE_BUTTON_CLICKED: '활동 사진 삭제 버튼클릭',

// 비밀번호 수정
PASSWORD_CHANGE_BUTTON_CLICKED: '비밀번호 변경 버튼클릭',
NEW_PASSWORD_CLEAR_BUTTON_CLICKED: '새 비밀번호 입력 초기화 버튼클릭',
CONFIRM_PASSWORD_CLEAR_BUTTON_CLICKED: '확인 비밀번호 입력 초기화 버튼클릭',
} as const;

export const PAGE_VIEW = {
APPLICATION_FORM_PAGE: 'ApplicationFormPage' as const,
CLUB_DETAIL_PAGE: 'ClubDetailPage' as const,
MAIN_PAGE: 'MainPage' as const,
INTRODUCE_PAGE: 'IntroducePage' as const,
CLUB_UNION_PAGE: 'ClubUnionPage' as const,
} as const;
// 사용자
APPLICATION_FORM_PAGE: 'ApplicationFormPage',
CLUB_DETAIL_PAGE: 'ClubDetailPage',
MAIN_PAGE: 'MainPage',
INTRODUCE_PAGE: 'IntroducePage',
CLUB_UNION_PAGE: 'ClubUnionPage',

// 관리자
LOGIN_PAGE: '로그인페이지',
CLUB_INFO_EDIT_PAGE: '동아리 기본 정보 수정 페이지',
RECRUITMENT_INFO_EDIT_PAGE: '동아리 모집 정보 수정 페이지',
PHOTO_EDIT_PAGE: '동아리 활동 사진 수정 페이지',
ADMIN_ACCOUNT_EDIT_PAGE: '관리자 계정 수정 페이지',
} as const;
28 changes: 19 additions & 9 deletions frontend/src/pages/AdminPage/auth/LoginTab/LoginTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import Button from '@/components/common/Button/Button';
import { login } from '@/apis/auth/login';
import moadong_name_logo from '@/assets/images/logos/moadong_name_logo.svg';
import useAuth from '@/hooks/useAuth';
import useTrackPageView from '@/hooks/useTrackPageView';
import { ADMIN_EVENT, PAGE_VIEW } from '@/constants/eventName';
import useMixpanelTrack from '@/hooks/useMixpanelTrack';

const LoginTab = () => {
useTrackPageView(PAGE_VIEW.LOGIN_PAGE);
const trackEvent = useMixpanelTrack();

const [userId, setUserId] = useState<string>('');
const [password, setPassword] = useState<string>('');
const [loading, setLoading] = useState(false);
Expand Down Expand Up @@ -40,6 +46,7 @@ const LoginTab = () => {
} finally {
setLoading(false);
}
trackEvent(ADMIN_EVENT.LOGIN_BUTTON_CLICKED);
};

if (authLoading) return <div>로딩 중...</div>;
Expand Down Expand Up @@ -82,33 +89,36 @@ const LoginTab = () => {
<Styled.ForgotLinks>
<Styled.LinkButton
type='button'
onClick={() =>
onClick={() => {
trackEvent(ADMIN_EVENT.SIGNUP_BUTTON_CLICKED);
alert(
'해당 기능은 아직 준비 중이에요.\n필요하신 경우 관리자에게 문의해주세요☺',
)
}
);
}}
>
회원가입
</Styled.LinkButton>
<span>|</span>
<Styled.LinkButton
type='button'
onClick={() =>
onClick={() => {
trackEvent(ADMIN_EVENT.FORGOT_ID_BUTTON_CLICKED);
alert(
'해당 기능은 아직 준비 중이에요.\n필요하신 경우 관리자에게 문의해주세요☺',
)
}
);
}}
>
아이디 찾기
</Styled.LinkButton>
<span>|</span>
<Styled.LinkButton
type='button'
onClick={() =>
onClick={() => {
trackEvent(ADMIN_EVENT.FORGOT_PASSWORD_BUTTON_CLICKED);
alert(
'해당 기능은 아직 준비 중이에요.\n필요하신 경우 관리자에게 문의해주세요☺',
)
}
);
}}
>
비밀번호 찾기
</Styled.LinkButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ import {
} from '@/hooks/queries/club/useClubLogo';
import { useAdminClubContext } from '@/context/AdminClubContext';
import { MAX_FILE_SIZE } from '@/constants/uploadLimit';
import { ADMIN_EVENT } from '@/constants/eventName';
import useMixpanelTrack from '@/hooks/useMixpanelTrack';

interface ClubLogoEditorProps {
clubLogo?: string | null;
}

const ClubLogoEditor = ({ clubLogo }: ClubLogoEditorProps) => {
const trackEvent = useMixpanelTrack();

const { clubId } = useAdminClubContext();
if (!clubId) return null;

Expand All @@ -32,7 +36,7 @@ const ClubLogoEditor = ({ clubLogo }: ClubLogoEditorProps) => {

const toggleMenu = useCallback(() => {
setIsMenuOpen((prev) => !prev);
}, []);
}, [trackEvent]);

const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
Expand Down Expand Up @@ -101,6 +105,7 @@ const ClubLogoEditor = ({ clubLogo }: ClubLogoEditorProps) => {
<Styled.EditMenu ref={menuRef}>
<Styled.EditMenuItem
onClick={() => {
trackEvent(ADMIN_EVENT.CLUB_LOGO_EDIT_BUTTON_CLICKED);
triggerFileInput();
setIsMenuOpen(false);
}}
Expand All @@ -113,6 +118,7 @@ const ClubLogoEditor = ({ clubLogo }: ClubLogoEditorProps) => {

<Styled.EditMenuItem
onClick={() => {
trackEvent(ADMIN_EVENT.CLUB_LOGO_RESET_BUTTON_CLICKED);
handleLogoReset();
setIsMenuOpen(false);
}}
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React, { useMemo } from 'react';
import * as Styled from './SideBar.styles';
import { useNavigate, useLocation } from 'react-router-dom';
import ClubLogoEditor from '@/pages/AdminPage/components/ClubLogoEditor/ClubLogoEditor';

import { logout } from '@/apis/auth/logout';
import useMixpanelTrack from '@/hooks/useMixpanelTrack';
import { ADMIN_EVENT } from '@/constants/eventName';

interface SideBarProps {
clubName: string;
Expand Down Expand Up @@ -49,6 +50,7 @@ const tabs: TabCategory[] = [
];

const SideBar = ({ clubLogo, clubName }: SideBarProps) => {
const trackEvent = useMixpanelTrack();
const location = useLocation();
const navigate = useNavigate();

Expand All @@ -59,6 +61,9 @@ const SideBar = ({ clubLogo, clubName }: SideBarProps) => {
}, [location.pathname]);

const handleTabClick = (item: TabItem) => {
trackEvent(ADMIN_EVENT.TAB_CLICKED, {
tabName: item.label,
});
// if (item.label === '아이디/비밀번호 수정') {
// alert('아이디/비밀번호 수정 기능은 아직 준비 중이에요. ☺️');
// return;
Expand All @@ -82,6 +87,9 @@ const SideBar = ({ clubLogo, clubName }: SideBarProps) => {
) {
await logout();
}

trackEvent(ADMIN_EVENT.LOGOUT_BUTTON_CLICKED);

localStorage.removeItem('accessToken');
navigate('/admin/login', { replace: true });
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// AccountEditTab.tsx

import React, { useState } from 'react'; // useMemo 제거
import { useState } from 'react';
import * as Styled from './AccountEditTab.styles';
import InputField from '@/components/common/InputField/InputField';
import Button from '@/components/common/Button/Button';
import { changePassword } from '@/apis/auth/changePassword';
import useMixpanelTrack from '@/hooks/useMixpanelTrack';
import { ADMIN_EVENT, PAGE_VIEW } from '@/constants/eventName';
import useTrackPageView from '@/hooks/useTrackPageView';

const PASSWORD_REGEX = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^])(?!.*\s).{8,20}$/;

const AccountEditTab = () => {
const trackEvent = useMixpanelTrack();
useTrackPageView(PAGE_VIEW.ADMIN_ACCOUNT_EDIT_PAGE);

const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [successMessage, setSuccessMessage] = useState('');
Expand Down Expand Up @@ -40,6 +44,12 @@ const AccountEditTab = () => {

try {
await changePassword({ password: newPassword });

trackEvent(ADMIN_EVENT.PASSWORD_CHANGE_BUTTON_CLICKED, {
newPasswordLength: newPassword.length,
confirmPasswordLength: confirmPassword.length,
});

setSuccessMessage('비밀번호가 성공적으로 변경되었습니다.');
setNewPassword('');
setConfirmPassword('');
Expand Down Expand Up @@ -72,7 +82,10 @@ const AccountEditTab = () => {
type='password'
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
onClear={() => setNewPassword('')}
onClear={() => {
setNewPassword('');
trackEvent(ADMIN_EVENT.NEW_PASSWORD_CLEAR_BUTTON_CLICKED);
}}
maxLength={20}
isError={isPasswordValid}
isSuccess={newPassword.length > 0 && !isPasswordValid}
Expand All @@ -85,7 +98,10 @@ const AccountEditTab = () => {
type='password'
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
onClear={() => setConfirmPassword('')}
onClear={() => {
setConfirmPassword('');
trackEvent(ADMIN_EVENT.CONFIRM_PASSWORD_CLEAR_BUTTON_CLICKED);
}}
maxLength={20}
isError={isPasswordMatching}
isSuccess={confirmPassword.length > 0 && !isPasswordMatching}
Expand Down
Loading