Skip to content

Commit

Permalink
Merge pull request #388 from gsainfoteam/380-feature-attach-client-le…
Browse files Browse the repository at this point in the history
…vel-logger

380 feature attach client level logger
  • Loading branch information
dohyun-ko authored Nov 19, 2024
2 parents 257c5f1 + a27064e commit 84dbe2b
Show file tree
Hide file tree
Showing 34 changed files with 652 additions and 227 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
echo "${{ vars.ENV }}" >> .env.production
echo "NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET }}" >> .env.production
echo "IDP_CLIENT_SECRET=${{ secrets.IDP_CLIENT_SECRET }}" >> .env.production
echo "NEXT_PUBLIC_GA_TRACKING_ID=${{ secrets.GA_TRACKING_ID }}" >> .env.production
echo "NEXT_PUBLIC_AMPLITUDE_API_KEY=${{ secrets.AMPLITUDE_API_KEY }}" >> .env.production
- name: Build, tag, and push image to Amazon ECR
uses: docker/build-push-action@v5
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"build-storybook": "storybook build"
},
"dependencies": {
"@amplitude/analytics-browser": "^2.11.9",
"@next/third-parties": "^15.0.3",
"@tinymce/tinymce-react": "^4.3.0",
"@toss/use-overlay": "^1.4.0",
"@vercel/og": "^0.5.17",
Expand Down
73 changes: 52 additions & 21 deletions src/api/log/log-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,76 @@ const LogEvents = {
screenView: 'screen_view',

// Nav Bar
navBarClickLogo: 'nav_bar_click_logo',
navBarClickAll: 'nav_bar_click_all',
navBarClickWrite: 'nav_bar_click_write',
navBarClickSearch: 'nav_bar_click_search',
navBarClickLogin: 'nav_bar_click_login',
navBarClickMyPage: 'nav_bar_click_my_page',
navBarClickLogo: 'navbar_click_logo',
navBarClickLogin: 'navbar_click_login',
navBarClickMyPage: 'navbar_click_my_page',
// only for mobile
navBarClickMenu: 'navbar_click_menu',

// Search Page
searchSubmit: 'search_submit',
searchClickSearch: 'search_click_search',
searchChangeKeyword: 'search_change_keyword',
searchClickClear: 'search_click_clear',
// only for mobile
searchClickExpand: 'search_click_expand',
searchClickCancel: 'search_click_cancel',

// Footer
footerClickGithub: 'footer_click_github',
footerClickPlayStore: 'footer_click_play_store',
footerClickAppStore: 'footer_click_app_store',
footerClickInfo: 'footer_click_info',
footerClickBugReport: 'footer_click_bug_report',
footerClickServiceTerms: 'footer_click_service_terms',
footerClickPrivacyPolicy: 'footer_click_privacy_policy',
footerClickContact: 'footer_click_contact',
footerClickHouse: 'footer_click_house',
footerClickGist: 'footer_click_gist',
footerClickGijol: 'footer_click_gijol',

// Home Page
// Sidebar
sidebarClickLink: 'sidebar_click_link',
sidebarClickProfile: 'sidebar_click_profile',

// Search Page
searchPageSubmit: 'search_page_submit',
searchPageTypeChange: 'search_page_type_change',
searchPageClickCancel: 'search_page_click_cancel',
// My Page
myClickMyNotice: 'my_click_my_notice',
myClickReminded: 'my_click_reminded',
myClickBugReport: 'my_click_bug_report',
myToggleLanguage: 'my_toggle_language',
myClickMode: 'my_click_mode',
myClickLogout: 'my_click_logout',
myClickUnregister: 'my_click_unregister',

// Notice Detail Page
detailClickImage: 'detail_click_image',
detailClickReaction: 'detail_click_reaction',
detailClickShare: 'detail_click_share',
detailClickCopyLink: 'detail_click_copy_link',
// only for author
detailClickEdit: 'detail_click_edit',
detailClickRemove: 'detail_click_remove',

// Notice Writing Page
noticeWritingPageTypeTitle: 'notice_writing_page_type_title',
noticeWritingPageCheckDeadline: 'notice_writing_page_check_deadline',
noticeWritingPageCheckEnglish: 'notice_writing_page_check_english',
noticeWritingPageSetDeadline: 'notice_writing_page_set_deadline',
noticeWritingPageSetType: 'notice_writing_page_set_type',
noticeWritingPageTypeTag: 'notice_writing_page_type_tag',
noticeWritingPageTypeContent: 'notice_writing_page_type_content',
noticeWritingPageClickSubmit: 'notice_writing_page_click_submit',
writingAcceptSaved: 'writing_accept_saved',
writingRejectSaved: 'writing_reject_saved',
writingToggleEnglish: 'writing_toggle_english',
writingChangeTab: 'writing_change_tab',
writingClickDeepl: 'writing_click_deepl',
writingSelectType: 'writing_select_type',
writingToggleDeadline: 'writing_toggle_deadline',
writingSetDeadline: 'writing_set_deadline',
writingSubmit: 'writing_submit',
// edit mode
writingModify: 'writing_modify',

// Category Page
categoryToggleDeadline: 'category_toggle_deadline',

// Components
noticeClick: 'notice_click',
searchResultClick: 'search_result_click',
search: 'search',
noticeClickReaction: 'notice_click_reaction',
noticeClickShare: 'notice_click_share',
} as const;

export default LogEvents;
30 changes: 16 additions & 14 deletions src/api/log/send-log.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { track } from '@amplitude/analytics-browser';
import { sendGAEvent } from '@next/third-parties/google';

import LogEvents from './log-events';

declare global {
Expand All @@ -6,22 +9,21 @@ declare global {
}
}

export const analytics = Object.entries(LogEvents).reduce(
(acc, [key, value]) => ({
...acc,
[`log${key.charAt(0).toUpperCase() + key.slice(1)}`]: (
properties?: object,
) => sendLog(value, properties),
}),
{},
) as Record<
`log${Capitalize<keyof typeof LogEvents>}`,
(properties?: object) => void
>;

const sendLog = (
event: (typeof LogEvents)[keyof typeof LogEvents],
properties?: object,
) => window.smartlook('track', event, properties);
) => {
if (process.env.NODE_ENV !== 'production') {
return;
}

window.smartlook('track', event, properties);

properties
? sendGAEvent('event', event, properties)
: sendGAEvent('event', event);

track(event, properties);
};

export default sendLog;
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react';

import LogEvents from '@/api/log/log-events';
import { NoticeSearchParams } from '@/api/notice/notice';
import { getAllNotices } from '@/api/notice/notice-server';
import Analytics from '@/app/components/shared/Analytics';
import Pagination from '@/app/components/shared/Pagination';
import Zabo from '@/app/components/shared/Zabo';
import { createTranslation, PropsWithLng } from '@/app/i18next';
Expand Down Expand Up @@ -38,7 +40,16 @@ const CategorizedNotices = async ({
<div className="flex w-full flex-col md:max-w-[800px]">
{...notices.list.map((notice) => (
<React.Fragment key={notice.id}>
<Zabo key={notice.id} {...notice} lng={lng} />
<Analytics
event={LogEvents.noticeClick}
properties={{
type: 'zabo',
id: notice.id,
searchParams: noticeSearchParams,
}}
>
<Zabo key={notice.id} {...notice} lng={lng} />
</Analytics>
<div className="my-[30px] h-[1px] bg-greyLight dark:bg-dark_greyBorder" />
</React.Fragment>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import Link from 'next/link';
import { redirect } from 'next/navigation';
import { Suspense } from 'react';

import LogEvents from '@/api/log/log-events';
import { sidebarObject } from '@/app/components/layout/Sidebar/sidebarObject';
import Analytics from '@/app/components/shared/Analytics';
import LoadingCatAnimation from '@/app/components/shared/LoadingCatAnimation';
import styles from '@/app/components/shared/Toggle/toggle.module.css';
import { createTranslation, PropsWithLng } from '@/app/i18next';
Expand Down Expand Up @@ -52,19 +54,27 @@ export default async function CategoryPage({
</div>
{category !== 'deadline' && category !== 'zigglepick' && (
<div className={`flex items-center gap-3`}>
<Link
href={`${category}?page=0${
sortByDeadline ? '' : '&deadline=true'
}`}
className="flex rounded-full"
<Analytics
event={LogEvents.categoryToggleDeadline}
properties={{
category,
sortByDeadline,
}}
>
<input
className={styles.checkbox}
type="checkbox"
checked={sortByDeadline}
readOnly
/>
</Link>
<Link
href={`${category}?page=0${
sortByDeadline ? '' : '&deadline=true'
}`}
className="flex rounded-full"
>
<input
className={styles.checkbox}
type="checkbox"
checked={sortByDeadline}
readOnly
/>
</Link>
</Analytics>

<p
className={`text-lg font-medium ${
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import { Trans } from 'react-i18next';
import { toast } from 'react-toastify';
import Swal from 'sweetalert2';

import LogEvents from '@/api/log/log-events';
import sendLog from '@/api/log/send-log';
import {
addReaction,
deleteReaction,
EmojiString,
Notice,
Reaction,
} from '@/api/notice/notice';
import Analytics from '@/app/components/shared/Analytics';
import { useTranslation } from '@/app/i18next/client';
import { Locale } from '@/app/i18next/settings';
import AnguishedFace from '@/assets/icons/anguished-face.svg';
Expand Down Expand Up @@ -132,6 +135,7 @@ const Actions = ({ notice: { title, id, reactions }, lng }: ReactionsProps) => {
};

const handleEmojiClick = async (emoji: string, isReacted: boolean) => {
sendLog(LogEvents.detailClickReaction, { id, type: emoji, isReacted });
const reactions = await toggleReaction(emoji, isReacted);

if (reactions) {
Expand Down Expand Up @@ -163,9 +167,23 @@ const Actions = ({ notice: { title, id, reactions }, lng }: ReactionsProps) => {
/>
))}

<ShareButton title={title} lng={lng} />

<CopyLinkButton title={title} lng={lng} />
<Analytics
event={LogEvents.detailClickShare}
properties={{
id,
}}
>
<ShareButton title={title} lng={lng} />
</Analytics>

<Analytics
event={LogEvents.detailClickCopyLink}
properties={{
id,
}}
>
<CopyLinkButton title={title} lng={lng} />
</Analytics>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import Link from 'next/link';
import { useRouter } from 'next/navigation';
import Swal from 'sweetalert2';

import LogEvents from '@/api/log/log-events';
import { deleteNotice } from '@/api/notice/notice';
import Analytics from '@/app/components/shared/Analytics';
import { PropsWithLng } from '@/app/i18next';
import { useTranslation } from '@/app/i18next/client';
import EditPencilIcon from '@/assets/icons/edit-pencil.svg';
Expand Down Expand Up @@ -50,23 +52,35 @@ const AuthorActions = ({ noticeId, lng }: PropsWithLng<WriterActionsProps>) => {

return (
<div className={'flex gap-6'}>
<Link
href={`/write?noticeId=${noticeId}`}
className={'flex items-center gap-[10px]'}
<Analytics
event={LogEvents.detailClickEdit}
properties={{ id: noticeId }}
>
<EditPencilIcon
className={'w-5 stroke-greyDark dark:stroke-dark_white'}
/>
<p className={'text-greyDark'}>{t('zabo.authorActions.edit')}</p>
</Link>
<Link
href={`/write?noticeId=${noticeId}`}
className={'flex items-center gap-[10px]'}
>
<EditPencilIcon
className={'w-5 stroke-greyDark dark:stroke-dark_white'}
/>
<p className={'text-greyDark'}>{t('zabo.authorActions.edit')}</p>
</Link>
</Analytics>

<button
className={'flex items-center gap-[10px]'}
onClick={handleRemoveNotice}
<Analytics
event={LogEvents.detailClickRemove}
properties={{ id: noticeId }}
>
<RemoveIcon className={'w-5 stroke-greyDark dark:stroke-dark_white'} />
<p className={'text-greyDark'}>{t('zabo.authorActions.remove')}</p>
</button>
<button
className={'flex items-center gap-[10px]'}
onClick={handleRemoveNotice}
>
<RemoveIcon
className={'w-5 stroke-greyDark dark:stroke-dark_white'}
/>
<p className={'text-greyDark'}>{t('zabo.authorActions.remove')}</p>
</button>
</Analytics>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import Image from 'next/image';
import { useState } from 'react';

import LogEvents from '@/api/log/log-events';
import Analytics from '@/app/components/shared/Analytics';
import { PropsWithLng } from '@/app/i18next';

import ShowcaseModal from './ShowcaseModal';
Expand Down Expand Up @@ -34,14 +36,16 @@ const ImageStack = ({
<div className="flex flex-col gap-[10px]">
{sources.map((src, i) => (
<div key={src} className="relative cursor-pointer">
<Image
src={src}
alt={alt}
width={width ?? 400}
height={300}
onClick={() => onImageClick(i)}
className="shrink-0 basis-48 rounded-[10px] border-2 border-greyBorder object-cover md:basis-80"
/>
<Analytics event={LogEvents.detailClickImage}>
<Image
src={src}
alt={alt}
width={width ?? 400}
height={300}
onClick={() => onImageClick(i)}
className="shrink-0 basis-48 rounded-[10px] border-2 border-greyBorder object-cover md:basis-80"
/>
</Analytics>
</div>
))}
</div>
Expand Down
Loading

0 comments on commit 84dbe2b

Please sign in to comment.