Skip to content

Commit

Permalink
✨ [Feature/#314] 알림 기능 추가 (#321)
Browse files Browse the repository at this point in the history
* 💄design: 리더 권한 모달 퍼블리싱

* ✨feat: 리더 양도 권한 기능 추가

* ✨feat: comment 버그 수정 및 알림기능 수정

* 💄design: 템플릿 설명 모달창 반응형 디자인

* 🐛fix: filter 함수 수정

* 🐛fix: 알람 모달 zindex 수정
  • Loading branch information
heejung0413 authored Jul 17, 2024
1 parent e281182 commit 8518efe
Show file tree
Hide file tree
Showing 19 changed files with 274 additions and 130 deletions.
2 changes: 2 additions & 0 deletions src/api/@types/Notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface NotificationData {
thumbnail: string;
notificationType: TNotificationType;
dateTime: string;
retrospectiveId: number;
teamId: number;
}

export interface PostNotificationRequest {
Expand Down
23 changes: 23 additions & 0 deletions src/api/@types/Retrospectives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,34 @@ export interface PatchRetrospectiveResponse {
data: boolean;
}

export interface PostTransferLeaderRequest {
newLeaderId: number;
retrospectiveId: number;
}

export interface PostTransferLeaderResponse {
code: number;
message: string;
}

export interface LeaderData {
id: number;
title: string;
teamId: number;
userId: number;
templateId: number;
status: keyof TStatus;
thumbnail: string;
startedDate: string;
description: string;
}

export interface RetrospectivesClient {
onlyGet(request: onlyGetRetrospectiveRequest): Promise<onlyGetRetrospectiveResponse>;
create(request: PostRetrospectivesRequest): Promise<PostRetrospectivesResponse>;
get(request: GetRetrospectiveRequest): Promise<GetRetrospectiveData>;
delete(request: DeleteRetrospectiveRequest): Promise<void>;
put(request: PutTeamRetrospectiveRequest): Promise<RetrospectiveResponse>;
leaderPost(request: PostTransferLeaderRequest): Promise<PostTransferLeaderResponse>;
patch(request: PatchRetrospectiveRequest): Promise<PatchRetrospectiveResponse>;
}
3 changes: 3 additions & 0 deletions src/api/@types/Section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ export interface ActionItemData {
}

export interface CommentData {
sectionId: number;
commentId: number;
userId: number;
content: string;
username: string;
thumbnail: string;
lastModifiedDate: string;
createdDate: string;
}

export interface AddedImageCommentData extends CommentData {
Expand Down
6 changes: 6 additions & 0 deletions src/api/@types/Users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ export interface UserData {
updatedDate: string;
}

export interface PostAdminRequest {
email: string;
admin: boolean;
}

export interface UserClient {
get(): Promise<GetUserResponse>;
adminPost(reuest: PostAdminRequest): Promise<void>;
}
1 change: 0 additions & 1 deletion src/api/imageApi/postImageToS3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { PostImageToS3Request, PostImageToS3Response } from '@/api/@types/Thumbn
const postImageToS3 = async (requestData: PostImageToS3Request): Promise<PostImageToS3Response> => {
try {
const response = await axiosInstance.post<PostImageToS3Response>('/s3/pre-signed-url', requestData);
console.log('사진 s3 요청 성공', response.data);
return response.data;
} catch (error) {
throw new Error('s3 요청 실패');
Expand Down
15 changes: 15 additions & 0 deletions src/api/services/Retrospectives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
onlyGetRetrospectiveResponse,
PostRetrospectivesRequest,
PostRetrospectivesResponse,
PostTransferLeaderRequest,
PostTransferLeaderResponse,
RetrospectivesClient,
} from '../@types/Retrospectives';
import axiosInstance from '../axiosConfig';
Expand Down Expand Up @@ -57,6 +59,19 @@ export const RetrospectiveService: RetrospectivesClient = {
throw new Error(error as string);
}
},
leaderPost: async ({
retrospectiveId,
newLeaderId,
}: PostTransferLeaderRequest): Promise<PostTransferLeaderResponse> => {
try {
const response = await axiosInstance.post(
`${ROUTE}/${retrospectiveId}/transferLeadership?newLeaderId=${newLeaderId}`,
);
return response.data;
} catch (error) {
throw new Error(error as string);
}
},

patch: async (retrospectiveId, ...request) => {
return await axiosInstance.patch(`${ROUTE}/${retrospectiveId}/bookmark`, request);
Expand Down
10 changes: 9 additions & 1 deletion src/api/services/User.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UserClient } from '../@types/Users';
import { PostAdminRequest, UserClient } from '../@types/Users';
import axiosInstance from '../axiosConfig';

const ROUTE = 'users';
Expand All @@ -12,4 +12,12 @@ export const UserServices: UserClient = {
throw new Error(error as string);
}
},
adminPost: async (request: PostAdminRequest) => {
try {
const response = await axiosInstance.post(`/${ROUTE}/me/admin-status`, request);
return response.data;
} catch (error) {
throw new Error(error as string);
}
},
};
161 changes: 92 additions & 69 deletions src/components/alarm/Alarm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,21 @@ const Alarm = () => {
const filterNotification = (notification: NotificationData[]) => {
const todayFiltered: NotificationData[] = [];
const otherFiltered: NotificationData[] = [];
notification.forEach(item => {
const dateTime = new Date(item.dateTime);
if (dateTime.toISOString().slice(0, 10) === today.toISOString().slice(0, 10)) {
todayFiltered.push(item);
} else {
otherFiltered.push(item);
}
setTodayNotification(todayFiltered);
setOtherNotification(otherFiltered);
});
notification
.filter(item => item.receiverId === user?.userId)
.forEach(item => {
const dateTime = new Date(item.dateTime);
if (dateTime.toISOString().slice(0, 10) === today.toISOString().slice(0, 10)) {
todayFiltered.push(item);
} else {
otherFiltered.push(item);
}

setTodayNotification(todayFiltered);
setOtherNotification(otherFiltered);
});
};
console.log('today', todayNotification);

const fetchUser = async () => {
try {
const data = await UserServices.get();
Expand Down Expand Up @@ -107,13 +110,15 @@ const Alarm = () => {
<Bell size={20} />
{notification && notification.length > 0 && (
<>
<S.notificationBadge>{notification.length}</S.notificationBadge>
<S.notificationBadge>
{notification.filter(item => item.receiverId === user?.userId).length}
</S.notificationBadge>
</>
)}
</S.IconStyle>
</PopoverTrigger>
<Portal>
<PopoverContent minW={{ base: '200', md: '500' }} zIndex={999}>
<PopoverContent minW={{ base: '200', md: '500' }}>
<PopoverArrow />
<PopoverHeader>
<BellFill style={{ fontSize: '30px' }} />
Expand All @@ -128,69 +133,87 @@ const Alarm = () => {
<PopoverFooter style={{ overflow: 'auto' }} maxH={300}>
<S.MenuText>최근에 받은 알림</S.MenuText>
<Flex flexDirection="column-reverse">
{todayNotification && todayNotification.length > 0 ? (
todayNotification.map(item => (
<S.AlarmContents onClick={() => navigate(`/`)}>
<Flex justifyContent="space-between">
<S.AlarmTitle>
[{item.retrospectiveTitle}]에서 알림{' '}
<Icon viewBox="0 0 200 200" color="red.500" margin="auto 0">
<path fill="currentColor" d="M 100, 100 m -75, 0 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0" />
</Icon>
</S.AlarmTitle>
<MdDelete
style={{ margin: 'auto 0', minHeight: '30' }}
onClick={() => {
ReadNotification(item.notificationId);
}}
/>
</Flex>
{item.senderName}님이 {NOTIFICATION_TYPE_LABEL[item.notificationType]}
{item.notificationType === 'COMMENT' ? '을' : '를'}{' '}
{item.notificationType === 'COMMENT' ? '작성했습니다.' : '남겼습니다'}
<Flex justifyContent="flex-end">
<T.SubTaskIcon>
<MdAccessAlarm size="20px" color="#DADEE5" />
</T.SubTaskIcon>
<T.SubTaskCount>{convertToLocalTime(item.dateTime)}</T.SubTaskCount>
</Flex>
</S.AlarmContents>
))
{notification && todayNotification.length > 0 ? (
todayNotification
.filter(item => item.receiverId === user?.userId)
.map(item => (
<S.AlarmContents
onClick={() =>
navigate(`/sections?retrospectiveId=${item.retrospectiveId}&teamId=${item.teamId}`)
}
>
<Flex justifyContent="space-between">
<S.AlarmTitle>
[{item.retrospectiveTitle}]에서 알림{' '}
<Icon viewBox="0 0 200 200" color="red.500" margin="auto 0">
<path
fill="currentColor"
d="M 100, 100 m -75, 0 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0"
/>
</Icon>
</S.AlarmTitle>
<MdDelete
style={{ margin: 'auto 0', minHeight: '40' }}
onClick={() => {
ReadNotification(item.notificationId);
}}
/>
</Flex>
{item.senderName}님이 {NOTIFICATION_TYPE_LABEL[item.notificationType]}
{item.notificationType === 'COMMENT' ? '을' : '를'}{' '}
{item.notificationType === 'COMMENT' ? '작성했습니다.' : '남겼습니다'}
<Flex justifyContent="flex-end">
<T.SubTaskIcon>
<MdAccessAlarm size="20px" color="#DADEE5" />
</T.SubTaskIcon>
<T.SubTaskCount>{convertToLocalTime(item.dateTime)}</T.SubTaskCount>
</Flex>
</S.AlarmContents>
))
) : (
<S.MenuText style={{ margin: '0 auto' }}>알림 없음</S.MenuText>
)}
</Flex>
<Divider />
<S.MenuText style={{ margin: '5px 0' }}>저번에 받은 알림</S.MenuText>
<S.MenuText style={{ margin: '10px 0' }}>저번에 받은 알림</S.MenuText>
<Flex flexDirection="column-reverse">
{otherNotification && otherNotification.length > 0 ? (
otherNotification.map(item => (
<S.AlarmContents onClick={() => navigate(`/`)}>
<Flex justifyContent="space-between">
<S.AlarmTitle>
[{item.retrospectiveTitle}]에서 알림{' '}
<Icon viewBox="0 0 200 200" color="red.500" margin="auto 0">
<path fill="currentColor" d="M 100, 100 m -75, 0 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0" />
</Icon>
</S.AlarmTitle>
<MdDelete
style={{ margin: 'auto 0', minHeight: '30' }}
onClick={() => {
ReadNotification(item.notificationId);
}}
/>
</Flex>
{item.senderName}님이 {NOTIFICATION_TYPE_LABEL[item.notificationType]}
{item.notificationType === 'COMMENT' ? '을' : '를'}{' '}
{item.notificationType === 'COMMENT' ? '작성했습니다.' : '남겼습니다.'}
<Flex justifyContent="flex-end">
<T.SubTaskIcon>
<MdAccessAlarm size="20px" color="#DADEE5" />
</T.SubTaskIcon>
<T.SubTaskCount>{convertToLocalTime(item.dateTime)}</T.SubTaskCount>
</Flex>
</S.AlarmContents>
))
otherNotification
.filter(item => item.receiverId === user?.userId)
.map(item => (
<S.AlarmContents
onClick={() =>
navigate(`/sections?retrospectiveId=${item.retrospectiveId}&teamId=${item.teamId}`)
}
>
<Flex justifyContent="space-between">
<S.AlarmTitle>
[{item.retrospectiveTitle}]에서 알림{' '}
<Icon viewBox="0 0 200 200" color="red.500" margin="auto 0">
<path
fill="currentColor"
d="M 100, 100 m -75, 0 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0"
/>
</Icon>
</S.AlarmTitle>
<MdDelete
style={{ margin: 'auto 0', minHeight: '40' }}
onClick={() => {
ReadNotification(item.notificationId);
}}
/>
</Flex>
{item.senderName}님이 {NOTIFICATION_TYPE_LABEL[item.notificationType]}
{item.notificationType === 'COMMENT' ? '을' : '를'}{' '}
{item.notificationType === 'COMMENT' ? '작성했습니다.' : '남겼습니다.'}
<Flex justifyContent="flex-end">
<T.SubTaskIcon>
<MdAccessAlarm size="20px" color="#DADEE5" />
</T.SubTaskIcon>
<T.SubTaskCount>{convertToLocalTime(item.dateTime)}</T.SubTaskCount>
</Flex>
</S.AlarmContents>
))
) : (
<S.MenuText style={{ margin: '0 auto' }}>알림 없음</S.MenuText>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/parts/MainNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const MainNavBar = () => {
<div
style={{
backgroundColor: 'white',
zIndex: 999,
zIndex: 3,
borderBottom: '1px solid rgba(184, 184, 184, 0.5)',
position: 'fixed',
left: 0,
Expand Down
24 changes: 13 additions & 11 deletions src/components/writeRetro/explainModal/explainRetro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { IoPersonSharp } from 'react-icons/io5';
import { IoPeopleSharp } from 'react-icons/io5';
import { IoPersonCircleSharp } from 'react-icons/io5';

import { Modal, ModalOverlay, ModalContent, ModalCloseButton, useDisclosure } from '@chakra-ui/react';
import { Modal, ModalOverlay, ModalContent, ModalCloseButton, useDisclosure, Flex } from '@chakra-ui/react';
import * as S from '@/styles/writeRetroStyles/Explain.style';

interface ExplainButtonProps {
Expand All @@ -29,7 +29,7 @@ export const ExplainButton = ({ templateId }: ExplainButtonProps) => {
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />

<ModalContent style={{ maxWidth: '934px', height: '523px', borderRadius: '10px' }}>
<ModalContent style={{ maxWidth: '934px', height: '523px', borderRadius: '10px' }} margin="auto 10px">
{modalBody}
{/* <ModalCloseButton /> */}
<ModalCloseButton
Expand Down Expand Up @@ -64,14 +64,16 @@ export const ExplainKPT = () => {
return (
<>
<S.ExplainStyle>
<div style={{ display: 'flex' }}>
<div style={{ display: 'flex', marginTop: '15px', position: 'absolute', left: '120px' }}>
<IoPersonSharp color="#878787" size="26px" />
<S.ExplainSideTitle>개인 단위 회고에 추천!</S.ExplainSideTitle>
</div>
<S.ExplainTitle>KPT 회고 더 잘 활용하기</S.ExplainTitle>
</div>
<div style={{ display: 'flex' }}>
<Flex justifyContent="center">
<Flex margin="0 auto" flexDirection={{ md: 'row', base: 'column' }}>
<Flex margin="auto 10px">
<IoPersonSharp color="#878787" size="26px" />
<S.ExplainSideTitle>개인 단위 회고에 추천!</S.ExplainSideTitle>
</Flex>
<S.ExplainTitle>KPT 회고 더 잘 활용하기</S.ExplainTitle>
</Flex>
</Flex>
<Flex flexDirection={{ md: 'row', base: 'column' }}>
{/* ExplainKPT */}
<S.ExplainBody>
<S.ExplainSubTitle>진행 방법</S.ExplainSubTitle>
Expand Down Expand Up @@ -156,7 +158,7 @@ export const ExplainKPT = () => {
</S.ExplainContentSpan>
</S.ExplainContent>
</S.ExplainBody>
</div>
</Flex>
</S.ExplainStyle>
</>
);
Expand Down
Loading

0 comments on commit 8518efe

Please sign in to comment.