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
37 changes: 37 additions & 0 deletions .github/workflows/share-extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: 📦 Share Pinback Extension

on:
workflow_dispatch:
inputs:
branch:
description: '빌드할 브랜치를 확인하세요 (기본: develop)'
required: true
default: 'develop'

jobs:
build-and-send:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}

- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build and Share Extension
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: pnpm share:ext
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.eslint": "always",
"source.organizeImports": "always"
},
"tailwindCSS.suggestions": true,
Expand Down
1 change: 1 addition & 0 deletions apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"@pinback/contracts": "workspace:*",
"@pinback/analytics": "workspace:*",
"@tanstack/react-query": "^5.85.3",
"axios": "^1.11.0",
"class-variance-authority": "^0.7.1",
Expand Down
23 changes: 23 additions & 0 deletions apps/client/public/firebase-messaging-sw.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
/* eslint-env serviceworker */
/* eslint-disable no-undef */

const AMPLITUDE_API_KEY = 'bb48a29e445e2f350a1d23ad67f38d55';

const trackAmplitudeEvent = (eventType) => {
const isProd = self.location.hostname === 'pinback.today';
if (!isProd) {
console.log('[Analytics] track', eventType);
return;
}

fetch('https://api2.amplitude.com/2/httpapi', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: AMPLITUDE_API_KEY,
events: [{ device_id: 'serviceworker', event_type: eventType }],
}),
}).catch((err) => console.warn('[Amplitude] 이벤트 전송 실패', err));
};

const firebaseConfig = {
apiKey: 'AIzaSyD3KM0IQ4Ro3Dd2fyAY8fnhE1bQ_NesrBc',
authDomain: 'pinback-c55de.firebaseapp.com',
Expand Down Expand Up @@ -35,6 +54,8 @@ firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
trackAmplitudeEvent('Triggered_Reminder');

const url = payload.data?.url || 'https://pinback.today';
const notificationTitle = payload.notification?.title || 'pinback';
const notificationOptions = {
Expand All @@ -50,6 +71,8 @@ messaging.onBackgroundMessage((payload) => {
self.addEventListener('notificationclick', (event) => {
const targetUrl = event.notification.data?.url || 'https://pinback.today';

trackAmplitudeEvent('Clicked_alarm');

fetch(
`https://www.google-analytics.com/mp/collect?measurement_id=G-847ZNSCC3J&api_secret=1hei57fPTKyGX5Cw73rwgA`,
{
Expand Down
20 changes: 15 additions & 5 deletions apps/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { analytics } from '@pinback/analytics';
import { router } from '@routes/router';
import { useGetAmplitudeUserProperties } from '@shared/apis/queries';
import { useEffect } from 'react';
import { RouterProvider } from 'react-router-dom';
import './App.css';

function App() {
return (
<>
<RouterProvider router={router} />
</>
);
const { data: userProperties } = useGetAmplitudeUserProperties();

useEffect(() => {
if (!userProperties) return;
analytics.identify(
String(userProperties.userId),
userProperties.jobRole ? { job_role: userProperties.jobRole } : undefined
);
}, [userProperties]);

return <RouterProvider router={router} />;
}

export default App;
2 changes: 1 addition & 1 deletion apps/client/src/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Outlet, useLocation } from 'react-router-dom';
const Layout = () => {
const location = useLocation();
const queryClient = useQueryClient();
const isLoggedIn = authStorage.hasAccessToken();

const isPolicyPage =
location.pathname === ROUTES_CONFIG.privacyPolicy.path ||
Expand All @@ -20,7 +21,6 @@ const Layout = () => {
location.pathname.startsWith(ROUTES_CONFIG.onboardingCallback.path);

const isSidebarHidden = isAuthPage || isPolicyPage;
const isLoggedIn = authStorage.hasAccessToken();

const { data: hasJobData, isLoading: isHasJobLoading } = useGetHasJob(
isLoggedIn && !isAuthPage
Expand Down
6 changes: 6 additions & 0 deletions apps/client/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { initAnalytics } from '@pinback/analytics';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';

initAnalytics({
apiKey: import.meta.env.VITE_AMPLITUDE_API_KEY,
isDev: import.meta.env.DEV,
});
import { QueryClientProvider } from '@tanstack/react-query';
import getQueryClient from '@shared/apis/setting/query/getQueryClient.ts';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
Expand Down
36 changes: 25 additions & 11 deletions apps/client/src/pages/jobPins/JobPins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import JobPinsBottomNotice from '@pages/jobPins/components/JobPinsBottomNotice';
import MemoPopup from '@pages/jobPins/components/MemoPopup';
import { useJobPinsBottomNotice } from '@pages/jobPins/hooks/useJobPinsBottomNotice';
import Footer from '@pages/myBookmark/components/footer/Footer';
import { analytics } from '@pinback/analytics';
import { Card } from '@pinback/design-system/ui';
import AnalyticsCardWrapper from '@shared/components/analyticsCardWrapper/AnalyticsCardWrapper';
import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll';

const JobPins = () => {
Expand Down Expand Up @@ -67,18 +69,30 @@ const JobPins = () => {
const displayImageUrl = article.thumbnailUrl || undefined;

return (
<Card
<AnalyticsCardWrapper
key={article.articleId}
type="bookmark"
variant="save"
title={displayTitle}
imageUrl={displayImageUrl}
content={article.memo}
category={article.category?.categoryName}
categoryColor={article.category?.categoryColor}
nickname={article.ownerName}
onClick={() => getJobPinDetail(article.articleId)}
/>
bookmarkType="jobpin"
>
<Card
type="bookmark"
variant="save"
title={displayTitle}
imageUrl={displayImageUrl}
content={article.memo}
category={article.category?.categoryName}
categoryColor={article.category?.categoryColor}
nickname={article.ownerName}
onClick={() => {
analytics.track('Clicked_Shared_Bookmark', {
article_id: String(article.articleId),
category_id: article.category
? String(article.category.categoryId)
: undefined,
});
getJobPinDetail(article.articleId);
}}
/>
</AnalyticsCardWrapper>
);
})}

Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/pages/jobPins/apis/axios.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JobPinsResponse } from '@pages/jobPins/types/api';
import { type JobPinsResponse } from '@pages/jobPins/types/api';
import apiRequest from '@shared/apis/setting/axiosInstance';

interface ApiResponse<T> {
Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/pages/jobPins/apis/queries.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { JobPinsResponse } from '@pages/jobPins/types/api';
import { type JobPinsResponse } from '@pages/jobPins/types/api';
import { useInfiniteQuery, useMutation } from '@tanstack/react-query';
import {
getJobPinsArticleDetail,
getJobPinsArticles,
JobPinsDetailResponse,
type JobPinsDetailResponse,
} from './axios';

const PAGE_SIZE = 20;
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/pages/level/Level.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import LevelScene from '@pages/level/components/LevelScene';
import { TreeLevel } from '@pages/level/types/treeLevelType';
import { type TreeLevel } from '@pages/level/types/treeLevelType';
import { Icon } from '@pinback/design-system/icons';
import { Badge } from '@pinback/design-system/ui';
import { cn } from '@pinback/design-system/utils';
Expand Down
27 changes: 13 additions & 14 deletions apps/client/src/pages/level/components/LevelInfoCard.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { cn } from '@pinback/design-system/utils';
import { TREE_LEVEL_TABLE, type TreeLevel } from '@pages/level/types/treeLevelType';
import { Level } from '@pinback/design-system/ui';
import { Icon, type IconName } from '@pinback/design-system/icons';
import { TREE_LEVEL_TABLE, TreeLevel } from '@pages/level/types/treeLevelType';
import { cn } from '@pinback/design-system/utils';

const LEVEL_TOOLTIP_ICON = {
1: 'tooltip_1',
2: 'tooltip_2',
3: 'tooltip_3',
4: 'tooltip_4',
5: 'tooltip_5',
} as const satisfies Record<TreeLevel, IconName>;
const LEVEL_TOOLTIP_IMAGE = {
1: '/assets/tooltip_1.svg',
2: '/assets/tooltip_2.svg',
3: '/assets/tooltip_3.svg',
4: '/assets/tooltip_4.svg',
5: '/assets/tooltip_5.svg',
} as const satisfies Record<TreeLevel, string>;

export default function LevelInfoCard() {
const rows = [...TREE_LEVEL_TABLE].reverse();
Expand All @@ -33,12 +32,12 @@ export default function LevelInfoCard() {
>
<div className="flex w-full items-center gap-[1.2rem]">
<div className="bg-gray0 flex h-[4.6rem] w-[4.6rem] items-center justify-center rounded-[0.8rem]">
<Icon
name={LEVEL_TOOLTIP_ICON[row.level]}
<img
src={LEVEL_TOOLTIP_IMAGE[row.level]}
alt={`${row.level} 썸네일 아이콘`}
width={46}
height={46}
className="rounded-[0.8rem]"
aria-label={`${row.level} 썸네일 아이콘`}
className="object-cover"
/>
</div>

Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/pages/level/components/LevelScene.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cn } from '@pinback/design-system/utils';
import { TreeLevel } from '@pages/level/types/treeLevelType';
import { HTMLAttributes } from 'react';
import { type TreeLevel } from '@pages/level/types/treeLevelType';
import { type HTMLAttributes } from 'react';

import chippi_level1 from '../../../assets/Lv.1.webp';
import chippi_level2 from '../../../assets/Lv.2.webp';
Expand Down
1 change: 0 additions & 1 deletion apps/client/src/pages/myBookmark/MyBookmark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ const MyBookmark = () => {
onBadgeChange={setActiveBadge}
updateToReadStatus={updateToReadStatus}
openMenu={openMenu}
queryClient={queryClient}
scrollContainerRef={scrollContainerRef}
/>
</ErrorBoundary>
Expand Down
6 changes: 3 additions & 3 deletions apps/client/src/pages/myBookmark/apis/axios.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
BookmarkArticlesCountResponse,
BookmarkArticlesResponse,
CategoryBookmarkArticleResponse,
type BookmarkArticlesCountResponse,
type BookmarkArticlesResponse,
type CategoryBookmarkArticleResponse,
} from '@pages/myBookmark/types/api';
import apiRequest from '@shared/apis/setting/axiosInstance';

Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/pages/myBookmark/apis/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
useSuspenseInfiniteQuery,
useSuspenseQuery,
} from '@tanstack/react-query';
import { CategoryBookmarkArticleResponse } from '../types/api';
import { type CategoryBookmarkArticleResponse } from '../types/api';
import {
getBookmarkArticles,
getBookmarkArticlesCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ const Footer = () => {
aria-label="Pinback 블로그"
className="transition-opacity hover:opacity-80"
>
<Icon name="palms" width={28} height={28} />
<img
src="/assets/palms.svg"
alt="Palms 로고"
width={28}
height={28}
className="object-contain"
/>
</a>
</div>
</div>
Expand Down
Loading
Loading