sales-frontend-features 0.0.1

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.
package/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # Features 패키지
2
+
3
+ - Logic 중심의 리액트 컴포넌트
4
+ - UI 적인 부분은 제외
package/dist/index.cjs ADDED
@@ -0,0 +1,138 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var salesFrontendStores = require('sales-frontend-stores');
5
+ var salesFrontendUtils = require('sales-frontend-utils');
6
+ var method = require('sales-frontend-api/method');
7
+ var salesFrontendBridge = require('sales-frontend-bridge');
8
+ var jsxRuntime = require('react/jsx-runtime');
9
+
10
+ // src/client-session/use-setup-client-session.tsx
11
+ var getFormFactor = () => {
12
+ const { pathname } = location;
13
+ if (pathname) {
14
+ const segments = pathname.split("/").filter(Boolean);
15
+ if (segments.length >= 2) {
16
+ const secondSegment = segments[1]?.toLowerCase();
17
+ if (secondSegment === "mobile") {
18
+ return "phone";
19
+ }
20
+ if (secondSegment === "tablet") {
21
+ return "tablet";
22
+ }
23
+ if (secondSegment === "pc") {
24
+ return "pc";
25
+ }
26
+ }
27
+ }
28
+ return salesFrontendUtils.getFormFactorFromUserAgent(navigator.userAgent);
29
+ };
30
+ var createPureWebClientSession = async () => {
31
+ const hasAccessToken = !!salesFrontendUtils.getCookie("accessToken");
32
+ return {
33
+ isInitialized: true,
34
+ isLoggedIn: hasAccessToken,
35
+ isWebView: false,
36
+ acceptLanguage: navigator.language || "ko",
37
+ formFactor: getFormFactor(),
38
+ deviceId: salesFrontendUtils.getOrCreateDeviceId(),
39
+ appVersion: `${salesFrontendUtils.getBrowserName(navigator.userAgent)} ${salesFrontendUtils.getBrowserVersion(navigator.userAgent)}`,
40
+ loginType: "web-login",
41
+ platformName: salesFrontendUtils.getOSName(navigator.userAgent),
42
+ platformVersion: salesFrontendUtils.getOSVersion(navigator.userAgent),
43
+ deviceModel: salesFrontendUtils.getDeviceModel(),
44
+ loginChannel: salesFrontendUtils.isSalesPortal() ? "HMP" : "ETC"
45
+ };
46
+ };
47
+ var createWebviewClientSession = async () => {
48
+ const [appInfo, accessToken] = await Promise.allSettled([
49
+ salesFrontendBridge.Bridge.native.getAppInfo(),
50
+ salesFrontendUtils.isFpPlannerApp() ? Promise.resolve(salesFrontendUtils.getCookie("accessToken")) : salesFrontendBridge.Bridge.native.getAccessToken().then((res) => res.accessToken)
51
+ ]);
52
+ const appInfoData = appInfo.status === "fulfilled" ? appInfo.value : null;
53
+ const isLoggedIn = accessToken.status === "fulfilled" ? !!accessToken.value : false;
54
+ const formFactor = appInfoData?.["X-Channel-FormFactor"]?.toLowerCase() ?? salesFrontendUtils.getCookie("formFactor")?.toLowerCase() ?? getFormFactor();
55
+ const loginChannel = appInfoData?.["X-Channel-LoginChannel"] ?? salesFrontendUtils.getCookie("loginChannel") ?? "DSP";
56
+ return {
57
+ isInitialized: true,
58
+ isLoggedIn,
59
+ isWebView: true,
60
+ acceptLanguage: appInfoData?.["Accept-Language"] ?? "ko",
61
+ formFactor,
62
+ deviceId: appInfoData?.["X-Channel-DeviceId"] ?? salesFrontendUtils.getCookie("deviceId"),
63
+ appVersion: appInfoData?.["X-Channel-AppVersion"] ?? salesFrontendUtils.getCookie("appVersion"),
64
+ loginType: appInfoData?.["X-Channel-LoginType"] ?? salesFrontendUtils.getCookie("loginType"),
65
+ platformName: appInfoData?.["X-Channel-PlatformName"] ?? salesFrontendUtils.getCookie("platformName"),
66
+ platformVersion: appInfoData?.["X-Channel-PlatformVersion"] ?? salesFrontendUtils.getCookie("platformVersion"),
67
+ deviceModel: appInfoData?.["X-Channel-DeviceModel"] ?? salesFrontendUtils.getCookie("deviceModel"),
68
+ loginChannel
69
+ };
70
+ };
71
+ var getLoginUserInfo = async () => {
72
+ try {
73
+ const { isSuccess, data } = await method.getUserProfile();
74
+ if (isSuccess && data) {
75
+ console.log("[useSetupClientSession] \uD504\uB85C\uD544 \uB85C\uB4DC \uC131\uACF5");
76
+ return data;
77
+ }
78
+ console.warn("[getLoginUserInfo] \uD504\uB85C\uD544 \uB85C\uB4DC \uC2E4\uD328: \uC751\uB2F5 \uC5C6\uC74C");
79
+ return null;
80
+ } catch (error) {
81
+ console.error("[getLoginUserInfo] \uD504\uB85C\uD544 \uB85C\uB4DC \uC2E4\uD328:", error);
82
+ return null;
83
+ }
84
+ };
85
+
86
+ // src/client-session/use-setup-client-session.tsx
87
+ var useSetupClientSession = () => {
88
+ const { isInitialized, isLoggedIn } = salesFrontendStores.useClientSession();
89
+ const setClientSession = salesFrontendStores.useSetClientSession();
90
+ react.useEffect(() => {
91
+ if (isInitialized) {
92
+ console.log("[useSetupClientSession] Already initialized");
93
+ return;
94
+ }
95
+ const initialize = async () => {
96
+ try {
97
+ console.log("[useSetupClientSession] \uCD08\uAE30\uD654 \uC2DC\uC791...");
98
+ if (salesFrontendUtils.isWebView()) {
99
+ console.log("[useSetupClientSession] \uC6F9\uBDF0 \uD658\uACBD \uAC10\uC9C0");
100
+ setClientSession(await createWebviewClientSession());
101
+ } else {
102
+ console.log("[useSetupClientSession] \uC21C\uC218 \uC6F9 \uD658\uACBD \uAC10\uC9C0");
103
+ setClientSession(await createPureWebClientSession());
104
+ }
105
+ console.log("[useSetupClientSession] \uAE30\uBCF8 \uC138\uC158 \uC124\uC815 \uC644\uB8CC");
106
+ } catch (error) {
107
+ console.error("[useSetupClientSession] \uCD08\uAE30\uD654 \uC2E4\uD328:", error);
108
+ }
109
+ };
110
+ initialize();
111
+ }, [isInitialized, setClientSession]);
112
+ react.useEffect(() => {
113
+ if (!isInitialized || !isLoggedIn) {
114
+ return;
115
+ }
116
+ const loadUserInfo = async () => {
117
+ console.log("[useSetupClientSession] \uC0AC\uC6A9\uC790 \uD504\uB85C\uD544 \uB85C\uB4DC \uC2DC\uC791...");
118
+ const userInfo = await getLoginUserInfo();
119
+ if (userInfo) {
120
+ setClientSession(userInfo);
121
+ }
122
+ };
123
+ loadUserInfo();
124
+ }, [isInitialized, isLoggedIn, setClientSession]);
125
+ return isInitialized ?? false;
126
+ };
127
+ var ClientSessionProvider = ({ children, loadingComponent = null }) => {
128
+ const isInitialized = useSetupClientSession();
129
+ if (!isInitialized) {
130
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent });
131
+ }
132
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
133
+ };
134
+
135
+ exports.ClientSessionProvider = ClientSessionProvider;
136
+ exports.useSetupClientSession = useSetupClientSession;
137
+ //# sourceMappingURL=index.cjs.map
138
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client-session/client-session-utils.ts","../src/client-session/use-setup-client-session.tsx","../src/client-session/client-session-provider.tsx"],"names":["getFormFactorFromUserAgent","getCookie","getOrCreateDeviceId","getBrowserName","getBrowserVersion","getOSName","getOSVersion","getDeviceModel","isSalesPortal","Bridge","isFpPlannerApp","getUserProfile","useClientSession","useSetClientSession","useEffect","isWebView"],"mappings":"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","file":"index.cjs","sourcesContent":["import { getUserProfile } from 'sales-frontend-api/method';\nimport { Bridge } from 'sales-frontend-bridge';\nimport {\n getCookie,\n getOrCreateDeviceId,\n getBrowserName,\n getBrowserVersion,\n getOSName,\n getOSVersion,\n getDeviceModel,\n isSalesPortal,\n getFormFactorFromUserAgent,\n isFpPlannerApp\n} from 'sales-frontend-utils';\n\nimport type { ClientSession } from 'sales-frontend-stores';\n\n/**\n * URL 경로의 두 번째 세그먼트에서 폼팩터를 추출하거나 User Agent로 감지\n * @param pathname - URL 경로 (예: /aaa/mobile, /aaa/tablet, /aaa/pc)\n * @returns 'mobile' | 'tablet' | 'pc'\n */\nconst getFormFactor = (): 'phone' | 'tablet' | 'pc' | undefined => {\n const { pathname } = location;\n\n // pathname이 제공된 경우 URL에서 추출 시도\n if (pathname) {\n const segments = pathname.split('/').filter(Boolean);\n\n // 두 번째 세그먼트 확인 (인덱스 1)\n if (segments.length >= 2) {\n const secondSegment = segments[1]?.toLowerCase();\n\n if (secondSegment === 'mobile') {\n return 'phone';\n }\n if (secondSegment === 'tablet') {\n return 'tablet';\n }\n if (secondSegment === 'pc') {\n return 'pc';\n }\n }\n }\n\n // URL에 없으면 User Agent로 감지\n return getFormFactorFromUserAgent(navigator.userAgent);\n};\n\n/**\n * 순수 웹 상태값 초기화\n * @returns\n */\nexport const createPureWebClientSession = async (): Promise<ClientSession> => {\n const hasAccessToken = !!getCookie('accessToken');\n\n return {\n isInitialized: true,\n isLoggedIn: hasAccessToken,\n isWebView: false,\n acceptLanguage: navigator.language || 'ko',\n formFactor: getFormFactor(),\n deviceId: getOrCreateDeviceId(),\n appVersion: `${getBrowserName(navigator.userAgent)} ${getBrowserVersion(navigator.userAgent)}`,\n loginType: 'web-login',\n platformName: getOSName(navigator.userAgent),\n platformVersion: getOSVersion(navigator.userAgent),\n deviceModel: getDeviceModel(),\n loginChannel: isSalesPortal() ? 'HMP' : 'ETC'\n };\n};\n\n/**\n * 웹뷰 상태값 초기화\n * @returns\n */\nexport const createWebviewClientSession = async (): Promise<ClientSession> => {\n const [appInfo, accessToken] = await Promise.allSettled([\n Bridge.native.getAppInfo(),\n isFpPlannerApp()\n ? Promise.resolve(getCookie('accessToken'))\n : Bridge.native.getAccessToken().then((res) => res.accessToken)\n ]);\n\n const appInfoData = appInfo.status === 'fulfilled' ? appInfo.value : null;\n const isLoggedIn = accessToken.status === 'fulfilled' ? !!accessToken.value : false;\n\n const formFactor =\n appInfoData?.['X-Channel-FormFactor']?.toLowerCase() ?? getCookie('formFactor')?.toLowerCase() ?? getFormFactor();\n\n const loginChannel = appInfoData?.['X-Channel-LoginChannel'] ?? getCookie('loginChannel') ?? 'DSP';\n\n return {\n isInitialized: true,\n isLoggedIn,\n isWebView: true,\n acceptLanguage: appInfoData?.['Accept-Language'] ?? 'ko',\n formFactor: formFactor as 'tablet' | 'phone' | 'pc' | undefined,\n deviceId: appInfoData?.['X-Channel-DeviceId'] ?? getCookie('deviceId'),\n appVersion: appInfoData?.['X-Channel-AppVersion'] ?? getCookie('appVersion'),\n loginType: appInfoData?.['X-Channel-LoginType'] ?? getCookie('loginType'),\n platformName: appInfoData?.['X-Channel-PlatformName'] ?? getCookie('platformName'),\n platformVersion: appInfoData?.['X-Channel-PlatformVersion'] ?? getCookie('platformVersion'),\n deviceModel: appInfoData?.['X-Channel-DeviceModel'] ?? getCookie('deviceModel'),\n loginChannel: loginChannel as 'DSP' | 'MSP'\n };\n};\n\nexport const getLoginUserInfo = async () => {\n try {\n const { isSuccess, data } = await getUserProfile();\n\n if (isSuccess && data) {\n console.log('[useSetupClientSession] 프로필 로드 성공');\n\n return data;\n }\n\n console.warn('[getLoginUserInfo] 프로필 로드 실패: 응답 없음');\n\n return null;\n } catch (error) {\n console.error('[getLoginUserInfo] 프로필 로드 실패:', error);\n\n return null;\n }\n};\n","import { useEffect } from 'react';\n\nimport { useClientSession, useSetClientSession } from 'sales-frontend-stores';\nimport { isWebView } from 'sales-frontend-utils';\n\nimport { createWebviewClientSession, createPureWebClientSession, getLoginUserInfo } from './client-session-utils';\n\n/**\n * 앱 시작 시 ClientSession을 플랫폼(웹뷰, 순수 웹)에 맞게 자동 설정하는 훅\n *\n * @returns {boolean} 초기화 완료 여부\n */\nexport const useSetupClientSession = (): boolean => {\n const { isInitialized, isLoggedIn } = useClientSession();\n const setClientSession = useSetClientSession();\n\n useEffect(() => {\n if (isInitialized) {\n console.log('[useSetupClientSession] Already initialized');\n\n return;\n }\n\n const initialize = async () => {\n try {\n console.log('[useSetupClientSession] 초기화 시작...');\n\n if (isWebView()) {\n console.log('[useSetupClientSession] 웹뷰 환경 감지');\n setClientSession(await createWebviewClientSession());\n } else {\n console.log('[useSetupClientSession] 순수 웹 환경 감지');\n setClientSession(await createPureWebClientSession());\n }\n\n console.log('[useSetupClientSession] 기본 세션 설정 완료');\n } catch (error) {\n console.error('[useSetupClientSession] 초기화 실패:', error);\n }\n };\n\n // 기본 초기화\n initialize();\n }, [isInitialized, setClientSession]);\n\n useEffect(() => {\n if (!isInitialized || !isLoggedIn) {\n return;\n }\n\n const loadUserInfo = async () => {\n console.log('[useSetupClientSession] 사용자 프로필 로드 시작...');\n\n const userInfo = await getLoginUserInfo();\n if (userInfo) {\n setClientSession(userInfo);\n }\n };\n\n // 로그인 FP 사용자 불러오기\n loadUserInfo();\n }, [isInitialized, isLoggedIn, setClientSession]);\n\n // 초기화 완료 여부 반환\n return isInitialized ?? false;\n};\n","import { useSetupClientSession } from './use-setup-client-session';\n\ninterface ClientSessionProviderProps {\n children: React.ReactNode;\n /**\n * 초기화 중 표시할 로딩 컴포넌트\n */\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * ClientSession 초기화를 담당하는 Provider 컴포넌트\n * 앱의 최상위에서 사용하여 플랫폼별 세션 정보를 자동으로 설정합니다.\n */\nexport const ClientSessionProvider = ({ children, loadingComponent = null }: ClientSessionProviderProps) => {\n const isInitialized = useSetupClientSession();\n\n // 초기화 완료 전까지 fallback 표시\n if (!isInitialized) {\n return <>{loadingComponent}</>;\n }\n\n return <>{children}</>;\n};\n"]}
@@ -0,0 +1,23 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ /**
4
+ * 앱 시작 시 ClientSession을 플랫폼(웹뷰, 순수 웹)에 맞게 자동 설정하는 훅
5
+ *
6
+ * @returns {boolean} 초기화 완료 여부
7
+ */
8
+ declare const useSetupClientSession: () => boolean;
9
+
10
+ interface ClientSessionProviderProps {
11
+ children: React.ReactNode;
12
+ /**
13
+ * 초기화 중 표시할 로딩 컴포넌트
14
+ */
15
+ loadingComponent?: React.ReactNode;
16
+ }
17
+ /**
18
+ * ClientSession 초기화를 담당하는 Provider 컴포넌트
19
+ * 앱의 최상위에서 사용하여 플랫폼별 세션 정보를 자동으로 설정합니다.
20
+ */
21
+ declare const ClientSessionProvider: ({ children, loadingComponent }: ClientSessionProviderProps) => react_jsx_runtime.JSX.Element;
22
+
23
+ export { ClientSessionProvider, useSetupClientSession };
@@ -0,0 +1,23 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ /**
4
+ * 앱 시작 시 ClientSession을 플랫폼(웹뷰, 순수 웹)에 맞게 자동 설정하는 훅
5
+ *
6
+ * @returns {boolean} 초기화 완료 여부
7
+ */
8
+ declare const useSetupClientSession: () => boolean;
9
+
10
+ interface ClientSessionProviderProps {
11
+ children: React.ReactNode;
12
+ /**
13
+ * 초기화 중 표시할 로딩 컴포넌트
14
+ */
15
+ loadingComponent?: React.ReactNode;
16
+ }
17
+ /**
18
+ * ClientSession 초기화를 담당하는 Provider 컴포넌트
19
+ * 앱의 최상위에서 사용하여 플랫폼별 세션 정보를 자동으로 설정합니다.
20
+ */
21
+ declare const ClientSessionProvider: ({ children, loadingComponent }: ClientSessionProviderProps) => react_jsx_runtime.JSX.Element;
22
+
23
+ export { ClientSessionProvider, useSetupClientSession };
package/dist/index.js ADDED
@@ -0,0 +1,135 @@
1
+ import { useEffect } from 'react';
2
+ import { useClientSession, useSetClientSession } from 'sales-frontend-stores';
3
+ import { isWebView, isFpPlannerApp, getCookie, getDeviceModel, getOSVersion, getOSName, getBrowserName, getBrowserVersion, getOrCreateDeviceId, isSalesPortal, getFormFactorFromUserAgent } from 'sales-frontend-utils';
4
+ import { getUserProfile } from 'sales-frontend-api/method';
5
+ import { Bridge } from 'sales-frontend-bridge';
6
+ import { jsx, Fragment } from 'react/jsx-runtime';
7
+
8
+ // src/client-session/use-setup-client-session.tsx
9
+ var getFormFactor = () => {
10
+ const { pathname } = location;
11
+ if (pathname) {
12
+ const segments = pathname.split("/").filter(Boolean);
13
+ if (segments.length >= 2) {
14
+ const secondSegment = segments[1]?.toLowerCase();
15
+ if (secondSegment === "mobile") {
16
+ return "phone";
17
+ }
18
+ if (secondSegment === "tablet") {
19
+ return "tablet";
20
+ }
21
+ if (secondSegment === "pc") {
22
+ return "pc";
23
+ }
24
+ }
25
+ }
26
+ return getFormFactorFromUserAgent(navigator.userAgent);
27
+ };
28
+ var createPureWebClientSession = async () => {
29
+ const hasAccessToken = !!getCookie("accessToken");
30
+ return {
31
+ isInitialized: true,
32
+ isLoggedIn: hasAccessToken,
33
+ isWebView: false,
34
+ acceptLanguage: navigator.language || "ko",
35
+ formFactor: getFormFactor(),
36
+ deviceId: getOrCreateDeviceId(),
37
+ appVersion: `${getBrowserName(navigator.userAgent)} ${getBrowserVersion(navigator.userAgent)}`,
38
+ loginType: "web-login",
39
+ platformName: getOSName(navigator.userAgent),
40
+ platformVersion: getOSVersion(navigator.userAgent),
41
+ deviceModel: getDeviceModel(),
42
+ loginChannel: isSalesPortal() ? "HMP" : "ETC"
43
+ };
44
+ };
45
+ var createWebviewClientSession = async () => {
46
+ const [appInfo, accessToken] = await Promise.allSettled([
47
+ Bridge.native.getAppInfo(),
48
+ isFpPlannerApp() ? Promise.resolve(getCookie("accessToken")) : Bridge.native.getAccessToken().then((res) => res.accessToken)
49
+ ]);
50
+ const appInfoData = appInfo.status === "fulfilled" ? appInfo.value : null;
51
+ const isLoggedIn = accessToken.status === "fulfilled" ? !!accessToken.value : false;
52
+ const formFactor = appInfoData?.["X-Channel-FormFactor"]?.toLowerCase() ?? getCookie("formFactor")?.toLowerCase() ?? getFormFactor();
53
+ const loginChannel = appInfoData?.["X-Channel-LoginChannel"] ?? getCookie("loginChannel") ?? "DSP";
54
+ return {
55
+ isInitialized: true,
56
+ isLoggedIn,
57
+ isWebView: true,
58
+ acceptLanguage: appInfoData?.["Accept-Language"] ?? "ko",
59
+ formFactor,
60
+ deviceId: appInfoData?.["X-Channel-DeviceId"] ?? getCookie("deviceId"),
61
+ appVersion: appInfoData?.["X-Channel-AppVersion"] ?? getCookie("appVersion"),
62
+ loginType: appInfoData?.["X-Channel-LoginType"] ?? getCookie("loginType"),
63
+ platformName: appInfoData?.["X-Channel-PlatformName"] ?? getCookie("platformName"),
64
+ platformVersion: appInfoData?.["X-Channel-PlatformVersion"] ?? getCookie("platformVersion"),
65
+ deviceModel: appInfoData?.["X-Channel-DeviceModel"] ?? getCookie("deviceModel"),
66
+ loginChannel
67
+ };
68
+ };
69
+ var getLoginUserInfo = async () => {
70
+ try {
71
+ const { isSuccess, data } = await getUserProfile();
72
+ if (isSuccess && data) {
73
+ console.log("[useSetupClientSession] \uD504\uB85C\uD544 \uB85C\uB4DC \uC131\uACF5");
74
+ return data;
75
+ }
76
+ console.warn("[getLoginUserInfo] \uD504\uB85C\uD544 \uB85C\uB4DC \uC2E4\uD328: \uC751\uB2F5 \uC5C6\uC74C");
77
+ return null;
78
+ } catch (error) {
79
+ console.error("[getLoginUserInfo] \uD504\uB85C\uD544 \uB85C\uB4DC \uC2E4\uD328:", error);
80
+ return null;
81
+ }
82
+ };
83
+
84
+ // src/client-session/use-setup-client-session.tsx
85
+ var useSetupClientSession = () => {
86
+ const { isInitialized, isLoggedIn } = useClientSession();
87
+ const setClientSession = useSetClientSession();
88
+ useEffect(() => {
89
+ if (isInitialized) {
90
+ console.log("[useSetupClientSession] Already initialized");
91
+ return;
92
+ }
93
+ const initialize = async () => {
94
+ try {
95
+ console.log("[useSetupClientSession] \uCD08\uAE30\uD654 \uC2DC\uC791...");
96
+ if (isWebView()) {
97
+ console.log("[useSetupClientSession] \uC6F9\uBDF0 \uD658\uACBD \uAC10\uC9C0");
98
+ setClientSession(await createWebviewClientSession());
99
+ } else {
100
+ console.log("[useSetupClientSession] \uC21C\uC218 \uC6F9 \uD658\uACBD \uAC10\uC9C0");
101
+ setClientSession(await createPureWebClientSession());
102
+ }
103
+ console.log("[useSetupClientSession] \uAE30\uBCF8 \uC138\uC158 \uC124\uC815 \uC644\uB8CC");
104
+ } catch (error) {
105
+ console.error("[useSetupClientSession] \uCD08\uAE30\uD654 \uC2E4\uD328:", error);
106
+ }
107
+ };
108
+ initialize();
109
+ }, [isInitialized, setClientSession]);
110
+ useEffect(() => {
111
+ if (!isInitialized || !isLoggedIn) {
112
+ return;
113
+ }
114
+ const loadUserInfo = async () => {
115
+ console.log("[useSetupClientSession] \uC0AC\uC6A9\uC790 \uD504\uB85C\uD544 \uB85C\uB4DC \uC2DC\uC791...");
116
+ const userInfo = await getLoginUserInfo();
117
+ if (userInfo) {
118
+ setClientSession(userInfo);
119
+ }
120
+ };
121
+ loadUserInfo();
122
+ }, [isInitialized, isLoggedIn, setClientSession]);
123
+ return isInitialized ?? false;
124
+ };
125
+ var ClientSessionProvider = ({ children, loadingComponent = null }) => {
126
+ const isInitialized = useSetupClientSession();
127
+ if (!isInitialized) {
128
+ return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent });
129
+ }
130
+ return /* @__PURE__ */ jsx(Fragment, { children });
131
+ };
132
+
133
+ export { ClientSessionProvider, useSetupClientSession };
134
+ //# sourceMappingURL=index.js.map
135
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client-session/client-session-utils.ts","../src/client-session/use-setup-client-session.tsx","../src/client-session/client-session-provider.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { getUserProfile } from 'sales-frontend-api/method';\nimport { Bridge } from 'sales-frontend-bridge';\nimport {\n getCookie,\n getOrCreateDeviceId,\n getBrowserName,\n getBrowserVersion,\n getOSName,\n getOSVersion,\n getDeviceModel,\n isSalesPortal,\n getFormFactorFromUserAgent,\n isFpPlannerApp\n} from 'sales-frontend-utils';\n\nimport type { ClientSession } from 'sales-frontend-stores';\n\n/**\n * URL 경로의 두 번째 세그먼트에서 폼팩터를 추출하거나 User Agent로 감지\n * @param pathname - URL 경로 (예: /aaa/mobile, /aaa/tablet, /aaa/pc)\n * @returns 'mobile' | 'tablet' | 'pc'\n */\nconst getFormFactor = (): 'phone' | 'tablet' | 'pc' | undefined => {\n const { pathname } = location;\n\n // pathname이 제공된 경우 URL에서 추출 시도\n if (pathname) {\n const segments = pathname.split('/').filter(Boolean);\n\n // 두 번째 세그먼트 확인 (인덱스 1)\n if (segments.length >= 2) {\n const secondSegment = segments[1]?.toLowerCase();\n\n if (secondSegment === 'mobile') {\n return 'phone';\n }\n if (secondSegment === 'tablet') {\n return 'tablet';\n }\n if (secondSegment === 'pc') {\n return 'pc';\n }\n }\n }\n\n // URL에 없으면 User Agent로 감지\n return getFormFactorFromUserAgent(navigator.userAgent);\n};\n\n/**\n * 순수 웹 상태값 초기화\n * @returns\n */\nexport const createPureWebClientSession = async (): Promise<ClientSession> => {\n const hasAccessToken = !!getCookie('accessToken');\n\n return {\n isInitialized: true,\n isLoggedIn: hasAccessToken,\n isWebView: false,\n acceptLanguage: navigator.language || 'ko',\n formFactor: getFormFactor(),\n deviceId: getOrCreateDeviceId(),\n appVersion: `${getBrowserName(navigator.userAgent)} ${getBrowserVersion(navigator.userAgent)}`,\n loginType: 'web-login',\n platformName: getOSName(navigator.userAgent),\n platformVersion: getOSVersion(navigator.userAgent),\n deviceModel: getDeviceModel(),\n loginChannel: isSalesPortal() ? 'HMP' : 'ETC'\n };\n};\n\n/**\n * 웹뷰 상태값 초기화\n * @returns\n */\nexport const createWebviewClientSession = async (): Promise<ClientSession> => {\n const [appInfo, accessToken] = await Promise.allSettled([\n Bridge.native.getAppInfo(),\n isFpPlannerApp()\n ? Promise.resolve(getCookie('accessToken'))\n : Bridge.native.getAccessToken().then((res) => res.accessToken)\n ]);\n\n const appInfoData = appInfo.status === 'fulfilled' ? appInfo.value : null;\n const isLoggedIn = accessToken.status === 'fulfilled' ? !!accessToken.value : false;\n\n const formFactor =\n appInfoData?.['X-Channel-FormFactor']?.toLowerCase() ?? getCookie('formFactor')?.toLowerCase() ?? getFormFactor();\n\n const loginChannel = appInfoData?.['X-Channel-LoginChannel'] ?? getCookie('loginChannel') ?? 'DSP';\n\n return {\n isInitialized: true,\n isLoggedIn,\n isWebView: true,\n acceptLanguage: appInfoData?.['Accept-Language'] ?? 'ko',\n formFactor: formFactor as 'tablet' | 'phone' | 'pc' | undefined,\n deviceId: appInfoData?.['X-Channel-DeviceId'] ?? getCookie('deviceId'),\n appVersion: appInfoData?.['X-Channel-AppVersion'] ?? getCookie('appVersion'),\n loginType: appInfoData?.['X-Channel-LoginType'] ?? getCookie('loginType'),\n platformName: appInfoData?.['X-Channel-PlatformName'] ?? getCookie('platformName'),\n platformVersion: appInfoData?.['X-Channel-PlatformVersion'] ?? getCookie('platformVersion'),\n deviceModel: appInfoData?.['X-Channel-DeviceModel'] ?? getCookie('deviceModel'),\n loginChannel: loginChannel as 'DSP' | 'MSP'\n };\n};\n\nexport const getLoginUserInfo = async () => {\n try {\n const { isSuccess, data } = await getUserProfile();\n\n if (isSuccess && data) {\n console.log('[useSetupClientSession] 프로필 로드 성공');\n\n return data;\n }\n\n console.warn('[getLoginUserInfo] 프로필 로드 실패: 응답 없음');\n\n return null;\n } catch (error) {\n console.error('[getLoginUserInfo] 프로필 로드 실패:', error);\n\n return null;\n }\n};\n","import { useEffect } from 'react';\n\nimport { useClientSession, useSetClientSession } from 'sales-frontend-stores';\nimport { isWebView } from 'sales-frontend-utils';\n\nimport { createWebviewClientSession, createPureWebClientSession, getLoginUserInfo } from './client-session-utils';\n\n/**\n * 앱 시작 시 ClientSession을 플랫폼(웹뷰, 순수 웹)에 맞게 자동 설정하는 훅\n *\n * @returns {boolean} 초기화 완료 여부\n */\nexport const useSetupClientSession = (): boolean => {\n const { isInitialized, isLoggedIn } = useClientSession();\n const setClientSession = useSetClientSession();\n\n useEffect(() => {\n if (isInitialized) {\n console.log('[useSetupClientSession] Already initialized');\n\n return;\n }\n\n const initialize = async () => {\n try {\n console.log('[useSetupClientSession] 초기화 시작...');\n\n if (isWebView()) {\n console.log('[useSetupClientSession] 웹뷰 환경 감지');\n setClientSession(await createWebviewClientSession());\n } else {\n console.log('[useSetupClientSession] 순수 웹 환경 감지');\n setClientSession(await createPureWebClientSession());\n }\n\n console.log('[useSetupClientSession] 기본 세션 설정 완료');\n } catch (error) {\n console.error('[useSetupClientSession] 초기화 실패:', error);\n }\n };\n\n // 기본 초기화\n initialize();\n }, [isInitialized, setClientSession]);\n\n useEffect(() => {\n if (!isInitialized || !isLoggedIn) {\n return;\n }\n\n const loadUserInfo = async () => {\n console.log('[useSetupClientSession] 사용자 프로필 로드 시작...');\n\n const userInfo = await getLoginUserInfo();\n if (userInfo) {\n setClientSession(userInfo);\n }\n };\n\n // 로그인 FP 사용자 불러오기\n loadUserInfo();\n }, [isInitialized, isLoggedIn, setClientSession]);\n\n // 초기화 완료 여부 반환\n return isInitialized ?? false;\n};\n","import { useSetupClientSession } from './use-setup-client-session';\n\ninterface ClientSessionProviderProps {\n children: React.ReactNode;\n /**\n * 초기화 중 표시할 로딩 컴포넌트\n */\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * ClientSession 초기화를 담당하는 Provider 컴포넌트\n * 앱의 최상위에서 사용하여 플랫폼별 세션 정보를 자동으로 설정합니다.\n */\nexport const ClientSessionProvider = ({ children, loadingComponent = null }: ClientSessionProviderProps) => {\n const isInitialized = useSetupClientSession();\n\n // 초기화 완료 전까지 fallback 표시\n if (!isInitialized) {\n return <>{loadingComponent}</>;\n }\n\n return <>{children}</>;\n};\n"]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "sales-frontend-features",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.cts",
19
+ "default": "./dist/index.cjs"
20
+ }
21
+ }
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.14.0",
25
+ "@types/react": "19.1.0",
26
+ "@types/react-dom": "19.1.1",
27
+ "react": "^19.1.0",
28
+ "tsup": "^8.4.0",
29
+ "typescript": "5.8.2",
30
+ "eslint-config-sales-frontend-eslint-config-v8": "^0.0.6",
31
+ "sales-frontend-typescript-config": "0.0.2",
32
+ "sales-frontend-stores": "0.0.5",
33
+ "sales-frontend-bridge": "0.0.48",
34
+ "sales-frontend-api": "0.0.69"
35
+ },
36
+ "peerDependencies": {
37
+ "react": ">=18.0.0",
38
+ "react-dom": ">=18.0.0",
39
+ "sales-frontend-api": "0.0.69",
40
+ "sales-frontend-bridge": "0.0.48",
41
+ "sales-frontend-stores": "0.0.5"
42
+ },
43
+ "dependencies": {
44
+ "sales-frontend-utils": "0.0.15"
45
+ },
46
+ "scripts": {
47
+ "lint": "eslint . --max-warnings 0",
48
+ "generate:component": "turbo gen react-component",
49
+ "check-types": "tsc --noEmit",
50
+ "storybook": "tsup --watch",
51
+ "build": "tsup",
52
+ "release": "pnpm publish"
53
+ }
54
+ }