Skip to content

Commit 8206b64

Browse files
authored
Merge pull request #26 from Sysone-Mini-2nd/hotfix/마지막1
hotfix/마지막1
2 parents 71a0c81 + 9f35048 commit 8206b64

8 files changed

Lines changed: 189 additions & 7 deletions

File tree

package-lock.json

Lines changed: 16 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: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,18 @@
2222
"@tanstack/react-table": "^8.21.3",
2323
"axios": "^1.12.1",
2424
"dayjs": "^1.11.18",
25+
"lodash": "^4.17.21",
2526
"react": "^18.3.1",
2627
"react-big-calendar": "^1.19.4",
2728
"react-dom": "^18.3.1",
2829
"react-error-boundary": "^6.0.0",
2930
"react-router-dom": "^7.8.2",
30-
"sockjs-client": "^1.6.1",
3131
"recharts": "^3.2.0",
32+
"sockjs-client": "^1.6.1",
3233
"tailwindcss": "^4.1.12",
34+
"wx-gantt-data-provider": "^2.2.1",
3335
"wx-react-gantt": "^1.3.1",
34-
"zustand": "^5.0.8",
35-
"lodash": "^4.17.21"
36+
"zustand": "^5.0.8"
3637
},
3738
"devDependencies": {
3839
"@eslint/js": "^9.33.0",
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React from 'react';
2+
import { AlertTriangle, RefreshCw } from '@mui/icons-material';
3+
4+
class ErrorBoundary extends React.Component {
5+
constructor(props) {
6+
super(props);
7+
this.state = { hasError: false, error: null, errorInfo: null };
8+
}
9+
10+
static getDerivedStateFromError() {
11+
// 다음 렌더링에서 폴백 UI가 보이도록 상태를 업데이트 합니다.
12+
return { hasError: true };
13+
}
14+
15+
componentDidCatch(error, errorInfo) {
16+
// 에러 리포팅 서비스에 에러를 기록할 수도 있습니다
17+
console.error('ErrorBoundary caught an error:', error, errorInfo);
18+
this.setState({
19+
error: error,
20+
errorInfo: errorInfo
21+
});
22+
}
23+
24+
handleRetry = () => {
25+
this.setState({ hasError: false, error: null, errorInfo: null });
26+
};
27+
28+
render() {
29+
if (this.state.hasError) {
30+
// 커스텀 에러 UI를 렌더링할 수 있습니다
31+
return (
32+
<div className="flex flex-col items-center justify-center p-8 bg-red-50 border border-red-200 rounded-lg">
33+
<AlertTriangle className="w-12 h-12 text-red-500 mb-4" />
34+
<h2 className="text-xl font-semibold text-red-800 mb-2">
35+
{this.props.title || '오류가 발생했습니다'}
36+
</h2>
37+
<p className="text-red-600 text-center mb-4">
38+
{this.props.message || '컴포넌트를 로드하는 중 문제가 발생했습니다.'}
39+
</p>
40+
41+
{import.meta.env.DEV && this.state.error && (
42+
<details className="w-full mb-4">
43+
<summary className="cursor-pointer text-sm text-red-500 hover:text-red-700">
44+
상세 오류 정보 (개발 모드)
45+
</summary>
46+
<div className="mt-2 p-3 bg-red-100 rounded text-sm text-red-800 overflow-auto max-h-32">
47+
<div className="font-semibold">Error:</div>
48+
<div className="mb-2">{this.state.error.toString()}</div>
49+
<div className="font-semibold">Stack Trace:</div>
50+
<pre className="whitespace-pre-wrap text-xs">
51+
{this.state.errorInfo.componentStack}
52+
</pre>
53+
</div>
54+
</details>
55+
)}
56+
57+
<button
58+
onClick={this.handleRetry}
59+
className="flex items-center gap-2 px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
60+
>
61+
<RefreshCw className="w-4 h-4" />
62+
다시 시도
63+
</button>
64+
</div>
65+
);
66+
}
67+
68+
return this.props.children;
69+
}
70+
}
71+
72+
export default ErrorBoundary;

src/components/common/loading/LoadingComponents.jsx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,100 @@
11
import { Replay, Warning} from "@mui/icons-material";
22
import { Skeleton } from "@mui/material";
33

4+
// 대시보드 KPI 카드용 스켈레톤
5+
export function KPICardSkeleton({ count = 2 }) {
6+
return (
7+
<div className="grid grid-cols-2 gap-6">
8+
{Array.from({ length: count }).map((_, index) => (
9+
<div key={index} className="bg-white p-6 rounded-lg border border-gray-200">
10+
<div className="flex items-center justify-between">
11+
<div className="flex-1">
12+
<Skeleton variant="text" width="70%" height={20} className="mb-2" />
13+
<Skeleton variant="text" width="40%" height={32} />
14+
</div>
15+
<Skeleton variant="circular" width={40} height={40} />
16+
</div>
17+
<div className="flex items-center gap-2 mt-4">
18+
<Skeleton variant="text" width={30} height={16} />
19+
<Skeleton variant="text" width={40} height={16} />
20+
</div>
21+
</div>
22+
))}
23+
</div>
24+
);
25+
}
26+
27+
// 차트용 스켈레톤
28+
export function ChartSkeleton({ height = "h-64", title }) {
29+
return (
30+
<div className="bg-white p-6 rounded-lg border border-gray-200">
31+
{title && (
32+
<div className="mb-4">
33+
<Skeleton variant="text" width="40%" height={24} />
34+
</div>
35+
)}
36+
<div className={`${height} flex items-center justify-center`}>
37+
<div className="w-full space-y-3">
38+
<Skeleton variant="rounded" width="100%" height={40} />
39+
<Skeleton variant="rounded" width="80%" height={40} />
40+
<Skeleton variant="rounded" width="60%" height={40} />
41+
<Skeleton variant="rounded" width="90%" height={40} />
42+
</div>
43+
</div>
44+
</div>
45+
);
46+
}
47+
48+
// 테이블/리스트용 스켈레톤
49+
export function TableSkeleton({ rows = 5, title }) {
50+
return (
51+
<div className="bg-white p-6 rounded-lg border border-gray-200">
52+
{title && (
53+
<div className="mb-4">
54+
<Skeleton variant="text" width="40%" height={24} />
55+
</div>
56+
)}
57+
<div className="space-y-3">
58+
{Array.from({ length: rows }).map((_, index) => (
59+
<div key={index} className="flex items-center space-x-4">
60+
<Skeleton variant="circular" width={32} height={32} />
61+
<div className="flex-1 space-y-2">
62+
<Skeleton variant="text" width="60%" height={16} />
63+
<Skeleton variant="text" width="40%" height={14} />
64+
</div>
65+
<Skeleton variant="text" width="20%" height={16} />
66+
</div>
67+
))}
68+
</div>
69+
</div>
70+
);
71+
}
72+
73+
// 위클리 스케줄용 스켈레톤
74+
export function WeeklyScheduleSkeleton() {
75+
return (
76+
<div className="bg-white p-6 rounded-lg border border-gray-200">
77+
<div className="mb-4">
78+
<Skeleton variant="text" width="40%" height={24} />
79+
</div>
80+
<div className="space-y-4">
81+
{Array.from({ length: 7 }).map((_, index) => (
82+
<div key={index} className="flex items-center space-x-4">
83+
<Skeleton variant="text" width={40} height={16} />
84+
<div className="flex-1">
85+
<div className="flex space-x-2">
86+
<Skeleton variant="rounded" width={80} height={24} />
87+
<Skeleton variant="rounded" width={100} height={24} />
88+
<Skeleton variant="rounded" width={60} height={24} />
89+
</div>
90+
</div>
91+
</div>
92+
))}
93+
</div>
94+
</div>
95+
);
96+
}
97+
498
// ProjectList용 Skeleton UI
599
export function ProjectListSkeleton({ count = 4 }) {
6100
return (

src/components/dashboard/TeamProductivityTrend.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function TeamProductivityTrend({ selectedProjectId, title = "인원별 이슈
5454
<ResponsiveContainer width="100%" height="100%">
5555
<BarChart
5656
data={barData}
57-
margin={{ top: 20, right: 30, left: 20, bottom: 60 }}
57+
margin={{ top: 20, right: 30, left: 20, bottom: 30 }}
5858
>
5959
<CartesianGrid strokeDasharray="3 3" />
6060
<XAxis

src/hooks/useIssueQueries.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function useProjectIssues(projectId, filters = {}) {
1414
queryKey: ['issues', projectId, filters],
1515
queryFn: () => getProjectIssues(projectId, filters),
1616
staleTime: 0,
17-
cacheTime: 5 * 60 * 1000,
17+
// cacheTime: 5 * 60 * 1000,
1818
refetchOnWindowFocus: false,
1919
select: (response) => {
2020
if (response && response.data && Array.isArray(response.data.issues)) {

src/hooks/useProjectQueries.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function useProjects(filters = {}) {
2020
}
2121
return { projects: [], total: 0, statusCounts: {}, delayed: 0 };
2222
},
23-
staleTime: 5 * 60 * 1000, // 5분
23+
// staleTime: 5 * 60 * 1000,
2424
});
2525
}
2626

src/main.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { ToastProvider } from './contexts/ToastContext.jsx'
1111
const queryClient = new QueryClient({
1212
defaultOptions: {
1313
queries: {
14-
// staleTime: 3 * 60 * 1000,
1514
retry: 1,
1615
},
1716
},

0 commit comments

Comments
 (0)