@sonhoseong/mfa-lib 1.3.7 → 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 (162) 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.d.ts +2 -0
  27. package/dist/components/logo/Logo.d.ts.map +1 -1
  28. package/dist/components/logo/Logo.js +13 -4
  29. package/dist/components/modal/ModalContainer.js +17 -8
  30. package/dist/components/modal/ModalContext.js +2 -3
  31. package/dist/components/navigation/AppNavbar.js +21 -9
  32. package/dist/components/navigation/AppSidebar.css +58 -3
  33. package/dist/components/navigation/AppSidebar.d.ts +1 -1
  34. package/dist/components/navigation/AppSidebar.d.ts.map +1 -1
  35. package/dist/components/navigation/AppSidebar.js +58 -15
  36. package/dist/components/navigation/Footer.d.ts +15 -0
  37. package/dist/components/navigation/Footer.d.ts.map +1 -0
  38. package/dist/components/navigation/Footer.js +12 -0
  39. package/dist/components/navigation/Header.d.ts.map +1 -1
  40. package/dist/components/navigation/Header.js +17 -4
  41. package/dist/components/navigation/Lnb.d.ts +2 -7
  42. package/dist/components/navigation/Lnb.d.ts.map +1 -1
  43. package/dist/components/navigation/Lnb.js +34 -6
  44. package/dist/components/navigation/StickyNav.js +19 -11
  45. package/dist/components/navigation/index.d.ts +1 -0
  46. package/dist/components/navigation/index.d.ts.map +1 -1
  47. package/dist/components/navigation/index.js +1 -0
  48. package/dist/components/page/LoginPage.d.ts +4 -1
  49. package/dist/components/page/LoginPage.d.ts.map +1 -1
  50. package/dist/components/page/LoginPage.js +146 -21
  51. package/dist/components/remote/RemoteErrorBoundary.d.ts +28 -0
  52. package/dist/components/remote/RemoteErrorBoundary.d.ts.map +1 -0
  53. package/dist/components/remote/RemoteErrorBoundary.js +44 -0
  54. package/dist/components/remote/RemoteErrorFallback.d.ts +16 -0
  55. package/dist/components/remote/RemoteErrorFallback.d.ts.map +1 -0
  56. package/dist/components/remote/RemoteErrorFallback.js +76 -0
  57. package/dist/components/remote/index.d.ts +8 -0
  58. package/dist/components/remote/index.d.ts.map +1 -0
  59. package/dist/components/remote/index.js +5 -0
  60. package/dist/components/router/BrowserRouter.d.ts +13 -0
  61. package/dist/components/router/BrowserRouter.d.ts.map +1 -0
  62. package/dist/components/router/BrowserRouter.js +17 -0
  63. package/dist/components/router/RouteGuard.d.ts +79 -0
  64. package/dist/components/router/RouteGuard.d.ts.map +1 -0
  65. package/dist/components/router/RouteGuard.js +86 -0
  66. package/dist/components/router/index.d.ts +4 -0
  67. package/dist/components/router/index.d.ts.map +1 -0
  68. package/dist/components/router/index.js +2 -0
  69. package/dist/components/toast/ToastContainer.js +17 -6
  70. package/dist/components/toast/ToastContext.js +2 -3
  71. package/dist/hooks/index.d.ts +9 -1
  72. package/dist/hooks/index.d.ts.map +1 -1
  73. package/dist/hooks/index.js +15 -1
  74. package/dist/hooks/use-auth.d.ts +2 -1
  75. package/dist/hooks/use-auth.d.ts.map +1 -1
  76. package/dist/hooks/use-auth.js +19 -18
  77. package/dist/hooks/use-debounce.d.ts +56 -0
  78. package/dist/hooks/use-debounce.d.ts.map +1 -0
  79. package/dist/hooks/use-debounce.js +140 -0
  80. package/dist/hooks/use-effect-once.d.ts +77 -0
  81. package/dist/hooks/use-effect-once.d.ts.map +1 -0
  82. package/dist/hooks/use-effect-once.js +124 -0
  83. package/dist/hooks/use-error-notification.d.ts +1 -1
  84. package/dist/hooks/use-error-notification.js +1 -1
  85. package/dist/hooks/use-global-loading.d.ts +1 -1
  86. package/dist/hooks/use-global-loading.js +1 -1
  87. package/dist/hooks/use-initialize.d.ts +8 -1
  88. package/dist/hooks/use-initialize.d.ts.map +1 -1
  89. package/dist/hooks/use-initialize.js +126 -23
  90. package/dist/hooks/use-modal.d.ts +21 -5
  91. package/dist/hooks/use-modal.d.ts.map +1 -1
  92. package/dist/hooks/use-modal.js +57 -17
  93. package/dist/hooks/use-navigate.d.ts +1 -1
  94. package/dist/hooks/use-navigate.js +1 -1
  95. package/dist/hooks/use-network-status.d.ts +15 -0
  96. package/dist/hooks/use-network-status.d.ts.map +1 -0
  97. package/dist/hooks/use-network-status.js +49 -0
  98. package/dist/hooks/use-permission.d.ts +22 -0
  99. package/dist/hooks/use-permission.d.ts.map +1 -0
  100. package/dist/hooks/use-permission.js +73 -0
  101. package/dist/hooks/use-recent-menu.d.ts +46 -0
  102. package/dist/hooks/use-recent-menu.d.ts.map +1 -0
  103. package/dist/hooks/use-recent-menu.js +169 -0
  104. package/dist/hooks/use-scroll-restoration.d.ts +51 -0
  105. package/dist/hooks/use-scroll-restoration.d.ts.map +1 -0
  106. package/dist/hooks/use-scroll-restoration.js +143 -0
  107. package/dist/hooks/use-supabase-auth.d.ts +49 -0
  108. package/dist/hooks/use-supabase-auth.d.ts.map +1 -0
  109. package/dist/hooks/use-supabase-auth.js +229 -0
  110. package/dist/hooks/use-track-history.d.ts +2 -1
  111. package/dist/hooks/use-track-history.d.ts.map +1 -1
  112. package/dist/hooks/use-track-history.js +14 -2
  113. package/dist/index.d.ts +1 -1
  114. package/dist/index.js +1 -1
  115. package/dist/network/axios-factory.d.ts +30 -1
  116. package/dist/network/axios-factory.d.ts.map +1 -1
  117. package/dist/network/axios-factory.js +192 -24
  118. package/dist/network/index.d.ts +3 -1
  119. package/dist/network/index.d.ts.map +1 -1
  120. package/dist/network/index.js +5 -1
  121. package/dist/network/supabase-client.d.ts +28 -0
  122. package/dist/network/supabase-client.d.ts.map +1 -0
  123. package/dist/network/supabase-client.js +46 -0
  124. package/dist/store/app-store.d.ts +222 -12
  125. package/dist/store/app-store.d.ts.map +1 -1
  126. package/dist/store/app-store.js +46 -29
  127. package/dist/store/index.d.ts +2 -0
  128. package/dist/store/index.d.ts.map +1 -1
  129. package/dist/store/index.js +3 -0
  130. package/dist/store/menu-slice.d.ts +96 -0
  131. package/dist/store/menu-slice.d.ts.map +1 -0
  132. package/dist/store/menu-slice.js +98 -0
  133. package/dist/store/recent-menu-slice.d.ts +209 -0
  134. package/dist/store/recent-menu-slice.d.ts.map +1 -0
  135. package/dist/store/recent-menu-slice.js +110 -0
  136. package/dist/store/store-access.d.ts +1 -1
  137. package/dist/store/store-access.js +1 -1
  138. package/dist/types/index.d.ts +74 -17
  139. package/dist/types/index.d.ts.map +1 -1
  140. package/dist/types/service.d.ts +1 -1
  141. package/dist/types/service.js +1 -1
  142. package/dist/utils/classnames.d.ts +65 -0
  143. package/dist/utils/classnames.d.ts.map +1 -0
  144. package/dist/utils/classnames.js +98 -0
  145. package/dist/utils/formatter.d.ts +78 -0
  146. package/dist/utils/formatter.d.ts.map +1 -0
  147. package/dist/utils/formatter.js +216 -0
  148. package/dist/utils/index.d.ts +5 -0
  149. package/dist/utils/index.d.ts.map +1 -1
  150. package/dist/utils/index.js +5 -0
  151. package/dist/utils/permission.d.ts +33 -0
  152. package/dist/utils/permission.d.ts.map +1 -0
  153. package/dist/utils/permission.js +132 -0
  154. package/dist/utils/query-string.d.ts +67 -0
  155. package/dist/utils/query-string.d.ts.map +1 -0
  156. package/dist/utils/query-string.js +136 -0
  157. package/dist/utils/storage.d.ts +1 -1
  158. package/dist/utils/storage.js +1 -1
  159. package/dist/utils/validation.d.ts +98 -0
  160. package/dist/utils/validation.d.ts.map +1 -0
  161. package/dist/utils/validation.js +260 -0
  162. package/package.json +5 -3
@@ -1,9 +1,8 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
1
  /**
3
2
  * Modal Context - KOMCA 패턴
4
3
  * 전역 모달 상태 관리
5
4
  */
6
- import { createContext, useContext, useCallback, useState } from 'react';
5
+ import React, { createContext, useContext, useCallback, useState } from 'react';
7
6
  const ModalContext = createContext(null);
8
7
  /**
9
8
  * Modal Provider
@@ -63,7 +62,7 @@ export const ModalProvider = ({ children }) => {
63
62
  ]);
64
63
  });
65
64
  }, []);
66
- return (_jsx(ModalContext.Provider, { value: { modals, alert, confirm, openModal, closeModal, closeAll }, children: children }));
65
+ return (React.createElement(ModalContext.Provider, { value: { modals, alert, confirm, openModal, closeModal, closeAll } }, children));
67
66
  };
68
67
  /**
69
68
  * useModal Hook
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import React from 'react';
2
2
  /**
3
3
  * 공통 네비게이션 바 - KOMCA 패턴
4
4
  * Remote 앱 단독 실행 시 사용
@@ -60,13 +60,25 @@ export const AppNavbar = ({ appName = '앱', homePath = '/', adminPath = '/admin
60
60
  background: 'rgba(14, 165, 233, 0.1)',
61
61
  },
62
62
  };
63
- return (_jsx("nav", { style: styles.navbar, children: _jsxs("div", { style: styles.inner, children: [_jsxs("button", { style: styles.logoLink, onClick: () => onNavigate(homePath), children: [_jsx("svg", { viewBox: "0 0 48 48", width: "24", height: "24", fill: "none", children: _jsx("path", { d: "M 8 40 L 24 8 L 40 40", stroke: "#1E3A5F", strokeWidth: "8", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }) }), _jsx("span", { children: appName })] }), _jsxs("div", { style: styles.links, children: [_jsx("button", { style: styles.link, onClick: () => onNavigate(homePath), onMouseOver: (e) => (e.currentTarget.style.background = '#F8FAFC'), onMouseOut: (e) => (e.currentTarget.style.background = 'transparent'), children: "\uD648" }), extraLinks.map((link) => (_jsx("button", { style: {
64
- ...styles.link,
65
- ...(link.isActive ? styles.linkActive : {}),
66
- }, onClick: () => onNavigate(link.path), children: link.label }, link.path))), isAuthenticated && (_jsx("button", { style: styles.link, onClick: () => onNavigate(adminPath), onMouseOver: (e) => (e.currentTarget.style.background = '#F8FAFC'), onMouseOut: (e) => (e.currentTarget.style.background = 'transparent'), children: "\uAD00\uB9AC" })), !isAuthenticated ? (_jsx("button", { style: {
67
- ...styles.link,
68
- background: '#1E3A5F',
69
- color: 'white',
70
- }, onClick: () => onNavigate(loginPath), onMouseOver: (e) => (e.currentTarget.style.background = '#0EA5E9'), onMouseOut: (e) => (e.currentTarget.style.background = '#1E3A5F'), children: "\uB85C\uADF8\uC778" })) : (_jsxs("button", { style: styles.link, onClick: onLogout, onMouseOver: (e) => (e.currentTarget.style.background = '#F8FAFC'), onMouseOut: (e) => (e.currentTarget.style.background = 'transparent'), children: ["\uB85C\uADF8\uC544\uC6C3 ", userName && `(${userName})`] }))] })] }) }));
63
+ return (React.createElement("nav", { style: styles.navbar },
64
+ React.createElement("div", { style: styles.inner },
65
+ React.createElement("button", { style: styles.logoLink, onClick: () => onNavigate(homePath) },
66
+ React.createElement("svg", { viewBox: "0 0 48 48", width: "24", height: "24", fill: "none" },
67
+ React.createElement("path", { d: "M 8 40 L 24 8 L 40 40", stroke: "#1E3A5F", strokeWidth: "8", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" })),
68
+ React.createElement("span", null, appName)),
69
+ React.createElement("div", { style: styles.links },
70
+ React.createElement("button", { style: styles.link, onClick: () => onNavigate(homePath), onMouseOver: (e) => (e.currentTarget.style.background = '#F8FAFC'), onMouseOut: (e) => (e.currentTarget.style.background = 'transparent') }, "\uD648"),
71
+ extraLinks.map((link) => (React.createElement("button", { key: link.path, style: {
72
+ ...styles.link,
73
+ ...(link.isActive ? styles.linkActive : {}),
74
+ }, onClick: () => onNavigate(link.path) }, link.label))),
75
+ isAuthenticated && (React.createElement("button", { style: styles.link, onClick: () => onNavigate(adminPath), onMouseOver: (e) => (e.currentTarget.style.background = '#F8FAFC'), onMouseOut: (e) => (e.currentTarget.style.background = 'transparent') }, "\uAD00\uB9AC")),
76
+ !isAuthenticated ? (React.createElement("button", { style: {
77
+ ...styles.link,
78
+ background: '#1E3A5F',
79
+ color: 'white',
80
+ }, onClick: () => onNavigate(loginPath), onMouseOver: (e) => (e.currentTarget.style.background = '#0EA5E9'), onMouseOut: (e) => (e.currentTarget.style.background = '#1E3A5F') }, "\uB85C\uADF8\uC778")) : (React.createElement("button", { style: styles.link, onClick: onLogout, onMouseOver: (e) => (e.currentTarget.style.background = '#F8FAFC'), onMouseOut: (e) => (e.currentTarget.style.background = 'transparent') },
81
+ "\uB85C\uADF8\uC544\uC6C3 ",
82
+ userName && `(${userName})`))))));
71
83
  };
72
84
  export default AppNavbar;
@@ -60,15 +60,49 @@
60
60
  color: var(--color-accent, #0EA5E9);
61
61
  }
62
62
 
63
+ /* Toggle Button - 우측 상단 고정 */
64
+ .sidebar-toggle-btn {
65
+ position: absolute;
66
+ top: 16px;
67
+ right: 16px;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ width: 32px;
72
+ height: 32px;
73
+ background: transparent;
74
+ border: 1px solid var(--color-border, #E2E8F0);
75
+ border-radius: 8px;
76
+ cursor: pointer;
77
+ color: var(--color-text-secondary, #64748B);
78
+ transition: all 0.2s ease;
79
+ z-index: 10;
80
+ }
81
+
82
+ .sidebar-toggle-btn:hover {
83
+ background: var(--color-bg-secondary, #F8FAFC);
84
+ border-color: var(--color-accent, #0EA5E9);
85
+ color: var(--color-accent, #0EA5E9);
86
+ }
87
+
88
+ .app-sidebar.collapsed .sidebar-toggle-btn {
89
+ right: 50%;
90
+ transform: translateX(50%);
91
+ }
92
+
63
93
  /* Sidebar Header */
64
94
  .sidebar-header {
95
+ display: flex;
96
+ align-items: center;
65
97
  padding: 20px;
98
+ padding-right: 56px; /* 토글 버튼 공간 확보 */
66
99
  border-bottom: 1px solid var(--color-border, #E2E8F0);
67
100
  transition: padding 0.3s ease;
68
101
  }
69
102
 
70
103
  .app-sidebar.collapsed .sidebar-header {
71
- padding: 16px 12px;
104
+ padding: 60px 12px 16px; /* 토글 버튼 아래로 */
105
+ justify-content: center;
72
106
  }
73
107
 
74
108
  .sidebar-logo {
@@ -221,9 +255,30 @@
221
255
  transition: justify-content 0.3s ease;
222
256
  }
223
257
 
224
- .sidebar-user.collapsed {
258
+ .sidebar-footer.collapsed {
259
+ display: flex;
225
260
  justify-content: center;
226
- padding: 8px;
261
+ }
262
+
263
+ /* 접힌 상태 로그아웃 버튼 */
264
+ .sidebar-logout-btn-collapsed {
265
+ width: 44px;
266
+ height: 44px;
267
+ display: flex;
268
+ align-items: center;
269
+ justify-content: center;
270
+ background: transparent;
271
+ border: 1px solid var(--color-border, #E2E8F0);
272
+ border-radius: 10px;
273
+ color: var(--color-text-muted, #94A3B8);
274
+ cursor: pointer;
275
+ transition: all 0.2s ease;
276
+ }
277
+
278
+ .sidebar-logout-btn-collapsed:hover {
279
+ background: #FEE2E2;
280
+ border-color: #FECACA;
281
+ color: #DC2626;
227
282
  }
228
283
 
229
284
  .sidebar-user-avatar {
@@ -36,6 +36,6 @@ export interface AppSidebarProps {
36
36
  /** 접힘 상태 변경 콜백 */
37
37
  onCollapsedChange?: (collapsed: boolean) => void;
38
38
  }
39
- export declare function AppSidebar({ appName, isAuthenticated, userName, userEmail, menuItems, onLogout, onNavigate, currentPath, logo, collapsed: controlledCollapsed, onCollapsedChange, }: AppSidebarProps): import("react/jsx-runtime").JSX.Element;
39
+ export declare function AppSidebar({ appName, isAuthenticated, userName, userEmail, menuItems, onLogout, onNavigate, currentPath, logo, collapsed: controlledCollapsed, onCollapsedChange, }: AppSidebarProps): React.JSX.Element;
40
40
  export default AppSidebar;
41
41
  //# sourceMappingURL=AppSidebar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/AppSidebar.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC5B,WAAW;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa;IACb,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa;IACb,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,eAAe;IACf,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,gBAAgB;IAChB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,YAAY;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa;IACb,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,oBAAoB;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB;IAClB,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD;AAED,wBAAgB,UAAU,CAAC,EACvB,OAAe,EACf,eAAuB,EACvB,QAAQ,EACR,SAAS,EACT,SAAc,EACd,QAAQ,EACR,UAAU,EACV,WAAiB,EACjB,IAAI,EACJ,SAAS,EAAE,mBAAmB,EAC9B,iBAAiB,GACpB,EAAE,eAAe,2CAwLjB;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"AppSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/AppSidebar.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC5B,WAAW;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa;IACb,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa;IACb,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,eAAe;IACf,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,gBAAgB;IAChB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,YAAY;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa;IACb,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,oBAAoB;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB;IAClB,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD;AAED,wBAAgB,UAAU,CAAC,EACvB,OAAe,EACf,eAAuB,EACvB,QAAQ,EACR,SAAS,EACT,SAAc,EACd,QAAQ,EACR,UAAU,EACV,WAAiB,EACjB,IAAI,EACJ,SAAS,EAAE,mBAAmB,EAC9B,iBAAiB,GACpB,EAAE,eAAe,qBAwLjB;AAED,eAAe,UAAU,CAAC"}
@@ -1,11 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  /**
3
2
  * AppSidebar - KOMCA 패턴
4
3
  *
5
4
  * 사이드바 네비게이션 컴포넌트
6
5
  * Remote 앱 단독 실행 시 사용
7
6
  */
8
- import { useState, useCallback } from 'react';
7
+ import React, { useState, useCallback } from 'react';
9
8
  export function AppSidebar({ appName = 'MFA', isAuthenticated = false, userName, userEmail, menuItems = [], onLogout, onNavigate, currentPath = '/', logo, collapsed: controlledCollapsed, onCollapsedChange, }) {
10
9
  const [expandedMenus, setExpandedMenus] = useState(new Set());
11
10
  const [internalCollapsed, setInternalCollapsed] = useState(false);
@@ -35,19 +34,63 @@ export function AppSidebar({ appName = 'MFA', isAuthenticated = false, userName,
35
34
  const hasChildren = item.children && item.children.length > 0;
36
35
  const isExpanded = expandedMenus.has(item.id);
37
36
  const isActive = item.path === currentPath;
38
- return (_jsxs("li", { className: "sidebar-menu-item", children: [_jsxs("button", { className: `sidebar-menu-btn ${isActive ? 'active' : ''} ${depth > 0 ? 'child' : ''}`, onClick: () => {
39
- if (hasChildren) {
40
- toggleMenu(item.id);
41
- }
42
- else if (item.path) {
43
- handleNavigate(item.path);
44
- }
45
- }, title: collapsed ? item.title : undefined, children: [item.icon && _jsx("span", { className: "sidebar-menu-icon", children: item.icon }), !collapsed && _jsx("span", { className: "sidebar-menu-title", children: item.title }), !collapsed && hasChildren && (_jsx("svg", { className: `sidebar-menu-arrow ${isExpanded ? 'expanded' : ''}`, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "m9 18 6-6-6-6" }) }))] }), !collapsed && hasChildren && isExpanded && (_jsx("ul", { className: "sidebar-submenu", children: item.children.map(child => renderMenuItem(child, depth + 1)) }))] }, item.id));
37
+ return (React.createElement("li", { key: item.id, className: "sidebar-menu-item" },
38
+ React.createElement("button", { className: `sidebar-menu-btn ${isActive ? 'active' : ''} ${depth > 0 ? 'child' : ''}`, onClick: () => {
39
+ if (hasChildren) {
40
+ toggleMenu(item.id);
41
+ }
42
+ else if (item.path) {
43
+ handleNavigate(item.path);
44
+ }
45
+ }, title: collapsed ? item.title : undefined },
46
+ item.icon && React.createElement("span", { className: "sidebar-menu-icon" }, item.icon),
47
+ !collapsed && React.createElement("span", { className: "sidebar-menu-title" }, item.title),
48
+ !collapsed && hasChildren && (React.createElement("svg", { className: `sidebar-menu-arrow ${isExpanded ? 'expanded' : ''}`, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
49
+ React.createElement("path", { d: "m9 18 6-6-6-6" })))),
50
+ !collapsed && hasChildren && isExpanded && (React.createElement("ul", { className: "sidebar-submenu" }, item.children.map(child => renderMenuItem(child, depth + 1))))));
46
51
  };
47
- return (_jsxs("aside", { className: `app-sidebar ${collapsed ? 'collapsed' : ''}`, children: [_jsx("button", { className: "sidebar-toggle-btn", onClick: toggleCollapsed, title: collapsed ? '메뉴 열기' : '메뉴 닫기', children: _jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: collapsed ? (_jsx("path", { d: "M3 12h18M3 6h18M3 18h18" })) : (_jsx("path", { d: "M18 6L6 18M6 6l12 12" })) }) }), _jsx("div", { className: "sidebar-header", children: _jsxs("button", { className: "sidebar-logo", onClick: () => handleNavigate('/'), children: [logo || (_jsxs("svg", { viewBox: "0 0 48 48", fill: "none", width: "32", height: "32", children: [_jsx("rect", { x: "20", y: "2", width: "8", height: "16", rx: "4", fill: "#0EA5E9" }), _jsx("rect", { x: "6", y: "16", width: "36", height: "6", rx: "3", fill: "#0EA5E9" }), _jsx("ellipse", { cx: "24", cy: "36", rx: "18", ry: "12", fill: "#0EA5E9" }), _jsx("ellipse", { cx: "17", cy: "36", rx: "4", ry: "6", fill: "#FFFFFF" }), _jsx("ellipse", { cx: "31", cy: "36", rx: "4", ry: "6", fill: "#FFFFFF" })] })), !collapsed && _jsx("span", { className: "sidebar-app-name", children: appName })] }) }), _jsx("nav", { className: "sidebar-nav", children: menuItems.length > 0 ? (_jsx("ul", { className: "sidebar-menu", children: menuItems.map(item => renderMenuItem(item)) })) : (_jsx("ul", { className: "sidebar-menu", children: _jsx("li", { className: "sidebar-menu-item", children: _jsxs("button", { className: `sidebar-menu-btn ${currentPath === '/' ? 'active' : ''}`, onClick: () => handleNavigate('/'), title: collapsed ? '홈' : undefined, children: [_jsx("span", { className: "sidebar-menu-icon", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), _jsx("polyline", { points: "9 22 9 12 15 12 15 22" })] }) }), !collapsed && _jsx("span", { className: "sidebar-menu-title", children: "\uD648" })] }) }) })) }), _jsx("div", { className: `sidebar-footer ${collapsed ? 'collapsed' : ''}`, children: isAuthenticated ? (collapsed ? (
48
- // 접힌 상태: 로그아웃 버튼만 표시
49
- _jsx("button", { className: "sidebar-logout-btn-collapsed", onClick: onLogout, title: "\uB85C\uADF8\uC544\uC6C3", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }), _jsx("polyline", { points: "16 17 21 12 16 7" }), _jsx("line", { x1: "21", y1: "12", x2: "9", y2: "12" })] }) })) : (
50
- // 펼친 상태: 전체 유저 정보 표시
51
- _jsxs("div", { className: "sidebar-user", children: [_jsx("div", { className: "sidebar-user-avatar", children: userName?.charAt(0).toUpperCase() || 'U' }), _jsxs("div", { className: "sidebar-user-info", children: [_jsx("span", { className: "sidebar-user-name", children: userName || '사용자' }), userEmail && _jsx("span", { className: "sidebar-user-email", children: userEmail })] }), _jsx("button", { className: "sidebar-logout-btn", onClick: onLogout, title: "\uB85C\uADF8\uC544\uC6C3", children: _jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }), _jsx("polyline", { points: "16 17 21 12 16 7" }), _jsx("line", { x1: "21", y1: "12", x2: "9", y2: "12" })] }) })] }))) : (_jsxs("button", { className: `sidebar-login-btn ${collapsed ? 'collapsed' : ''}`, onClick: () => collapsed ? toggleCollapsed() : handleNavigate('/login'), title: collapsed ? '로그인' : undefined, children: [_jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" }), _jsx("polyline", { points: "10 17 15 12 10 7" }), _jsx("line", { x1: "15", y1: "12", x2: "3", y2: "12" })] }), !collapsed && '로그인'] })) })] }));
52
+ return (React.createElement("aside", { className: `app-sidebar ${collapsed ? 'collapsed' : ''}` },
53
+ React.createElement("button", { className: "sidebar-toggle-btn", onClick: toggleCollapsed, title: collapsed ? '메뉴 열기' : '메뉴 닫기' },
54
+ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, collapsed ? (React.createElement("path", { d: "M3 12h18M3 6h18M3 18h18" })) : (React.createElement("path", { d: "M18 6L6 18M6 6l12 12" })))),
55
+ React.createElement("div", { className: "sidebar-header" },
56
+ React.createElement("button", { className: "sidebar-logo", onClick: () => handleNavigate('/') },
57
+ logo || (React.createElement("svg", { viewBox: "0 0 48 48", fill: "none", width: "32", height: "32" },
58
+ React.createElement("rect", { x: "20", y: "2", width: "8", height: "16", rx: "4", fill: "#0EA5E9" }),
59
+ React.createElement("rect", { x: "6", y: "16", width: "36", height: "6", rx: "3", fill: "#0EA5E9" }),
60
+ React.createElement("ellipse", { cx: "24", cy: "36", rx: "18", ry: "12", fill: "#0EA5E9" }),
61
+ React.createElement("ellipse", { cx: "17", cy: "36", rx: "4", ry: "6", fill: "#FFFFFF" }),
62
+ React.createElement("ellipse", { cx: "31", cy: "36", rx: "4", ry: "6", fill: "#FFFFFF" }))),
63
+ !collapsed && React.createElement("span", { className: "sidebar-app-name" }, appName))),
64
+ React.createElement("nav", { className: "sidebar-nav" }, menuItems.length > 0 ? (React.createElement("ul", { className: "sidebar-menu" }, menuItems.map(item => renderMenuItem(item)))) : (React.createElement("ul", { className: "sidebar-menu" },
65
+ React.createElement("li", { className: "sidebar-menu-item" },
66
+ React.createElement("button", { className: `sidebar-menu-btn ${currentPath === '/' ? 'active' : ''}`, onClick: () => handleNavigate('/'), title: collapsed ? '홈' : undefined },
67
+ React.createElement("span", { className: "sidebar-menu-icon" },
68
+ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
69
+ React.createElement("path", { d: "m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
70
+ React.createElement("polyline", { points: "9 22 9 12 15 12 15 22" }))),
71
+ !collapsed && React.createElement("span", { className: "sidebar-menu-title" }, "\uD648")))))),
72
+ React.createElement("div", { className: `sidebar-footer ${collapsed ? 'collapsed' : ''}` }, isAuthenticated ? (collapsed ? (
73
+ // 접힌 상태: 로그아웃 버튼만 표시
74
+ React.createElement("button", { className: "sidebar-logout-btn-collapsed", onClick: onLogout, title: "\uB85C\uADF8\uC544\uC6C3" },
75
+ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
76
+ React.createElement("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
77
+ React.createElement("polyline", { points: "16 17 21 12 16 7" }),
78
+ React.createElement("line", { x1: "21", y1: "12", x2: "9", y2: "12" })))) : (
79
+ // 펼친 상태: 전체 유저 정보 표시
80
+ React.createElement("div", { className: "sidebar-user" },
81
+ React.createElement("div", { className: "sidebar-user-avatar" }, userName?.charAt(0).toUpperCase() || 'U'),
82
+ React.createElement("div", { className: "sidebar-user-info" },
83
+ React.createElement("span", { className: "sidebar-user-name" }, userName || '사용자'),
84
+ userEmail && React.createElement("span", { className: "sidebar-user-email" }, userEmail)),
85
+ React.createElement("button", { className: "sidebar-logout-btn", onClick: onLogout, title: "\uB85C\uADF8\uC544\uC6C3" },
86
+ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
87
+ React.createElement("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
88
+ React.createElement("polyline", { points: "16 17 21 12 16 7" }),
89
+ React.createElement("line", { x1: "21", y1: "12", x2: "9", y2: "12" })))))) : (React.createElement("button", { className: `sidebar-login-btn ${collapsed ? 'collapsed' : ''}`, onClick: () => collapsed ? toggleCollapsed() : handleNavigate('/login'), title: collapsed ? '로그인' : undefined },
90
+ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
91
+ React.createElement("path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" }),
92
+ React.createElement("polyline", { points: "10 17 15 12 10 7" }),
93
+ React.createElement("line", { x1: "15", y1: "12", x2: "3", y2: "12" })),
94
+ !collapsed && '로그인')))));
52
95
  }
53
96
  export default AppSidebar;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Footer Component
3
+ */
4
+ import React from 'react';
5
+ export interface FooterProps {
6
+ appName?: string;
7
+ copyright?: string;
8
+ links?: {
9
+ label: string;
10
+ href: string;
11
+ }[];
12
+ }
13
+ export declare const Footer: React.FC<FooterProps>;
14
+ export default Footer;
15
+ //# sourceMappingURL=Footer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/Footer.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,WAAW;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7C;AAED,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA+BxC,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Footer Component
3
+ */
4
+ import React from 'react';
5
+ export const Footer = ({ appName = 'App', copyright, links = [], }) => {
6
+ const year = new Date().getFullYear();
7
+ return (React.createElement("footer", { className: "app-footer" },
8
+ React.createElement("div", { className: "app-footer-inner" },
9
+ React.createElement("div", { className: "app-footer-copyright" }, copyright || `© ${year} ${appName}. All rights reserved.`),
10
+ links.length > 0 && (React.createElement("nav", { className: "app-footer-links" }, links.map((link, index) => (React.createElement("a", { key: index, href: link.href, className: "app-footer-link", target: "_blank", rel: "noopener noreferrer" }, link.label))))))));
11
+ };
12
+ export default Footer;
@@ -1 +1 @@
1
- {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/Header.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA4CxC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/Header.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAkDxC,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1,11 +1,18 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ /**
2
+ * Header Component - KOMCA 패턴
3
+ *
4
+ * gnbItems만 받고 내부에서 navigate, logout 처리
5
+ */
6
+ import React from 'react';
2
7
  import { useNavigate } from 'react-router-dom';
3
8
  import { useSelector, useDispatch } from 'react-redux';
4
- import { logout } from '../../store/app-store';
9
+ import { logout, selectAccessToken, selectUser } from '../../store/app-store';
5
10
  export const Header = ({ gnbItems, appName = '앱', logo }) => {
6
11
  const navigate = useNavigate();
7
12
  const dispatch = useDispatch();
8
- const user = useSelector((state) => state.app?.user);
13
+ const accessToken = useSelector(selectAccessToken);
14
+ const user = useSelector(selectUser);
15
+ const isAuthenticated = !!accessToken;
9
16
  const handleNavigate = (path) => {
10
17
  navigate(path);
11
18
  };
@@ -13,6 +20,12 @@ export const Header = ({ gnbItems, appName = '앱', logo }) => {
13
20
  dispatch(logout());
14
21
  navigate('/');
15
22
  };
16
- return (_jsx("header", { className: "app-header", children: _jsxs("div", { className: "app-header-inner", children: [_jsx("div", { className: "app-header-logo", onClick: () => handleNavigate('/'), children: logo || appName }), _jsx("nav", { className: "app-header-nav", children: gnbItems.map((item) => (_jsx("button", { className: "app-header-nav-item", onClick: () => handleNavigate(item.path), children: item.title }, item.id))) }), _jsx("div", { className: "app-header-user", children: user && (_jsxs(_Fragment, { children: [_jsx("span", { className: "app-header-user-name", children: user.name || user.email }), _jsx("button", { className: "app-header-logout", onClick: handleLogout, children: "\uB85C\uADF8\uC544\uC6C3" })] })) })] }) }));
23
+ return (React.createElement("header", { className: "app-header" },
24
+ React.createElement("div", { className: "app-header-inner" },
25
+ React.createElement("div", { className: "app-header-logo", onClick: () => handleNavigate('/') }, logo || appName),
26
+ React.createElement("nav", { className: "app-header-nav" }, gnbItems.map((item) => (React.createElement("button", { key: item.id, className: "app-header-nav-item", onClick: () => handleNavigate(item.path) }, item.title)))),
27
+ React.createElement("div", { className: "app-header-user" }, isAuthenticated ? (React.createElement(React.Fragment, null,
28
+ React.createElement("span", { className: "app-header-user-name" }, user?.name || user?.email),
29
+ React.createElement("button", { className: "app-header-logout", onClick: handleLogout }, "\uB85C\uADF8\uC544\uC6C3"))) : (React.createElement("button", { className: "app-header-login", onClick: () => handleNavigate('/login') }, "\uB85C\uADF8\uC778"))))));
17
30
  };
18
31
  export default Header;
@@ -4,13 +4,8 @@
4
4
  * lnbItems만 받고 내부에서 navigate 처리
5
5
  */
6
6
  import React from 'react';
7
- export interface LnbMenuItem {
8
- id: string;
9
- title: string;
10
- path?: string;
11
- icon?: React.ReactNode;
12
- children?: Omit<LnbMenuItem, 'icon' | 'children'>[];
13
- }
7
+ import { LnbMenuItem } from '../../types';
8
+ export type { LnbMenuItem } from '../../types';
14
9
  export interface LnbProps {
15
10
  lnbItems: LnbMenuItem[];
16
11
  title?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Lnb.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/Lnb.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAKxC,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAkHlC,CAAC;AAEF,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"Lnb.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/Lnb.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAiD,MAAM,OAAO,CAAC;AAItE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAoIlC,CAAC;AAEF,eAAe,GAAG,CAAC"}
@@ -1,21 +1,21 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
1
  /**
3
2
  * Lnb (Left Navigation Bar) Component - KOMCA 패턴
4
3
  *
5
4
  * lnbItems만 받고 내부에서 navigate 처리
6
5
  */
7
- import { useState } from 'react';
6
+ import React, { useState, isValidElement, cloneElement } from 'react';
8
7
  import { useNavigate, useLocation } from 'react-router-dom';
9
8
  import { useSelector, useDispatch } from 'react-redux';
10
- import { logout } from '../../store/app-store';
9
+ import { logout, selectAccessToken, selectUser } from '../../store/app-store';
11
10
  export const Lnb = ({ lnbItems, title, appName, logo }) => {
12
11
  const navigate = useNavigate();
13
12
  const location = useLocation();
14
13
  const dispatch = useDispatch();
15
14
  const [expandedItems, setExpandedItems] = useState([]);
16
15
  const [collapsed, setCollapsed] = useState(false);
17
- const user = useSelector((state) => state.app?.user);
18
- const isAuthenticated = useSelector((state) => !!state.app?.accessToken);
16
+ const accessToken = useSelector(selectAccessToken);
17
+ const user = useSelector(selectUser);
18
+ const isAuthenticated = !!accessToken;
19
19
  const handleNavigate = (path) => {
20
20
  navigate(path);
21
21
  };
@@ -33,6 +33,34 @@ export const Lnb = ({ lnbItems, title, appName, logo }) => {
33
33
  return false;
34
34
  return location.pathname === path || location.pathname.startsWith(path + '/');
35
35
  };
36
- return (_jsxs("aside", { className: `app-lnb ${collapsed ? 'collapsed' : ''}`, children: [_jsxs("div", { className: "app-lnb-header", children: [(logo || appName) && (_jsx("div", { className: "app-lnb-logo", onClick: () => handleNavigate('/'), children: logo || appName })), title && !collapsed && _jsx("div", { className: "app-lnb-title", children: title }), _jsx("button", { className: "app-lnb-toggle", onClick: () => setCollapsed(!collapsed), children: collapsed ? '›' : '‹' })] }), _jsx("nav", { className: "app-lnb-nav", children: lnbItems.map((item) => (_jsx("div", { className: "app-lnb-item", children: item.children ? (_jsxs(_Fragment, { children: [_jsxs("button", { className: `app-lnb-item-btn ${expandedItems.includes(item.id) ? 'expanded' : ''}`, onClick: () => toggleExpand(item.id), children: [item.icon && _jsx("span", { className: "app-lnb-icon", children: item.icon }), !collapsed && _jsx("span", { className: "app-lnb-text", children: item.title }), !collapsed && (_jsx("span", { className: "app-lnb-arrow", children: expandedItems.includes(item.id) ? '▼' : '▶' }))] }), expandedItems.includes(item.id) && !collapsed && (_jsx("div", { className: "app-lnb-subitems", children: item.children.map((child) => (_jsx("button", { className: `app-lnb-subitem ${isActive(child.path) ? 'active' : ''}`, onClick: () => child.path && handleNavigate(child.path), children: child.title }, child.id))) }))] })) : (_jsxs("button", { className: `app-lnb-item-btn ${isActive(item.path) ? 'active' : ''}`, onClick: () => item.path && handleNavigate(item.path), children: [item.icon && _jsx("span", { className: "app-lnb-icon", children: item.icon }), !collapsed && _jsx("span", { className: "app-lnb-text", children: item.title })] })) }, item.id))) }), isAuthenticated && (_jsxs("div", { className: `app-lnb-footer ${collapsed ? 'collapsed' : ''}`, children: [_jsxs("div", { className: "app-lnb-user-section", children: [_jsx("div", { className: "app-lnb-avatar", children: user?.name?.charAt(0) || user?.email?.charAt(0) || '?' }), !collapsed && user && (_jsx("span", { className: "app-lnb-user-name", children: user.name || user.email }))] }), _jsx("button", { className: "app-lnb-logout-icon", onClick: handleLogout, title: "\uB85C\uADF8\uC544\uC6C3", children: _jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }), _jsx("polyline", { points: "16 17 21 12 16 7" }), _jsx("line", { x1: "21", y1: "12", x2: "9", y2: "12" })] }) })] }))] }));
36
+ return (React.createElement("aside", { className: `app-lnb ${collapsed ? 'collapsed' : ''}` },
37
+ React.createElement("div", { className: "app-lnb-header" },
38
+ (logo || appName) && (React.createElement("div", { className: "app-lnb-logo", onClick: () => handleNavigate('/') }, isValidElement(logo)
39
+ ? cloneElement(logo, { centerOnly: collapsed })
40
+ : (collapsed ? null : appName))),
41
+ title && !collapsed && React.createElement("div", { className: "app-lnb-title" }, title),
42
+ React.createElement("button", { className: "app-lnb-toggle", onClick: () => setCollapsed(!collapsed) }, collapsed ? '›' : '‹')),
43
+ React.createElement("nav", { className: "app-lnb-nav" }, lnbItems.map((item) => (React.createElement("div", { key: item.id, className: "app-lnb-item" }, item.children ? (React.createElement(React.Fragment, null,
44
+ React.createElement("button", { className: `app-lnb-item-btn ${expandedItems.includes(item.id) ? 'expanded' : ''}`, onClick: () => toggleExpand(item.id) },
45
+ item.icon && React.createElement("span", { className: "app-lnb-icon" }, item.icon),
46
+ !collapsed && React.createElement("span", { className: "app-lnb-text" }, item.title),
47
+ !collapsed && (React.createElement("span", { className: "app-lnb-arrow" }, expandedItems.includes(item.id) ? '▼' : '▶'))),
48
+ expandedItems.includes(item.id) && !collapsed && (React.createElement("div", { className: "app-lnb-subitems" }, item.children.map((child) => (React.createElement("button", { key: child.id, className: `app-lnb-subitem ${isActive(child.path) ? 'active' : ''}`, onClick: () => child.path && handleNavigate(child.path) }, child.title))))))) : (React.createElement("button", { className: `app-lnb-item-btn ${isActive(item.path) ? 'active' : ''}`, onClick: () => item.path && handleNavigate(item.path) },
49
+ item.icon && React.createElement("span", { className: "app-lnb-icon" }, item.icon),
50
+ !collapsed && React.createElement("span", { className: "app-lnb-text" }, item.title))))))),
51
+ React.createElement("div", { className: `app-lnb-footer ${collapsed ? 'collapsed' : ''}` }, isAuthenticated ? (React.createElement(React.Fragment, null,
52
+ React.createElement("div", { className: "app-lnb-user-section" },
53
+ React.createElement("div", { className: "app-lnb-avatar" }, user?.name?.charAt(0) || user?.email?.charAt(0) || '?'),
54
+ !collapsed && user && (React.createElement("span", { className: "app-lnb-user-name" }, user.name || user.email))),
55
+ React.createElement("button", { className: "app-lnb-logout-icon", onClick: handleLogout, title: "\uB85C\uADF8\uC544\uC6C3" },
56
+ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
57
+ React.createElement("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
58
+ React.createElement("polyline", { points: "16 17 21 12 16 7" }),
59
+ React.createElement("line", { x1: "21", y1: "12", x2: "9", y2: "12" }))))) : (React.createElement("button", { className: "app-lnb-login-btn", onClick: () => handleNavigate('/login') },
60
+ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
61
+ React.createElement("path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" }),
62
+ React.createElement("polyline", { points: "10 17 15 12 10 7" }),
63
+ React.createElement("line", { x1: "15", y1: "12", x2: "3", y2: "12" })),
64
+ !collapsed && React.createElement("span", null, "\uB85C\uADF8\uC778"))))));
37
65
  };
38
66
  export default Lnb;
@@ -1,5 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect, useCallback } from 'react';
1
+ import React, { useState, useEffect, useCallback } from 'react';
3
2
  export const StickyNav = ({ sections, triggerPoint = 0.2, scrollOffset = 80, topPosition = 20, onLogoClick, showLogo = true, className = '', updateHash = false }) => {
4
3
  const [activeSection, setActiveSection] = useState('');
5
4
  const [hoveredSection, setHoveredSection] = useState(null);
@@ -140,14 +139,23 @@ export const StickyNav = ({ sections, triggerPoint = 0.2, scrollOffset = 80, top
140
139
  boxShadow: '0 2px 8px rgba(30, 58, 95, 0.25)',
141
140
  },
142
141
  };
143
- return (_jsx("div", { style: styles.wrapper, className: `sticky-nav-wrapper ${className}`, children: _jsxs("nav", { style: styles.nav, className: "sticky-nav", children: [showLogo && (_jsxs("button", { style: styles.logoButton, onClick: handleLogoClick, className: "nav-logo-dots", "aria-label": "\uB9E8 \uC704\uB85C", children: [_jsx("span", { style: { ...styles.dot, background: '#3B82F6' }, className: "dot blue" }), _jsx("span", { style: { ...styles.dot, background: '#22C55E' }, className: "dot green" }), _jsx("span", { style: { ...styles.dot, background: '#F59E0B' }, className: "dot yellow" })] })), _jsx("ul", { style: styles.pillList, className: "nav-pills", children: sections.map((section) => {
144
- const isActive = activeSection === section.id;
145
- const isHovered = hoveredSection === section.id && !isActive;
146
- return (_jsx("li", { children: _jsxs("button", { style: {
147
- ...styles.pill,
148
- ...(isHovered ? styles.pillHover : {}),
149
- ...(isActive ? styles.pillActive : {}),
150
- }, className: `nav-pill ${isActive ? 'active' : ''}`, onClick: () => scrollToSection(section.id), onMouseEnter: () => setHoveredSection(section.id), onMouseLeave: () => setHoveredSection(null), children: [section.icon && _jsx("span", { style: { marginRight: '6px' }, children: section.icon }), section.label] }) }, section.id));
151
- }) })] }) }));
142
+ return (React.createElement("div", { style: styles.wrapper, className: `sticky-nav-wrapper ${className}` },
143
+ React.createElement("nav", { style: styles.nav, className: "sticky-nav" },
144
+ showLogo && (React.createElement("button", { style: styles.logoButton, onClick: handleLogoClick, className: "nav-logo-dots", "aria-label": "\uB9E8 \uC704\uB85C" },
145
+ React.createElement("span", { style: { ...styles.dot, background: '#3B82F6' }, className: "dot blue" }),
146
+ React.createElement("span", { style: { ...styles.dot, background: '#22C55E' }, className: "dot green" }),
147
+ React.createElement("span", { style: { ...styles.dot, background: '#F59E0B' }, className: "dot yellow" }))),
148
+ React.createElement("ul", { style: styles.pillList, className: "nav-pills" }, sections.map((section) => {
149
+ const isActive = activeSection === section.id;
150
+ const isHovered = hoveredSection === section.id && !isActive;
151
+ return (React.createElement("li", { key: section.id },
152
+ React.createElement("button", { style: {
153
+ ...styles.pill,
154
+ ...(isHovered ? styles.pillHover : {}),
155
+ ...(isActive ? styles.pillActive : {}),
156
+ }, className: `nav-pill ${isActive ? 'active' : ''}`, onClick: () => scrollToSection(section.id), onMouseEnter: () => setHoveredSection(section.id), onMouseLeave: () => setHoveredSection(null) },
157
+ section.icon && React.createElement("span", { style: { marginRight: '6px' } }, section.icon),
158
+ section.label)));
159
+ })))));
152
160
  };
153
161
  export default StickyNav;
@@ -3,4 +3,5 @@ export * from './AppNavbar';
3
3
  export * from './AppSidebar';
4
4
  export * from './Header';
5
5
  export * from './Lnb';
6
+ export * from './Footer';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC"}
@@ -3,3 +3,4 @@ export * from './AppNavbar';
3
3
  export * from './AppSidebar';
4
4
  export * from './Header';
5
5
  export * from './Lnb';
6
+ export * from './Footer';
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * 공통 로그인 페이지 컴포넌트
5
5
  * Host/Remote 모두에서 사용 가능
6
+ * Supabase Auth 지원
6
7
  */
7
8
  import React from 'react';
8
9
  import { User } from '../../types';
@@ -23,7 +24,9 @@ export interface LoginPageProps {
23
24
  }>;
24
25
  /** 테스트 계정 표시 여부 */
25
26
  showTestAccount?: boolean;
27
+ /** Supabase Auth 사용 여부 (기본: true) */
28
+ useSupabase?: boolean;
26
29
  }
27
- export declare function LoginPage({ redirectPath, onLoginSuccess, appName, logo, onGoogleLogin, showTestAccount, }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
30
+ export declare function LoginPage({ redirectPath, onLoginSuccess, appName, logo, onGoogleLogin, showTestAccount, useSupabase, }: LoginPageProps): React.JSX.Element;
28
31
  export default LoginPage;
29
32
  //# sourceMappingURL=LoginPage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoginPage.d.ts","sourceRoot":"","sources":["../../../src/components/page/LoginPage.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,iBAAiB,CAAC;AAEzB,MAAM,WAAW,cAAc;IAC3B,8BAA8B;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB;IAChB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACtC,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7D,mBAAmB;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,SAAS,CAAC,EACtB,YAAkB,EAClB,cAAc,EACd,OAAe,EACf,IAAI,EACJ,aAAa,EACb,eAAsB,GACzB,EAAE,cAAc,2CAgOhB;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"LoginPage.d.ts","sourceRoot":"","sources":["../../../src/components/page/LoginPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AAGrD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,OAAO,iBAAiB,CAAC;AAezB,MAAM,WAAW,cAAc;IAC3B,8BAA8B;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB;IAChB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACtC,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7D,mBAAmB;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qCAAqC;IACrC,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,SAAS,CAAC,EACtB,YAAkB,EAClB,cAAc,EACd,OAAe,EACf,IAAI,EACJ,aAAa,EACb,eAAuB,EACvB,WAAkB,GACrB,EAAE,cAAc,qBAwRhB;AAED,eAAe,SAAS,CAAC"}