Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
7018c31
🫧modify: 페이지에 따른 제목 변경 로직 반영
yuj2n Jun 18, 2025
87fab0b
🫧modify: 불필요한 import 제거
yuj2n Jun 18, 2025
63061c7
✨feat: 모달 공용화를 위한 수정
yuj2n Jun 18, 2025
84dcc8f
✨feat: 모달 공용화로 인한 코드 수정
yuj2n Jun 18, 2025
6bff947
✨feat: 초대하기 모달 작성
yuj2n Jun 18, 2025
4286ed6
✨feat: 사이드바에서 선택한 대시보드 정보 사용을 위한 전역 관리
yuj2n Jun 18, 2025
828abf9
✨feat: 모달 공용화 수정 내역 반영 및 대시보드 정보 전역 저장
yuj2n Jun 18, 2025
ef28cc5
✨feat: 헤더 컴포넌트 분리 및 초대하기 모달 적용
yuj2n Jun 18, 2025
050f912
✨feat: 우측 헤더 내용 분리
yuj2n Jun 18, 2025
761fc98
✨feat: 좌측 대시보드 명 분리 및 왕관 이미지 적용 여부 구현
yuj2n Jun 18, 2025
208a9e9
✨feat: 사용자 정보 반영
yuj2n Jun 18, 2025
014c115
✨feat: 사용자 정보 반영
yuj2n Jun 18, 2025
e820949
✨feat: 대시보드 구성원 수정 컴포넌트 페이지네이션 적용 및 버튼 활성화 구현
yuj2n Jun 18, 2025
eb6dd9d
🫧modify: import 순서 수정
yuj2n Jun 18, 2025
c39a3d2
🫧modify: 페이지네이션 활성화 여부에 따른 버튼 반영을 위한 이미지 추가
yuj2n Jun 18, 2025
e820e39
🫧modify: tester 페이지 오류 수정
yuj2n Jun 18, 2025
f226e67
✨feat: 모달 공용화
yuj2n Jun 18, 2025
f0ed638
🫧modify: 공용화를 위한 폴더 이동 및 변수 변경
yuj2n Jun 18, 2025
d4cffcc
🫧modify: 페이지네이션 확인을 위한 데이터 추가
yuj2n Jun 18, 2025
aab7719
✨feat: 모달 전역 상태 관리 및 적용
yuj2n Jun 18, 2025
c054e68
🫧modify: 모달 공용화 반영에 따른 변수 변경 및 경로 수정
yuj2n Jun 18, 2025
11d99f5
🔧chore: 폴더 구조 변경
yuj2n Jun 18, 2025
898d8e6
✨feat: 이메일 초대 컴포넌트 페이지네이션 구현
yuj2n Jun 18, 2025
423b098
🫧modify: 삭제 버튼 컴포넌트 작성 및 스타일 수정
yuj2n Jun 19, 2025
fdbef31
🫧modify: 삭제 버튼 컴포넌트 작성 및 스타일 수정
yuj2n Jun 19, 2025
9f1433c
✨feat: 돌아가기 버튼 로직 router 반영 수정
yuj2n Jun 19, 2025
c801675
🫧modify: 모달 타입 없는 경우 타입 일치를 위한 none 처리
yuj2n Jun 19, 2025
c2ee79a
✨feat: tanstackQuery를 반영하여 간단하게 대시보드 목록 데이터 불러오기
yuj2n Jun 19, 2025
ce774d3
✨feat: 이메일 초대하기 기능 적용을 위한 post로 api 호출 코드 작성
yuj2n Jun 19, 2025
7a3d269
✨feat: 드롭다운 로그아웃 로직 구현
yuj2n Jun 19, 2025
61ee4ec
✨feat: 대시보드 삭제 버튼 에러 처리 작성
yuj2n Jun 19, 2025
d859068
✨feat: 사이드 바의 대시보드 데이터 정보 가져와 사용하기 위한 prop 삭제 및 데이터 반영
yuj2n Jun 19, 2025
694e306
🫧modify: tanstackQuery 적용으로 인한 데이터 명 변경 및 에러 타입 string화
yuj2n Jun 19, 2025
be57a9c
✨feat: 이메일 초대하기 API 적용, 에러 처리, 토스트 적용
yuj2n Jun 19, 2025
27487fb
✨feat: 대시보드 수정 시 react-query 캐시 무효화 및 전역 상태 동기화
yuj2n Jun 19, 2025
c3067b8
✨feat: 대시보드 정보 관리하기 버튼 로직 API 구현
yuj2n Jun 19, 2025
7eaea05
🐛fix: user가 없는 경우에 발생하는 에러로 인해 해당 경우 프로필 이미지를 렌더링 하지 않도록 수정
yuj2n Jun 19, 2025
2a1c7e4
Merge branch 'develop' into feature/Header-API
yuj2n Jun 19, 2025
18cacdf
🫧modify: 함수명 네이밍 규칙 반영
yuj2n Jun 19, 2025
861ed90
🫧modify: 드롭다운 컴포넌트 커스터마이징을 위한 타입 추가
yuj2n Jun 19, 2025
89584e9
🫧modify: 대시보드 페이지 layout 통합
yuj2n Jun 19, 2025
2081282
✨feat: link와 button 컴포넌트의 재사용성과 가독성 향상을 위한 컴포넌트 작성
yuj2n Jun 19, 2025
322800a
🎨style: 화면 크기 줄어드는 경우 버튼이 잘리는 문제 수정
yuj2n Jun 19, 2025
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
Binary file added public/images/next-disabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/images/next.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/prev-disabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/images/prev.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 0 additions & 3 deletions src/app/dashboard/[id]/edit/components/EditInfo.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions src/app/dashboard/[id]/edit/components/EditInvitation.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions src/app/dashboard/[id]/edit/components/EditMember.tsx

This file was deleted.

14 changes: 3 additions & 11 deletions src/app/dashboard/[id]/edit/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import Header from '@components/common/header/Header'

import Sidebar from '@/app/shared/components/common/sidebar/Sidebar'

export default function AboutLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<>
<Sidebar />
<div className="pl-300">
<Header />
<main>{children}</main>
</div>
</>
<div>
<main>{children}</main>
</div>
)
}
24 changes: 17 additions & 7 deletions src/app/dashboard/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ import EditInvitation from '@dashboard/components/edit/EditInvitation'
import EditMember from '@dashboard/components/edit/EditMember'
import { showError, showSuccess } from '@lib/toast'
import Image from 'next/image'
import { useParams } from 'next/navigation'
import { useRouter } from 'next/navigation'

import DeleteDashboardButton from '@/app/features/dashboard/components/edit/DeleteDashboardButton'

export default function DashBoardEditPage() {
const { id } = useParams()
const router = useRouter()

const handleSuccess = () => {
showSuccess('대시보드가 성공적으로 수정되었습니다.')
}
Expand All @@ -16,22 +23,25 @@ export default function DashBoardEditPage() {
}

return (
<div className="BG-gray">
<div
<div className="BG-gray pb-16">
<button
className="flex cursor-pointer items-center gap-12 p-16"
onClick={() => window.history.back()}
type="button"
onClick={() => router.back()}
>
<Image src="/images/back.png" alt="돌아가기" width={8} height={4} />
<p>돌아가기</p>
</div>
</button>
<div className="flex w-500 flex-col gap-16 p-16">
<EditInfo />
<EditMember />
<EditInvitation />
</div>
<button className="Text-btn Border-btn ml-16 rounded-md px-64 py-12">
대시보드 삭제하기
</button>

{/* 삭제 버튼 영역 */}
<div className="BG-white align-center Text-btn i8 ml-16 flex w-292 justify-center rounded-md px-64 py-6">
<DeleteDashboardButton dashboardId={String(id)} />
</div>
</div>
)
}
12 changes: 7 additions & 5 deletions src/app/dashboard/[id]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ export default function AboutLayout({
children: React.ReactNode
}) {
return (
<div>
{/* <Sidebar /> */}
<Header />
<div>{children}</div> {/* 여기에 page.tsx 내용이 들어옴 */}
</div>
<>
<Sidebar />
<div className="pl-300">
<Header />
<main>{children}</main>
</div>
</>
)
}
1 change: 1 addition & 0 deletions src/app/features/auth/store/useAuthStore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

import { AuthState } from '../types/auth.type'

export const useAuthStore = create<AuthState>()(
Expand Down
17 changes: 17 additions & 0 deletions src/app/features/dashboard/api/invitation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// src/app/features/dashboard/api/invitation.ts
import api from '@lib/axios'

type InvitationRequest = {
email: string
dashboardId: number | string
}

export const inviteUser = async ({ email, dashboardId }: InvitationRequest) => {
const response = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}/invitations`,
{
email,
},
)
return response.data
}
Comment on lines +9 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

에러 처리 추가를 고려해보세요.

API 함수가 잘 구조화되어 있지만, 에러 핸들링이 누락되어 있습니다. try-catch 블록이나 상위 레벨에서의 에러 처리 방식을 고려해보세요.

export const inviteUser = async ({ email, dashboardId }: InvitationRequest) => {
+  try {
    const response = await api.post(
      `/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}/invitations`,
      {
        email,
      },
    )
    return response.data
+  } catch (error) {
+    console.error('Failed to invite user:', error)
+    throw error
+  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const inviteUser = async ({ email, dashboardId }: InvitationRequest) => {
const response = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}/invitations`,
{
email,
},
)
return response.data
}
export const inviteUser = async ({ email, dashboardId }: InvitationRequest) => {
try {
const response = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}/invitations`,
{
email,
},
)
return response.data
} catch (error) {
console.error('Failed to invite user:', error)
throw error
}
}
🤖 Prompt for AI Agents
In src/app/features/dashboard/api/invitation.ts around lines 9 to 17, the
inviteUser function lacks error handling for the API call. Wrap the API request
inside a try-catch block to catch any errors during the post request. In the
catch block, handle or rethrow the error appropriately to ensure the calling
code can respond to failures gracefully.

Comment on lines +9 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

에러 핸들링과 환경 변수 검증을 추가하세요.

API 함수에 다음 개선사항을 적용하는 것을 권장합니다:

  1. 환경 변수 존재 여부 검증
  2. API 응답 타입 정의
  3. 에러 핸들링 개선
+type InvitationResponse = {
+  // API 응답 구조에 맞게 정의
+  success: boolean
+  message?: string
+}
+
-export const inviteUser = async ({ email, dashboardId }: InvitationRequest) => {
+export const inviteUser = async ({ email, dashboardId }: InvitationRequest): Promise<InvitationResponse> => {
+  if (!process.env.NEXT_PUBLIC_TEAM_ID) {
+    throw new Error('NEXT_PUBLIC_TEAM_ID 환경 변수가 설정되지 않았습니다.')
+  }
+
+  try {
     const response = await api.post(
       `/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}/invitations`,
       {
         email,
       },
     )
     return response.data
+  } catch (error) {
+    console.error('사용자 초대 중 오류 발생:', error)
+    throw error
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const inviteUser = async ({ email, dashboardId }: InvitationRequest) => {
const response = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}/invitations`,
{
email,
},
)
return response.data
}
type InvitationResponse = {
// API 응답 구조에 맞게 정의
success: boolean
message?: string
}
export const inviteUser = async (
{ email, dashboardId }: InvitationRequest
): Promise<InvitationResponse> => {
if (!process.env.NEXT_PUBLIC_TEAM_ID) {
throw new Error('NEXT_PUBLIC_TEAM_ID 환경 변수가 설정되지 않았습니다.')
}
try {
const response = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}/invitations`,
{
email,
},
)
return response.data
} catch (error) {
console.error('사용자 초대 중 오류 발생:', error)
throw error
}
}
🤖 Prompt for AI Agents
In src/app/features/dashboard/api/invitation.ts around lines 9 to 17, the
inviteUser function lacks environment variable validation, explicit API response
typing, and error handling. Add a check to ensure
process.env.NEXT_PUBLIC_TEAM_ID is defined before making the API call and throw
a clear error if missing. Define and use a TypeScript type for the expected API
response to improve type safety. Wrap the API call in a try-catch block to
handle and log errors gracefully, rethrowing or returning a meaningful error as
appropriate.

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use client'

import api from '@lib/axios'
import axios from 'axios'
import { useRouter } from 'next/navigation'
import React, { useState } from 'react'

type DeleteDashboardButtonProps = {
dashboardId: string
}

export default function DeleteDashboardButton({
dashboardId,
}: DeleteDashboardButtonProps) {
const router = useRouter()
const [isDeleting, setIsDeleting] = useState(false)

console.log('DeleteDashboardButton 렌더됨:', dashboardId)

const handleDelete = async () => {
const confirmed = confirm(
'정말로 이 대시보드를 삭제하시겠습니까? 삭제 후 되돌릴 수 없습니다.',
)

if (!confirmed) return

try {
setIsDeleting(true)

if (!process.env.NEXT_PUBLIC_TEAM_ID) {
throw new Error('NEXT_PUBLIC_TEAM_ID 환경변수가 설정되지 않았습니다.')
}

await api.delete(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${dashboardId}`,
)

// 삭제 후 대시보드 목록 페이지로 이동
router.push('/dashboard')
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
const message =
error.response?.data?.message ||
'대시보드 삭제 중 오류가 발생했습니다.'
console.error('대시보드 삭제 오류:', message)
alert(message) // 또는 showError(message) 등으로 사용자에게 표시
} else {
console.error('대시보드 삭제 오류:', error)
alert('알 수 없는 오류가 발생했습니다.')
}
} finally {
setIsDeleting(false)
}
}

return (
<button
onClick={handleDelete}
disabled={isDeleting}
className={`Text-black my-8 rounded-8 font-semibold transition-opacity ${
isDeleting ? 'cursor-not-allowed opacity-50' : 'hover:opacity-90'
}`}
>
대시보드 삭제하기
</button>
)
}
69 changes: 53 additions & 16 deletions src/app/features/dashboard/components/edit/EditInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,91 @@
'use client'

import api from '@lib/axios'
import { useQueryClient } from '@tanstack/react-query'
import axios from 'axios'
import Image from 'next/image'
import { useRouter } from 'next/navigation'
import React, { useEffect, useState } from 'react'

import { DASHBOARD_COLORS } from '@/app/shared/constants/colors'
import { useSelectedDashboardStore } from '@/app/shared/store/useSelectedDashboardStore'
import { CreateDashboardRequest } from '@/app/shared/types/dashboard'

export default function EditInfo() {
const router = useRouter()
const { selectedDashboard, setSelectedDashboard } =
useSelectedDashboardStore()
const queryClient = useQueryClient()

const [formData, setFormData] = useState<CreateDashboardRequest>({
title: '',
color: DASHBOARD_COLORS[0],
})

const [isSubmitting, setIsSubmitting] = useState(false)

/// 입력값 변경 처리
// selectedDashboard가 있을 때 formData 초기화
useEffect(() => {
if (selectedDashboard) {
setFormData({
title: selectedDashboard.title,
color: selectedDashboard.color,
})
}
}, [selectedDashboard])

// 입력값 변경 핸들러
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target
setFormData((prev) => ({
...prev,
[name]: value,
}))
}
// 색상 선택 처리

// 색상 선택 핸들러
const handleColorSelect = (color: string) => {
setFormData((prev) => ({ ...prev, color }))
}

// 제출 핸들러
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()

if (!formData.title || !formData.color) {
return
}
if (!formData.title || !formData.color) return

try {
setIsSubmitting(true)

if (!process.env.NEXT_PUBLIC_TEAM_ID) {
throw new Error('NEXT_PUBLIC_TEAM_ID 환경변수가 설정되지 않았습니다.')
if (!process.env.NEXT_PUBLIC_TEAM_ID || !selectedDashboard?.id) {
throw new Error('필수 정보가 누락되었습니다.')
}

const response = await api.post(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards`,
const response = await api.put(
`/${process.env.NEXT_PUBLIC_TEAM_ID}/dashboards/${selectedDashboard.id}`,
formData,
)

const data = response.data

// 성공 시 대시보드 상세 페이지로 이동
router.push(`/dashboard/${data.id}`)
} catch (error) {
console.error('대시보드 생성 오류:', error)
// 1. 상태 업데이트 (헤더, 수정정보 실시간 반영)
setSelectedDashboard(data)

// 2. react-query 캐시 무효화 → Sidebar 목록 재요청 유도
await queryClient.invalidateQueries({ queryKey: ['dashboards'] })

// 성공 시 상세 페이지 이동
router.push(`/dashboard/${data.id}/edit`)
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
const message =
error.response?.data?.message ||
'대시보드 수정 중 오류가 발생했습니다.'
console.error('대시보드 수정 오류:', message)
alert(message)
} else {
console.error('대시보드 수정 오류:', error)
alert('알 수 없는 오류가 발생했습니다.')
}
} finally {
setIsSubmitting(false)
}
Expand All @@ -60,7 +95,9 @@ export default function EditInfo() {
<div>
{/* 컨테이너 */}
<div className="BG-white h-300 w-584 rounded-16 px-32 py-24">
<h2 className="Text-black mb-24 text-18 font-bold">새로운 대시보드</h2>
<h2 className="Text-black mb-24 text-18 font-bold">
{selectedDashboard?.title || '대시보드 편집'}
</h2>

<form onSubmit={handleSubmit}>
{/* 제목 입력 */}
Expand Down Expand Up @@ -92,7 +129,7 @@ export default function EditInfo() {
style={{ backgroundColor: color }}
aria-label={`색상 ${color}`}
>
{/* 선택된 색상 체크 */}
{/* 선택된 색상에 체크 표시 */}
{formData.color === color && (
<div className="relative size-24 items-center justify-center">
<Image
Expand Down
Loading
Loading