Skip to content

Commit 0748ae1

Browse files
authored
Merge pull request #94 from TaskFlow-CLAP/CLAP-225
CLAP-225 알림, 프로필 모달 api 연결
2 parents af1a5c5 + 489f95b commit 0748ae1

File tree

10 files changed

+218
-61
lines changed

10 files changed

+218
-61
lines changed

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"js-cookie": "^3.0.5",
2020
"pinia": "^2.3.0",
2121
"tailwind-scrollbar-hide": "^2.0.0",
22+
"v3-infinite-loading": "^1.3.2",
2223
"vue": "^3.5.13",
2324
"vue-chartjs": "^5.3.2",
2425
"vue-router": "^4.5.0",

src/api/auth.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,44 @@
11
import { axiosInstance } from '@/utils/axios'
22
import Cookies from 'js-cookie'
33
import type { loginDataTypes } from '@/types/auth'
4+
import { useMemberStore } from '@/stores/member'
45

56
export const postLogin = async (loginData: loginDataTypes, sessionId: string) => {
7+
const memberStore = useMemberStore()
68
const response = await axiosInstance.post('/api/auths/login', loginData, {
79
headers: { sessionId: sessionId }
810
})
911
Cookies.set('accessToken', response.data.accessToken, {
1012
path: '/',
1113
sameSite: 'strict'
1214
})
15+
Cookies.set('refreshToken', response.data.refreshToken, {
16+
path: '/',
17+
sameSite: 'strict'
18+
})
19+
20+
await memberStore.updateMemberInfoWithToken()
1321
return response.data
1422
}
1523

1624
export const patchPassword = async (password: string) => {
17-
const accessToken = Cookies.get('accessToken')
25+
const response = await axiosInstance.patch('/api/members/password', password)
1826

19-
if (!accessToken) return
27+
return response.data
28+
}
29+
30+
export const deleteLogout = async () => {
31+
const memberStore = useMemberStore()
32+
const refreshToken = Cookies.get('refreshToken')
2033

21-
const response = await axiosInstance.patch('/api/members/password', password, {
34+
const response = await axiosInstance.delete('/api/auths/logout', {
2235
headers: {
23-
Authorization: `Bearer ${accessToken}`
36+
Authorization: `Bearer ${import.meta.env.VITE_ACCESS_TOKEN}`,
37+
refreshToken: refreshToken
2438
}
2539
})
26-
27-
return response.data
40+
Cookies.remove('accessToken', { path: '/' })
41+
Cookies.remove('refreshToken', { path: '/' })
42+
await memberStore.updateMemberInfoWithToken()
43+
return response
2844
}

src/api/common.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
import { axiosInstance } from '../utils/axios'
22

3+
export const getNotification = async (pageNum: number, sizeNum: number) => {
4+
const response = await axiosInstance.get(`/api/notifications?page=${pageNum}&size=${sizeNum}`)
5+
6+
return response.data
7+
}
8+
9+
export const patchNotificationRead = async (notificationId: number) => {
10+
const response = await axiosInstance.patch(`/api/notification/${notificationId}`)
11+
console.log(notificationId)
12+
return response.data
13+
}
14+
15+
export const getNotifiCount = async () => {
16+
const response = await axiosInstance.get(`/api/notifications/count`)
17+
console.log(response.data)
18+
return response.data
19+
}
20+
321
export const getMainCategory = async () => {
422
const response = await axiosInstance.get('/api/main-category')
523
return response.data

src/components/EditInformation.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
class="w-24 h-24 rounded-full object-cover border mt-3" />
1717
<div
1818
v-else
19-
class="w-24 h-24 rounded-full bg-background-1 flex items-center justify-center"></div>
19+
class="w-24 h-24 rounded-full bg-background-1 flex items-center justify-center mt-3"></div>
2020
<!-- 파일 업로드 필요 -->
2121
<p class="mt-3 text-xs text-primary1 font-bold cursor-pointer">변경</p>
2222
</div>
@@ -91,7 +91,7 @@ const memberId = ref('Chole.yeon')
9191
const memberEmail = ref('[email protected]')
9292
const memberDepartment = ref('인프라팀')
9393
const memberJob = ref('인프라 아키텍처')
94-
const imageUrl = ref('../../public/images/mockProfile.jpg')
94+
const imageUrl = ref('')
9595
const isModalVisible = ref(false)
9696
9797
const memberForm = ref({

src/components/top-bar/NotificationModal.vue

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class="flex relative w-[1200px] h-[72px] bg-opacity-15"
88
@click.self="closeModal">
99
<div
10-
class="absolute right-6 top-[72px] h-60 w-80 bg-white rounded-lg shadow-[0_3px_10px_rgb(0,0,0,0.2)]">
10+
class="absolute right-6 top-[72px] h-60 w-80 bg-white rounded-lg shadow-[0_3px_10px_rgb(0,0,0,0.2)] overflow-hidden">
1111
<div class="flex relative px-4 pt-3 pb-2 border-b border-border-2">
1212
<p class="text-body font-bold text-xs">알림</p>
1313
<div class="absolute right-4">
@@ -25,48 +25,91 @@
2525
</div>
2626
</div>
2727
</div>
28-
<div class="max-h-[186px] flex flex-col h-full overflow-y-auto">
29-
<div class="overflow-y-scroll flex"></div>
30-
<button :class="['flex flex-col border-b py-3 px-4', { 'bg-primary2': isCheck }]">
31-
<p class="text-xs text-body font-bold">{{ title }}</p>
32-
<div class="flex text-xs pt-2">
33-
<span class="text-black">
34-
<span class="text-primary1 font-bold">"{{ taskTitle }}"</span>
35-
&nbsp;요청이&nbsp;
36-
<span class="text-primary1 font-bold">{{ message }}</span>
37-
&nbsp;상태로 변경 되었습니다
38-
</span>
39-
</div>
40-
</button>
41-
<button :class="['flex flex-col border-b py-3 px-4', { 'bg-primary2': !isCheck }]">
42-
<p class="text-xs text-body font-bold">{{ title }}</p>
43-
<div class="flex text-xs pt-2">
44-
<span class="text-black">
45-
<span class="text-primary1 font-bold">"{{ taskTitle }}"</span>
46-
&nbsp;요청이&nbsp;
47-
<span class="text-primary1 font-bold">{{ message }}</span>
48-
&nbsp;상태로 변경 되었습니다
49-
</span>
50-
</div>
51-
</button>
28+
<div class="max-h-[185px] flex flex-col h-full overflow-y-auto">
29+
<div class="overflow-y-scroll flex flex-col">
30+
<button
31+
v-for="notification in notifications"
32+
:key="notification.notificationId"
33+
@click="readNotifi(notification.notificationId)"
34+
:class="[
35+
'flex flex-col border-b py-3 px-4',
36+
{ 'bg-primary2': !notification.isRead }
37+
]">
38+
<p class="text-xs text-body font-bold">
39+
{{ notification.notificationType }}
40+
</p>
41+
<div class="flex text-xs pt-2">
42+
<span class="text-black">
43+
<span class="text-primary1 font-bold"> "{{ notification.taskTitle }}" </span>
44+
&nbsp;요청이&nbsp;
45+
<span class="text-primary1 font-bold">
46+
{{ notification.message }}
47+
</span>
48+
&nbsp;상태로 변경되었습니다.
49+
</span>
50+
</div>
51+
</button>
52+
<InfiniteLoading
53+
@infinite="loadMoreNotifications"
54+
class="flex items-center justify-center"
55+
><template v-slot:complete>
56+
<span class="flex py-2 items-center justify-center text-xs text-primary1"
57+
>더 이상 없음</span
58+
>
59+
</template></InfiniteLoading
60+
>
61+
</div>
5262
</div>
5363
</div>
5464
</div>
5565
</div>
5666
</template>
5767

5868
<script setup lang="ts">
59-
import { ref } from 'vue'
69+
import { onMounted, ref } from 'vue'
70+
import InfiniteLoading from 'v3-infinite-loading'
71+
import 'v3-infinite-loading/lib/style.css'
6072
import CommonIcons from '../common/CommonIcons.vue'
6173
import { smallCheckIcon, closeIcon } from '@/constants/iconPath'
74+
import { getNotification, patchNotificationRead } from '@/api/common'
75+
6276
const props = defineProps<{
6377
isOpen: boolean
6478
}>()
6579
66-
const title = ref('작업 상태 변경 알림')
67-
const taskTitle = ref('VM 생성 요청')
68-
const message = ref('진행 중')
69-
const isCheck = ref(true)
80+
const notifications = ref<any[]>([])
81+
const page = ref(0)
82+
const pageSize = 5
83+
const hasNext = ref(true)
84+
85+
const loadMoreNotifications = async ($state: any) => {
86+
try {
87+
const response = await getNotification(page.value, pageSize)
88+
console.log(response)
89+
90+
if (response.isFirst) {
91+
notifications.value = response.content
92+
} else {
93+
notifications.value.push(...response.content)
94+
}
95+
96+
hasNext.value = response.hasNext
97+
98+
if (hasNext.value) {
99+
page.value++
100+
$state.loaded()
101+
} else {
102+
$state.complete()
103+
}
104+
} catch (error) {
105+
console.error('알림을 불러오는 중 오류 발생:', error)
106+
$state.error()
107+
}
108+
}
109+
110+
const readNotifi = (id: number) => {
111+
patchNotificationRead(id)
112+
}
70113
71114
const emit = defineEmits<{
72115
(e: 'close'): void

src/components/top-bar/ProfileModal.vue

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
v-if="isOpen"
44
class="fixed inset-0 flex z-50 justify-center"
55
@click.self="closeModal">
6+
<ModalView
7+
:isOpen="isModalVisible"
8+
type="successType"
9+
@close="closeLogoutModal">
10+
<template #header> 로그아웃 되었습니다. </template>
11+
</ModalView>
612
<div
713
class="flex relative w-[1200px] h-[72px] bg-opacity-15"
814
@click.self="closeModal">
@@ -11,10 +17,18 @@
1117
<div
1218
class="flex items-center justify-center relative px-6 py-8 border-b bg-primary2 border-border-2">
1319
<div class="flex flex-col items-center justify-center">
14-
<div class="w-24 h-24 bg-background-1 rounded-full mb-6"></div>
20+
<img
21+
v-if="imgUrl"
22+
class="rounded-full mb-6 w-24 h-24"
23+
:src="info.imageUrl"
24+
alt="프로필 이미지" />
25+
<div
26+
v-else
27+
class="w-24 h-24 bg-background-1 rounded-full mb-6" />
1528
<div>
1629
<div class="flex flex-col justify-center items-center w-[172px]">
1730
<p class="text-xs text-body font-bold">{{ name }}</p>
31+
1832
<p class="text-black">{{ nickname }}</p>
1933
</div>
2034
</div>
@@ -40,16 +54,27 @@
4054
<script setup lang="ts">
4155
import { ref } from 'vue'
4256
import { useRouter } from 'vue-router'
57+
import { deleteLogout } from '@/api/auth'
58+
import ModalView from '../ModalView.vue'
59+
import { useMemberStore } from '@/stores/member'
60+
import { storeToRefs } from 'pinia'
61+
import { computed } from 'vue'
62+
63+
const isModalVisible = ref(false)
64+
65+
const memberStore = useMemberStore()
66+
const { info } = storeToRefs(memberStore)
67+
68+
const imgUrl = computed(() => info.value.imageUrl)
69+
const name = computed(() => info.value.memberName)
70+
const nickname = computed(() => info.value.nickname)
4371
4472
const router = useRouter()
4573
4674
const props = defineProps<{
4775
isOpen: boolean
4876
}>()
4977
50-
const name = ref('벡지연')
51-
const nickname = ref('Chloe.yeon')
52-
5378
const emit = defineEmits<{
5479
(e: 'close'): void
5580
}>()
@@ -61,7 +86,17 @@ const handleEdit = () => {
6186
router.push('/edit-information')
6287
emit('close')
6388
}
89+
const openLogoutModal = () => {
90+
isModalVisible.value = true
91+
}
92+
const closeLogoutModal = () => {
93+
isModalVisible.value = false
94+
emit('close')
95+
router.push('/login')
96+
}
97+
6498
const handleLogout = () => {
65-
// 로그아웃 API 추가 필요
99+
deleteLogout()
100+
openLogoutModal()
66101
}
67102
</script>

0 commit comments

Comments
 (0)