@sonhoseong/mfa-lib 1.3.8 → 1.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/dist/components/button/ScrollTopButton.js +5 -3
  2. package/dist/components/error/ErrorBoundary.js +14 -4
  3. package/dist/components/error/NotFound.d.ts +20 -0
  4. package/dist/components/error/NotFound.d.ts.map +1 -0
  5. package/dist/components/error/NotFound.js +84 -0
  6. package/dist/components/error/index.d.ts +2 -0
  7. package/dist/components/error/index.d.ts.map +1 -1
  8. package/dist/components/error/index.js +1 -0
  9. package/dist/components/icons/Icons.d.ts +51 -0
  10. package/dist/components/icons/Icons.d.ts.map +1 -0
  11. package/dist/components/icons/Icons.js +100 -0
  12. package/dist/components/icons/index.d.ts +5 -0
  13. package/dist/components/icons/index.d.ts.map +1 -0
  14. package/dist/components/icons/index.js +4 -0
  15. package/dist/components/index.d.ts +3 -0
  16. package/dist/components/index.d.ts.map +1 -1
  17. package/dist/components/index.js +6 -0
  18. package/dist/components/layout/Container.js +7 -2
  19. package/dist/components/loading/DeferredComponent.d.ts +19 -0
  20. package/dist/components/loading/DeferredComponent.d.ts.map +1 -0
  21. package/dist/components/loading/DeferredComponent.js +32 -0
  22. package/dist/components/loading/GlobalLoading.js +14 -3
  23. package/dist/components/loading/index.d.ts +1 -0
  24. package/dist/components/loading/index.d.ts.map +1 -1
  25. package/dist/components/loading/index.js +1 -0
  26. package/dist/components/logo/Logo.js +12 -3
  27. package/dist/components/modal/ModalContainer.js +17 -8
  28. package/dist/components/modal/ModalContext.js +2 -3
  29. package/dist/components/navigation/AppNavbar.js +21 -9
  30. package/dist/components/navigation/AppSidebar.css +58 -3
  31. package/dist/components/navigation/AppSidebar.d.ts +1 -1
  32. package/dist/components/navigation/AppSidebar.d.ts.map +1 -1
  33. package/dist/components/navigation/AppSidebar.js +58 -15
  34. package/dist/components/navigation/Footer.d.ts +15 -0
  35. package/dist/components/navigation/Footer.d.ts.map +1 -0
  36. package/dist/components/navigation/Footer.js +12 -0
  37. package/dist/components/navigation/Header.d.ts.map +1 -1
  38. package/dist/components/navigation/Header.js +17 -4
  39. package/dist/components/navigation/Lnb.d.ts +2 -7
  40. package/dist/components/navigation/Lnb.d.ts.map +1 -1
  41. package/dist/components/navigation/Lnb.js +34 -8
  42. package/dist/components/navigation/StickyNav.js +19 -11
  43. package/dist/components/navigation/index.d.ts +1 -0
  44. package/dist/components/navigation/index.d.ts.map +1 -1
  45. package/dist/components/navigation/index.js +1 -0
  46. package/dist/components/page/LoginPage.d.ts +4 -1
  47. package/dist/components/page/LoginPage.d.ts.map +1 -1
  48. package/dist/components/page/LoginPage.js +146 -21
  49. package/dist/components/remote/RemoteErrorBoundary.d.ts +28 -0
  50. package/dist/components/remote/RemoteErrorBoundary.d.ts.map +1 -0
  51. package/dist/components/remote/RemoteErrorBoundary.js +44 -0
  52. package/dist/components/remote/RemoteErrorFallback.d.ts +16 -0
  53. package/dist/components/remote/RemoteErrorFallback.d.ts.map +1 -0
  54. package/dist/components/remote/RemoteErrorFallback.js +76 -0
  55. package/dist/components/remote/index.d.ts +8 -0
  56. package/dist/components/remote/index.d.ts.map +1 -0
  57. package/dist/components/remote/index.js +5 -0
  58. package/dist/components/router/BrowserRouter.d.ts +13 -0
  59. package/dist/components/router/BrowserRouter.d.ts.map +1 -0
  60. package/dist/components/router/BrowserRouter.js +17 -0
  61. package/dist/components/router/RouteGuard.d.ts +79 -0
  62. package/dist/components/router/RouteGuard.d.ts.map +1 -0
  63. package/dist/components/router/RouteGuard.js +86 -0
  64. package/dist/components/router/index.d.ts +4 -0
  65. package/dist/components/router/index.d.ts.map +1 -0
  66. package/dist/components/router/index.js +2 -0
  67. package/dist/components/toast/ToastContainer.js +17 -6
  68. package/dist/components/toast/ToastContext.js +2 -3
  69. package/dist/hooks/index.d.ts +9 -1
  70. package/dist/hooks/index.d.ts.map +1 -1
  71. package/dist/hooks/index.js +15 -1
  72. package/dist/hooks/use-auth.d.ts +2 -1
  73. package/dist/hooks/use-auth.d.ts.map +1 -1
  74. package/dist/hooks/use-auth.js +19 -18
  75. package/dist/hooks/use-debounce.d.ts +56 -0
  76. package/dist/hooks/use-debounce.d.ts.map +1 -0
  77. package/dist/hooks/use-debounce.js +140 -0
  78. package/dist/hooks/use-effect-once.d.ts +77 -0
  79. package/dist/hooks/use-effect-once.d.ts.map +1 -0
  80. package/dist/hooks/use-effect-once.js +124 -0
  81. package/dist/hooks/use-error-notification.d.ts +1 -1
  82. package/dist/hooks/use-error-notification.js +1 -1
  83. package/dist/hooks/use-global-loading.d.ts +1 -1
  84. package/dist/hooks/use-global-loading.js +1 -1
  85. package/dist/hooks/use-initialize.d.ts +8 -1
  86. package/dist/hooks/use-initialize.d.ts.map +1 -1
  87. package/dist/hooks/use-initialize.js +126 -23
  88. package/dist/hooks/use-modal.d.ts +21 -5
  89. package/dist/hooks/use-modal.d.ts.map +1 -1
  90. package/dist/hooks/use-modal.js +57 -17
  91. package/dist/hooks/use-navigate.d.ts +1 -1
  92. package/dist/hooks/use-navigate.js +1 -1
  93. package/dist/hooks/use-network-status.d.ts +15 -0
  94. package/dist/hooks/use-network-status.d.ts.map +1 -0
  95. package/dist/hooks/use-network-status.js +49 -0
  96. package/dist/hooks/use-permission.d.ts +22 -0
  97. package/dist/hooks/use-permission.d.ts.map +1 -0
  98. package/dist/hooks/use-permission.js +73 -0
  99. package/dist/hooks/use-recent-menu.d.ts +46 -0
  100. package/dist/hooks/use-recent-menu.d.ts.map +1 -0
  101. package/dist/hooks/use-recent-menu.js +169 -0
  102. package/dist/hooks/use-scroll-restoration.d.ts +51 -0
  103. package/dist/hooks/use-scroll-restoration.d.ts.map +1 -0
  104. package/dist/hooks/use-scroll-restoration.js +143 -0
  105. package/dist/hooks/use-supabase-auth.d.ts +49 -0
  106. package/dist/hooks/use-supabase-auth.d.ts.map +1 -0
  107. package/dist/hooks/use-supabase-auth.js +229 -0
  108. package/dist/hooks/use-track-history.d.ts +2 -1
  109. package/dist/hooks/use-track-history.d.ts.map +1 -1
  110. package/dist/hooks/use-track-history.js +14 -2
  111. package/dist/index.d.ts +1 -1
  112. package/dist/index.js +1 -1
  113. package/dist/network/axios-factory.d.ts +30 -1
  114. package/dist/network/axios-factory.d.ts.map +1 -1
  115. package/dist/network/axios-factory.js +192 -24
  116. package/dist/network/index.d.ts +3 -1
  117. package/dist/network/index.d.ts.map +1 -1
  118. package/dist/network/index.js +5 -1
  119. package/dist/network/supabase-client.d.ts +28 -0
  120. package/dist/network/supabase-client.d.ts.map +1 -0
  121. package/dist/network/supabase-client.js +46 -0
  122. package/dist/store/app-store.d.ts +222 -12
  123. package/dist/store/app-store.d.ts.map +1 -1
  124. package/dist/store/app-store.js +46 -29
  125. package/dist/store/index.d.ts +2 -0
  126. package/dist/store/index.d.ts.map +1 -1
  127. package/dist/store/index.js +3 -0
  128. package/dist/store/menu-slice.d.ts +96 -0
  129. package/dist/store/menu-slice.d.ts.map +1 -0
  130. package/dist/store/menu-slice.js +98 -0
  131. package/dist/store/recent-menu-slice.d.ts +209 -0
  132. package/dist/store/recent-menu-slice.d.ts.map +1 -0
  133. package/dist/store/recent-menu-slice.js +110 -0
  134. package/dist/store/store-access.d.ts +1 -1
  135. package/dist/store/store-access.js +1 -1
  136. package/dist/types/index.d.ts +74 -17
  137. package/dist/types/index.d.ts.map +1 -1
  138. package/dist/types/service.d.ts +1 -1
  139. package/dist/types/service.js +1 -1
  140. package/dist/utils/classnames.d.ts +65 -0
  141. package/dist/utils/classnames.d.ts.map +1 -0
  142. package/dist/utils/classnames.js +98 -0
  143. package/dist/utils/formatter.d.ts +78 -0
  144. package/dist/utils/formatter.d.ts.map +1 -0
  145. package/dist/utils/formatter.js +216 -0
  146. package/dist/utils/index.d.ts +5 -0
  147. package/dist/utils/index.d.ts.map +1 -1
  148. package/dist/utils/index.js +5 -0
  149. package/dist/utils/permission.d.ts +33 -0
  150. package/dist/utils/permission.d.ts.map +1 -0
  151. package/dist/utils/permission.js +132 -0
  152. package/dist/utils/query-string.d.ts +67 -0
  153. package/dist/utils/query-string.d.ts.map +1 -0
  154. package/dist/utils/query-string.js +136 -0
  155. package/dist/utils/storage.d.ts +1 -1
  156. package/dist/utils/storage.js +1 -1
  157. package/dist/utils/validation.d.ts +98 -0
  158. package/dist/utils/validation.d.ts.map +1 -0
  159. package/dist/utils/validation.js +260 -0
  160. package/package.json +5 -3
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Global Loading Hook - KOMCA 패턴
2
+ * Global Loading Hook
3
3
  * API 호출 시 전역 로딩 표시
4
4
  */
5
5
  import { useCallback } from 'react';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Initialize Hook - KOMCA 패턴
2
+ * Initialize Hook
3
3
  * 앱 시작시 초기화 (토큰 갱신, 사용자 정보 로드)
4
4
  */
5
5
  import { User } from '../types';
@@ -23,4 +23,11 @@ export declare function useInitialize(options?: InitializeOptions): {
23
23
  export declare function useSimpleInitialize(): {
24
24
  initialized: boolean;
25
25
  };
26
+ /**
27
+ * Supabase 초기화 Hook
28
+ * 앱 시작 시 Supabase 세션 복구 및 Auth 상태 변경 구독
29
+ */
30
+ export declare function useSupabaseInitialize(): {
31
+ initialized: boolean;
32
+ };
26
33
  //# sourceMappingURL=use-initialize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-initialize.d.ts","sourceRoot":"","sources":["../../src/hooks/use-initialize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGhC,MAAM,WAAW,iBAAiB;IAEhC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE5C,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAE3C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAE3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,iBAAsB;;;;EAyE5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB;;EA4BlC"}
1
+ {"version":3,"file":"use-initialize.d.ts","sourceRoot":"","sources":["../../src/hooks/use-initialize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAiBhC,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,iBAAsB;;;;EAmE5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB;;EAyBlC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB;;EAgHpC"}
@@ -1,10 +1,23 @@
1
1
  /**
2
- * Initialize Hook - KOMCA 패턴
2
+ * Initialize Hook
3
3
  * 앱 시작시 초기화 (토큰 갱신, 사용자 정보 로드)
4
4
  */
5
5
  import { useEffect, useState } from 'react';
6
- import { dispatchToHost } from '../store/store-access';
6
+ import { getStore, setAccessToken, setUser, logout } from '../store/app-store';
7
7
  import { storage } from '../utils/storage';
8
+ import { getSupabase } from '../network/supabase-client';
9
+ /**
10
+ * Supabase User를 앱 User 타입으로 변환
11
+ */
12
+ function mapSupabaseUser(supabaseUser) {
13
+ return {
14
+ id: supabaseUser.id,
15
+ email: supabaseUser.email || '',
16
+ name: supabaseUser.user_metadata?.name || supabaseUser.email?.split('@')[0] || '',
17
+ role: supabaseUser.user_metadata?.role || 'user',
18
+ avatar: supabaseUser.user_metadata?.avatar_url,
19
+ };
20
+ }
8
21
  /**
9
22
  * 앱 초기화 Hook
10
23
  */
@@ -16,28 +29,25 @@ export function useInitialize(options = {}) {
16
29
  const initialize = async () => {
17
30
  try {
18
31
  setLoading(true);
19
- // 1. Storage에서 기존 토큰 복구
32
+ const store = getStore();
20
33
  const savedToken = storage.getAccessToken();
21
34
  const savedUser = storage.getUser();
22
35
  if (savedToken) {
23
- // Store에 복구
24
- dispatchToHost({ type: 'app/setAccessToken', payload: savedToken });
36
+ store.dispatch(setAccessToken(savedToken));
25
37
  if (savedUser) {
26
- dispatchToHost({ type: 'app/setUser', payload: savedUser });
38
+ store.dispatch(setUser(savedUser));
27
39
  }
28
- // 2. 토큰 갱신 시도 (옵션)
29
40
  if (options.refreshToken) {
30
41
  try {
31
42
  const newToken = await options.refreshToken();
32
43
  if (newToken) {
33
- dispatchToHost({ type: 'app/setAccessToken', payload: newToken });
44
+ store.dispatch(setAccessToken(newToken));
34
45
  storage.setAccessToken(newToken);
35
46
  console.log('[Initialize] 토큰 갱신 성공');
36
- // 3. 사용자 정보 갱신 (옵션)
37
47
  if (options.fetchUserInfo) {
38
48
  const userInfo = await options.fetchUserInfo();
39
49
  if (userInfo) {
40
- dispatchToHost({ type: 'app/setUser', payload: userInfo });
50
+ store.dispatch(setUser(userInfo));
41
51
  storage.setUser(userInfo);
42
52
  console.log('[Initialize] 사용자 정보 갱신:', userInfo.email);
43
53
  }
@@ -49,13 +59,10 @@ export function useInitialize(options = {}) {
49
59
  }
50
60
  }
51
61
  }
52
- // 4. Recent Menu 복구
62
+ // Recent Menu 복구
53
63
  const savedRecentMenu = storage.getRecentMenu();
54
64
  if (savedRecentMenu.length > 0) {
55
- dispatchToHost({
56
- type: 'recentMenu/setRecentMenu',
57
- payload: { list: savedRecentMenu },
58
- });
65
+ store.dispatch({ type: 'recentMenu/setRecentMenu', payload: { list: savedRecentMenu } });
59
66
  }
60
67
  setInitialized(true);
61
68
  options.onInitialized?.();
@@ -81,24 +88,120 @@ export function useInitialize(options = {}) {
81
88
  export function useSimpleInitialize() {
82
89
  const [initialized, setInitialized] = useState(false);
83
90
  useEffect(() => {
84
- // Storage에서 복구
91
+ const store = getStore();
85
92
  const savedToken = storage.getAccessToken();
86
93
  const savedUser = storage.getUser();
87
94
  if (savedToken) {
88
- dispatchToHost({ type: 'app/setAccessToken', payload: savedToken });
95
+ store.dispatch(setAccessToken(savedToken));
89
96
  }
90
97
  if (savedUser) {
91
- dispatchToHost({ type: 'app/setUser', payload: savedUser });
98
+ store.dispatch(setUser(savedUser));
92
99
  }
93
- // Recent Menu 복구
94
100
  const savedRecentMenu = storage.getRecentMenu();
95
101
  if (savedRecentMenu.length > 0) {
96
- dispatchToHost({
97
- type: 'recentMenu/setRecentMenu',
98
- payload: { list: savedRecentMenu },
99
- });
102
+ store.dispatch({ type: 'recentMenu/setRecentMenu', payload: { list: savedRecentMenu } });
100
103
  }
101
104
  setInitialized(true);
102
105
  }, []);
103
106
  return { initialized };
104
107
  }
108
+ /**
109
+ * Supabase 초기화 Hook
110
+ * 앱 시작 시 Supabase 세션 복구 및 Auth 상태 변경 구독
111
+ */
112
+ export function useSupabaseInitialize() {
113
+ const [initialized, setInitialized] = useState(false);
114
+ useEffect(() => {
115
+ let cleanup;
116
+ const initialize = async () => {
117
+ const store = getStore();
118
+ try {
119
+ // Supabase 클라이언트 가져오기 (없으면 fallback)
120
+ let supabase;
121
+ try {
122
+ supabase = getSupabase();
123
+ }
124
+ catch {
125
+ // Supabase가 초기화되지 않은 경우 localStorage에서 복구
126
+ console.warn('[Supabase Init] Supabase 미초기화, localStorage fallback');
127
+ const savedToken = storage.getAccessToken();
128
+ const savedUser = storage.getUser();
129
+ if (savedToken) {
130
+ store.dispatch(setAccessToken(savedToken));
131
+ }
132
+ if (savedUser) {
133
+ store.dispatch(setUser(savedUser));
134
+ }
135
+ // Recent Menu 복구
136
+ const savedRecentMenu = storage.getRecentMenu();
137
+ if (savedRecentMenu.length > 0) {
138
+ store.dispatch({ type: 'recentMenu/setRecentMenuList', payload: savedRecentMenu });
139
+ }
140
+ setInitialized(true);
141
+ return;
142
+ }
143
+ // 1. 현재 세션 확인
144
+ const { data: { session }, error } = await supabase.auth.getSession();
145
+ if (error) {
146
+ console.warn('[Supabase Init] 세션 가져오기 실패:', error.message);
147
+ }
148
+ if (session) {
149
+ const user = mapSupabaseUser(session.user);
150
+ store.dispatch(setAccessToken(session.access_token));
151
+ store.dispatch(setUser(user));
152
+ storage.setAccessToken(session.access_token);
153
+ storage.setUser(user);
154
+ console.log('[Supabase Init] 세션 복구:', user.email);
155
+ }
156
+ else {
157
+ // 세션 없으면 localStorage에서 복구 시도
158
+ const savedToken = storage.getAccessToken();
159
+ const savedUser = storage.getUser();
160
+ if (savedToken) {
161
+ store.dispatch(setAccessToken(savedToken));
162
+ }
163
+ if (savedUser) {
164
+ store.dispatch(setUser(savedUser));
165
+ }
166
+ }
167
+ // 2. Recent Menu 복구
168
+ const savedRecentMenu = storage.getRecentMenu();
169
+ if (savedRecentMenu.length > 0) {
170
+ store.dispatch({ type: 'recentMenu/setRecentMenuList', payload: savedRecentMenu });
171
+ }
172
+ // 3. Auth 상태 변경 구독
173
+ const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
174
+ if (session) {
175
+ const user = mapSupabaseUser(session.user);
176
+ store.dispatch(setAccessToken(session.access_token));
177
+ store.dispatch(setUser(user));
178
+ storage.setAccessToken(session.access_token);
179
+ storage.setUser(user);
180
+ }
181
+ else if (event === 'SIGNED_OUT') {
182
+ store.dispatch(logout());
183
+ storage.clearAuth();
184
+ }
185
+ });
186
+ cleanup = () => subscription.unsubscribe();
187
+ setInitialized(true);
188
+ }
189
+ catch (err) {
190
+ console.error('[Supabase Init] 초기화 실패:', err);
191
+ // 에러 발생해도 localStorage에서 복구 시도
192
+ const savedToken = storage.getAccessToken();
193
+ const savedUser = storage.getUser();
194
+ if (savedToken) {
195
+ store.dispatch(setAccessToken(savedToken));
196
+ }
197
+ if (savedUser) {
198
+ store.dispatch(setUser(savedUser));
199
+ }
200
+ setInitialized(true);
201
+ }
202
+ };
203
+ initialize();
204
+ return () => cleanup?.();
205
+ }, []);
206
+ return { initialized };
207
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Modal Hooks - KOMCA 패턴
2
+ * Modal Hooks
3
3
  * Alert, Confirm 모달 관리
4
4
  */
5
5
  export interface SimpleModalOptions {
@@ -29,11 +29,27 @@ export declare function useAlertModal(): AlertModalResult;
29
29
  */
30
30
  export declare function useConfirmModal(): ConfirmModalResult;
31
31
  /**
32
- * 비동기 Alert 모달 - 간편 사용
32
+ * 비동기 Alert 모달 - ModalContext 연동
33
+ * KOMCA 패턴 업그레이드: Context 기반으로 커스텀 모달 사용
34
+ *
35
+ * @example
36
+ * const alert = useAsyncAlert();
37
+ * await alert('저장되었습니다.');
38
+ * await alert('오류가 발생했습니다.', '오류');
33
39
  */
34
- export declare function useAsyncAlertModal(): (message: string, title?: string) => Promise<void>;
40
+ export declare function useAsyncAlert(): (message: string, title?: string) => Promise<void>;
35
41
  /**
36
- * 비동기 Confirm 모달 - 간편 사용
42
+ * 비동기 Confirm 모달 - ModalContext 연동
43
+ * KOMCA 패턴 업그레이드: Context 기반으로 커스텀 모달 사용
44
+ *
45
+ * @example
46
+ * const confirm = useAsyncConfirm();
47
+ * const result = await confirm('삭제하시겠습니까?');
48
+ * if (result) {
49
+ * // 삭제 처리
50
+ * }
37
51
  */
38
- export declare function useAsyncConfirmModal(): (message: string, title?: string) => Promise<boolean>;
52
+ export declare function useAsyncConfirm(): (message: string, title?: string) => Promise<boolean>;
53
+ export declare const useAsyncAlertModal: typeof useAsyncAlert;
54
+ export declare const useAsyncConfirmModal: typeof useAsyncConfirm;
39
55
  //# sourceMappingURL=use-modal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-modal.d.ts","sourceRoot":"","sources":["../../src/hooks/use-modal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAGD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjE,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,gBAAgB,CA4BhD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,kBAAkB,CA4BpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,cACH,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,cACL,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC,CAOvE"}
1
+ {"version":3,"file":"use-modal.d.ts","sourceRoot":"","sources":["../../src/hooks/use-modal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAGD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjE,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,gBAAgB,CA4BhD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,kBAAkB,CA4BpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,cAYQ,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAQ1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,cAWM,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC,CAQ7E;AAGD,eAAO,MAAM,kBAAkB,sBAAgB,CAAC;AAChD,eAAO,MAAM,oBAAoB,wBAAkB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Modal Hooks - KOMCA 패턴
2
+ * Modal Hooks
3
3
  * Alert, Confirm 모달 관리
4
4
  */
5
5
  import { useCallback, useState } from 'react';
@@ -58,26 +58,66 @@ export function useConfirmModal() {
58
58
  return { isOpen, options, show, close };
59
59
  }
60
60
  /**
61
- * 비동기 Alert 모달 - 간편 사용
61
+ * 비동기 Alert 모달 - ModalContext 연동
62
+ * KOMCA 패턴 업그레이드: Context 기반으로 커스텀 모달 사용
63
+ *
64
+ * @example
65
+ * const alert = useAsyncAlert();
66
+ * await alert('저장되었습니다.');
67
+ * await alert('오류가 발생했습니다.', '오류');
62
68
  */
63
- export function useAsyncAlertModal() {
64
- return useCallback((message, title) => {
65
- return new Promise((resolve) => {
66
- // 브라우저 기본 alert 사용 (커스텀 모달로 교체 가능)
67
- alert(title ? `${title}\n\n${message}` : message);
68
- resolve();
69
- });
69
+ export function useAsyncAlert() {
70
+ // ModalContext를 직접 import하지 않고 동적으로 가져옴 (순환 참조 방지)
71
+ const getModalContext = useCallback(() => {
72
+ try {
73
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
74
+ const { useModalContext } = require('../components/modal/ModalContext');
75
+ return useModalContext();
76
+ }
77
+ catch {
78
+ return null;
79
+ }
70
80
  }, []);
81
+ return useCallback(async (message, title) => {
82
+ const context = getModalContext();
83
+ if (context?.alert) {
84
+ return context.alert(message, title);
85
+ }
86
+ // fallback: 브라우저 기본 alert
87
+ alert(title ? `${title}\n\n${message}` : message);
88
+ }, [getModalContext]);
71
89
  }
72
90
  /**
73
- * 비동기 Confirm 모달 - 간편 사용
91
+ * 비동기 Confirm 모달 - ModalContext 연동
92
+ * KOMCA 패턴 업그레이드: Context 기반으로 커스텀 모달 사용
93
+ *
94
+ * @example
95
+ * const confirm = useAsyncConfirm();
96
+ * const result = await confirm('삭제하시겠습니까?');
97
+ * if (result) {
98
+ * // 삭제 처리
99
+ * }
74
100
  */
75
- export function useAsyncConfirmModal() {
76
- return useCallback((message, title) => {
77
- return new Promise((resolve) => {
78
- // 브라우저 기본 confirm 사용 (커스텀 모달로 교체 가능)
79
- const result = confirm(title ? `${title}\n\n${message}` : message);
80
- resolve(result);
81
- });
101
+ export function useAsyncConfirm() {
102
+ const getModalContext = useCallback(() => {
103
+ try {
104
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
105
+ const { useModalContext } = require('../components/modal/ModalContext');
106
+ return useModalContext();
107
+ }
108
+ catch {
109
+ return null;
110
+ }
82
111
  }, []);
112
+ return useCallback(async (message, title) => {
113
+ const context = getModalContext();
114
+ if (context?.confirm) {
115
+ return context.confirm(message, title);
116
+ }
117
+ // fallback: 브라우저 기본 confirm
118
+ return confirm(title ? `${title}\n\n${message}` : message);
119
+ }, [getModalContext]);
83
120
  }
121
+ // 하위 호환성을 위한 alias
122
+ export const useAsyncAlertModal = useAsyncAlert;
123
+ export const useAsyncConfirmModal = useAsyncConfirm;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * MFA Navigate Hook - KOMCA 패턴
2
+ * MFA Navigate Hook
3
3
  * 서비스 인식 네비게이션
4
4
  */
5
5
  import { NavigateOptions } from 'react-router-dom';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * MFA Navigate Hook - KOMCA 패턴
2
+ * MFA Navigate Hook
3
3
  * 서비스 인식 네비게이션
4
4
  */
5
5
  import { useCallback, useMemo } from 'react';
@@ -0,0 +1,15 @@
1
+ export type ConnectionType = 'slow-2g' | '2g' | '3g' | '4g' | 'unknown';
2
+ interface NetworkStatus {
3
+ connectionType: ConnectionType;
4
+ isSlowNetwork: boolean;
5
+ isOnline: boolean;
6
+ }
7
+ /**
8
+ * 사용자의 네트워크 상태를 감지하는 훅
9
+ * - effectiveType: 연결 품질 (slow-2g, 2g, 3g, 4g)
10
+ * - isSlowNetwork: 느린 네트워크 여부 (slow-2g, 2g, 3g)
11
+ * - isOnline: 온라인 상태
12
+ */
13
+ declare const useNetworkStatus: () => NetworkStatus;
14
+ export default useNetworkStatus;
15
+ //# sourceMappingURL=use-network-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-network-status.d.ts","sourceRoot":"","sources":["../../src/hooks/use-network-status.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;AAExE,UAAU,aAAa;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,QAAA,MAAM,gBAAgB,QAAO,aAiD5B,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { useEffect, useState } from 'react';
2
+ /**
3
+ * 사용자의 네트워크 상태를 감지하는 훅
4
+ * - effectiveType: 연결 품질 (slow-2g, 2g, 3g, 4g)
5
+ * - isSlowNetwork: 느린 네트워크 여부 (slow-2g, 2g, 3g)
6
+ * - isOnline: 온라인 상태
7
+ */
8
+ const useNetworkStatus = () => {
9
+ const getConnectionType = () => {
10
+ if ('connection' in navigator) {
11
+ const nav = navigator;
12
+ if (nav.connection?.effectiveType) {
13
+ return nav.connection.effectiveType;
14
+ }
15
+ }
16
+ return 'unknown';
17
+ };
18
+ const [connectionType, setConnectionType] = useState(getConnectionType());
19
+ const [isOnline, setIsOnline] = useState(navigator.onLine);
20
+ useEffect(() => {
21
+ // Network Information API
22
+ if ('connection' in navigator) {
23
+ const nav = navigator;
24
+ if (nav.connection) {
25
+ const updateConnectionStatus = () => {
26
+ setConnectionType(nav.connection?.effectiveType || 'unknown');
27
+ };
28
+ nav.connection.addEventListener('change', updateConnectionStatus);
29
+ return () => {
30
+ nav.connection?.removeEventListener('change', updateConnectionStatus);
31
+ };
32
+ }
33
+ }
34
+ }, []);
35
+ useEffect(() => {
36
+ // Online/Offline 상태
37
+ const handleOnline = () => setIsOnline(true);
38
+ const handleOffline = () => setIsOnline(false);
39
+ window.addEventListener('online', handleOnline);
40
+ window.addEventListener('offline', handleOffline);
41
+ return () => {
42
+ window.removeEventListener('online', handleOnline);
43
+ window.removeEventListener('offline', handleOffline);
44
+ };
45
+ }, []);
46
+ const isSlowNetwork = ['slow-2g', '2g', '3g'].includes(connectionType);
47
+ return { connectionType, isSlowNetwork, isOnline };
48
+ };
49
+ export default useNetworkStatus;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * usePermission Hook
3
+ * 권한 기반 접근 제어를 위한 훅
4
+ */
5
+ import { UserRole, PermissionAction, MenuItem, MenuPermission } from '../types';
6
+ /**
7
+ * usePermission Hook
8
+ * 현재 사용자의 권한을 확인하는 훅
9
+ */
10
+ export declare function usePermission(): {
11
+ user: import("../types").User | null;
12
+ can: (code: string, action?: PermissionAction) => boolean;
13
+ canAny: (codes: string[], action?: PermissionAction) => boolean;
14
+ canAll: (codes: string[], action?: PermissionAction) => boolean;
15
+ isRole: (roles: UserRole | UserRole[]) => boolean;
16
+ isAdmin: boolean;
17
+ checkMenu: (permission?: MenuPermission) => boolean;
18
+ filterMenus: (menus: MenuItem[]) => MenuItem[];
19
+ canAccess: (menus: MenuItem[], path: string) => boolean;
20
+ };
21
+ export default usePermission;
22
+ //# sourceMappingURL=use-permission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-permission.d.ts","sourceRoot":"","sources":["../../src/hooks/use-permission.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEhF;;;GAGG;AACH,wBAAgB,aAAa;;gBAOlB,MAAM,WAAU,gBAAgB,KAAY,OAAO;oBAUlD,MAAM,EAAE,WAAU,gBAAgB,KAAY,OAAO;oBAUrD,MAAM,EAAE,WAAU,gBAAgB,KAAY,OAAO;oBAUrD,QAAQ,GAAG,QAAQ,EAAE,KAAG,OAAO;;6BAezB,cAAc,KAAG,OAAO;yBAU9B,QAAQ,EAAE,KAAG,QAAQ,EAAE;uBAUvB,QAAQ,EAAE,QAAQ,MAAM,KAAG,OAAO;EAiB7C;AAED,eAAe,aAAa,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * usePermission Hook
3
+ * 권한 기반 접근 제어를 위한 훅
4
+ */
5
+ import { useMemo, useCallback } from 'react';
6
+ import { useSelector } from 'react-redux';
7
+ import { selectUser } from '../store/app-store';
8
+ import { hasPermission, hasAnyPermission, hasAllPermissions, hasRole, checkMenuPermission, filterMenusByPermission, canAccessPath, } from '../utils/permission';
9
+ /**
10
+ * usePermission Hook
11
+ * 현재 사용자의 권한을 확인하는 훅
12
+ */
13
+ export function usePermission() {
14
+ const user = useSelector(selectUser);
15
+ /**
16
+ * 특정 코드에 대한 권한 확인
17
+ */
18
+ const can = useCallback((code, action = 'read') => {
19
+ return hasPermission(user, code, action);
20
+ }, [user]);
21
+ /**
22
+ * 여러 코드 중 하나라도 권한이 있는지 확인
23
+ */
24
+ const canAny = useCallback((codes, action = 'read') => {
25
+ return hasAnyPermission(user, codes, action);
26
+ }, [user]);
27
+ /**
28
+ * 모든 코드에 대한 권한이 있는지 확인
29
+ */
30
+ const canAll = useCallback((codes, action = 'read') => {
31
+ return hasAllPermissions(user, codes, action);
32
+ }, [user]);
33
+ /**
34
+ * 특정 역할인지 확인
35
+ */
36
+ const isRole = useCallback((roles) => {
37
+ return hasRole(user, roles);
38
+ }, [user]);
39
+ /**
40
+ * 관리자 여부
41
+ */
42
+ const isAdmin = useMemo(() => user?.role === 'admin', [user]);
43
+ /**
44
+ * 메뉴 권한 체크
45
+ */
46
+ const checkMenu = useCallback((permission) => {
47
+ return checkMenuPermission(user, permission);
48
+ }, [user]);
49
+ /**
50
+ * 메뉴 필터링
51
+ */
52
+ const filterMenus = useCallback((menus) => {
53
+ return filterMenusByPermission(menus, user);
54
+ }, [user]);
55
+ /**
56
+ * 특정 경로 접근 가능 여부
57
+ */
58
+ const canAccess = useCallback((menus, path) => {
59
+ return canAccessPath(menus, path, user);
60
+ }, [user]);
61
+ return {
62
+ user,
63
+ can,
64
+ canAny,
65
+ canAll,
66
+ isRole,
67
+ isAdmin,
68
+ checkMenu,
69
+ filterMenus,
70
+ canAccess,
71
+ };
72
+ }
73
+ export default usePermission;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * useRecentMenu Hook
3
+ * 최근 방문 메뉴 상태 저장/복구
4
+ */
5
+ import { RecentMenu } from '../types';
6
+ export interface UseRecentMenuOptions {
7
+ /** 자동으로 현재 경로를 최근 메뉴에 추가할지 여부 */
8
+ autoTrack?: boolean;
9
+ /** 제외할 경로 패턴 */
10
+ excludePaths?: (string | RegExp)[];
11
+ /** 메뉴 제목 생성 함수 */
12
+ getTitleFromPath?: (pathname: string) => string;
13
+ }
14
+ /**
15
+ * useRecentMenu Hook
16
+ */
17
+ export declare function useRecentMenu(options?: UseRecentMenuOptions): {
18
+ /** 최근 메뉴 목록 */
19
+ list: RecentMenu[];
20
+ /** 현재 활성 메뉴 */
21
+ current: RecentMenu | null;
22
+ /** 현재 활성 메뉴 ID */
23
+ currentId: string;
24
+ /** 메뉴 추가 */
25
+ add: (menu: Omit<RecentMenu, "id"> & {
26
+ id?: string;
27
+ }) => void;
28
+ /** 메뉴 제거 */
29
+ remove: (id: string) => void;
30
+ /** 현재 메뉴 설정 */
31
+ setCurrent: (id: string) => void;
32
+ /** 메뉴로 이동 */
33
+ goTo: (id: string) => void;
34
+ /** 메뉴 상태 업데이트 */
35
+ updateState: (id: string, state: any) => void;
36
+ /** 메뉴 데이터 업데이트 */
37
+ updateData: (id: string, data: any) => void;
38
+ /** 현재 메뉴 상태 업데이트 */
39
+ updateCurrentState: (state: any) => void;
40
+ /** 모든 메뉴 닫기 */
41
+ closeAll: () => void;
42
+ /** 다른 메뉴 모두 닫기 */
43
+ closeOthers: () => void;
44
+ };
45
+ export default useRecentMenu;
46
+ //# sourceMappingURL=use-recent-menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-recent-menu.d.ts","sourceRoot":"","sources":["../../src/hooks/use-recent-menu.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,WAAW,oBAAoB;IACnC,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB;IAChB,YAAY,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACnC,kBAAkB;IAClB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CACjD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB;IAoL5D,eAAe;;IAEf,eAAe;;IAEf,kBAAkB;;IAElB,YAAY;gBArGL,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE;IAuG/C,YAAY;iBA5FP,MAAM;IA8FX,eAAe;qBApFV,MAAM;IAsFX,aAAa;eA5ER,MAAM;IA8EX,iBAAiB;sBAzDZ,MAAM,SAAS,GAAG;IA2DvB,kBAAkB;qBAjDb,MAAM,QAAQ,GAAG;IAmDtB,oBAAoB;gCAzCZ,GAAG;IA2CX,eAAe;;IAEf,kBAAkB;;EAGrB;AAED,eAAe,aAAa,CAAC"}