첫 커밋
This commit is contained in:
6
src/app/app-children.tsx
Normal file
6
src/app/app-children.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import { useNativeNavigateListener } from './lib/use-native-navigate-listener';
|
||||
|
||||
export const AppChildren = () => {
|
||||
useNativeNavigateListener();
|
||||
return <></>;
|
||||
};
|
||||
19
src/app/app.tsx
Normal file
19
src/app/app.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import '@/bridge';
|
||||
import { router } from '@/shared/configs/sentry';
|
||||
import { Toasts } from '@/shared/ui/toasts/toasts';
|
||||
import { TopButton } from '@/widgets/top-button';
|
||||
import { useInitTheme } from './hooks';
|
||||
import { IOSStatusBar } from '@/widgets/ios-status-bar';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
|
||||
export const App = () => {
|
||||
useInitTheme();
|
||||
return (
|
||||
<>
|
||||
<RouterProvider router={router} />
|
||||
<Toasts />
|
||||
<TopButton />
|
||||
<IOSStatusBar />
|
||||
</>
|
||||
);
|
||||
};
|
||||
4
src/app/hooks/index.tsx
Normal file
4
src/app/hooks/index.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
export * from './use-redirect';
|
||||
export * from './use-init-theme';
|
||||
|
||||
8
src/app/hooks/use-init-theme.ts
Normal file
8
src/app/hooks/use-init-theme.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { config } from '@/shared/configs';
|
||||
import { useEffectOnce } from 'react-use';
|
||||
|
||||
export const useInitTheme = () => {
|
||||
useEffectOnce(() => {
|
||||
document.body.classList.add(config.APP_NAME);
|
||||
});
|
||||
};
|
||||
20
src/app/hooks/use-noti-bar.ts
Normal file
20
src/app/hooks/use-noti-bar.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { notiBar } from '@/shared/lib';
|
||||
import { useStore } from '@/shared/model/store';
|
||||
import { useEffect } from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
export const useNotiBar = () => {
|
||||
const [notiBarMessage, setNotiBarMessage] = useStore(
|
||||
useShallow((state: any) => [
|
||||
state.utilEventSlice.notiBarMessage,
|
||||
state.utilEventSlice.setNotiBarMessage
|
||||
])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if(notiBarMessage) {
|
||||
notiBar(notiBarMessage);
|
||||
setNotiBarMessage('');
|
||||
}
|
||||
}, [notiBarMessage]);
|
||||
};
|
||||
17
src/app/hooks/use-redirect.ts
Normal file
17
src/app/hooks/use-redirect.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
|
||||
export const useRedirect = () => {
|
||||
const navigate = useNavigate();
|
||||
const [path, , { removeItem }] = useLocalStorageState(StorageKeys.RedirectPath);
|
||||
|
||||
useEffect(() => {
|
||||
if(path){
|
||||
// navigate(path);
|
||||
removeItem();
|
||||
}
|
||||
}, [path]);
|
||||
};
|
||||
20
src/app/i18n.ts
Normal file
20
src/app/i18n.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import { kr, en } from '@/shared/constants/locales';
|
||||
export const defaultNS = 'common';
|
||||
export const resources = {
|
||||
kr: kr,
|
||||
ex: en,
|
||||
} as const;
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
defaultNS,
|
||||
resources,
|
||||
fallbackLng: ['kr', 'en'],
|
||||
lng: 'kr',
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
27
src/app/index.tsx
Normal file
27
src/app/index.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
// import '@/shared/ui/assets/styles/index.scss';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { App } from './app';
|
||||
import { initAxios } from '@/shared/configs/axios';
|
||||
import { initSentry } from '@/shared/configs/sentry';
|
||||
import { AppProvider } from './providers/app-provider';
|
||||
|
||||
const initApp = async () => {
|
||||
initAxios();
|
||||
initSentry();
|
||||
};
|
||||
|
||||
(async () => {
|
||||
await initApp();
|
||||
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<AppProvider>
|
||||
<App />
|
||||
</AppProvider>
|
||||
</React.StrictMode>,
|
||||
);
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
})();
|
||||
15
src/app/lib/use-native-navigate-listener.ts
Normal file
15
src/app/lib/use-native-navigate-listener.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { bridge } from '@/bridge';
|
||||
import { PathType } from '@/shared/constants/paths';
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from '@/shared/lib/hooks';
|
||||
import { NativeMessage } from '@/shared/constants/native-message';
|
||||
|
||||
export const useNativeNavigateListener = () => {
|
||||
const { navigate } = useNavigate();
|
||||
useEffect(() => {
|
||||
// Subscribe to events from react native.
|
||||
return bridge.addEventListener(NativeMessage.WebViewNavigate, (message: any) => {
|
||||
navigate(message.path as PathType);
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
28
src/app/providers/app-provider.tsx
Normal file
28
src/app/providers/app-provider.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Splash } from '@/widgets/splash/splash';
|
||||
import { Suspense } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { CookiesProvider } from 'react-cookie';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { GlobalErrorBoundary } from '@/widgets/error-boundaries';
|
||||
import { getGlobalQueryClient } from '@/shared/configs/query';
|
||||
|
||||
interface AppProviderProps {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const AppProvider = ({
|
||||
children
|
||||
}: AppProviderProps) => {
|
||||
return (
|
||||
<GlobalErrorBoundary>
|
||||
<RecoilRoot>
|
||||
<QueryClientProvider client={getGlobalQueryClient()}>
|
||||
<Suspense fallback={<Splash />}>
|
||||
<CookiesProvider>{children}</CookiesProvider>
|
||||
{/* <ReactQueryDevtools /> */}
|
||||
</Suspense>
|
||||
</QueryClientProvider>
|
||||
</RecoilRoot>
|
||||
</GlobalErrorBoundary>
|
||||
);
|
||||
};
|
||||
1
src/assets/react.svg
Normal file
1
src/assets/react.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
33
src/bridge.ts
Normal file
33
src/bridge.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { linkBridge, registerWebMethod } from '@webview-bridge/web';
|
||||
import { NavigateOptions } from 'react-router-dom';
|
||||
|
||||
import { AppBridge, AppPostMessageSchema } from './output';
|
||||
import { router } from './shared/configs/sentry';
|
||||
|
||||
// Register functions in the registerWebMethod object in your web code
|
||||
export const webBridge = registerWebMethod({
|
||||
async navigate(url: string, options?: NavigateOptions) {
|
||||
router.navigate(url, options);
|
||||
},
|
||||
async getPathname(): Promise<string> {
|
||||
return window.location.pathname;
|
||||
},
|
||||
async setLocalStorage(key: string, value: string) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
},
|
||||
async getLocalStorage(key: string): Promise<object | null> {
|
||||
const item = localStorage.getItem(key);
|
||||
return item && JSON.parse(item);
|
||||
},
|
||||
// ... Add more functions as needed
|
||||
});
|
||||
|
||||
// Export the bridge type to be used in the web application
|
||||
export type WebBridge = typeof webBridge;
|
||||
|
||||
export const bridge = linkBridge<AppBridge, AppPostMessageSchema>({
|
||||
throwOnError: true,
|
||||
onReady: () => {
|
||||
console.log('bridge is ready');
|
||||
},
|
||||
});
|
||||
109
src/components/Features.tsx
Normal file
109
src/components/Features.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
|
||||
interface Feature {
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const Features: React.FC = () => {
|
||||
const features: Feature[] = [
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
),
|
||||
title: '보안 결제',
|
||||
description: '최고 수준의 암호화 기술로 고객의 결제 정보를 안전하게 보호합니다.'
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
),
|
||||
title: '빠른 처리',
|
||||
description: '실시간 결제 처리로 빠르고 효율적인 거래 경험을 제공합니다.'
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
title: '모바일 최적화',
|
||||
description: '하이브리드 앱으로 iOS, Android 모든 기기에서 완벽하게 동작합니다.'
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||
</svg>
|
||||
),
|
||||
title: '24/7 지원',
|
||||
description: '언제든지 도움이 필요할 때 전문가 팀이 즉시 지원해드립니다.'
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
),
|
||||
title: '신뢰성',
|
||||
description: '15년간 축적된 노하우로 안정적이고 신뢰할 수 있는 서비스를 제공합니다.'
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
),
|
||||
title: '맞춤 설정',
|
||||
description: '비즈니스 요구사항에 맞게 유연하게 설정할 수 있는 결제 솔루션입니다.'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-20 bg-gray-50">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
|
||||
왜 나이스페이먼츠인가요?
|
||||
</h2>
|
||||
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||
고객의 성공이 우리의 성공입니다. 최고의 기술력과 서비스로 비즈니스 성장을 지원합니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{features.map((feature, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="bg-white p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2"
|
||||
>
|
||||
<div className="w-16 h-16 bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg flex items-center justify-center text-white mb-6">
|
||||
{feature.icon}
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-4">
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-16">
|
||||
<button className="bg-blue-600 text-white px-8 py-4 rounded-lg font-semibold text-lg hover:bg-blue-700 transition-all transform hover:scale-105 shadow-lg">
|
||||
더 많은 기능 보기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Features;
|
||||
121
src/components/Footer.tsx
Normal file
121
src/components/Footer.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
return (
|
||||
<footer className="bg-gray-900 text-white">
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="grid md:grid-cols-4 gap-8">
|
||||
{/* Company Info */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-8 h-8 bg-blue-600 rounded-md flex items-center justify-center">
|
||||
<span className="text-white font-bold text-sm">NP</span>
|
||||
</div>
|
||||
<span className="text-xl font-bold">NicePayments</span>
|
||||
</div>
|
||||
<p className="text-gray-400 leading-relaxed">
|
||||
15년 경험의 결제 전문 기업으로, 안전하고 편리한 결제 서비스를 제공합니다.
|
||||
</p>
|
||||
<div className="flex space-x-4">
|
||||
<a href="#" className="text-gray-400 hover:text-white transition-colors">
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="#" className="text-gray-400 hover:text-white transition-colors">
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M22.46 6c-.77.35-1.6.58-2.46.69.88-.53 1.56-1.37 1.88-2.38-.83.5-1.75.85-2.72 1.05C18.37 4.5 17.26 4 16 4c-2.35 0-4.27 1.92-4.27 4.29 0 .34.04.67.11.98C8.28 9.09 5.11 7.38 3 4.79c-.37.63-.58 1.37-.58 2.15 0 1.49.75 2.81 1.91 3.56-.71 0-1.37-.2-1.95-.5v.03c0 2.08 1.48 3.82 3.44 4.21a4.22 4.22 0 0 1-1.93.07 4.28 4.28 0 0 0 4 2.98 8.521 8.521 0 0 1-5.33 1.84c-.34 0-.68-.02-1.02-.06C3.44 20.29 5.7 21 8.12 21 16 21 20.33 14.46 20.33 8.79c0-.19 0-.37-.01-.56.84-.6 1.56-1.36 2.14-2.23z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="#" className="text-gray-400 hover:text-white transition-colors">
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="#" className="text-gray-400 hover:text-white transition-colors">
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.174-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.402.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.357-.629-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24.009 12.017 24c6.624 0 11.99-5.367 11.99-11.987C24.007 5.367 18.641.001 12.017.001z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Services */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">서비스</h3>
|
||||
<ul className="space-y-2 text-gray-400">
|
||||
<li><a href="#" className="hover:text-white transition-colors">카드 결제</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">계좌이체</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">모바일 결제</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">가상계좌</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">API 문서</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Company */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">회사</h3>
|
||||
<ul className="space-y-2 text-gray-400">
|
||||
<li><Link to="/about" className="hover:text-white transition-colors">회사소개</Link></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">채용정보</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">보도자료</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">파트너십</a></li>
|
||||
<li><Link to="/contact" className="hover:text-white transition-colors">문의하기</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Support */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">고객지원</h3>
|
||||
<ul className="space-y-2 text-gray-400">
|
||||
<li><a href="#" className="hover:text-white transition-colors">FAQ</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">개발자 가이드</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">상태 페이지</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">보안센터</a></li>
|
||||
<li><a href="#" className="hover:text-white transition-colors">1:1 문의</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Info */}
|
||||
<div className="border-t border-gray-800 mt-8 pt-8">
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<h4 className="font-semibold mb-2">고객센터</h4>
|
||||
<p className="text-gray-400">전화: 02-1234-5678</p>
|
||||
<p className="text-gray-400">이메일: contact@nicepayments.com</p>
|
||||
<p className="text-gray-400">운영시간: 평일 09:00 - 18:00</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold mb-2">회사정보</h4>
|
||||
<p className="text-gray-400">주소: 서울시 강남구 테헤란로 123</p>
|
||||
<p className="text-gray-400">사업자등록번호: 123-45-67890</p>
|
||||
<p className="text-gray-400">대표: 홍길동</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Copyright */}
|
||||
<div className="border-t border-gray-800 mt-8 pt-8 flex flex-col md:flex-row justify-between items-center">
|
||||
<p className="text-gray-400">
|
||||
© 2023 NicePayments. All rights reserved.
|
||||
</p>
|
||||
<div className="flex space-x-6 mt-4 md:mt-0">
|
||||
<a href="#" className="text-gray-400 hover:text-white transition-colors text-sm">
|
||||
개인정보처리방침
|
||||
</a>
|
||||
<a href="#" className="text-gray-400 hover:text-white transition-colors text-sm">
|
||||
이용약관
|
||||
</a>
|
||||
<a href="#" className="text-gray-400 hover:text-white transition-colors text-sm">
|
||||
쿠키정책
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
203
src/components/Header.tsx
Normal file
203
src/components/Header.tsx
Normal file
@@ -0,0 +1,203 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useRouterState } from '@tanstack/react-router';
|
||||
import { Toasts } from '@/shared/ui/toasts/toasts';
|
||||
import { notiBar, snackBar } from '@/shared/lib/toast';
|
||||
import { ToastContainer,toast } from 'react-toastify';
|
||||
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const [isOpenBottomSheet, setIsOpenBottomSheet] = useState(true);
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const router = useRouterState();
|
||||
|
||||
const isActive = (path: string) => {
|
||||
return router.location.pathname === path;
|
||||
};
|
||||
const toastTest = (str: string) => {
|
||||
//alert(str)
|
||||
toast(str);
|
||||
//notiBar(str)
|
||||
|
||||
};
|
||||
|
||||
const openBottomSheet = () => {
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
className="fixed left-0 right-0 bg-white shadow-lg z-50"
|
||||
style={{
|
||||
top: `env(safe-area-inset-top, 0px)`
|
||||
}}
|
||||
>
|
||||
<Toasts />
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex justify-between items-center py-4">
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center space-x-2">
|
||||
<div className="w-8 h-8 bg-blue-600 rounded-md flex items-center justify-center">
|
||||
<span className="text-white font-bold text-sm">NP</span>
|
||||
</div>
|
||||
<span className="text-xl font-bold text-gray-900">
|
||||
NicePayments
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden md:flex items-center space-x-8">
|
||||
<Link
|
||||
to="/"
|
||||
className={`font-medium transition-colors ${
|
||||
isActive('/')
|
||||
? 'text-blue-600 border-b-2 border-blue-600 pb-1'
|
||||
: 'text-gray-700 hover:text-blue-600'
|
||||
}`}
|
||||
>
|
||||
홈
|
||||
</Link>
|
||||
<Link
|
||||
to="/about"
|
||||
className={`font-medium transition-colors ${
|
||||
isActive('/about')
|
||||
? 'text-blue-600 border-b-2 border-blue-600 pb-1'
|
||||
: 'text-gray-700 hover:text-blue-600'
|
||||
}`}
|
||||
>
|
||||
회사소개
|
||||
</Link>
|
||||
<Link
|
||||
to="/contact"
|
||||
className={`font-medium transition-colors ${
|
||||
isActive('/contact')
|
||||
? 'text-blue-600 border-b-2 border-blue-600 pb-1'
|
||||
: 'text-gray-700 hover:text-blue-600'
|
||||
}`}
|
||||
>
|
||||
문의하기
|
||||
</Link>
|
||||
<Link
|
||||
to="/test"
|
||||
className={`font-medium transition-colors ${
|
||||
isActive('/test')
|
||||
? 'text-blue-600 border-b-2 border-blue-600 pb-1'
|
||||
: 'text-gray-700 hover:text-blue-600'
|
||||
}`}
|
||||
>
|
||||
테스트
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
{/* User Menu */}
|
||||
<div className="hidden md:flex items-center space-x-4">
|
||||
<div className="space-x-2">
|
||||
<button
|
||||
onClick={() => toastTest('로그인')}
|
||||
className="text-gray-700 px-4 py-2 rounded-md hover:bg-gray-100 transition-colors">
|
||||
로그인
|
||||
</button>
|
||||
<button className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition-colors">
|
||||
회원가입
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
className="md:hidden flex items-center justify-center w-8 h-8"
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
>
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
{isMenuOpen ? (
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
) : (
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
)}
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{isMenuOpen && (
|
||||
<div className="md:hidden py-4 border-t border-gray-200">
|
||||
<nav className="flex flex-col space-y-4">
|
||||
<Link
|
||||
to="/"
|
||||
className={`font-medium transition-colors ${
|
||||
isActive('/') ? 'text-blue-600' : 'text-gray-700'
|
||||
}`}
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
홈
|
||||
</Link>
|
||||
<Link
|
||||
to="/about"
|
||||
className={`font-medium transition-colors ${
|
||||
isActive('/about') ? 'text-blue-600' : 'text-gray-700'
|
||||
}`}
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
회사소개
|
||||
</Link>
|
||||
<Link
|
||||
to="/contact"
|
||||
className={`font-medium transition-colors ${
|
||||
isActive('/contact') ? 'text-blue-600' : 'text-gray-700'
|
||||
}`}
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
문의하기
|
||||
</Link>
|
||||
<Link
|
||||
to="/test"
|
||||
className={`font-medium transition-colors ${
|
||||
isActive('/test') ? 'text-blue-600' : 'text-gray-700'
|
||||
}`}
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
테스트
|
||||
</Link>
|
||||
|
||||
<div className="pt-4 border-t border-gray-200">
|
||||
<div className="space-y-2">
|
||||
<button
|
||||
className="w-full text-left text-gray-700 hover:text-blue-600 transition-colors"
|
||||
onClick={() => toastTest('로그인')}
|
||||
>
|
||||
로그인
|
||||
</button>
|
||||
<button
|
||||
className="w-full text-left bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition-colors"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
회원가입
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
56
src/components/Hero.tsx
Normal file
56
src/components/Hero.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
|
||||
const Hero: React.FC = () => {
|
||||
return (
|
||||
<section className="bg-gradient-to-r from-blue-600 to-purple-600 text-white">
|
||||
<div className="container mx-auto px-4 py-20">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6">
|
||||
안전하고 편리한
|
||||
<br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-yellow-400 to-pink-400">
|
||||
결제 서비스
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl mb-8 opacity-90">
|
||||
나이스페이먼츠와 함께 더 나은 결제 경험을 시작하세요.
|
||||
<br />
|
||||
하이브리드 앱으로 언제 어디서나 간편하게!
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<button className="bg-white text-blue-600 px-8 py-4 rounded-lg font-semibold text-lg hover:bg-gray-100 transition-all transform hover:scale-105 shadow-lg">
|
||||
서비스 시작하기
|
||||
</button>
|
||||
<button className="border-2 border-white text-white px-8 py-4 rounded-lg font-semibold text-lg hover:bg-white hover:text-blue-600 transition-all transform hover:scale-105">
|
||||
더 알아보기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating Elements */}
|
||||
<div className="absolute top-20 left-10 w-20 h-20 bg-white opacity-10 rounded-full animate-bounce"></div>
|
||||
<div className="absolute top-40 right-20 w-16 h-16 bg-yellow-400 opacity-20 rounded-full animate-pulse"></div>
|
||||
<div className="absolute bottom-20 left-1/4 w-12 h-12 bg-pink-400 opacity-20 rounded-full animate-bounce delay-1000"></div>
|
||||
|
||||
{/* Wave Bottom */}
|
||||
<div className="relative">
|
||||
<svg
|
||||
className="w-full h-20 fill-current text-white"
|
||||
viewBox="0 0 1200 120"
|
||||
preserveAspectRatio="none"
|
||||
>
|
||||
<path d="M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z"
|
||||
opacity=".25"
|
||||
></path>
|
||||
<path d="M0,0V15.81C13,36.92,27.64,56.86,47.69,72.05,99.41,111.27,165,111,224.58,91.58c31.15-10.15,60.09-26.07,89.67-39.8,40.92-19,84.73-46,130.83-49.67,36.26-2.85,70.9,9.42,98.6,31.56,31.77,25.39,62.32,62,103.63,73,40.44,10.79,81.35-6.69,119.13-24.28s75.16-39,116.92-43.05c59.73-5.85,113.28,22.88,168.9,38.84,30.2,8.66,59,6.17,87.09-7.5,22.43-10.89,48-26.93,60.65-49.24V0Z"
|
||||
opacity=".5"
|
||||
></path>
|
||||
<path d="M0,0V5.63C149.93,59,314.09,71.32,475.83,42.57c43-7.64,84.23-20.12,127.61-26.46,59-8.63,112.48,12.24,165.56,35.4C827.93,77.22,886,95.24,951.2,90c86.53-7,172.46-45.71,248.8-84.81V0Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
||||
38
src/components/LanguageSwitcher.tsx
Normal file
38
src/components/LanguageSwitcher.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { appBridge } from '@/utils/appBridge';
|
||||
|
||||
const LanguageSwitcher: React.FC = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const changeLanguage = async (language: string) => {
|
||||
// 웹 언어 변경
|
||||
i18n.changeLanguage(language);
|
||||
localStorage.setItem('i18nextLng', language);
|
||||
|
||||
// 네이티브 환경에서 네이티브 언어도 변경
|
||||
if (appBridge.isNativeEnvironment()) {
|
||||
try {
|
||||
await appBridge.setLanguage(language);
|
||||
} catch (error) {
|
||||
console.error('Failed to set native language:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-sm text-gray-600">{t('common.language')}</span>
|
||||
<select
|
||||
value={i18n.language}
|
||||
onChange={(e) => changeLanguage(e.target.value)}
|
||||
className="text-sm border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="ko">한국어</option>
|
||||
<option value="en">English</option>
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguageSwitcher;
|
||||
59
src/components/SearchFilterButton.tsx
Normal file
59
src/components/SearchFilterButton.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
|
||||
interface SearchFilterButtonProps {
|
||||
displayText: string;
|
||||
isActive: boolean;
|
||||
onClick: () => void;
|
||||
className?: string;
|
||||
showIcon?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const SearchFilterButton: React.FC<SearchFilterButtonProps> = ({
|
||||
displayText,
|
||||
isActive,
|
||||
onClick,
|
||||
className = '',
|
||||
showIcon = true,
|
||||
disabled = false
|
||||
}) => {
|
||||
const baseClasses = "inline-flex items-center px-4 py-2 text-sm font-medium rounded-lg border transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500";
|
||||
|
||||
const activeClasses = isActive
|
||||
? "bg-blue-50 border-blue-500 text-blue-700 hover:bg-blue-100"
|
||||
: "bg-white border-gray-300 text-gray-700 hover:bg-gray-50 hover:border-gray-400";
|
||||
|
||||
const disabledClasses = disabled
|
||||
? "opacity-50 cursor-not-allowed"
|
||||
: "cursor-pointer";
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={`${baseClasses} ${activeClasses} ${disabledClasses} ${className}`}
|
||||
>
|
||||
{showIcon && (
|
||||
<svg
|
||||
className="w-4 h-4 mr-2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.414A1 1 0 013 6.707V4z"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
<span className="truncate">{displayText}</span>
|
||||
{isActive && (
|
||||
<div className="ml-2 w-2 h-2 bg-blue-500 rounded-full flex-shrink-0" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchFilterButton;
|
||||
100
src/components/SearchFilterExample.tsx
Normal file
100
src/components/SearchFilterExample.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSearchFilter } from '@/hooks/useSearchFilter';
|
||||
import SearchFilterButton from './SearchFilterButton';
|
||||
import { SearchFilter, formatDateRange } from '@/types/filter';
|
||||
|
||||
interface SearchFilterExampleProps {
|
||||
onFilterChange?: (filter: SearchFilter) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const SearchFilterExample: React.FC<SearchFilterExampleProps> = ({
|
||||
onFilterChange,
|
||||
className = ''
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const searchFilterOptions = onFilterChange ? {
|
||||
storageKey: 'transaction_search_filter',
|
||||
onFilterChange
|
||||
} : {
|
||||
storageKey: 'transaction_search_filter'
|
||||
};
|
||||
|
||||
const {
|
||||
filter,
|
||||
isModalOpen,
|
||||
openModal,
|
||||
closeModal,
|
||||
handleFilterConfirm,
|
||||
getDisplayText,
|
||||
isFilterActive,
|
||||
resetFilter
|
||||
} = useSearchFilter(searchFilterOptions);
|
||||
|
||||
return (
|
||||
<div className={`space-y-4 ${className}`}>
|
||||
{/* 조회조건 버튼과 필터 정보 */}
|
||||
<div className="flex flex-col sm:flex-row gap-3 items-start sm:items-center justify-between">
|
||||
<div className="flex gap-2 items-center">
|
||||
<SearchFilterButton
|
||||
displayText={getDisplayText()}
|
||||
isActive={isFilterActive}
|
||||
onClick={openModal}
|
||||
/>
|
||||
|
||||
{/* 초기화 버튼 (필터가 활성화된 경우만 표시) */}
|
||||
{isFilterActive && (
|
||||
<button
|
||||
onClick={resetFilter}
|
||||
className="text-sm text-gray-500 hover:text-gray-700 underline"
|
||||
>
|
||||
{t('common.reset')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 현재 필터 요약 정보 */}
|
||||
<div className="text-sm text-gray-600">
|
||||
{filter.dateRange && (
|
||||
<span>
|
||||
{formatDateRange(filter.dateRange)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 현재 적용된 필터 상세 정보 (디버깅용) */}
|
||||
<div className="bg-gray-50 p-4 rounded-lg">
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-2">{t('filter.currentConditions')}</h3>
|
||||
<div className="space-y-1 text-sm text-gray-600">
|
||||
<div>
|
||||
<span className="font-medium">{t('filter.period')}:</span> {filter.period === 'custom' ? t('filter.periods.custom') : t(`filter.periods.${filter.period}`)}
|
||||
</div>
|
||||
{filter.dateRange && (
|
||||
<div>
|
||||
<span className="font-medium">{t('filter.dateRange')}:</span> {formatDateRange(filter.dateRange)}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<span className="font-medium">{t('filter.transactionType')}:</span> {t(`filter.transactionTypes.${filter.transactionType}`)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">{t('filter.sortOrder')}:</span> {t(`filter.sortOrders.${filter.sortOrder}`)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 조회조건 설정 모달
|
||||
<SearchFilterModal
|
||||
isOpen={isModalOpen}
|
||||
onClose={closeModal}
|
||||
onConfirm={handleFilterConfirm}
|
||||
initialFilter={filter}
|
||||
/>
|
||||
*/}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchFilterExample;
|
||||
150
src/components/Services.tsx
Normal file
150
src/components/Services.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
import React from 'react';
|
||||
|
||||
interface Service {
|
||||
title: string;
|
||||
description: string;
|
||||
features: string[];
|
||||
icon: React.ReactNode;
|
||||
color: string;
|
||||
}
|
||||
|
||||
const Services: React.FC = () => {
|
||||
const services: Service[] = [
|
||||
{
|
||||
title: '카드 결제',
|
||||
description: '국내외 모든 카드사와 연동된 안전한 카드 결제 서비스',
|
||||
features: [
|
||||
'국내외 주요 카드사 지원',
|
||||
'무이자 할부 서비스',
|
||||
'실시간 승인 처리',
|
||||
'3D Secure 보안 인증'
|
||||
],
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
|
||||
</svg>
|
||||
),
|
||||
color: 'from-blue-500 to-blue-600'
|
||||
},
|
||||
{
|
||||
title: '계좌이체',
|
||||
description: '은행 계좌를 통한 직접 이체 결제 서비스',
|
||||
features: [
|
||||
'전 은행 실시간 이체',
|
||||
'낮은 수수료율',
|
||||
'자동 입금 확인',
|
||||
'환불 처리 자동화'
|
||||
],
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
),
|
||||
color: 'from-green-500 to-green-600'
|
||||
},
|
||||
{
|
||||
title: '모바일 결제',
|
||||
description: '카카오페이, 네이버페이 등 간편결제 서비스',
|
||||
features: [
|
||||
'주요 간편결제 연동',
|
||||
'원터치 결제',
|
||||
'포인트 사용 가능',
|
||||
'멤버십 혜택 적용'
|
||||
],
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
),
|
||||
color: 'from-purple-500 to-purple-600'
|
||||
},
|
||||
{
|
||||
title: '가상계좌',
|
||||
description: '개인별 전용 가상계좌를 통한 무통장 입금 서비스',
|
||||
features: [
|
||||
'실시간 계좌 발급',
|
||||
'자동 입금 확인',
|
||||
'다중 은행 지원',
|
||||
'입금 알림 서비스'
|
||||
],
|
||||
icon: (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||
</svg>
|
||||
),
|
||||
color: 'from-orange-500 to-orange-600'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-20 bg-white">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
|
||||
다양한 결제 서비스
|
||||
</h2>
|
||||
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||
고객의 다양한 결제 니즈에 맞춰 최적화된 결제 솔루션을 제공합니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
{services.map((service, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="group bg-white border-2 border-gray-100 rounded-2xl p-8 hover:border-transparent hover:shadow-2xl transition-all duration-300 transform hover:-translate-y-1"
|
||||
>
|
||||
<div className={`w-16 h-16 bg-gradient-to-r ${service.color} rounded-xl flex items-center justify-center text-white mb-6 group-hover:scale-110 transition-transform duration-300`}>
|
||||
{service.icon}
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
{service.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-gray-600 mb-6 leading-relaxed">
|
||||
{service.description}
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h4 className="font-semibold text-gray-900 mb-3">주요 기능</h4>
|
||||
{service.features.map((feature, featureIndex) => (
|
||||
<div key={featureIndex} className="flex items-center space-x-3">
|
||||
<div className="w-2 h-2 bg-blue-500 rounded-full flex-shrink-0"></div>
|
||||
<span className="text-gray-700">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button className="mt-8 w-full bg-gray-50 text-gray-700 py-3 px-6 rounded-lg font-semibold hover:bg-blue-600 hover:text-white transition-all duration-300">
|
||||
자세히 보기
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-16">
|
||||
<div className="bg-gradient-to-r from-blue-50 to-purple-50 rounded-2xl p-8">
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
맞춤형 결제 솔루션이 필요하신가요?
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-6 max-w-2xl mx-auto">
|
||||
비즈니스 규모와 업종에 맞는 최적의 결제 솔루션을 제안해드립니다.
|
||||
전문 컨설턴트와 상담을 통해 최고의 결제 환경을 구축하세요.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<button className="bg-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:bg-blue-700 transition-colors">
|
||||
무료 상담 신청
|
||||
</button>
|
||||
<button className="border-2 border-blue-600 text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-600 hover:text-white transition-colors">
|
||||
서비스 가이드 다운로드
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Services;
|
||||
193
src/components/SlideMenu.tsx
Normal file
193
src/components/SlideMenu.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
interface SlideMenuProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const SlideMenu: React.FC<SlideMenuProps> = ({ isOpen, onClose }) => {
|
||||
// ESC 키로 메뉴 닫기
|
||||
useEffect(() => {
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
if (isOpen) {
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
// 배경 스크롤 방지
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = 'unset';
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
document.body.style.overflow = 'unset';
|
||||
};
|
||||
}, [isOpen, onClose]);
|
||||
return (
|
||||
<>
|
||||
{/* 오버레이 */}
|
||||
{isOpen && (
|
||||
<div
|
||||
className="fixed inset-0 bg-black bg-opacity-50 z-[60] transition-opacity duration-300"
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 슬라이드 메뉴 */}
|
||||
<div
|
||||
className={`fixed top-0 right-0 h-full w-full bg-white z-[60] transform transition-transform duration-300 ease-in-out flex flex-col ${
|
||||
isOpen ? 'translate-x-0' : 'translate-x-full'
|
||||
}`}
|
||||
>
|
||||
{/* 헤더 */}
|
||||
<div
|
||||
className="flex items-center justify-between px-4 py-4 border-b border-gray-200 bg-white sticky top-0 z-10"
|
||||
style={{
|
||||
paddingTop: `calc(1rem + env(safe-area-inset-top, 0px))`
|
||||
}}
|
||||
>
|
||||
<h2 className="text-xl font-bold text-gray-900">메뉴</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 rounded-md hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 메뉴 콘텐츠 */}
|
||||
<div className="p-4 flex-1 overflow-y-scroll pb-16 scrollbar-hide">
|
||||
<div className="space-y-6">
|
||||
<div className="border border-gray-200 rounded-lg p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">계정 관리</h3>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
</svg>
|
||||
프로필 설정
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
비밀번호 변경
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-5 5v-5zM8 17H3l5 5v-5zM12 3v3m6.364-.636l-2.121 2.121M21 12h-3M18.364 18.364l-2.121-2.121M12 21v-3m-6.364.636l2.121-2.121M3 12h3M5.636 5.636l2.121 2.121" />
|
||||
</svg>
|
||||
알림 설정
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border border-gray-200 rounded-lg p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">결제 서비스</h3>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
결제 내역
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" />
|
||||
</svg>
|
||||
환불 요청
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
|
||||
</svg>
|
||||
결제 방법 관리
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border border-gray-200 rounded-lg p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">고객 지원</h3>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
FAQ
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
||||
</svg>
|
||||
1:1 문의
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
이용약관
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border border-gray-200 rounded-lg p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">기타</h3>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
앱 정보
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
||||
</svg>
|
||||
개발자 정보
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="flex items-center text-gray-700 hover:text-blue-600 transition-colors">
|
||||
<svg className="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
로그아웃
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SlideMenu;
|
||||
114
src/components/TabBar.tsx
Normal file
114
src/components/TabBar.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useRouterState } from '@tanstack/react-router';
|
||||
import { useScrollDirection } from '../hooks/useScrollDirection';
|
||||
import SlideMenu from './SlideMenu';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const TabBar: React.FC = () => {
|
||||
const router = useRouterState();
|
||||
const { translateY, isScrollingState } = useScrollDirection();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isActive = (path: string) => {
|
||||
return router.location.pathname === path;
|
||||
};
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
path: '/',
|
||||
label: t('menu.home'),
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
label: t('menu.about'),
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||
</svg>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: '/contact',
|
||||
label: t('menu.contact'),
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: '/test',
|
||||
label: t('menu.test'),
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="fixed bottom-0 left-0 right-0 bg-white shadow-lg border-t border-gray-200 z-50 tabbar-scroll-effect"
|
||||
style={{
|
||||
transform: `translateY(${translateY}px)`,
|
||||
paddingBottom: `env(safe-area-inset-bottom, 0px)`,
|
||||
transition: isScrollingState
|
||||
? 'transform 0.05s linear'
|
||||
: 'transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94)'
|
||||
}}
|
||||
>
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex justify-around items-center py-1">
|
||||
{tabs.map((tab) => (
|
||||
<Link
|
||||
key={tab.path}
|
||||
to={tab.path}
|
||||
className={`flex flex-col items-center justify-center py-2 px-3 rounded-lg transition-colors min-w-[60px] ${
|
||||
isActive(tab.path)
|
||||
? 'text-blue-600 bg-blue-50'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-gray-50'
|
||||
}`}
|
||||
preload="intent"
|
||||
>
|
||||
<div className="mb-1">
|
||||
{tab.icon}
|
||||
</div>
|
||||
<span className="text-xs font-medium">{tab.label}</span>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{/* 메뉴 탭 */}
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(true)}
|
||||
className={`flex flex-col items-center justify-center py-2 px-3 rounded-lg transition-colors min-w-[60px] ${
|
||||
isMenuOpen
|
||||
? 'text-blue-600 bg-blue-50'
|
||||
: 'text-gray-600 hover:text-blue-600 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
<div className="mb-1">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-xs font-medium">{t('menu.menu')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 슬라이드 메뉴 */}
|
||||
<SlideMenu isOpen={isMenuOpen} onClose={() => setIsMenuOpen(false)} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabBar;
|
||||
10
src/components/index.ts
Normal file
10
src/components/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export { default as Header } from './Header';
|
||||
export { default as Footer } from './Footer';
|
||||
export { default as Hero } from './Hero';
|
||||
export { default as Features } from './Features';
|
||||
export { default as Services } from './Services';
|
||||
export { default as TabBar } from './TabBar';
|
||||
export { default as SlideMenu } from './SlideMenu';
|
||||
export { default as SearchFilterButton } from './SearchFilterButton';
|
||||
export { default as SearchFilterExample } from './SearchFilterExample';
|
||||
export { default as LanguageSwitcher } from './LanguageSwitcher';
|
||||
12
src/config/index.ts
Normal file
12
src/config/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
interface ViteEnv {
|
||||
readonly VITE_API_BASE_URL?: string;
|
||||
}
|
||||
|
||||
const config = {
|
||||
api: {
|
||||
baseURL: (import.meta as unknown as { env: ViteEnv }).env?.VITE_API_BASE_URL || 'http://localhost:3000/api',
|
||||
timeout: 30000,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export default config;
|
||||
43
src/entities/account/model/types.ts
Normal file
43
src/entities/account/model/types.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export enum AccountTabKeys {
|
||||
UserManage = 'UserManage',
|
||||
PasswordManage = 'PasswordManage',
|
||||
};
|
||||
export interface AccountTabProps {
|
||||
activeTab: AccountTabKeys;
|
||||
};
|
||||
export enum AccountUserTabKeys {
|
||||
LoginAuthInfo = 'LoginAuthInfo',
|
||||
AccountAuth = 'AccountAuth',
|
||||
};
|
||||
export interface AccountUserTabProps {
|
||||
activeTab: AccountUserTabKeys;
|
||||
tid: string;
|
||||
};
|
||||
export interface AuthItem {
|
||||
useYn?: boolean;
|
||||
authName?: string;
|
||||
tid?: string;
|
||||
};
|
||||
export interface UserManageAuthListProps {
|
||||
authItems: Array<AuthItem>
|
||||
};
|
||||
export interface UserManageAuthItemProps extends AuthItem {
|
||||
|
||||
};
|
||||
export interface UserLoginAuthInfoWrapProps {
|
||||
tid: string;
|
||||
};
|
||||
export interface UserAccountAuthWrapProps {
|
||||
tid: string;
|
||||
};
|
||||
export interface PermItem {
|
||||
menuId?: string;
|
||||
permName?: string;
|
||||
};
|
||||
export interface UserAccountAuthPermListProps {
|
||||
tid: string;
|
||||
permItems: Array<PermItem>;
|
||||
};
|
||||
export interface UserAccountAuthPermItemProps extends PermItem {
|
||||
tid: string;
|
||||
};
|
||||
37
src/entities/account/ui/account-tab.tsx
Normal file
37
src/entities/account/ui/account-tab.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import {
|
||||
AccountTabKeys,
|
||||
AccountTabProps
|
||||
} from '../model/types';
|
||||
|
||||
export const AccountTab = ({
|
||||
activeTab
|
||||
}: AccountTabProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToNavigation = (tab: AccountTabKeys) => {
|
||||
if(activeTab !== tab){
|
||||
if(tab === AccountTabKeys.UserManage){
|
||||
navigate(PATHS.account.user.manage);
|
||||
}
|
||||
else if(tab === AccountTabKeys.PasswordManage){
|
||||
navigate(PATHS.account.password.manage);
|
||||
}
|
||||
}
|
||||
};
|
||||
return(
|
||||
<>
|
||||
<div className="subTab">
|
||||
<button
|
||||
className={`subtab-btn ${(activeTab === AccountTabKeys.UserManage)? 'active': ''}` }
|
||||
onClick={ () => onClickToNavigation(AccountTabKeys.UserManage) }
|
||||
>사용자 관리</button>
|
||||
<button
|
||||
className={`subtab-btn ${(activeTab === AccountTabKeys.PasswordManage)? 'active': ''}` }
|
||||
onClick={ () => onClickToNavigation(AccountTabKeys.PasswordManage) }
|
||||
>비밀번호 관리</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
46
src/entities/account/ui/account-user-tab.tsx
Normal file
46
src/entities/account/ui/account-user-tab.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import {
|
||||
AccountUserTabKeys,
|
||||
AccountUserTabProps
|
||||
} from '../model/types';
|
||||
|
||||
export const AccountUserTab = ({
|
||||
activeTab,
|
||||
tid
|
||||
}: AccountUserTabProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToNavigation = (tab: AccountUserTabKeys) => {
|
||||
if(activeTab !== tab){
|
||||
if(tab === AccountUserTabKeys.LoginAuthInfo){
|
||||
navigate(PATHS.account.user.loginAuthInfo, {
|
||||
state: {
|
||||
tid: tid
|
||||
}
|
||||
});
|
||||
}
|
||||
else if(tab === AccountUserTabKeys.AccountAuth){
|
||||
navigate(PATHS.account.user.accountAuth, {
|
||||
state: {
|
||||
tid: tid
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
return(
|
||||
<>
|
||||
<div className="subTab">
|
||||
<button
|
||||
className={`subtab-btn ${(activeTab === AccountUserTabKeys.LoginAuthInfo)? 'active': ''}` }
|
||||
onClick={ () => onClickToNavigation(AccountUserTabKeys.LoginAuthInfo) }
|
||||
>로그인 인증정보</button>
|
||||
<button
|
||||
className={`subtab-btn ${(activeTab === AccountUserTabKeys.AccountAuth)? 'active': ''}` }
|
||||
onClick={ () => onClickToNavigation( AccountUserTabKeys.AccountAuth) }
|
||||
>계정권한</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
29
src/entities/account/ui/password-manage-wrap.tsx
Normal file
29
src/entities/account/ui/password-manage-wrap.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
|
||||
export const PasswordManageWrap = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickTonavigation = () => {
|
||||
navigate(PATHS.account.password.modifyLoginPassword);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="ing-list">
|
||||
<div className="pwd-manage mt-20">
|
||||
<div className="pwd-buttons">
|
||||
<button
|
||||
className="btn-44 btn-white pwd-btn"
|
||||
type="button"
|
||||
onClick={ () => onClickTonavigation() }
|
||||
>로그인 비밀번호 변경</button>
|
||||
<button
|
||||
className="btn-44 btn-white pwd-btn"
|
||||
type="button"
|
||||
>거래취소 비밀번호 변경</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
};
|
||||
31
src/entities/account/ui/user-account-auth-perm-item.tsx
Normal file
31
src/entities/account/ui/user-account-auth-perm-item.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { UserAccountAuthPermItemProps } from '../model/types';
|
||||
|
||||
export const UserAccountAuthPermItem = ({
|
||||
tid,
|
||||
menuId,
|
||||
permName
|
||||
}: UserAccountAuthPermItemProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToNavigation = () => {
|
||||
navigate(PATHS.account.user.menuAuth, {
|
||||
state: {
|
||||
tid: tid,
|
||||
menuId: menuId
|
||||
}
|
||||
})
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="perm-item"
|
||||
onClick={ () => onClickToNavigation() }
|
||||
>
|
||||
<span className="perm-name">{ permName }</span>
|
||||
<span className="ic20 arrow-right"></span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
30
src/entities/account/ui/user-account-auth-perm-list.tsx
Normal file
30
src/entities/account/ui/user-account-auth-perm-list.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { UserAccountAuthPermListProps } from '../model/types';
|
||||
import { UserAccountAuthPermItem } from './user-account-auth-perm-item';
|
||||
export const UserAccountAuthPermList = ({
|
||||
tid,
|
||||
permItems
|
||||
}: UserAccountAuthPermListProps) => {
|
||||
|
||||
const getPermItems = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<permItems.length;i++){
|
||||
rs.push(
|
||||
<UserAccountAuthPermItem
|
||||
key={ 'key-perm-item-' + i }
|
||||
tid={ tid }
|
||||
menuId={ permItems[i]?.menuId }
|
||||
permName={ permItems[i]?.permName }
|
||||
></UserAccountAuthPermItem>
|
||||
);
|
||||
}
|
||||
return rs;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="perm-list">
|
||||
{ getPermItems() }
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
58
src/entities/account/ui/user-account-auth-wrap.tsx
Normal file
58
src/entities/account/ui/user-account-auth-wrap.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { UserAccountAuthWrapProps } from '../model/types';
|
||||
import { UserAccountAuthPermList } from './user-account-auth-perm-list';
|
||||
|
||||
export const UserAccountAuthWrap = ({
|
||||
tid
|
||||
}: UserAccountAuthWrapProps) => {
|
||||
let menuItems = [
|
||||
{menuId: 'menu1', permName: '거래조회'},
|
||||
{menuId: 'menu2', permName: '정산조회'},
|
||||
{menuId: 'menu3', permName: '가맹점 관리'},
|
||||
{menuId: 'menu4', permName: '결제 관리'},
|
||||
{menuId: 'menu5', permName: '계정 관리'},
|
||||
{menuId: 'menu6', permName: '부가세 신고 자료'},
|
||||
{menuId: 'menu7', permName: '부가서비스'},
|
||||
{menuId: 'menu8', permName: '고객지원'},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<div className="ing-list pdtop">
|
||||
<div className="perm-form">
|
||||
<div className="perm-field">
|
||||
<div className="perm-label">계정 상태</div>
|
||||
<div className="perm-control">
|
||||
<select>
|
||||
<option selected>사용</option>
|
||||
<option>미사용</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="perm-field">
|
||||
<div className="perm-label">로그인 범위</div>
|
||||
<div className="perm-control">
|
||||
<select>
|
||||
<option>MID</option>
|
||||
<option>GID</option>
|
||||
<option selected>MID + GID</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ing-title fs18">메뉴별 권한 설정</div>
|
||||
|
||||
<UserAccountAuthPermList
|
||||
tid={ tid }
|
||||
permItems={ menuItems }
|
||||
></UserAccountAuthPermList>
|
||||
|
||||
<div className="apply-row bottom-padding">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
type="button"
|
||||
>저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
100
src/entities/account/ui/user-login-auth-info-wrap.tsx
Normal file
100
src/entities/account/ui/user-login-auth-info-wrap.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import { UserLoginAuthInfoWrapProps } from '../model/types';
|
||||
|
||||
export const UserLoginAuthInfoWrap = ({
|
||||
tid
|
||||
}: UserLoginAuthInfoWrapProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ing-list pdtop">
|
||||
<div className="settings-login-auth">
|
||||
<div className="group">
|
||||
<div className="group-header">
|
||||
<div className="title">이메일 주소</div>
|
||||
<button
|
||||
className="ic20 plus"
|
||||
type="button"
|
||||
aria-label="추가"
|
||||
></button>
|
||||
</div>
|
||||
<div className="input-row">
|
||||
<input
|
||||
type="text"
|
||||
value="nicetest01@nicepay.co.kr"
|
||||
placeholder="example@domain.com"
|
||||
/>
|
||||
<button
|
||||
className="icon-btn minus"
|
||||
type="button"
|
||||
aria-label="삭제"
|
||||
></button>
|
||||
</div>
|
||||
<div className="input-row">
|
||||
<input
|
||||
type="text"
|
||||
value="nicetest01@nicepay.co.kr"
|
||||
placeholder="example@domain.com"
|
||||
/>
|
||||
<button
|
||||
className="icon-btn minus"
|
||||
type="button"
|
||||
aria-label="삭제"
|
||||
></button>
|
||||
</div>
|
||||
<div className="input-row">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="example@domain.com"
|
||||
/>
|
||||
<button
|
||||
className="icon-btn minus"
|
||||
type="button"
|
||||
aria-label="삭제"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="group">
|
||||
<div className="group-header">
|
||||
<div className="title">휴대폰 번호</div>
|
||||
<button
|
||||
className="ic20 plus"
|
||||
type="button"
|
||||
aria-label="추가"
|
||||
></button>
|
||||
</div>
|
||||
<div className="input-row">
|
||||
<input
|
||||
type="text"
|
||||
value="01012345678"
|
||||
placeholder="휴대폰 번호 입력"
|
||||
/>
|
||||
<button
|
||||
className="icon-btn minus"
|
||||
type="button"
|
||||
aria-label="삭제"
|
||||
></button>
|
||||
</div>
|
||||
<div className="input-row">
|
||||
<input
|
||||
type="text"
|
||||
value="01012345678"
|
||||
placeholder="휴대폰 번호 입력"
|
||||
readOnly={ true }
|
||||
/>
|
||||
<button
|
||||
className="icon-btn minus"
|
||||
type="button"
|
||||
aria-label="삭제"
|
||||
></button>
|
||||
</div>
|
||||
<div className="notice-bar">※ 탭을 변경하면 미저장 내용은 초기화됩니다.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="apply-row bottom-padding">
|
||||
<button className="btn-50 btn-blue flex-1">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
33
src/entities/account/ui/user-manage-auth-item.tsx
Normal file
33
src/entities/account/ui/user-manage-auth-item.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { UserManageAuthItemProps } from '../model/types';
|
||||
|
||||
export const UserManageAuthItem = ({
|
||||
useYn,
|
||||
authName,
|
||||
tid
|
||||
}: UserManageAuthItemProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToNavigation = () => {
|
||||
navigate(PATHS.account.user.loginAuthInfo, {
|
||||
state: {
|
||||
tid: tid
|
||||
}
|
||||
});
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="auth-item"
|
||||
onClick={ () => onClickToNavigation() }
|
||||
>
|
||||
<div className="auth-item-left">
|
||||
<span className={ `tag-pill ${(!!useYn)? '': 'red'}` }>{ (!!useYn)? '사용': '미사용' }</span>
|
||||
<span className="auth-name">{ authName }</span>
|
||||
</div>
|
||||
<span className="ic20 arrow-right"></span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
29
src/entities/account/ui/user-manage-auth-list.tsx
Normal file
29
src/entities/account/ui/user-manage-auth-list.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { UserManageAuthItem } from './user-manage-auth-item';
|
||||
import { UserManageAuthListProps } from '../model/types';
|
||||
|
||||
export const UserManageAuthList = ({
|
||||
authItems
|
||||
}: UserManageAuthListProps) => {
|
||||
|
||||
const getUserManageAuthItems = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<authItems.length;i++){
|
||||
rs.push(
|
||||
<UserManageAuthItem
|
||||
key={ 'UserManageAuthItem-key-' + i }
|
||||
useYn={ authItems[i]?.useYn }
|
||||
authName= { authItems[i]?.authName }
|
||||
tid= { authItems[i]?.tid }
|
||||
></UserManageAuthItem>
|
||||
);
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="auth-list">
|
||||
{ getUserManageAuthItems() }
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
53
src/entities/account/ui/user-manage-wrap.tsx
Normal file
53
src/entities/account/ui/user-manage-wrap.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { AuthItem } from '../model/types';
|
||||
import { UserManageAuthList } from './user-manage-auth-list';
|
||||
|
||||
export const UserManageWrap = () => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const [authItems, setAuthItems] = useState<Array<AuthItem>>([]);
|
||||
|
||||
const callAuthList = () => {
|
||||
setAuthItems([
|
||||
{useYn: true, authName: 'test01', tid: 'A12334556'},
|
||||
{useYn: true, authName: 'test02', tid: 'A33334556'},
|
||||
{useYn: true, authName: 'test03', tid: 'A12345556'},
|
||||
{useYn: true, authName: 'test04', tid: 'A12978676'},
|
||||
{useYn: false, authName: 'test05', tid: 'A12344444'},
|
||||
]);
|
||||
};
|
||||
|
||||
const onClickToNavigation = () => {
|
||||
navigate(PATHS.account.user.addAccount);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
callAuthList();
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<div className="ing-list">
|
||||
<div className="input-wrapper top-select mt-16">
|
||||
<select>
|
||||
<option>nicetest00m</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="ing-title">등록 현황</div>
|
||||
{ (!!authItems && authItems.length > 0) &&
|
||||
<UserManageAuthList
|
||||
authItems={ authItems }
|
||||
></UserManageAuthList>
|
||||
}
|
||||
<div className="apply-row bottom-padding">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
onClick={ () => onClickToNavigation() }
|
||||
>사용자 추가</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionAlimtalkDetailParams,
|
||||
ExtensionAlimtalkDetailResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionAlimtalkDetail = (params: ExtensionAlimtalkDetailParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionAlimtalkDetailResponse>(API_URL.extensionArsDetail(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionAlimtalkDetailMutation = (options?: UseMutationOptions<ExtensionAlimtalkDetailResponse, CBDCAxiosError, ExtensionAlimtalkDetailParams>) => {
|
||||
const mutation = useMutation<ExtensionAlimtalkDetailResponse, CBDCAxiosError, ExtensionAlimtalkDetailParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionAlimtalkDetailParams) => extensionAlimtalkDetail(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionAlimtalkDownloadExcelParams,
|
||||
ExtensionAlimtalkDownloadExcelResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionAlimtalkDownloadExcel = (params: ExtensionAlimtalkDownloadExcelParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionAlimtalkDownloadExcelResponse>(API_URL.extensionAlimtalkDownloadExcel(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionAlimtalkDownloadExcelMutation = (options?: UseMutationOptions<ExtensionAlimtalkDownloadExcelResponse, CBDCAxiosError, ExtensionAlimtalkDownloadExcelParams>) => {
|
||||
const mutation = useMutation<ExtensionAlimtalkDownloadExcelResponse, CBDCAxiosError, ExtensionAlimtalkDownloadExcelParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionAlimtalkDownloadExcelParams) => extensionAlimtalkDownloadExcel(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionAlimtalkListParams,
|
||||
ExtensionAlimtalkListResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionAlimtalkList = (params: ExtensionAlimtalkListParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionAlimtalkListResponse>(API_URL.extensionAlimtalkList(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionAlimtalkListMutation = (options?: UseMutationOptions<ExtensionAlimtalkListResponse, CBDCAxiosError, ExtensionAlimtalkListParams>) => {
|
||||
const mutation = useMutation<ExtensionAlimtalkListResponse, CBDCAxiosError, ExtensionAlimtalkListParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionAlimtalkListParams) => extensionAlimtalkList(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionAlimtalkSettingDetailParams,
|
||||
ExtensionAlimtalkSettingDetailResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionAlimtalkSettingDetail = (params: ExtensionAlimtalkSettingDetailParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionAlimtalkSettingDetailResponse>(API_URL.extensionAlimtalkSettingDetail(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionAlimtalkSettingSaveMutation = (options?: UseMutationOptions<ExtensionAlimtalkSettingDetailResponse, CBDCAxiosError, ExtensionAlimtalkSettingDetailParams>) => {
|
||||
const mutation = useMutation<ExtensionAlimtalkSettingDetailResponse, CBDCAxiosError, ExtensionAlimtalkSettingDetailParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionAlimtalkSettingDetailParams) => extensionAlimtalkSettingDetail(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionAlimtalkSettingSaveParams,
|
||||
ExtensionAlimtalkSettingSaveResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionAlimtalkSettingSave = (params: ExtensionAlimtalkSettingSaveParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionAlimtalkSettingSaveResponse>(API_URL.extensionAlimtalkSettingSave(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionAlimtalkSettingSaveMutation = (options?: UseMutationOptions<ExtensionAlimtalkSettingSaveResponse, CBDCAxiosError, ExtensionAlimtalkSettingSaveParams>) => {
|
||||
const mutation = useMutation<ExtensionAlimtalkSettingSaveResponse, CBDCAxiosError, ExtensionAlimtalkSettingSaveParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionAlimtalkSettingSaveParams) => extensionAlimtalkSettingSave(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionArsApplyParams,
|
||||
ExtensionArsApplyResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionArsApply = (params: ExtensionArsApplyParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionArsApplyResponse>(API_URL.extensionArsApply(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionArsApplyMutation = (options?: UseMutationOptions<ExtensionArsApplyResponse, CBDCAxiosError, ExtensionArsApplyParams>) => {
|
||||
const mutation = useMutation<ExtensionArsApplyResponse, CBDCAxiosError, ExtensionArsApplyParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionArsApplyParams) => extensionArsApply(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionArsDetailParams,
|
||||
ExtensionArsDetailResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionArsDetail = (params: ExtensionArsDetailParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionArsDetailResponse>(API_URL.extensionArsDetail(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionArsDetailMutation = (options?: UseMutationOptions<ExtensionArsDetailResponse, CBDCAxiosError, ExtensionArsDetailParams>) => {
|
||||
const mutation = useMutation<ExtensionArsDetailResponse, CBDCAxiosError, ExtensionArsDetailParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionArsDetailParams) => extensionArsDetail(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionArsDownloadExcelParams,
|
||||
ExtensionArsDownloadExcelResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionArsDownloadExcel = (params: ExtensionArsDownloadExcelParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionArsDownloadExcelResponse>(API_URL.extensionArsDownloadExcel(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionArsDownloadExcelMutation = (options?: UseMutationOptions<ExtensionArsDownloadExcelResponse, CBDCAxiosError, ExtensionArsDownloadExcelParams>) => {
|
||||
const mutation = useMutation<ExtensionArsDownloadExcelResponse, CBDCAxiosError, ExtensionArsDownloadExcelParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionArsDownloadExcelParams) => extensionArsDownloadExcel(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionArsListParams,
|
||||
ExtensionArsListResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionArsList = (params: ExtensionArsListParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionArsListResponse>(API_URL.extensionArsList(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionArsListMutation = (options?: UseMutationOptions<ExtensionArsListResponse, CBDCAxiosError, ExtensionArsListParams>) => {
|
||||
const mutation = useMutation<ExtensionArsListResponse, CBDCAxiosError, ExtensionArsListParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionArsListParams) => extensionArsList(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionArsResendParams,
|
||||
ExtensionArsResendResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionArsResend = (params: ExtensionArsResendParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionArsResendResponse>(API_URL.extensionArsResend(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionArsResendMutation = (options?: UseMutationOptions<ExtensionArsResendResponse, CBDCAxiosError, ExtensionArsResendParams>) => {
|
||||
const mutation = useMutation<ExtensionArsResendResponse, CBDCAxiosError, ExtensionArsResendParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionArsResendParams) => extensionArsResend(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionKeyinApplyParams,
|
||||
ExtensionKeyinApplyResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionKeyinApply = (params: ExtensionKeyinApplyParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionKeyinApplyResponse>(API_URL.extensionKeyinApply(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionKeyinListMutation = (options?: UseMutationOptions<ExtensionKeyinApplyResponse, CBDCAxiosError, ExtensionKeyinApplyParams>) => {
|
||||
const mutation = useMutation<ExtensionKeyinApplyResponse, CBDCAxiosError, ExtensionKeyinApplyParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionKeyinApplyParams) => extensionKeyinApply(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionKeyinDownloadExcelParams,
|
||||
ExtensionKeyinDownloadExcelResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionKeyinDownloadExcel = (params: ExtensionKeyinDownloadExcelParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionKeyinDownloadExcelResponse>(API_URL.extensionKeyinDownloadExcel(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionKeyinDownloadExcelMutation = (options?: UseMutationOptions<ExtensionKeyinDownloadExcelResponse, CBDCAxiosError, ExtensionKeyinDownloadExcelParams>) => {
|
||||
const mutation = useMutation<ExtensionKeyinDownloadExcelResponse, CBDCAxiosError, ExtensionKeyinDownloadExcelParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionKeyinDownloadExcelParams) => extensionKeyinDownloadExcel(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionKeyinListParams,
|
||||
ExtensionKeyinListResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionKeyinList = (params: ExtensionKeyinListParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionKeyinListResponse>(API_URL.extensionKeyinList(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionKeyinListMutation = (options?: UseMutationOptions<ExtensionKeyinListResponse, CBDCAxiosError, ExtensionKeyinListParams>) => {
|
||||
const mutation = useMutation<ExtensionKeyinListResponse, CBDCAxiosError, ExtensionKeyinListParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionKeyinListParams) => extensionKeyinList(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionListParams,
|
||||
ExtensionListResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionList = (params: ExtensionListParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionListResponse>(API_URL.extensionList(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionListMutation = (options?: UseMutationOptions<ExtensionListResponse, CBDCAxiosError, ExtensionListParams>) => {
|
||||
const mutation = useMutation<ExtensionListResponse, CBDCAxiosError, ExtensionListParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionListParams) => extensionList(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionSmsDetailParams,
|
||||
ExtensionSmsDetailResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionSmsDetail = (params: ExtensionSmsDetailParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionSmsDetailResponse>(API_URL.extensionSmsDetail(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionSmsListMutation = (options?: UseMutationOptions<ExtensionSmsDetailResponse, CBDCAxiosError, ExtensionSmsDetailParams>) => {
|
||||
const mutation = useMutation<ExtensionSmsDetailResponse, CBDCAxiosError, ExtensionSmsDetailParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionSmsDetailParams) => extensionSmsDetail(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionSmsDownloadExcelParams,
|
||||
ExtensionSmsDownloadExcelResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionSmsDownloadExcel = (params: ExtensionSmsDownloadExcelParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionSmsDownloadExcelResponse>(API_URL.extensionSmsDownloadExcel(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionSmsDownloadExcelMutation = (options?: UseMutationOptions<ExtensionSmsDownloadExcelResponse, CBDCAxiosError, ExtensionSmsDownloadExcelParams>) => {
|
||||
const mutation = useMutation<ExtensionSmsDownloadExcelResponse, CBDCAxiosError, ExtensionSmsDownloadExcelParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionSmsDownloadExcelParams) => extensionSmsDownloadExcel(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionSmsListParams,
|
||||
ExtensionSmsListResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionSmsList = (params: ExtensionSmsListParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionSmsListResponse>(API_URL.extensionSmsList(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionSmsListMutation = (options?: UseMutationOptions<ExtensionSmsListResponse, CBDCAxiosError, ExtensionSmsListParams>) => {
|
||||
const mutation = useMutation<ExtensionSmsListResponse, CBDCAxiosError, ExtensionSmsListParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionSmsListParams) => extensionSmsList(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
ExtensionSmsResendParams,
|
||||
ExtensionSmsResendResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const extensionSmsResend = (params: ExtensionSmsResendParams) => {
|
||||
return resultify(
|
||||
axios.post<ExtensionSmsResendResponse>(API_URL.extensionSmsResend(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useExtensionSmsResendMutation = (options?: UseMutationOptions<ExtensionSmsResendResponse, CBDCAxiosError, ExtensionSmsResendParams>) => {
|
||||
const mutation = useMutation<ExtensionSmsResendResponse, CBDCAxiosError, ExtensionSmsResendParams>({
|
||||
...options,
|
||||
mutationFn: (params: ExtensionSmsResendParams) => extensionSmsResend(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
279
src/entities/additional-service/model/types.ts
Normal file
279
src/entities/additional-service/model/types.ts
Normal file
@@ -0,0 +1,279 @@
|
||||
import { DefaulResponsePagination } from '@/entities/common/model/types';
|
||||
|
||||
export interface ExtensionRequestParams {
|
||||
mid: string;
|
||||
};
|
||||
export interface ExtensionSmsResendParams extends ExtensionRequestParams {
|
||||
tid: string;
|
||||
};
|
||||
export interface ExtensionSmsResendResponse {
|
||||
|
||||
};
|
||||
export interface ExtensionSmsListParams extends ExtensionRequestParams {
|
||||
tid: string;
|
||||
searchCl: string;
|
||||
searchValue: string;
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
smsCl: string;
|
||||
};
|
||||
export interface ExtensionSmsListItemProps {
|
||||
mid: string;
|
||||
tid: string;
|
||||
paymentDate: string;
|
||||
paymentStatus: string;
|
||||
smsCl: string;
|
||||
};
|
||||
export interface ExtensionSmsListResponse extends DefaulResponsePagination {
|
||||
content: Array<ExtensionSmsListItemProps>
|
||||
};
|
||||
export interface ExtensionSmsDownloadExcelParams extends ExtensionRequestParams {
|
||||
searchCl: string;
|
||||
searchValue: string;
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
smsCl: string;
|
||||
};
|
||||
export interface ExtensionSmsDownloadExcelResponse {
|
||||
|
||||
};
|
||||
export interface ExtensionSmsDetailParams extends ExtensionRequestParams {
|
||||
tid: string;
|
||||
};
|
||||
export interface ExtensionSmsDetailResponse {
|
||||
senderNumber: string;
|
||||
senderName: string;
|
||||
receiverNumber: string;
|
||||
receiverName: string;
|
||||
sendMessage: string;
|
||||
};
|
||||
export interface ExtensionListParams extends ExtensionRequestParams {
|
||||
|
||||
};
|
||||
export interface activeExtensionListItem {
|
||||
extensionCode: string;
|
||||
extensionName: string;
|
||||
extensionInformation: string;
|
||||
};
|
||||
export interface availableExtensionListItem {
|
||||
extensionCode: string;
|
||||
extensionName: string;
|
||||
extensionInformation: string;
|
||||
};
|
||||
export interface ExtensionListItemProps {
|
||||
activeExtensionList: Array<activeExtensionListItem>;
|
||||
availableExtensionList: Array<availableExtensionListItem>;
|
||||
};
|
||||
export interface ExtensionListResponse extends DefaulResponsePagination {
|
||||
content: Array<ExtensionListItemProps>
|
||||
};
|
||||
export interface ExtensionKeyinListParams extends ExtensionRequestParams {
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
paymentStatus: string;
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
};
|
||||
export interface ExtensionKeyinListItemProps {
|
||||
tid: string;
|
||||
paymentDate: string;
|
||||
paymentStatus: string;
|
||||
amount: number;
|
||||
};
|
||||
export interface ExtensionKeyinListResponse extends DefaulResponsePagination {
|
||||
content: Array<ExtensionKeyinListItemProps>
|
||||
};
|
||||
export interface ExtensionKeyinDownloadExcelParams extends ExtensionRequestParams {
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
paymentStatus: string;
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
};
|
||||
export interface ExtensionKeyinDownloadExcelResponse {
|
||||
|
||||
};
|
||||
export interface ExtensionKeyinApplyParams extends ExtensionRequestParams {
|
||||
goodsName: string;
|
||||
amount: number;
|
||||
buyerName: string;
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
cardNo: string;
|
||||
cardExpirationDate: string;
|
||||
instmntMonth: string;
|
||||
moid: string;
|
||||
};
|
||||
export interface ExtensionKeyinApplyResponse {
|
||||
|
||||
};
|
||||
export interface ExtensionArsResendParams extends ExtensionRequestParams {
|
||||
tid: string;
|
||||
};
|
||||
export interface ExtensionArsResendResponse {
|
||||
|
||||
};
|
||||
export interface ExtensionArsListParams extends ExtensionRequestParams {
|
||||
moid: string;
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
paymentStatus: string;
|
||||
orderStatus: string;
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
};
|
||||
export interface ExtensionArsListItemProps {
|
||||
tid: string;
|
||||
paymentDate: string;
|
||||
paymentStatus: string;
|
||||
orderStatus: string;
|
||||
arsPaymentMethod: string;
|
||||
amount: number;
|
||||
};
|
||||
export interface ExtensionArsListResponse extends DefaulResponsePagination {
|
||||
content: Array<ExtensionArsListItemProps>
|
||||
};
|
||||
export interface ExtensionArsDownloadExcelParams extends ExtensionRequestParams {
|
||||
moid: string;
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
paymentStatus: string;
|
||||
orderStatus: string;
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
};
|
||||
export interface ExtensionArsDownloadExcelResponse {
|
||||
|
||||
};
|
||||
export interface ExtensionArsDetailParams extends ExtensionRequestParams {
|
||||
tid: string;
|
||||
};
|
||||
export interface ExtensionArsDetailResponse {
|
||||
corpName: string;
|
||||
mid: string;
|
||||
arsPaymentMethod: string;
|
||||
paymentStatus: string;
|
||||
orderStatus: string;
|
||||
paymentDate: string;
|
||||
goodsName: string;
|
||||
tid: string;
|
||||
buyerName: string;
|
||||
phoneNumber: string;
|
||||
maskPhoneNumber: string;
|
||||
email: string;
|
||||
smsVerificationCode: string;
|
||||
};
|
||||
export interface ExtensionArsApplyParams extends ExtensionRequestParams {
|
||||
moid: string;
|
||||
goodsName: string;
|
||||
amount: number;
|
||||
instmntMonth: string;
|
||||
buyerName: string;
|
||||
phoneNumber: string;
|
||||
email: string;
|
||||
arsPaymentMethod: string;
|
||||
};
|
||||
export interface ExtensionArsApplyResponse {
|
||||
|
||||
};
|
||||
export interface SendMerchantInfoItem {
|
||||
cardApprovalFlag: boolean;
|
||||
cardCancelFlag: boolean;
|
||||
bankApprovalFlag: boolean;
|
||||
bankCancelFlag: boolean;
|
||||
virtureAccountDepositRequestFlag: boolean;
|
||||
virtureAccountDepositCompleteFlag: boolean;
|
||||
virtureAccountRefundFlag: boolean;
|
||||
};
|
||||
export interface SendUserInfoItem {
|
||||
cardApprovalFlag: boolean;
|
||||
cardCancelFlag: boolean;
|
||||
bankApprovalFlag: boolean;
|
||||
bankCancelFlag: boolean;
|
||||
virtureAccountDepositRequestFlag: boolean;
|
||||
virtureAccountDepositCompleteFlag: boolean;
|
||||
virtureAccountRefundFlag: boolean;
|
||||
};
|
||||
export interface ExtensionAlimtalkSettingSaveParams extends ExtensionRequestParams {
|
||||
sendMerchantInfo: SendMerchantInfoItem;
|
||||
sendUserInfo: SendUserInfoItem;
|
||||
};
|
||||
export interface ExtensionAlimtalkSettingSaveResponse {
|
||||
|
||||
};
|
||||
export interface ExtensionAlimtalkSettingDetailParams extends ExtensionRequestParams {
|
||||
|
||||
};
|
||||
export interface ExtensionAlimtalkSettingDetailItem {
|
||||
sendMerchantInfo: SendMerchantInfoItem;
|
||||
sendUserInfo: SendUserInfoItem;
|
||||
};
|
||||
export interface ExtensionAlimtalkSettingDetailResponse extends DefaulResponsePagination {
|
||||
content: Array<ExtensionAlimtalkSettingDetailItem>
|
||||
};
|
||||
export interface ExtensionAlimtalkListParams extends ExtensionRequestParams {
|
||||
searchCl: string;
|
||||
searchValue: string;
|
||||
paymentMethod: string;
|
||||
alimCl: string;
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
sendType: string;
|
||||
sendCl: string;
|
||||
};
|
||||
export interface ExtensionAlimtalkListItem {
|
||||
tid: string;
|
||||
sendDate: string;
|
||||
alimCl: string;
|
||||
sendType: string;
|
||||
sendCl: string;
|
||||
paymentMethod: string;
|
||||
receiverName: string;
|
||||
};
|
||||
export interface ExtensionAlimtalkListResponse extends DefaulResponsePagination {
|
||||
content: Array<ExtensionAlimtalkListItem>
|
||||
};
|
||||
export interface ExtensionAlimtalkDownloadExcelParams extends ExtensionRequestParams {
|
||||
searchCl: string;
|
||||
searchValue: string;
|
||||
paymentMethod: string;
|
||||
alimCl: string;
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
sendType: string;
|
||||
sendCl: string;
|
||||
};
|
||||
export interface ExtensionAlimtalkDownloadExcelResponse {
|
||||
|
||||
};
|
||||
export interface ExtensionAlimtalkDetailParams extends ExtensionRequestParams {
|
||||
tid: string;
|
||||
};
|
||||
export interface ExtensionAlimtalkDetailResponse {
|
||||
receiverName: string;
|
||||
merchantName: string;
|
||||
sendDate: string;
|
||||
mid: string;
|
||||
tid: string;
|
||||
serviceName: string;
|
||||
sendType: string;
|
||||
senderName: string;
|
||||
paymentMethod: string;
|
||||
alimCl: string;
|
||||
sendCl: string;
|
||||
};
|
||||
export interface IntroListItemProps {
|
||||
className?: string;
|
||||
serviceName?: string;
|
||||
serviceDesc?: string;
|
||||
icon?: string;
|
||||
path?: string;
|
||||
};
|
||||
export interface ArsCardPaymentFinishProps {
|
||||
requestSuccess: boolean;
|
||||
setRequestSuccess: (requestSuccess: boolean) => void;
|
||||
};
|
||||
export interface SmsPaymentDetailResendProps {
|
||||
bottomSmsPaymentDetailResendOn: boolean;
|
||||
setBottomSmsPaymentDetailResendOn: (bottomSmsPaymentDetailResendOn: boolean) => void;
|
||||
};
|
||||
35
src/entities/additional-service/ui/intro-list-item.tsx
Normal file
35
src/entities/additional-service/ui/intro-list-item.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { IntroListItemProps } from '../model/types';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
|
||||
export const IntroListItem = ({
|
||||
className,
|
||||
serviceName,
|
||||
serviceDesc,
|
||||
icon,
|
||||
path
|
||||
}: IntroListItemProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToNavigate = () => {
|
||||
if(!!path){
|
||||
navigate(path);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={ className }
|
||||
onClick={ () => onClickToNavigate() }
|
||||
>
|
||||
<div>
|
||||
<div className="service-name">{ serviceName }</div>
|
||||
<p className="service-desc">{ serviceDesc }</p>
|
||||
</div>
|
||||
<img
|
||||
src={ icon }
|
||||
alt={ serviceName }
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { SmsPaymentDetailResendProps } from '../model/types';
|
||||
|
||||
export const SmsPaymentDetailResend = ({
|
||||
bottomSmsPaymentDetailResendOn,
|
||||
setBottomSmsPaymentDetailResendOn
|
||||
}: SmsPaymentDetailResendProps) => {
|
||||
|
||||
const variants = {
|
||||
hidden: { y: '100%' },
|
||||
visible: { y: '0%' },
|
||||
};
|
||||
const onClickToClose = () => {
|
||||
// close
|
||||
setBottomSmsPaymentDetailResendOn(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{ bottomSmsPaymentDetailResendOn &&
|
||||
<div className="bg-dim"></div>
|
||||
}
|
||||
<motion.div
|
||||
className="bottomsheet"
|
||||
initial="hidden"
|
||||
animate={ (bottomSmsPaymentDetailResendOn)? 'visible': 'hidden' }
|
||||
variants={ variants }
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<div className="bottomsheet-header">
|
||||
<div className="bottomsheet-title">
|
||||
<h2>SMS 상세 & 재발송</h2>
|
||||
<button
|
||||
className="close-btn"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_close.svg' }
|
||||
alt="닫기"
|
||||
onClick={ () => onClickToClose() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="resend-info">
|
||||
<div className="resend-row">발신자(번호) : 유앤아이피부과(16610808)</div>
|
||||
<div className="resend-row">수신자(번호) : 김*환(010****7000)</div>
|
||||
</div>
|
||||
<div className="resend-box">
|
||||
<p className="resend-text">[유앤아이피부과]김*환님, 신한은행 110322141414 (300원 06/08 입금완료)</p>
|
||||
</div>
|
||||
<div className="bottomsheet-footer">
|
||||
<button className="btn-50 btn-blue flex-1" type="button">신청</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
5
src/entities/alarm/model/types.ts
Normal file
5
src/entities/alarm/model/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface AlarmItemProps {
|
||||
title?: string,
|
||||
category?: string,
|
||||
date?: string
|
||||
};
|
||||
37
src/entities/alarm/ui/alarm-item.tsx
Normal file
37
src/entities/alarm/ui/alarm-item.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { AlarmItemProps } from '../model/types';
|
||||
|
||||
export const AlarmItem = ({
|
||||
title,
|
||||
category,
|
||||
date
|
||||
}: AlarmItemProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
const onClickToNavigate = (alarmId: number) => {
|
||||
let path = PATHS.support.notice.detail + alarmId;
|
||||
navigate(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="notice-item">
|
||||
<div className="notice-content">
|
||||
<div className="notice-title">{ title }</div>
|
||||
<div className="notice-meta">
|
||||
<strong>{ category }</strong>
|
||||
<span>{ date }</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="notice-arrow"
|
||||
onClick={ () => onClickToNavigate(4) }
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/Forward.svg' }
|
||||
alt={ category + '바로가기' }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
40
src/entities/alarm/ui/alarm-list.tsx
Normal file
40
src/entities/alarm/ui/alarm-list.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { AlarmItem } from './alarm-item';
|
||||
import { AlarmItemProps } from '../model/types';
|
||||
|
||||
|
||||
export const AlarmList = () => {
|
||||
|
||||
const alarmItems: Array<AlarmItemProps> = [
|
||||
{title: '시스템 안정화를 위한 정기 점검이 예정되어 있습니다.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
{title: '가맹점 관리 메뉴에 거래내역 엑셀 다운로드 기능이 추가 되었습니다.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
{title: '신규 가맹점을 대상으로 거래수수료 인하 혜택을 12월까지 제공합니다.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
{title: '앱의 안정성과 사용성을 개선한 버전 2.3.1이 출시되었습니다.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
{title: '점검 시간 동안 일부 서비스 이용이 제한될 수 있으니 미리 확인해주세요.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
{title: '가맹점 관리 메뉴에 거래내역 엑셀 다운로드 기능이 추가 되었습니다.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
{title: '신규 가맹점을 대상으로 거래수수료 인하 혜택을 12월까지 제공합니다.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
{title: '앱의 안정성과 사용성을 개선한 버전 2.3.1이 출시되었습니다.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
{title: '점검 시간 동안 일부 서비스 이용이 제한될 수 있으니 미리 확인해주세요.', category: '공지사항', date: '2025.06.01 10:00:00'},
|
||||
];
|
||||
const getAlarmItems = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<alarmItems.length;i++){
|
||||
rs.push(
|
||||
<AlarmItem
|
||||
title={ alarmItems[i]?.title }
|
||||
category={ alarmItems[i]?.category }
|
||||
date={ alarmItems[i]?.date }
|
||||
></AlarmItem>
|
||||
)
|
||||
}
|
||||
return rs;
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="notice-box sub">
|
||||
{ getAlarmItems() }
|
||||
</div>
|
||||
<div className="notice-alert">※ 알림은 90일간 보관 후 삭제됩니다.</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
BusinessMemberInfoParams,
|
||||
BusinessMemberInfoResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const businessMemberInfo = (params: BusinessMemberInfoParams) => {
|
||||
return resultify(
|
||||
axios.post<BusinessMemberInfoResponse>(API_URL.businessMemberInfo(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useBusinessMemberInfoMutation = (options?: UseMutationOptions<BusinessMemberInfoResponse, CBDCAxiosError, BusinessMemberInfoParams>) => {
|
||||
const mutation = useMutation<BusinessMemberInfoResponse, CBDCAxiosError, BusinessMemberInfoParams>({
|
||||
...options,
|
||||
mutationFn: (params: BusinessMemberInfoParams) => businessMemberInfo(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
56
src/entities/business-member/model/types.ts
Normal file
56
src/entities/business-member/model/types.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
export enum BusinessMemberTabKeys {
|
||||
Info = 'Info',
|
||||
RegistrationStatus = 'RegistrationStatus',
|
||||
};
|
||||
export enum BusinessMemberInfoKeys {
|
||||
InfoContractManager = 'InfoContractManager',
|
||||
InfoTechnicalManager = 'InfoTechnicalManager',
|
||||
InfoSettlementManager = 'InfoSettlementManager',
|
||||
InfoSettlementAccount = 'InfoSettlementAccount'
|
||||
};
|
||||
export interface BusinessMemberTabProps {
|
||||
activeTab: BusinessMemberTabKeys;
|
||||
};
|
||||
export interface InfoArrowProps {
|
||||
show?: boolean;
|
||||
};
|
||||
export interface BusinessMemberRequestParams {
|
||||
tid?: string;
|
||||
};
|
||||
export interface InfoBasicInfoProps {
|
||||
|
||||
};
|
||||
export interface InfoOwnerInfoProps {
|
||||
|
||||
};
|
||||
export interface InfoCompanyInfoProps {
|
||||
|
||||
};
|
||||
export interface InfoContractManagerProps {
|
||||
|
||||
};
|
||||
export interface InfoTechnicalManagerProps {
|
||||
|
||||
};
|
||||
export interface InfoSettlementManagerProps {
|
||||
|
||||
};
|
||||
export interface InfoSettlementAccountProps {
|
||||
|
||||
};
|
||||
export interface BusinessMemberInfoResponse {
|
||||
InfoBasicInfo?: InfoBasicInfoProps;
|
||||
InfoOwnerInfo?: InfoOwnerInfoProps;
|
||||
InfoCompanyInfo?: InfoCompanyInfoProps;
|
||||
infoContractManager?: InfoContractManagerProps;
|
||||
infoTechnicalManager?: InfoTechnicalManagerProps;
|
||||
infoSettlementManager?: InfoSettlementManagerProps;
|
||||
infoSettlementAccount?: InfoSettlementAccountProps;
|
||||
};
|
||||
export interface InfoProps extends BusinessMemberInfoResponse{
|
||||
show?: boolean;
|
||||
onClickToShowInfo?: (info: BusinessMemberInfoKeys) => void;
|
||||
};
|
||||
export interface BusinessMemberInfoParams extends BusinessMemberRequestParams {
|
||||
svcCd: string;
|
||||
};
|
||||
37
src/entities/business-member/ui/business-member-tab.tsx
Normal file
37
src/entities/business-member/ui/business-member-tab.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import {
|
||||
BusinessMemberTabKeys,
|
||||
BusinessMemberTabProps
|
||||
} from '../model/types';
|
||||
|
||||
export const BusinessMemberTab = ({
|
||||
activeTab
|
||||
}: BusinessMemberTabProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToNavigation = (tab: BusinessMemberTabKeys) => {
|
||||
if(activeTab !== tab){
|
||||
if(tab === BusinessMemberTabKeys.Info){
|
||||
navigate(PATHS.businessMember.info);
|
||||
}
|
||||
else if(tab === BusinessMemberTabKeys.RegistrationStatus){
|
||||
navigate(PATHS.businessMember.registrationStatus);
|
||||
}
|
||||
}
|
||||
};
|
||||
return(
|
||||
<>
|
||||
<div className="subTab">
|
||||
<button
|
||||
className={`subtab-btn ${(activeTab === BusinessMemberTabKeys.Info)? 'active': ''}` }
|
||||
onClick={ () => onClickToNavigation(BusinessMemberTabKeys.Info) }
|
||||
>가맹점 정보</button>
|
||||
<button
|
||||
className={`subtab-btn ${(activeTab === BusinessMemberTabKeys.RegistrationStatus)? 'active': ''}` }
|
||||
onClick={ () => onClickToNavigation(BusinessMemberTabKeys.RegistrationStatus) }
|
||||
>등록현황</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
23
src/entities/business-member/ui/info-arrow.tsx
Normal file
23
src/entities/business-member/ui/info-arrow.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { InfoArrowProps } from '../model/types';
|
||||
|
||||
export const InfoArrow = ({ show }: InfoArrowProps) => {
|
||||
const [altMsg, setAltMsg] = useState<'접기' | '펼치기'>('접기');
|
||||
const [className, setClassName] = useState<string>('ic20 rot-180');
|
||||
|
||||
useEffect(() => {
|
||||
setAltMsg((show)? '접기': '펼치기');
|
||||
setClassName(`ic20 ${(show)? 'rot-180': ''}`);
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<img
|
||||
className={ className }
|
||||
src={ IMAGE_ROOT + '/select_arrow.svg' }
|
||||
alt={ altMsg }
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
36
src/entities/business-member/ui/info-basic-info.tsx
Normal file
36
src/entities/business-member/ui/info-basic-info.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { InfoProps } from '../model/types';
|
||||
|
||||
export const InfoBasicInfo = ({
|
||||
InfoBasicInfo
|
||||
}: InfoProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="section">
|
||||
<div className="section-title">기본정보</div>
|
||||
<ul className="kv-list">
|
||||
<li className="kv-row">
|
||||
<span className="k">상호</span>
|
||||
<span className="v">나이스테스트</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">사업자번호</span>
|
||||
<span className="v">123-45-16798</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">사업자속성</span>
|
||||
<span className="v">일반</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">업종</span>
|
||||
<span className="v">식품</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">업태</span>
|
||||
<span className="v">식품</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
23
src/entities/business-member/ui/info-company-info.tsx
Normal file
23
src/entities/business-member/ui/info-company-info.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { InfoProps } from '../model/types';
|
||||
|
||||
export const InfoCompanyInfo = ({
|
||||
InfoCompanyInfo
|
||||
}: InfoProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="section">
|
||||
<ul className="kv-list">
|
||||
<li className="kv-row">
|
||||
<span className="k">사업장주소</span>
|
||||
<span className="v">서울특별시 마포구 마포대로 217(아현동)</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">홈페이지 주소</span>
|
||||
<span className="v">https://adm.dev-nicepay.co.kr:8011</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
53
src/entities/business-member/ui/info-contract-manager.tsx
Normal file
53
src/entities/business-member/ui/info-contract-manager.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { BusinessMemberInfoKeys, InfoProps } from '../model/types';
|
||||
import { InfoArrow } from './info-arrow';
|
||||
|
||||
export const InfoContractManager = ({
|
||||
infoContractManager,
|
||||
show,
|
||||
onClickToShowInfo
|
||||
}: InfoProps) => {
|
||||
|
||||
const variants = {
|
||||
hidden: { height: 0, padding: 0, display: 'none' },
|
||||
visible: { height: 'auto', paddingTop: '12px', display: 'block' },
|
||||
};
|
||||
|
||||
const onClickToSetShowInfo = () => {
|
||||
if(!!onClickToShowInfo){
|
||||
onClickToShowInfo(BusinessMemberInfoKeys.InfoContractManager);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="section">
|
||||
<div className="section-title"
|
||||
onClick={ () => onClickToSetShowInfo() }
|
||||
>
|
||||
계약 담당자 <InfoArrow show={ show }></InfoArrow>
|
||||
</div>
|
||||
<motion.ul
|
||||
className="kv-list"
|
||||
initial="hidden"
|
||||
animate={ (show)? 'visible': 'hidden' }
|
||||
variants={ variants }
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<li className="kv-row">
|
||||
<span className="k">김테스트</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">010-1234-****</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">testkim@nicepay.co.kr</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
</motion.ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
27
src/entities/business-member/ui/info-owner-info.tsx
Normal file
27
src/entities/business-member/ui/info-owner-info.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { InfoProps } from '../model/types';
|
||||
|
||||
export const InfoOwnerInfo = ({
|
||||
InfoOwnerInfo
|
||||
}: InfoProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="section">
|
||||
<ul className="kv-list">
|
||||
<li className="kv-row">
|
||||
<span className="k">대표자명</span>
|
||||
<span className="v">김테스트</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">대표 연락처</span>
|
||||
<span className="v">010-1234-1234</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">대표 이메일</span>
|
||||
<span className="v">testkim@nicepay.co.kr</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
53
src/entities/business-member/ui/info-settlement-account.tsx
Normal file
53
src/entities/business-member/ui/info-settlement-account.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { BusinessMemberInfoKeys, InfoProps } from '../model/types';
|
||||
import { InfoArrow } from './info-arrow';
|
||||
|
||||
export const InfoSettlementAccount = ({
|
||||
infoSettlementAccount,
|
||||
show,
|
||||
onClickToShowInfo
|
||||
}: InfoProps) => {
|
||||
|
||||
const variants = {
|
||||
hidden: { height: 0, padding: 0, display: 'none' },
|
||||
visible: { height: 'auto', paddingTop: '12px', display: 'block' },
|
||||
};
|
||||
|
||||
const onClickToSetShowInfo = () => {
|
||||
if(!!onClickToShowInfo){
|
||||
onClickToShowInfo(BusinessMemberInfoKeys.InfoSettlementAccount);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="section">
|
||||
<div className="section-title"
|
||||
onClick={ () => onClickToSetShowInfo() }
|
||||
>
|
||||
정산계좌 <InfoArrow show={ show }></InfoArrow>
|
||||
</div>
|
||||
<motion.ul
|
||||
className="kv-list"
|
||||
initial="hidden"
|
||||
animate={ (show)? 'visible': 'hidden' }
|
||||
variants={ variants }
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<li className="kv-row">
|
||||
<span className="k">은행</span>
|
||||
<span className="v">신한은행</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">계좌번호</span>
|
||||
<span className="v">123-45-16798</span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">예금주</span>
|
||||
<span className="v">김테스트</span>
|
||||
</li>
|
||||
</motion.ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
53
src/entities/business-member/ui/info-settlement-manager.tsx
Normal file
53
src/entities/business-member/ui/info-settlement-manager.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { BusinessMemberInfoKeys, InfoProps } from '../model/types';
|
||||
import { InfoArrow } from './info-arrow';
|
||||
|
||||
export const InfoSettlementManager = ({
|
||||
infoSettlementManager,
|
||||
show,
|
||||
onClickToShowInfo
|
||||
}: InfoProps) => {
|
||||
|
||||
const variants = {
|
||||
hidden: { height: 0, padding: 0, display: 'none' },
|
||||
visible: { height: 'auto', paddingTop: '12px', display: 'block' },
|
||||
};
|
||||
|
||||
const onClickToSetShowInfo = () => {
|
||||
if(!!onClickToShowInfo){
|
||||
onClickToShowInfo(BusinessMemberInfoKeys.InfoSettlementManager);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="section">
|
||||
<div className="section-title"
|
||||
onClick={ () => onClickToSetShowInfo() }
|
||||
>
|
||||
정산 담당자 <InfoArrow show={ show }></InfoArrow>
|
||||
</div>
|
||||
<motion.ul
|
||||
className="kv-list"
|
||||
initial="hidden"
|
||||
animate={ (show)? 'visible': 'hidden' }
|
||||
variants={ variants }
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<li className="kv-row">
|
||||
<span className="k">김테스트</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">010-1234-****</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">testkim@nicepay.co.kr</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
</motion.ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
53
src/entities/business-member/ui/info-technical-manager.tsx
Normal file
53
src/entities/business-member/ui/info-technical-manager.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { BusinessMemberInfoKeys, InfoProps } from '../model/types';
|
||||
import { InfoArrow } from './info-arrow';
|
||||
|
||||
export const InfoTechnicalManager = ({
|
||||
infoTechnicalManager,
|
||||
show,
|
||||
onClickToShowInfo
|
||||
}: InfoProps) => {
|
||||
|
||||
const variants = {
|
||||
hidden: { height: 0, padding: 0, display: 'none' },
|
||||
visible: { height: 'auto', paddingTop: '12px', display: 'block' },
|
||||
};
|
||||
|
||||
const onClickToSetShowInfo = () => {
|
||||
if(!!onClickToShowInfo){
|
||||
onClickToShowInfo(BusinessMemberInfoKeys.InfoTechnicalManager);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="section">
|
||||
<div className="section-title"
|
||||
onClick={ () => onClickToSetShowInfo() }
|
||||
>
|
||||
기술 담당자 <InfoArrow show={ show }></InfoArrow>
|
||||
</div>
|
||||
<motion.ul
|
||||
className="kv-list"
|
||||
initial="hidden"
|
||||
animate={ (show)? 'visible': 'hidden' }
|
||||
variants={ variants }
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<li className="kv-row">
|
||||
<span className="k">김테스트</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">010-1234-****</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
<li className="kv-row">
|
||||
<span className="k">testkim@nicepay.co.kr</span>
|
||||
<span className="v"></span>
|
||||
</li>
|
||||
</motion.ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
121
src/entities/business-member/ui/info-wrap.tsx
Normal file
121
src/entities/business-member/ui/info-wrap.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBusinessMemberInfoMutation } from '../api/use-business-member-info-mutation';
|
||||
import { InfoBasicInfo } from './info-basic-info';
|
||||
import { InfoOwnerInfo } from './info-owner-info';
|
||||
import { InfoCompanyInfo } from './info-company-info';
|
||||
import { InfoContractManager } from './info-contract-manager';
|
||||
import { InfoTechnicalManager } from './info-technical-manager';
|
||||
import { InfoSettlementManager } from './info-settlement-manager';
|
||||
import { InfoSettlementAccount } from './info-settlement-account';
|
||||
import {
|
||||
BusinessMemberInfoParams,
|
||||
BusinessMemberInfoResponse,
|
||||
InfoContractManagerProps,
|
||||
InfoTechnicalManagerProps,
|
||||
InfoSettlementManagerProps,
|
||||
InfoSettlementAccountProps,
|
||||
BusinessMemberInfoKeys
|
||||
} from '../model/types';
|
||||
|
||||
export const InfoWrap = () => {
|
||||
const [infoContractManager, setInfoContractManager] = useState<InfoContractManagerProps>();
|
||||
const [infoTechnicalManager, setInfoTechnicalManager] = useState<InfoTechnicalManagerProps>();
|
||||
const [infoSettlementManager, setInfoSettlementManager] = useState<InfoSettlementManagerProps>();
|
||||
const [infoSettlementAccount, setInfoSettlementAccount] = useState<InfoSettlementAccountProps>();
|
||||
const [showInfoContractManager, setShowInfoContractManager] = useState<boolean>(false);
|
||||
const [showInfoTechnicalManager, setShowInfoTechnicalManager] = useState<boolean>(false);
|
||||
const [showInfoSettlementManager, setShowInfoSettlementManager] = useState<boolean>(false);
|
||||
const [showInfoSettlementAccount, setShowInfoSettlemenAccount] = useState<boolean>(false);
|
||||
|
||||
const { mutateAsync: businessMemberInfo } = useBusinessMemberInfoMutation();
|
||||
|
||||
const callInfo = () => {
|
||||
let businessMemberInfoParams: BusinessMemberInfoParams = {
|
||||
svcCd: 'st',
|
||||
};
|
||||
businessMemberInfo(businessMemberInfoParams).then((rs: BusinessMemberInfoResponse) => {
|
||||
setInfoContractManager(rs.infoContractManager);
|
||||
setInfoTechnicalManager(rs.infoTechnicalManager);
|
||||
setInfoSettlementManager(rs.infoSettlementManager);
|
||||
setInfoSettlementAccount(rs.infoSettlementAccount);
|
||||
});
|
||||
};
|
||||
|
||||
const onClickToShowInfo = (info: BusinessMemberInfoKeys) => {
|
||||
if(info === BusinessMemberInfoKeys.InfoContractManager){
|
||||
setShowInfoContractManager(!showInfoContractManager);
|
||||
}
|
||||
else if(info === BusinessMemberInfoKeys.InfoTechnicalManager){
|
||||
setShowInfoTechnicalManager(!showInfoTechnicalManager);
|
||||
}
|
||||
else if(info === BusinessMemberInfoKeys.InfoSettlementManager){
|
||||
setShowInfoSettlementManager(!showInfoSettlementManager);
|
||||
}
|
||||
else if(info === BusinessMemberInfoKeys.InfoSettlementAccount){
|
||||
setShowInfoSettlemenAccount(!showInfoSettlementAccount);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
callInfo();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="input-wrapper top-select mt-30">
|
||||
<select>
|
||||
<option value="1">nicetest00g</option>
|
||||
<option value="2">nicetest00g</option>
|
||||
<option value="3">nicetest00g</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="merchant-info">
|
||||
<InfoBasicInfo></InfoBasicInfo>
|
||||
<div className="info-divider mb-16"></div>
|
||||
|
||||
<InfoOwnerInfo></InfoOwnerInfo>
|
||||
|
||||
<div className="info-divider mb-16"></div>
|
||||
|
||||
<InfoCompanyInfo></InfoCompanyInfo>
|
||||
|
||||
<div className="info-divider mb-16"></div>
|
||||
|
||||
<InfoContractManager
|
||||
infoContractManager={ infoContractManager }
|
||||
show={ showInfoContractManager }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></InfoContractManager>
|
||||
|
||||
<div className="info-divider mb-16"></div>
|
||||
|
||||
<InfoTechnicalManager
|
||||
infoTechnicalManager={ infoTechnicalManager }
|
||||
show={ showInfoTechnicalManager }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></InfoTechnicalManager>
|
||||
|
||||
<div className="info-divider mb-16"></div>
|
||||
|
||||
<InfoSettlementManager
|
||||
infoSettlementManager={ infoSettlementManager }
|
||||
show={ showInfoSettlementManager }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></InfoSettlementManager>
|
||||
|
||||
<div className="info-divider mb-16"></div>
|
||||
|
||||
<InfoSettlementAccount
|
||||
infoSettlementManager={ infoSettlementAccount }
|
||||
show={ showInfoSettlementAccount }
|
||||
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
|
||||
></InfoSettlementAccount>
|
||||
|
||||
<div className="notice-bottom left-align">
|
||||
<p className="notice-tip">※ 가맹점 정보는 앱에서 수정할 수 없습니다.<br/>PC 가맹점 관리자에서 설정해 주세요.</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
export const RegistrationStatusWrap = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
CodesCacheRefreshByCodeClParams,
|
||||
CodesCacheRefreshByCodeClResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const codesCacheRefreshByCodeCl = ({
|
||||
codeCl
|
||||
}: CodesCacheRefreshByCodeClParams) => {
|
||||
return resultify(
|
||||
axios.post<CodesCacheRefreshByCodeClResponse>(API_URL.codesCacheRefreshByCodelCl(codeCl)),
|
||||
);
|
||||
};
|
||||
|
||||
export const useCodesCacheRefreshByCodeClMutation = (options?: UseMutationOptions<CodesCacheRefreshByCodeClResponse, CBDCAxiosError, CodesCacheRefreshByCodeClParams>) => {
|
||||
const mutation = useMutation<CodesCacheRefreshByCodeClResponse, CBDCAxiosError, CodesCacheRefreshByCodeClParams>({
|
||||
...options,
|
||||
mutationFn: ({
|
||||
codeCl
|
||||
}: CodesCacheRefreshByCodeClParams) => codesCacheRefreshByCodeCl({
|
||||
codeCl
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
26
src/entities/common/api/use-codes-cache-refresh-mutation.ts
Normal file
26
src/entities/common/api/use-codes-cache-refresh-mutation.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import { CodesCacheRefreshResponse } from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const codesCacheRefresh = () => {
|
||||
return resultify(
|
||||
axios.post<CodesCacheRefreshResponse>(API_URL.counselList()),
|
||||
);
|
||||
};
|
||||
|
||||
export const useCodesCacheRefreshMutation = (options?: UseMutationOptions<CodesCacheRefreshResponse, CBDCAxiosError>) => {
|
||||
const mutation = useMutation<CodesCacheRefreshResponse, CBDCAxiosError>({
|
||||
...options,
|
||||
mutationFn: () => codesCacheRefresh(),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
26
src/entities/common/api/use-codes-cache-status-mutaion.ts
Normal file
26
src/entities/common/api/use-codes-cache-status-mutaion.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import { CodesCacheStatusResponse } from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const codesCacheStatus = () => {
|
||||
return resultify(
|
||||
axios.get<CodesCacheStatusResponse>(API_URL.codesCacheStatus()),
|
||||
);
|
||||
};
|
||||
|
||||
export const useCodesGroupByCodeClMutation = (options?: UseMutationOptions<CodesCacheStatusResponse, CBDCAxiosError>) => {
|
||||
const mutation = useMutation<CodesCacheStatusResponse, CBDCAxiosError>({
|
||||
...options,
|
||||
mutationFn: () => codesCacheStatus(),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
33
src/entities/common/api/use-codes-group-by-codeCl-mutaion.ts
Normal file
33
src/entities/common/api/use-codes-group-by-codeCl-mutaion.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
CodesGroupByCodeClParams,
|
||||
CodesGroupByCodeClResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const codesGroupByCodeCl = ({ codeCl }: CodesGroupByCodeClParams) => {
|
||||
return resultify(
|
||||
axios.get<CodesGroupByCodeClResponse>(API_URL.codesGroupByCodeCl(codeCl)),
|
||||
);
|
||||
};
|
||||
|
||||
export const useCodesGroupByCodeClMutation = (options?: UseMutationOptions<CodesGroupByCodeClResponse, CBDCAxiosError, CodesGroupByCodeClParams>) => {
|
||||
const mutation = useMutation<CodesGroupByCodeClResponse, CBDCAxiosError, CodesGroupByCodeClParams>({
|
||||
...options,
|
||||
mutationFn: ({
|
||||
codeCl
|
||||
}: CodesGroupByCodeClParams) => codesGroupByCodeCl({
|
||||
codeCl
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
33
src/entities/common/api/use-codes-list-by-codeCl-mutaion.ts
Normal file
33
src/entities/common/api/use-codes-list-by-codeCl-mutaion.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
CodesListByCodeClParams,
|
||||
CodesListByCodeClResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const codesListByCodeCl = ({ codeCl }: CodesListByCodeClParams) => {
|
||||
return resultify(
|
||||
axios.get<CodesListByCodeClResponse>(API_URL.codesListByCodeCl(codeCl)),
|
||||
);
|
||||
};
|
||||
|
||||
export const useCodesListByCodeClMutation = (options?: UseMutationOptions<CodesListByCodeClResponse, CBDCAxiosError, CodesListByCodeClParams>) => {
|
||||
const mutation = useMutation<CodesListByCodeClResponse, CBDCAxiosError, CodesListByCodeClParams>({
|
||||
...options,
|
||||
mutationFn: ({
|
||||
codeCl
|
||||
}: CodesListByCodeClParams) => codesListByCodeCl({
|
||||
codeCl
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
64
src/entities/common/api/use-codes-select-mutation.ts
Normal file
64
src/entities/common/api/use-codes-select-mutation.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
CodesSelectParams,
|
||||
CodesSelectGetResponse,
|
||||
CodesSelectPostResponse,
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const codesSelect = ({
|
||||
codeCl,
|
||||
colNm,
|
||||
code1,
|
||||
code2,
|
||||
useCl,
|
||||
method
|
||||
}: CodesSelectParams) => {
|
||||
if(method === 'get'){
|
||||
return resultify(
|
||||
axios.get<CodesSelectGetResponse>(API_URL.codesSelect()),
|
||||
);
|
||||
}
|
||||
else{
|
||||
return resultify(
|
||||
axios.post<CodesSelectPostResponse>(API_URL.codesSelect(), {
|
||||
codeCl,
|
||||
colNm,
|
||||
code1,
|
||||
code2,
|
||||
useCl
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const useCodesSelectMutation = (options?: UseMutationOptions<CodesSelectGetResponse | CodesSelectPostResponse, CBDCAxiosError, CodesSelectParams>) => {
|
||||
const mutation = useMutation<CodesSelectGetResponse | CodesSelectPostResponse, CBDCAxiosError, CodesSelectParams>({
|
||||
...options,
|
||||
mutationFn: ({
|
||||
codeCl,
|
||||
colNm,
|
||||
code1,
|
||||
code2,
|
||||
useCl,
|
||||
method
|
||||
}: CodesSelectParams) => codesSelect({
|
||||
codeCl,
|
||||
colNm,
|
||||
code1,
|
||||
code2,
|
||||
useCl,
|
||||
method
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
EmptyTokenAddSendCodeParams,
|
||||
EmptyTokenAddSendCodeResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const emptyTokenAddSendCode = (params: EmptyTokenAddSendCodeParams) => {
|
||||
return resultify(
|
||||
axios.post<EmptyTokenAddSendCodeResponse>(API_URL.emptyTokenFindSendCode(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useEmptyTokenAddSendCodeMutation = (options?: UseMutationOptions<EmptyTokenAddSendCodeResponse, CBDCAxiosError, EmptyTokenAddSendCodeParams>) => {
|
||||
const mutation = useMutation<EmptyTokenAddSendCodeResponse, CBDCAxiosError, EmptyTokenAddSendCodeParams>({
|
||||
...options,
|
||||
mutationFn: (params: EmptyTokenAddSendCodeParams) => emptyTokenAddSendCode(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
29
src/entities/common/api/use-empty-token-change-mutation.ts
Normal file
29
src/entities/common/api/use-empty-token-change-mutation.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
EmptyTokenChangeParams,
|
||||
EmptyTokenChangeResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const emptyTokenChange = (params: EmptyTokenChangeParams) => {
|
||||
return resultify(
|
||||
axios.post<EmptyTokenChangeResponse>(API_URL.emptyTokenChange(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useEmptyTokenChangeMutation = (options?: UseMutationOptions<EmptyTokenChangeResponse, CBDCAxiosError, EmptyTokenChangeParams>) => {
|
||||
const mutation = useMutation<EmptyTokenChangeResponse, CBDCAxiosError, EmptyTokenChangeParams>({
|
||||
...options,
|
||||
mutationFn: (params: EmptyTokenChangeParams) => emptyTokenChange(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
EmptyTokenFindSendCodeParams,
|
||||
EmptyTokenFindSendCodeResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const emptyTokenFindSendCode = (params: EmptyTokenFindSendCodeParams) => {
|
||||
return resultify(
|
||||
axios.post<EmptyTokenFindSendCodeResponse>(API_URL.emptyTokenFindSendCode(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useEmptyTokenFindSendCodeMutation = (options?: UseMutationOptions<EmptyTokenFindSendCodeResponse, CBDCAxiosError, EmptyTokenFindSendCodeParams>) => {
|
||||
const mutation = useMutation<EmptyTokenFindSendCodeResponse, CBDCAxiosError, EmptyTokenFindSendCodeParams>({
|
||||
...options,
|
||||
mutationFn: (params: EmptyTokenFindSendCodeParams) => emptyTokenFindSendCode(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL } from '@/shared/api/urls';
|
||||
import { resultify } from '@/shared/lib/resultify';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import {
|
||||
EmptyTokenVerifyCodeParams,
|
||||
EmptyTokenVerifyCodeResponse
|
||||
} from '../model/types';
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationOptions
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
export const emptyTokenVerifyCode = (params: EmptyTokenVerifyCodeParams) => {
|
||||
return resultify(
|
||||
axios.post<EmptyTokenVerifyCodeResponse>(API_URL.emptyTokenVerifyCode(), params),
|
||||
);
|
||||
};
|
||||
|
||||
export const useEmptyTokenVerifyCodeMutation = (options?: UseMutationOptions<EmptyTokenVerifyCodeResponse, CBDCAxiosError, EmptyTokenVerifyCodeParams>) => {
|
||||
const mutation = useMutation<EmptyTokenVerifyCodeResponse, CBDCAxiosError, EmptyTokenVerifyCodeParams>({
|
||||
...options,
|
||||
mutationFn: (params: EmptyTokenVerifyCodeParams) => emptyTokenVerifyCode(params),
|
||||
});
|
||||
|
||||
return {
|
||||
...mutation,
|
||||
};
|
||||
};
|
||||
8
src/entities/common/model/constants.ts
Normal file
8
src/entities/common/model/constants.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const DEFAULT_PAGE_PARAM = {
|
||||
cursor: 'string',
|
||||
size: 0,
|
||||
sortBy: 'string',
|
||||
sortOrder: 'ASC',
|
||||
orderBy: 'string',
|
||||
limit: 0,
|
||||
};
|
||||
130
src/entities/common/model/types.ts
Normal file
130
src/entities/common/model/types.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
export interface DefaulResponsePagination {
|
||||
nextCursor: string | null;
|
||||
hasNext: boolean;
|
||||
};
|
||||
export enum HeaderType {
|
||||
NoHeader = 'NoHeader',
|
||||
Home = 'Home',
|
||||
Alim = 'Alim',
|
||||
LeftArrow = 'LeftArrow',
|
||||
RightClose = 'RightClose',
|
||||
};
|
||||
export interface HeaderNavigationProps {
|
||||
onBack?: (() => void) | undefined;
|
||||
headerTitle?: string;
|
||||
headerLeft?: React.ReactNode;
|
||||
headerRight?: React.ReactNode;
|
||||
menuOn: boolean;
|
||||
headerType: HeaderType;
|
||||
setMenuOn: (menuOn: boolean) => void;
|
||||
};
|
||||
|
||||
export interface FooterProps {
|
||||
setMenuOn: (menuOn: boolean) => void;
|
||||
footerCurrentPage?: string | null;
|
||||
};
|
||||
export enum FooterItemActiveKey {
|
||||
Home = 'Home',
|
||||
Transaction = 'Transaction',
|
||||
Settlement = 'Settlement',
|
||||
Account = 'Account',
|
||||
};
|
||||
export interface CodesSelectParams {
|
||||
codeCl?: string;
|
||||
colNm?: string;
|
||||
code1?: string;
|
||||
code2?: string;
|
||||
useCl?: string;
|
||||
method: 'get' | 'post';
|
||||
};
|
||||
export interface CodeListItem {
|
||||
codeCl: string;
|
||||
code1: string;
|
||||
code2: string;
|
||||
desc1: string;
|
||||
desc2: string;
|
||||
desc3: string;
|
||||
encDesc1: string;
|
||||
encDesc2: string;
|
||||
encDesc3: string;
|
||||
colNm: string;
|
||||
orderNo: number;
|
||||
};
|
||||
export interface CodeGroupItem {
|
||||
groupCode: string;
|
||||
groupType: string;
|
||||
groupName: string;
|
||||
codeList: Array<CodeListItem>;
|
||||
};
|
||||
export interface CodesSelectGetResponse {
|
||||
codeGroups: Array<CodeGroupItem>;
|
||||
};
|
||||
export interface CodesSelectPostResponse {
|
||||
codeList: Array<CodeListItem>;
|
||||
};
|
||||
export interface CodesListByCodeClParams {
|
||||
codeCl: string;
|
||||
};
|
||||
export interface CodesListByCodeClResponse {
|
||||
codeList: Array<CodeListItem>;
|
||||
};
|
||||
export interface CodesGroupByCodeClParams {
|
||||
codeCl: string;
|
||||
};
|
||||
export interface CodesGroupByCodeClResponse extends CodeGroupItem {
|
||||
|
||||
}
|
||||
export interface CodesCacheStatusResponse {
|
||||
additionalProp1: string;
|
||||
additionalProp2: string;
|
||||
additionalProp3: string;
|
||||
};
|
||||
export interface CodesCacheRefreshResponse {
|
||||
|
||||
};
|
||||
export interface CodesCacheRefreshByCodeClParams {
|
||||
codeCl: string;
|
||||
};
|
||||
export interface CodesCacheRefreshByCodeClResponse {
|
||||
|
||||
};
|
||||
export interface EmptyTokenVerifyCodeParams {
|
||||
usrid: string;
|
||||
authType: string;
|
||||
content: string;
|
||||
authCode: string;
|
||||
};
|
||||
export interface EmptyTokenVerifyCodeResponse {
|
||||
tfaId: string;
|
||||
valid: boolean;
|
||||
};
|
||||
export interface EmptyTokenFindSendCodeParams {
|
||||
usrid: string;
|
||||
password: string;
|
||||
authType: string;
|
||||
content: string;
|
||||
};
|
||||
export interface EmptyTokenFindSendCodeResponse {
|
||||
expiresIn: string;
|
||||
authCode: string;
|
||||
};
|
||||
export interface EmptyTokenChangeParams {
|
||||
usrid: string;
|
||||
password: string;
|
||||
tfaId: string;
|
||||
newPassword: string;
|
||||
newConfirmPassword: string;
|
||||
};
|
||||
export interface EmptyTokenChangeResponse {
|
||||
|
||||
};
|
||||
export interface EmptyTokenAddSendCodeParams {
|
||||
usrid: string;
|
||||
password: string;
|
||||
authType: string;
|
||||
content: string;
|
||||
};
|
||||
export interface EmptyTokenAddSendCodeResponse {
|
||||
expiresIn: string;
|
||||
authCode: string;
|
||||
};
|
||||
18
src/entities/home/model/types.ts
Normal file
18
src/entities/home/model/types.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export interface FavoriteItemProps {
|
||||
img?: string,
|
||||
text?: string
|
||||
};
|
||||
export interface NoticeItemProps {
|
||||
title?: string,
|
||||
meta1?: string,
|
||||
meta2?: string,
|
||||
img?: string,
|
||||
};
|
||||
export interface HomeBottomBannerProps {
|
||||
setBottomBannerOn: (bottomBannerOn: boolean) => void;
|
||||
bottomBannerOn: boolean;
|
||||
};
|
||||
export interface AuthRegisterProps {
|
||||
setAuthRegisterOn: (authRegisterOn: boolean) => void;
|
||||
authRegisterOn: boolean;
|
||||
};
|
||||
74
src/entities/home/ui/auth-reguster.tsx
Normal file
74
src/entities/home/ui/auth-reguster.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { AuthRegisterProps } from '../model/types';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export const AuthRegister = ({
|
||||
setAuthRegisterOn,
|
||||
authRegisterOn,
|
||||
}: AuthRegisterProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToClose = () => {
|
||||
setAuthRegisterOn(false);
|
||||
};
|
||||
const onClickToRegister = () => {
|
||||
// register
|
||||
};
|
||||
|
||||
const variants = {
|
||||
hidden: { y: '100%' },
|
||||
visible: { y: '0%' },
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{ (authRegisterOn) &&
|
||||
<div className="bg-dim"></div>
|
||||
}
|
||||
<motion.div
|
||||
className="bottomsheet"
|
||||
initial="hidden"
|
||||
animate={ (authRegisterOn)? 'visible': 'hidden' }
|
||||
variants={ variants }
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<div className="bottomsheet-header">
|
||||
<div className="bottomsheet-title">
|
||||
<h2>간편 인증 등록</h2>
|
||||
<button
|
||||
className="close-btn"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_close.svg' }
|
||||
alt="닫기"
|
||||
onClick={ () => onClickToClose() }
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bottomsheet-content">
|
||||
<div className="text-section">
|
||||
<div>
|
||||
간편 인증 등록 한번으로,<br/>
|
||||
비밀번호 없이 편리하게 로그인하세요.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bottomsheet-footer">
|
||||
<button
|
||||
className="btn-50 btn-darkgray flex-1"
|
||||
type="button"
|
||||
onClick={ () => onClickToClose() }
|
||||
>다음에</button>
|
||||
<button
|
||||
className="btn-50 btn-blue flex-2"
|
||||
type="button"
|
||||
onClick={ () => onClickToRegister() }
|
||||
>지금 등록하기</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
44
src/entities/home/ui/day-status-box-container1.tsx
Normal file
44
src/entities/home/ui/day-status-box-container1.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
|
||||
export const BoxContainer1 = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="box-wrap">
|
||||
<h3>오늘 매출</h3>
|
||||
<div className="today-sales">
|
||||
<span className="won01">937,284,000원</span>
|
||||
<span className="per">↑ 43.6%</span>
|
||||
<a href="#" className="arrow">
|
||||
<img src={ IMAGE_ROOT + '/ico_arrow.svg' } alt="오늘 매출 바로가기" />
|
||||
</a>
|
||||
</div>
|
||||
<ul className="dales-detail">
|
||||
<li className="approve">승인 건수 <strong>8,277건</strong></li>
|
||||
<li className="cancel">취소 건수 <strong>320건</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="box-wrap">
|
||||
<h3>오늘 정산</h3>
|
||||
<div className="today-sales">
|
||||
<span className="won02">24,734,000원</span>
|
||||
<span className="per">입금완료</span>
|
||||
<a href="#" className="arrow">
|
||||
<img src={ IMAGE_ROOT + '/ico_arrow.svg' } alt="오늘 매출 바로가기" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="progressbar">
|
||||
<div className="progress-header">
|
||||
<span className="progress-title">정산한도</span>
|
||||
<span className="progress-remaining">23% 남음</span>
|
||||
</div>
|
||||
<div className="progress-container">
|
||||
<div className="progress-bar">
|
||||
<div className="progress-fill" style={{width: '77%'}}></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="remain-limit">잔여정산한도 <strong>7,833,000원</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
114
src/entities/home/ui/day-status-box-container2.tsx
Normal file
114
src/entities/home/ui/day-status-box-container2.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
|
||||
export const BoxContainer2 = () => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="section-header">
|
||||
<h3>매출/정산 현황</h3>
|
||||
<p>(전월 전체 매출/정산 대비 기준 )</p>
|
||||
</div>
|
||||
<div className="box-wrap two-sales">
|
||||
<h4>총 매출액</h4>
|
||||
<div className="today-sales mt-sty">
|
||||
<span className="won01">937,284,000원</span>
|
||||
<span className="per">↑ 43.6%</span>
|
||||
<a className="arrow">
|
||||
<img src={ IMAGE_ROOT + '/ico_arrow.svg' } alt="오늘 매출 바로가기" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="box-wrap two-sales">
|
||||
<h4>총 정산액</h4>
|
||||
<div className="today-sales mt-sty">
|
||||
<span className="won02">24,734,000원</span>
|
||||
<span className="per">↑ 26.8%</span>
|
||||
<a className="arrow">
|
||||
<img src={ IMAGE_ROOT + '/ico_arrow.svg' } alt="오늘 매출 바로가기" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="section-header">
|
||||
<h3>거래 인사이트</h3>
|
||||
<p>(최근 일주일 기준)</p>
|
||||
</div>
|
||||
<div className="box-wrap two-sales img-customer">
|
||||
<h4>평균 객단가</h4>
|
||||
<div className="two-account">
|
||||
<span>937,284,000원</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="box-wrap two-sales img-states">
|
||||
<h4>하루 평균 매출 및 건수</h4>
|
||||
<div className="two-account">
|
||||
<span>73,000,000원(1,800건)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="box-wrap ranking">
|
||||
<h4>매출이 가장 높은 요일</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<span className="ranking-num-01">1</span>
|
||||
<span>월요일 (354,342,000원)</span>
|
||||
<span className="last-per-01">30%</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="ranking-num-ot">2</span>
|
||||
<span>화요일 (63,983,000원)</span>
|
||||
<span className="last-per-ot">20%</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="ranking-num-ot">3</span>
|
||||
<span>수요일 (5,938,000원)</span>
|
||||
<span className="last-per-ot">10%</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="box-wrap ranking">
|
||||
<h4>매출이 가장 높은 시간</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<span className="ranking-num-01">1</span>
|
||||
<span>18시 (5,342,000원)</span>
|
||||
<span className="last-per-01">30%</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="ranking-num-ot">2</span>
|
||||
<span>10시 (994,000원)</span>
|
||||
<span className="last-per-ot">20%</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="ranking-num-ot">3</span>
|
||||
<span>23시 (478,000원)</span>
|
||||
<span className="last-per-ot">10%</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="box-wrap ranking">
|
||||
<h4>가장 많이쓰인 결제 수단</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<span className="ranking-num-01">1</span>
|
||||
<span>신용카드 (354,342,000원)</span>
|
||||
<span className="last-per-01">30%</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="ranking-num-ot">2</span>
|
||||
<span>가상계좌 (64,094,000원)</span>
|
||||
<span className="last-per-ot">20%</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="ranking-num-ot">3</span>
|
||||
<span>휴대폰 (478,000원)</span>
|
||||
<span className="last-per-ot">10%</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
50
src/entities/home/ui/day-status-box.tsx
Normal file
50
src/entities/home/ui/day-status-box.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { HomeNoticeList } from './home-notice-list';
|
||||
import { BoxContainer1 } from './day-status-box-container1';
|
||||
import { BoxContainer2 } from './day-status-box-container2';
|
||||
export const DayStatusBox = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="day-status">
|
||||
<div className="day-tab">
|
||||
<div>2025.06.16</div>
|
||||
<div>
|
||||
<button className="day-tab-btn active" data-target="one">일</button>
|
||||
<button className="day-tab-btn" data-target="two">월</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="con-box one active">
|
||||
{ <BoxContainer1></BoxContainer1> }
|
||||
</div>
|
||||
<div className="con-box two">
|
||||
{ <BoxContainer2></BoxContainer2> }
|
||||
</div>
|
||||
<div className="swiper-banner">
|
||||
<div className="banner-wrapper">
|
||||
<div className="banner-slide active">
|
||||
<div className="banner-content">
|
||||
<img src={ IMAGE_ROOT + '/home-banner01.png' } alt="배너 이미지" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="banner-slide">
|
||||
<div className="banner-content">
|
||||
<img src={ IMAGE_ROOT + '/home-banner01.png' } alt="배너 이미지" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="banner-slide">
|
||||
<div className="banner-content">
|
||||
<img src={ IMAGE_ROOT + '/home-banner01.png' } alt="배너 이미지" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="banner-pagination">
|
||||
<span className="banner-dot active" data-slide="0"></span>
|
||||
<span className="banner-dot" data-slide="1"></span>
|
||||
<span className="banner-dot" data-slide="2"></span>
|
||||
</div>
|
||||
</div>
|
||||
{ <HomeNoticeList></HomeNoticeList> }
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
17
src/entities/home/ui/favorite-item.tsx
Normal file
17
src/entities/home/ui/favorite-item.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { FavoriteItemProps } from '../model/types';
|
||||
|
||||
export const FavoriteItem = ({
|
||||
img,
|
||||
text
|
||||
}: FavoriteItemProps) => {
|
||||
return (
|
||||
<>
|
||||
<div className="swiper-item">
|
||||
<div className="swiper-icon coin-icon">
|
||||
<img src={ img } alt={ text } />
|
||||
</div>
|
||||
<span className="swiper-text">{ text }</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
60
src/entities/home/ui/favorite-wrapper.tsx
Normal file
60
src/entities/home/ui/favorite-wrapper.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import 'swiper/css';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { FavoriteItem } from './favorite-item';
|
||||
import { FavoriteItemProps } from '../model/types'
|
||||
|
||||
|
||||
export const FavoriteWrapper = () => {
|
||||
const items: Array<FavoriteItemProps> = [
|
||||
{img: IMAGE_ROOT + '/ico_menu_01.svg', text: '지급대행'},
|
||||
{img: IMAGE_ROOT + '/ico_menu_02.svg', text: '거래내역조회'},
|
||||
{img: IMAGE_ROOT + '/ico_menu_03.svg', text: '정산달력'},
|
||||
{img: IMAGE_ROOT + '/ico_menu_02.svg', text: '거래내역조회'},
|
||||
{img: IMAGE_ROOT + '/ico_menu_03.svg', text: '정산달력'}
|
||||
];
|
||||
|
||||
const itemAdd = {
|
||||
img: IMAGE_ROOT + '/ico_menu_plus.svg',
|
||||
text: '편집하기'
|
||||
};
|
||||
|
||||
const getItems = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<items.length;i++){
|
||||
let img = items[i]?.img;
|
||||
let text = items[i]?.text;
|
||||
let key = 'slide-key-'+i;
|
||||
rs.push(
|
||||
<SwiperSlide
|
||||
key={ key }
|
||||
>
|
||||
<FavoriteItem
|
||||
img={ img }
|
||||
text={ text }
|
||||
></FavoriteItem>
|
||||
</SwiperSlide>
|
||||
);
|
||||
}
|
||||
|
||||
return rs;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Swiper
|
||||
spaceBetween={9}
|
||||
slidesPerView={4}
|
||||
onSlideChange={() => console.log('slide change')}
|
||||
>
|
||||
{ getItems() }
|
||||
<SwiperSlide>
|
||||
<FavoriteItem
|
||||
img={ itemAdd.img }
|
||||
text={ itemAdd.text }
|
||||
></FavoriteItem>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
61
src/entities/home/ui/home-bottom-banner.tsx
Normal file
61
src/entities/home/ui/home-bottom-banner.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import moment from 'moment';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import { setLocalStorage } from '@/shared/lib/web-view-bridge';
|
||||
import { HomeBottomBannerProps } from '../model/types';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export const HomeBottomBanner = ({
|
||||
setBottomBannerOn,
|
||||
bottomBannerOn
|
||||
}: HomeBottomBannerProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToClose = () => {
|
||||
// close
|
||||
setBottomBannerOn(false);
|
||||
};
|
||||
const onClickToCloseDay = () => {
|
||||
// 오늘 날짜 기록
|
||||
const today = moment().format('YYYYMMDD');
|
||||
setLocalStorage(StorageKeys.BottomBannerClose, today);
|
||||
onClickToClose();
|
||||
};
|
||||
|
||||
const variants = {
|
||||
hidden: { y: '100%' },
|
||||
visible: { y: '0%' },
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{bottomBannerOn &&
|
||||
<div className="bg-dim"></div>
|
||||
}
|
||||
<motion.div
|
||||
className="bottomsheet banner"
|
||||
initial="hidden"
|
||||
animate={ (bottomBannerOn)? 'visible': 'hidden' }
|
||||
variants={ variants }
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<div className="bottomsheet-content">
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/sample_banner.png' }
|
||||
alt="배너"
|
||||
/>
|
||||
<div className="banner-page">
|
||||
<span className="current">1</span>
|
||||
/
|
||||
<span className="total">3</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bottom-btn">
|
||||
<span onClick={ () => onClickToCloseDay() }>오늘 하루 보지 않기</span>
|
||||
<span onClick={ () => onClickToClose() }>닫기</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
33
src/entities/home/ui/home-notice-item.tsx
Normal file
33
src/entities/home/ui/home-notice-item.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { NoticeItemProps } from '../model/types';
|
||||
|
||||
export const HomeNoticeItem = ({
|
||||
title,
|
||||
meta1,
|
||||
meta2,
|
||||
img
|
||||
}: NoticeItemProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToNavigate = (path: string) => {
|
||||
navigate(path + '14');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="notice-item">
|
||||
<div className="notice-content">
|
||||
<div className="notice-title">{ title }</div>
|
||||
<div className="notice-meta">{ meta1}<span>{ meta2 }</span></div>
|
||||
</div>
|
||||
<div
|
||||
className="notice-arrow"
|
||||
onClick={ () => onClickToNavigate(PATHS.support.notice.detail) }
|
||||
>
|
||||
<img src={ img } alt="공지사항 바로가기" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
66
src/entities/home/ui/home-notice-list.tsx
Normal file
66
src/entities/home/ui/home-notice-list.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
/* eslint-disable @cspell/spellchecker */
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { HomeNoticeItem } from './home-notice-item';
|
||||
|
||||
export const HomeNoticeList = () => {
|
||||
const items = [
|
||||
{
|
||||
title: '시스템 안정화를 위한 정기 점검이 예정되어 있습니다.',
|
||||
meta1: '공지사항',
|
||||
meta2: '25년 5월 23일',
|
||||
img: IMAGE_ROOT + '/Forward.svg'
|
||||
},
|
||||
{
|
||||
title: '가맹점 관리 메뉴에 거래내역 엑셀 다운로드 기능이 추가 되었습니다.',
|
||||
meta1: '공지사항',
|
||||
meta2: '25년 5월 23일',
|
||||
img: IMAGE_ROOT + '/Forward.svg'
|
||||
},
|
||||
{
|
||||
title: '신규 가맹점을 대상으로 거래수수료 인하 혜택을 12월까지 제공합니다.',
|
||||
meta1: '공지사항',
|
||||
meta2: '25년 5월 23일',
|
||||
img: IMAGE_ROOT + '/Forward.svg'
|
||||
},
|
||||
{
|
||||
title: '앱의 안정성과 사용성을 개선한 버전 2.3.1이 출시되었습니다.',
|
||||
meta1: '공지사항',
|
||||
meta2: '25년 5월 23일',
|
||||
img: IMAGE_ROOT + '/Forward.svg'
|
||||
},
|
||||
{
|
||||
title: '점검 시간 동안 일부 서비스 이용이 제한될 수 있으니 미리 확인해주세요.',
|
||||
meta1: '공지사항',
|
||||
meta2: '25년 5월 23일',
|
||||
img: IMAGE_ROOT + '/Forward.svg'
|
||||
},
|
||||
];
|
||||
|
||||
const getItems = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<items.length;i++){
|
||||
let key = 'notice-key-'+i;
|
||||
rs.push(
|
||||
<HomeNoticeItem
|
||||
key={ key }
|
||||
title={ items[i]?.title }
|
||||
meta1={ items[i]?.meta1 }
|
||||
meta2={ items[i]?.meta2 }
|
||||
img={ items[i]?.img }
|
||||
></HomeNoticeItem>
|
||||
);
|
||||
}
|
||||
return rs;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="notice-list">
|
||||
<h3>공지 & 최신정보</h3>
|
||||
<div className="notice-box">
|
||||
{ getItems() }
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
10
src/entities/menu/model/types.ts
Normal file
10
src/entities/menu/model/types.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export interface MenuCategoryItem {
|
||||
title: string;
|
||||
path: string;
|
||||
};
|
||||
export interface MenuCategoryProps {
|
||||
category: string;
|
||||
categoryIcon?: string;
|
||||
items: Array<MenuCategoryItem>;
|
||||
setMenuOn: (menuOn: boolean) => void;
|
||||
};
|
||||
50
src/entities/menu/ui/menu-category.tsx
Normal file
50
src/entities/menu/ui/menu-category.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { PATHS } from '@/shared/constants/paths';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { MenuCategoryProps } from '../model/types';
|
||||
|
||||
export const MenuCategory = ({
|
||||
category,
|
||||
categoryIcon,
|
||||
items,
|
||||
setMenuOn
|
||||
}: MenuCategoryProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
|
||||
const onClickToNavigate = (path?: string) => {
|
||||
if(!!path){
|
||||
setMenuOn(false);
|
||||
navigate(path);
|
||||
}
|
||||
};
|
||||
|
||||
const getMenuItems = () => {
|
||||
let rs = [];
|
||||
for(let i=0;i<items.length;i++){
|
||||
let title = items[i]?.title;
|
||||
let path = items[i]?.path;
|
||||
let key = 'menu-item-key-'+i;
|
||||
rs.push(
|
||||
<li
|
||||
key={ key }
|
||||
onClick={ () => onClickToNavigate(path) }
|
||||
>{ title }</li>
|
||||
);
|
||||
}
|
||||
return rs;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="menu-category">
|
||||
<div className="category-header">
|
||||
<div className={ 'category-icon ' + categoryIcon }></div>
|
||||
<span>{ category }</span>
|
||||
</div>
|
||||
<ul className="category-items">
|
||||
{ getMenuItems() }
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
7
src/entities/payment/model/types.ts
Normal file
7
src/entities/payment/model/types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum PaymentTabKeys {
|
||||
Info = 'Info',
|
||||
DataNotification = 'DataNotification',
|
||||
};
|
||||
export interface PaymentTabProps {
|
||||
activeTab: PaymentTabKeys;
|
||||
};
|
||||
108
src/entities/payment/ui/card-commission-bottom-sheet.tsx
Normal file
108
src/entities/payment/ui/card-commission-bottom-sheet.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
|
||||
export const CardCommissionCycleBottomSheet = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-dim"></div>
|
||||
<div className="bottomsheet">
|
||||
<div className="bottomsheet-header">
|
||||
<div className="bottomsheet-title">
|
||||
<h2>수수료 및 정산주기</h2>
|
||||
<button
|
||||
className="close-btn"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/images/ico_close.svg' }
|
||||
alt="닫기"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bottomsheet-content">
|
||||
<div className="card-fee">
|
||||
<div className="desc">정산주기 : 일일(+3일)</div>
|
||||
|
||||
<div className="notice-tabs">
|
||||
<button
|
||||
className="tab36 on"
|
||||
type="button"
|
||||
>일반</button>
|
||||
<button
|
||||
className="tab36"
|
||||
type="button"
|
||||
>무이자</button>
|
||||
<button
|
||||
className="tab36"
|
||||
type="button"
|
||||
>머니/포인트</button>
|
||||
</div>
|
||||
|
||||
<div className="card-fee-box">
|
||||
<span className="label">카드사</span>
|
||||
<div className="field wid-100">
|
||||
<select>
|
||||
<option>KB국민</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-fee-list">
|
||||
<div className="card-fee-list-header">
|
||||
<span className="th-left">할부개월</span>
|
||||
<span className="th-right">수수료</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>02 개월</span>
|
||||
<span>2.000%</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>03 개월</span>
|
||||
<span>3.100%</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>04 개월</span>
|
||||
<span>4.400%</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>05 개월</span>
|
||||
<span>5.200%</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>06 개월</span>
|
||||
<span>6.000%</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>07 개월</span>
|
||||
<span>6.600%</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>08 개월</span>
|
||||
<span>7.500%</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>09 개월</span>
|
||||
<span>8.000%</span>
|
||||
</div>
|
||||
<div className="card-fee-row">
|
||||
<span>10 개월</span>
|
||||
<span>9.000%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/*
|
||||
<div className="bottomsheet-footer">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
type="button"
|
||||
disabled
|
||||
>신청</button>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
65
src/entities/payment/ui/credit-card-list-bottom-sheet.tsx
Normal file
65
src/entities/payment/ui/credit-card-list-bottom-sheet.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
|
||||
export const CreditCardListBottomSheet = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-dim"></div>
|
||||
<div className="bottomsheet">
|
||||
<div className="bottomsheet-header">
|
||||
<div className="bottomsheet-title">
|
||||
<h2>카드사</h2>
|
||||
<button
|
||||
className="close-btn"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
src={ IMAGE_ROOT + '/ico_close.svg' }
|
||||
alt="닫기"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bottomsheet-content">
|
||||
<div className="card-list">
|
||||
<div className="card-option selected">
|
||||
<span className="name">KB국민</span>
|
||||
<i className="check"></i>
|
||||
</div>
|
||||
<div className="card-option">
|
||||
<span className="name">비씨</span>
|
||||
<i className="check"></i>
|
||||
</div>
|
||||
<div className="card-option">
|
||||
<span className="name">하나(외환)</span>
|
||||
<i className="check"></i>
|
||||
</div>
|
||||
<div className="card-option">
|
||||
<span className="name">삼성</span>
|
||||
<i className="check"></i>
|
||||
</div>
|
||||
<div className="card-option">
|
||||
<span className="name">신한</span>
|
||||
<i className="check"></i>
|
||||
</div>
|
||||
<div className="card-option">
|
||||
<span className="name">현대</span>
|
||||
<i className="check"></i>
|
||||
</div>
|
||||
<div className="card-option">
|
||||
<span className="name">롯데</span>
|
||||
<i className="check"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bottomsheet-footer">
|
||||
<button
|
||||
className="btn-50 btn-blue flex-1"
|
||||
type="button"
|
||||
>확인</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
60
src/entities/payment/ui/data-notification-wrap.tsx
Normal file
60
src/entities/payment/ui/data-notification-wrap.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
export const DataNotificationWrap = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ing-list">
|
||||
<div className="input-wrapper top-select mt-16">
|
||||
<select>
|
||||
<option value="1">nicetest00g</option>
|
||||
<option value="2">nicetest00g</option>
|
||||
<option value="3">nicetest00g</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="notify-container">
|
||||
<div className="notify-header">
|
||||
<h3 className="notify-title">결제데이터 통보 조회</h3>
|
||||
</div>
|
||||
<ul className="notify-list">
|
||||
<li>
|
||||
<div className="notify-row">
|
||||
<span className="notify-name">신용카드</span>
|
||||
<span className="ic20 arrow-down"></span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="notify-divider"></li>
|
||||
<li>
|
||||
<div className="notify-row">
|
||||
<span className="notify-name">계좌이체</span>
|
||||
<span className="ic20 arrow-down"></span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="notify-divider"></li>
|
||||
<li>
|
||||
<div className="notify-row">
|
||||
<span className="notify-name">가상계좌</span>
|
||||
<span className="ic20 arrow-down"></span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="notify-divider"></li>
|
||||
<li>
|
||||
<div className="notify-row">
|
||||
<span className="notify-name">휴대폰</span>
|
||||
<span className="ic20 arrow-down"></span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="notify-row">
|
||||
<span className="notify-name">에스크로 결제</span>
|
||||
<span className="ic20 arrow-down"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="notify-bar">
|
||||
<span>결제데이터 통보 설정은 PC에서 가능합니다.</span>
|
||||
<span>전송 설정한 지불수단만 노출됩니다.</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user