Skip to content

Commit eb9a2bd

Browse files
committed
Merge branch 'develop'
2 parents d1cc859 + c751e65 commit eb9a2bd

39 files changed

Lines changed: 3702 additions & 736 deletions

package-lock.json

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

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@
1616
"@tailwindcss/vite": "^4.1.18",
1717
"axios": "^1.13.2",
1818
"express": "^5.2.1",
19+
"chart.js": "^4.5.1",
20+
"date-fns": "^4.1.0",
1921
"jwt-decode": "^4.0.0",
22+
"lucide-vue-next": "^0.562.0",
2023
"pinia": "^3.0.4",
2124
"tailwindcss": "^4.1.18",
2225
"vue": "^3.5.25",
26+
"vue-chartjs": "^5.3.3",
2327
"vue-router": "^4.6.4"
2428
},
2529
"devDependencies": {
2630
"@vitejs/plugin-vue": "^6.0.2",
2731
"vite": "^7.2.4",
2832
"vite-plugin-vue-devtools": "^8.0.5"
2933
}
30-
}
34+
}

src/App.vue

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,38 @@
11
<template>
22
<RouterView />
3+
<ExpireSessionModal v-if="userStore.isExpireModalOpen" :seconds="userStore.remainingSeconds" @extend="handleExtend"
4+
@logout="() => logout()" />
35
</template>
46

57
<script setup>
68
import { RouterView } from 'vue-router';
9+
import { useSessionTimer } from "@/composables/useSessionTimer";
10+
import { useUserStore } from "@/stores/user";
11+
import { reissue as reissueApi } from '@/api/auth';
12+
import ExpireSessionModal from "@/components/common/ExpireSessionModal.vue";
13+
14+
const userStore = useUserStore();
15+
16+
const { logout } = useSessionTimer();
17+
18+
const handleExtend = async () => {
19+
try {
20+
const type = userStore.hasAuthority("AC_CLI")
21+
? "client"
22+
: "hq";
23+
24+
const res = await reissueApi(type);
25+
26+
const { accessToken, name } = res.data;
27+
28+
localStorage.setItem('accessToken', accessToken);
29+
localStorage.setItem('name', name);
30+
31+
userStore.setFromToken(accessToken);
32+
} catch (e) {
33+
console.error("토큰 재발급 API 실패", e);
34+
} finally {
35+
userStore.isExpireModalOpen = false;
36+
}
37+
};
738
</script>

src/api/auth.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,12 @@ const LOGOUT_URL = {
1010
client: '/clients/auth/logout'
1111
};
1212

13+
const REISSUE_URL = {
14+
hq: '/auth/reissue',
15+
client: '/clients/auth/reissue'
16+
};
17+
1318
export const login = (employeeType, data) => api.post(LOGIN_URL[employeeType], data);
14-
export const logout = (employeeType) => api.post(LOGOUT_URL[employeeType]);
19+
export const logout = (employeeType) => api.post(LOGOUT_URL[employeeType]);
20+
21+
export const reissue = (employeeType) => api.post(REISSUE_URL[employeeType]);

src/api/axios.js

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
import axios from 'axios';
1+
import axios from "axios";
2+
import { useUserStore } from "@/stores/user";
23

3-
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080';
4+
const API_BASE_URL =
5+
import.meta.env.VITE_API_BASE_URL || "http://localhost:8080";
46

57
const api = axios.create({
68
baseURL: API_BASE_URL,
79
withCredentials: true,
810
headers: {
9-
'Content-Type': 'application/json',
10-
}
11+
"Content-Type": "application/json",
12+
},
1113
});
1214

1315
api.interceptors.request.use(
1416
(config) => {
15-
const token = localStorage.getItem('accessToken');
17+
const token = localStorage.getItem("accessToken");
1618

1719
if (token) {
1820
// 토큰에서 공백 제거
@@ -25,7 +27,7 @@ api.interceptors.request.use(
2527
(error) => {
2628
return Promise.reject(error);
2729
}
28-
)
30+
);
2931

3032
api.interceptors.response.use(
3133
(response) => {
@@ -36,24 +38,36 @@ api.interceptors.response.use(
3638
const { status } = error.response;
3739

3840
if (status === 401) {
39-
localStorage.removeItem('accessToken');
41+
if (window.location.pathname === "/session-expired") {
42+
return Promise.reject(error);
43+
}
44+
45+
localStorage.removeItem("accessToken");
46+
const userStore = useUserStore();
47+
48+
if (userStore.isSessionHandling) {
49+
return Promise.reject(error);
50+
}
4051

4152
const currentPath = window.location.pathname;
4253

4354
// 로그인 페이지가 아닌 경우에만 리다이렉트
44-
if (!currentPath.startsWith('/login') && !currentPath.includes('/delivery/login')) {
55+
if (
56+
!currentPath.startsWith("/login") &&
57+
!currentPath.includes("/delivery/login")
58+
) {
4559
// 배송 앱에서는 배송 로그인으로, 일반 앱에서는 일반 로그인으로
46-
if (currentPath.startsWith('/delivery/')) {
47-
window.location.href = '/delivery/login';
60+
if (currentPath.startsWith("/delivery/")) {
61+
window.location.href = "/delivery/login";
4862
} else {
49-
window.location.href = '/login';
63+
window.location.href = "/login";
5064
}
5165
}
5266
}
5367
}
5468

5569
return Promise.reject(error);
5670
}
57-
)
71+
);
5872

59-
export default api;
73+
export default api;

src/api/client/clientDashboard.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import api from '../axios'
2+
3+
// 고객사 대시보드
4+
export const getClientDashboard = () => {
5+
return api.get('/clients/orders/dashboard').then(res => res.data);
6+
};

src/api/order/salesOrder.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
11
import api from '@/api/axios.js';
22

3+
// ====================== 본사 대시보드 =============================
4+
5+
// 대시보드 기본 데이터 조회
6+
export const getDashboard = (params = {}) => {
7+
return api.get('/orders/dashboard', { params })
8+
.then(res => res.data)
9+
}
10+
11+
// 납기 일정 캘린더 조회
12+
export const getDashboardCalendar = (params = {}) => {
13+
return api.get('/orders/dashboard/calendar', { params })
14+
.then(res => res.data)
15+
}
16+
17+
// 수주 목표 조회
18+
export const getDashboardGoals = (params = {}) => {
19+
return api.get('/orders/dashboard/goal', { params })
20+
.then(res => res.data)
21+
}
22+
23+
// 수주 목표 업데이트
24+
export const updateDashboardGoals = (goalId, updateData) => {
25+
return api.put(`/orders/dashboard/goal/${goalId}`,updateData)
26+
.then(res => res.data)
27+
}
28+
329
// ====================== 본사 =============================
430

531
// 주문 목록 조회
@@ -42,6 +68,11 @@ export const cancelOrder = (orderId, cancelData) => {
4268

4369
// ====================== 고객사 =============================
4470

71+
// 고객사 대시보드
72+
export const getClientDashboard = () => {
73+
return api.get('/clients/orders/dashboard').then(res => res.data);
74+
};
75+
4576
// 고객사 주문
4677
export const createClientOrder = (orderData) => {
4778
return api.post('/clients/orders', orderData)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import api from '@/api/axios.js';
2+
3+
/**
4+
* 생산 대시보드 KPI 요약
5+
*/
6+
export const getDashboardSummary = () =>
7+
api.get('/production/dashboard/summary')
8+
.then(res => res.data);
9+
10+
/**
11+
* 라인 가동 상태 (도넛)
12+
*/
13+
export const getLineStatus = () =>
14+
api.get('/production/dashboard/line-status')
15+
.then(res => res.data);
16+
17+
/**
18+
* 라인 CAPA 부하
19+
*/
20+
export const getLineCapa = () =>
21+
api.get('/production/dashboard/line-capa')
22+
.then(res => res.data);
23+
24+
/**
25+
* 자재 부족 현황
26+
*/
27+
export const getMaterialShortage = () =>
28+
api.get('/production/dashboard/material-shortage')
29+
.then(res => res.data);
30+
31+
/**
32+
* 월별 생산 추이
33+
*/
34+
export const getMonthlyTrend = () =>
35+
api.get('/production/dashboard/monthly-trend')
36+
.then(res => res.data);
37+
38+
/**
39+
* PR 리스크 리스트
40+
*/
41+
export const getPrRiskList = () =>
42+
api.get('/production/dashboard/pr-risk')
43+
.then(res => res.data);

0 commit comments

Comments
 (0)