첫 커밋
14
src/shared/@types/.env.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/// <reference types="vite/client" />
|
||||
interface ImportMetaEnv {
|
||||
readonly DEV: boolean;
|
||||
readonly VITE_APP_NAME: string;
|
||||
readonly VITE_APP_VERSION: string;
|
||||
readonly VITE_APP_ENV: string;
|
||||
readonly VITE_APP_AUTH_PROXY_HOST: string;
|
||||
readonly VITE_APP_API_PROXY_HOST: string;
|
||||
readonly VITE_APP_WEB_VERSION: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
33
src/shared/@types/banking-code.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/* eslint-disable @cspell/spellchecker */
|
||||
/**
|
||||
* 은행에서 사용하는 코드 모음
|
||||
*/
|
||||
|
||||
export type YN = 'Y' | 'N';
|
||||
|
||||
/**
|
||||
* 은행코드
|
||||
*/
|
||||
export const enum BankCode {
|
||||
A = '011',
|
||||
B = '004',
|
||||
}
|
||||
|
||||
/**
|
||||
* 은행이름
|
||||
*/
|
||||
export const enum BankName {
|
||||
A = 'A은행',
|
||||
B = 'B은행',
|
||||
}
|
||||
|
||||
export const enum BankValue {
|
||||
A = 'NHB',
|
||||
B = 'KBB',
|
||||
}
|
||||
|
||||
/**
|
||||
* 은행코드로 어떤 은행인지 확인
|
||||
*/
|
||||
export const isBankA = (bkcd: BankCode) => bkcd === BankCode.A;
|
||||
export const isBankB = (bkcd: BankCode) => bkcd === BankCode.B;
|
||||
14
src/shared/@types/error.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
export type CBDCAxiosError = AxiosError<{
|
||||
errorCode?: string;
|
||||
code?: string;
|
||||
message?: string;
|
||||
data?: any;
|
||||
status?: string;
|
||||
}>;
|
||||
|
||||
export interface CBDCAxiosFallbackProps {
|
||||
error: CBDCAxiosError;
|
||||
resetErrorBoundary?: (...args: unknown[]) => void;
|
||||
}
|
||||
45
src/shared/@types/global.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { DefaultEvents, EventEmitter } from '@webview-bridge/web';
|
||||
|
||||
export {};
|
||||
|
||||
declare module '*.svg' {
|
||||
import React = require('react');
|
||||
|
||||
export const ReactComponent: React.SFC<React.SVGProps<SVGSVGElement>>;
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.png';
|
||||
|
||||
declare module '*.scss' {
|
||||
const content: Record<string, string>;
|
||||
export = content;
|
||||
}
|
||||
|
||||
declare module '*.css' {
|
||||
const css: string;
|
||||
export default css;
|
||||
}
|
||||
|
||||
declare module 'lodash-es';
|
||||
|
||||
declare module '*.lottie';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
store: StoreApi<RootStore>;
|
||||
webViewBridge: {
|
||||
send<T = any, R = Promise<T>>(url: string, data?: any): Promise<R>;
|
||||
};
|
||||
__bridgeMethods__?: string[];
|
||||
|
||||
__bridgeInitialState__?: Record<string, any>;
|
||||
nativeEmitter?: EventEmitter<DefaultEvents>;
|
||||
nativeBatchedEvents?: [string, ...any][];
|
||||
webEmitter?: EventEmitter<DefaultEvents>;
|
||||
ReactNativeWebView: {
|
||||
postMessage: (data: string) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
9
src/shared/@types/qrcode.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* eslint-disable @cspell/spellchecker */
|
||||
export interface MyWalletQRCode {
|
||||
walletAddr: string;
|
||||
bankCode: string;
|
||||
}
|
||||
|
||||
export interface MyPaymentQRcode extends MyWalletQRCode {
|
||||
exptime: string; // 'YYYY-MM-DD H:m:s';
|
||||
}
|
||||
31
src/shared/@types/query.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { QueryKey, UseInfiniteQueryOptions, UseMutationOptions, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
|
||||
export type QueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = CBDCAxiosError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> = Omit<UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryKey' | 'queryFn' | 'initialData'>;
|
||||
|
||||
export type MutationOptions<TVariables = unknown, TError = CBDCAxiosError, TData = void, TContext = unknown> = Omit<
|
||||
UseMutationOptions<TData, TError, TVariables, TContext>,
|
||||
'mutationKey' | 'mutationFn'
|
||||
>;
|
||||
|
||||
export type InfiniteQueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = CBDCAxiosError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
> = Omit<
|
||||
UseInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
'queryKey' | 'queryFn' | 'initialData' | 'getNextPageParam' | 'initialPageParam'
|
||||
>;
|
||||
18
src/shared/@types/webview-bridge.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export interface WebViewBridgeResponse {
|
||||
targetFunc: string;
|
||||
data: any;
|
||||
msgId: string;
|
||||
isSuccessful: boolean;
|
||||
}
|
||||
|
||||
export const PERMISSION_RESULTS = Object.freeze({
|
||||
UNAVAILABLE: 'unavailable', // This feature is not available (on this device / in this context)
|
||||
BLOCKED: 'blocked', // The permission has not been requested / is denied but requestable
|
||||
DENIED: 'denied', // The permission is limited: some actions are possible
|
||||
GRANTED: 'granted', // The permission is granted
|
||||
LIMITED: 'limited', // The permission is denied and not requestable anymore
|
||||
} as const);
|
||||
|
||||
export type PermissionResultMap = typeof PERMISSION_RESULTS;
|
||||
|
||||
export type PermissionResultValues = PermissionResultMap[keyof PermissionResultMap];
|
||||
42
src/shared/api/query-keys.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/* eslint-disable @cspell/spellchecker */
|
||||
import { createQueryKeys, mergeQueryKeys } from '@lukemorales/query-key-factory';
|
||||
|
||||
const appDataQueryKey = createQueryKeys('appData', {
|
||||
appInfo: null,
|
||||
});
|
||||
|
||||
const myWalletQueryKey = createQueryKeys('myWallet', {
|
||||
walletReadList: ({ paging, search }) => [{ paging, search }],
|
||||
walletRead: ({ tbAcnutMastr }) => [{ tbAcnutMastr }],
|
||||
});
|
||||
|
||||
const comCodeQueryKey = createQueryKeys('comCode', {
|
||||
codeManage: ({ id }) => [{ id }],
|
||||
});
|
||||
|
||||
const exchangeQueryKey = createQueryKeys('exchange', {
|
||||
findRecipient: ({ aliasNm, trgetInstt, bizNo }) => [{ aliasNm, trgetInstt, bizNo }],
|
||||
recentRecipients: ({ paging, search }) => [{ paging, search }],
|
||||
});
|
||||
|
||||
const shopListQueryKey = createQueryKeys('shopList', {
|
||||
getShopList: ({ paging, search }) => [{ paging, search }],
|
||||
});
|
||||
|
||||
const notificationQueryKey = createQueryKeys('notification', {
|
||||
notificationList: ({ paging, search }) => [{ paging, search }],
|
||||
});
|
||||
|
||||
const cstrQueryKey = createQueryKeys('cstr', {
|
||||
cstmr: ({ cstmrNo }) => [{ cstmrNo }],
|
||||
});
|
||||
|
||||
export const queries = mergeQueryKeys(
|
||||
appDataQueryKey,
|
||||
myWalletQueryKey,
|
||||
comCodeQueryKey,
|
||||
exchangeQueryKey,
|
||||
shopListQueryKey,
|
||||
notificationQueryKey,
|
||||
cstrQueryKey,
|
||||
);
|
||||
354
src/shared/api/urls.ts
Normal file
@@ -0,0 +1,354 @@
|
||||
import { businessMemberInfo } from '@/entities/business-member/api/use-business-member-info-mutation';
|
||||
import {
|
||||
API_BASE_URL,
|
||||
API_URL_KEY,
|
||||
API_PARAM
|
||||
} from './../constants/url';
|
||||
|
||||
// all api URL
|
||||
export const API_URL = {
|
||||
/* AUTH */
|
||||
login: () => {
|
||||
// AUTH-API-001
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/login`;
|
||||
},
|
||||
delegatedLogin: () => {
|
||||
// AUTH-API-002
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/delegated-login`;
|
||||
},
|
||||
delegatedLoginAuthentication: () => {
|
||||
// AUTH-API-003
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/delegated-login/authentication`;
|
||||
},
|
||||
logout: () => {
|
||||
// AUTH-API-004
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/logout`;
|
||||
},
|
||||
refresh: () => {
|
||||
// AUTH-API-005
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/refresh`;
|
||||
},
|
||||
verify: () => {
|
||||
// AUTH-API-006
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/verify`;
|
||||
},
|
||||
emailSend: () => {
|
||||
// AUTH-API-007
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/email/send`;
|
||||
},
|
||||
emailVerify: () => {
|
||||
// AUTH-API-008
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/email/verify`;
|
||||
},
|
||||
phoneSend: () => {
|
||||
// AUTH-API-009
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/phone/send`;
|
||||
},
|
||||
phoneVerify: () => {
|
||||
// AUTH-API-010
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/phone/verify`;
|
||||
},
|
||||
fidoRegisterBegin: () => {
|
||||
// AUTH-API-011
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/fido/register/begin`;
|
||||
},
|
||||
fidoRegisterComplete: () => {
|
||||
// AUTH-API-012
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/fido/register/complete`;
|
||||
},
|
||||
fidoLoginBegin: () => {
|
||||
// AUTH-API-013
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/fido/login/begin`;
|
||||
},
|
||||
fidoLoginComplete: () => {
|
||||
// AUTH-API-014
|
||||
return `${API_BASE_URL}/auth/v1/${API_URL_KEY}/fido/login/complete`;
|
||||
},
|
||||
|
||||
/* Code Management 코드 관리 API */
|
||||
codesSelect: () => {
|
||||
// GET: 전체 코드 조회 (캐시)
|
||||
// POST: 조건별 코드 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/codes/select`;
|
||||
},
|
||||
codesListByCodeCl: (codeCl: string) => {
|
||||
// GET: 특정 CodelCl 코드 리스트 조회 (캐시);
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/codes/list/${codeCl}`;
|
||||
},
|
||||
codesGroupByCodeCl: (codeCl: string) => {
|
||||
// GET: 특정 CodeCl 그룹 조회 (캐시)
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/codes/group/${codeCl}`;
|
||||
},
|
||||
codesCacheStatus: () => {
|
||||
// GET: 캐시 상태 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/codes/cache/status`;
|
||||
},
|
||||
codesCacheRefresh: () => {
|
||||
// POST: 전체 캐시 갱신
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/codes/cache/refresh`;
|
||||
},
|
||||
codesCacheRefreshByCodelCl: (codeCl: string) => {
|
||||
// POST: 특정 CodeCl 캐시 갱신
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/codes/cache/refresh/${codeCl}`;
|
||||
},
|
||||
|
||||
|
||||
|
||||
/* transaction Management - 거래관리 API */
|
||||
allTransactionList: () => {
|
||||
// POST: 거래 내역 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/list`;
|
||||
},
|
||||
cashReceitList: () => {
|
||||
// POST: 거래 내역 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/list`;
|
||||
},
|
||||
escroList: () => {
|
||||
// POST: 거래 내역 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/list`;
|
||||
},
|
||||
billingList: () => {
|
||||
// POST: 거래 내역 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/list`;
|
||||
},
|
||||
allTransactionListSummary: () => {
|
||||
// POST: 거래 내역 합계 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/list/summary`;
|
||||
},
|
||||
downloadExcel: () => {
|
||||
// POST: 거래 엑셀 다운로드
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/download/excel`;
|
||||
},
|
||||
downloadConfirmation: () => {
|
||||
// POST: 거래 확인서 다운로드
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/download/confirmation`;
|
||||
},
|
||||
allTransactionDetail: () => {
|
||||
// POST: 거래내역 상세
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/detail`;
|
||||
},
|
||||
cashReceitDetail: () => {
|
||||
// POST: 현금영수증 상세
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/detail`;
|
||||
},
|
||||
escroDetail: () => {
|
||||
// POST: 에스크로 상세
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/detail`;
|
||||
},
|
||||
billingDetail: () => {
|
||||
// POST: 빌링 상세
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/detail`;
|
||||
},
|
||||
allTransactionCancel: () => {
|
||||
// POST: 거래취소 요청
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/cancel`;
|
||||
},
|
||||
allTransactionCancelInfo: () => {
|
||||
// POST: 거래취소 정보 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/cancel-info`;
|
||||
},
|
||||
|
||||
settlementList: () => {
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/list`;
|
||||
},
|
||||
|
||||
businessMemberInfo: () => {
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/transaction/detail`;
|
||||
},
|
||||
|
||||
/* User Management - 사용자 관리 API */
|
||||
userExistsUserid: () => {
|
||||
// 중복 사용자 ID 확인
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/user/exists/userid`;
|
||||
},
|
||||
userCreate: () => {
|
||||
// 사용자 추가
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/user/create`;
|
||||
},
|
||||
|
||||
/* Counsel Management - 문의상담 API */
|
||||
counselList: () => {
|
||||
// POST: 1:1 문의 목록 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/counsel/list`;
|
||||
},
|
||||
counselSave: () => {
|
||||
// POST: 1:1 문의 등록
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/counsel/save`;
|
||||
},
|
||||
|
||||
/* FAQ Management - FAQ(자주 묻는 질문) API */
|
||||
faqList: () => {
|
||||
// POST: FAQ 목록 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/faq/list`;
|
||||
},
|
||||
|
||||
/* Extension Management - 부가서비스 API */
|
||||
extensionSmsResend: () => {
|
||||
// POST: SMS 결제 통보 > SMS 재발송
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/sms/resend`;
|
||||
},
|
||||
extensionSmsList: () => {
|
||||
// POST: SMS 결제 통보 목록 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/sms/list`;
|
||||
},
|
||||
extensionSmsDownloadExcel: () => {
|
||||
// POST: SMS 결제 통보 엑셀 다운
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/sms/download/excel`;
|
||||
},
|
||||
extensionSmsDetail: () => {
|
||||
// POST: SMS 결제 통보 상세 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/sms/detail`;
|
||||
},
|
||||
extensionList: () => {
|
||||
// POST: 부가서비스 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/list`;
|
||||
},
|
||||
extensionKeyinList: () => {
|
||||
// POST: KEY-IN 결제 목록 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/keyin/list`;
|
||||
},
|
||||
extensionKeyinDownloadExcel: () => {
|
||||
// POST: KEY-IN 결제 엑셀 다운
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/keyin/download/excel`;
|
||||
},
|
||||
extensionKeyinApply: () => {
|
||||
// POST: KEY-IN 결제 > 결제 신청
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/keyin/apply`;
|
||||
},
|
||||
extensionArsResend: () => {
|
||||
// POST: SMS 신용카드 ARS 결제 > SMS 재전송
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/ars/resend`;
|
||||
},
|
||||
extensionArsList: () => {
|
||||
// POST: 신용카드 ARS 결제 > 목록 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/ars/list`;
|
||||
},
|
||||
extensionArsDownloadExcel: () => {
|
||||
// POST: 신용카드 ARS 결제 > 엑셀 다운
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/ars/download/excel`;
|
||||
},
|
||||
extensionArsDetail: () => {
|
||||
// POST: 신용카드 ARS 결제 > 상세 내용 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/ars/detail`;
|
||||
},
|
||||
extensionArsApply: () => {
|
||||
// POST: 신용카드 ARS 결제 > 결제 신청
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/ars/apply`;
|
||||
},
|
||||
extensionAlimtalkSettingSave: () => {
|
||||
// POST: 알림톡 결제 통보 > 서비스 설정 저장
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/alimtalk/setting/save`;
|
||||
},
|
||||
extensionAlimtalkSettingDetail: () => {
|
||||
// POST: 알림톡 결제 통보 > 서비스 설정 목록 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/alimtalk/setting/detail`;
|
||||
},
|
||||
extensionAlimtalkList: () => {
|
||||
// POST: 알림톡 결제 통보 목록 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/alimtalk/list`;
|
||||
},
|
||||
extensionAlimtalkDownloadExcel: () => {
|
||||
// POST: 알림톡 결제 통보 엑셀 다운
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/alimtalk/download/excel`;
|
||||
},
|
||||
extensionAlimtalkDetail: () => {
|
||||
// POST: 알림톡 결제 통보 상세 조회
|
||||
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/extension/alimtalk/detail`;
|
||||
},
|
||||
|
||||
/* Empty Token API Management - jwt 토큰이 없는 API 관리 */
|
||||
emptyTokenVerifyCode: () => {
|
||||
// POST: 인증 코드 검증
|
||||
return `${API_BASE_URL}/api/v1/empty-token/${API_URL_KEY}/verify/code`;
|
||||
},
|
||||
emptyTokenFindSendCode: () => {
|
||||
// POST: 인증 코드 전솔
|
||||
return `${API_BASE_URL}/api/v1/empty-token/${API_URL_KEY}/find-send/code`;
|
||||
},
|
||||
emptyTokenChange: () => {
|
||||
// POST: 비밀번호 변경
|
||||
return `${API_BASE_URL}/api/v1/empty-token/${API_URL_KEY}/change`;
|
||||
},
|
||||
emptyTokenAddSendCode: () => {
|
||||
// POST: 인증 코드 전송
|
||||
return `${API_BASE_URL}/api/v1/empty-token/${API_URL_KEY}/add-send/code`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
getAppInfo: `${API_BASE_URL}/ewa/common/AppManage/getAppInfo`, // 이용자 APP 버전 조회
|
||||
checkIdentifyInfo: `${API_BASE_URL}/ewa/common/checkIdentifyInfo`, // 신분증검증요청
|
||||
checkPinNum: `${API_BASE_URL}/ewa/common/checkPinNum`, // 인증번호검증(핀 검증)
|
||||
confirmUser: `${API_BASE_URL}/ewa/common/confirmUser`, // 사용자정보조회
|
||||
createUser: `${API_BASE_URL}/ewa/common/createUser`, // 회원가입(고객등록)
|
||||
regLbdyUse: `${API_BASE_URL}/ewa/common/regLbdyUse`, // 생체인증설정여부등록
|
||||
regPinNum: `${API_BASE_URL}/ewa/common/regPinNum`, // 인증번호등록(핀)
|
||||
selfAuth: `${API_BASE_URL}/ewa/common/selfAuth`, // 본인인증정보입력
|
||||
selfAuthNum: `${API_BASE_URL}/ewa/common/selfAuthNum`, // 본인인증(검증)
|
||||
cstmrReadList: `${API_BASE_URL}/ewa/cstmr/readList`, // 고객목록조회
|
||||
stplatReadList: `${API_BASE_URL}/ewa/stplat/readList`, // 약관목록조회
|
||||
login: `${API_BASE_URL}/users/login`, // 고객로그인
|
||||
//login: `${API_BASE_URL}/users/login`, // 고객로그인
|
||||
accountValid: `${API_BASE_URL}/ewa/acnut/accountValid`, // 계좌인증정보입력(검증)
|
||||
createWallet: `${API_BASE_URL}/ewa/wallet/createWallet`, // 이용자 지갑 생성
|
||||
deleteWallet: `${API_BASE_URL}/ewa/wallet/deleteWallet`, // 이용자 지갑 삭제
|
||||
walletReadList: `${API_BASE_URL}/ewa/wallet/readList`, // 이용자지갑 거래내역조회
|
||||
walletRead: `${API_BASE_URL}/ewa/wallet/read`, // 이용자지갑 거래내역상세
|
||||
cstmr: (cstmrNo?: string) => {
|
||||
return `${API_BASE_URL}/ewa/cstmr${cstmrNo ? '/' + cstmrNo : ''}`;
|
||||
}, // 고객
|
||||
cmmntyManage: (id?: string) => {
|
||||
return `${API_BASE_URL}/ewa/manage/cmmnty/CmmntyManage${id ? '/' + id : ''}`;
|
||||
}, // 이용자 커뮤니티
|
||||
codeManage: (id: string) => {
|
||||
return `${API_BASE_URL}/common/codedata/CodeData${id ? '/' + id : ''}`;
|
||||
}, // 공통코드
|
||||
recentRecipients: `${API_BASE_URL}/ewa/wallet/recent`, // 최근 이체대상 조회
|
||||
findUserAlias: `${API_BASE_URL}/ewa/manage/Alias/find`, // 이체를 위한 사용자 이름, 지갑주소 조회
|
||||
depositTrans: `${API_BASE_URL}/ewa/acnut/depositTrans`, // 예금 토큰 이체 , 송금
|
||||
emoneyTrans: `${API_BASE_URL}/ewa/acnut/emoneyTrans`, // 이머니 토큰 이체, 송금
|
||||
convDeposit: `${API_BASE_URL}/ewa/acnut/convDeposit`, // 전환 입금
|
||||
depositConv: `${API_BASE_URL}/ewa/acnut/depositConv`, // 예금 전환
|
||||
payment: `${API_BASE_URL}/ewa/acnut/payment`, // 결제
|
||||
topUp: `${API_BASE_URL}/ewa/manage/emoney/chargeEmoneyToken`, // 충전
|
||||
sendDsuseEmoneyInfo: `${API_BASE_URL}/ewa/manage/emoney/sendDsuseEmoneyInfo`, // 예금 토큰 전환 (타행)
|
||||
dsuseOwnEmoneyToken: `${API_BASE_URL}/ewa/manage/emoney/dsuseOwnEmoneyToken`, // 예금 토큰 전환 (당행)
|
||||
readFranchiseList: `${API_BASE_URL}/ewa/common/voucher/readFranchiseList`, // 사용처 목록 조회
|
||||
readFranchise: (franchiseId: string) => `${API_BASE_URL}/ewa/common/voucher/readFranchise/${franchiseId}`, // 사용처 상세 조회
|
||||
notificationList: `${API_BASE_URL}/com/manage/pushMsg/readList`, // 알림 메세지 목록
|
||||
updateIndict: (mssageManageId: string) => `${API_BASE_URL}/com/manage/pushMsg/updateIndict/${mssageManageId}`, // 알림 메세지 확인 처리
|
||||
*/
|
||||
};
|
||||
|
||||
export type API_URL_TYPE = typeof API_URL;
|
||||
|
||||
export const WHITE_LIST_URLS: string[] = [
|
||||
API_URL.login(),
|
||||
API_URL.delegatedLogin(),
|
||||
API_URL.delegatedLoginAuthentication(),
|
||||
API_URL.logout(),
|
||||
API_URL.refresh(),
|
||||
API_URL.verify(),
|
||||
API_URL.emailSend(),
|
||||
API_URL.emailVerify(),
|
||||
API_URL.phoneSend(),
|
||||
API_URL.phoneVerify(),
|
||||
API_URL.fidoRegisterBegin(),
|
||||
API_URL.fidoRegisterComplete(),
|
||||
API_URL.fidoLoginBegin(),
|
||||
API_URL.fidoLoginComplete(),
|
||||
/*
|
||||
API_URL.confirmUser,
|
||||
|
||||
API_URL.selfAuth,
|
||||
API_URL.selfAuthNum,
|
||||
API_URL.createUser,
|
||||
API_URL.regPinNum,
|
||||
API_URL.stplatReadList,
|
||||
*/
|
||||
];
|
||||
|
||||
export const getApiPathname = (url: string) => {
|
||||
return new URL(url).pathname;
|
||||
};
|
||||
79
src/shared/configs/axios/index.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
import { WHITE_LIST_URLS } from '@/shared/api/urls';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import { checkIsAxiosError, getLocalStorage } from '@/shared/lib';
|
||||
import { finalizeConfig, extractAccessToken, extractRequestId } from './utils';
|
||||
import { HEADER_USER_AGENT } from '@/shared/constants/url';
|
||||
|
||||
const onRequestFulfilled = (config: InternalAxiosRequestConfig) => {
|
||||
config.headers['Content-Type'] = 'application/json;charset=UTF-8';
|
||||
config.headers['X-User-Agent'] = HEADER_USER_AGENT;
|
||||
|
||||
if(WHITE_LIST_URLS.includes(config?.url ?? '')){
|
||||
if(config.headers.hasOwnProperty('Authorization')){
|
||||
delete config.headers.Authorization;
|
||||
}
|
||||
}
|
||||
else{
|
||||
const accessToken = getLocalStorage(StorageKeys.AccessToken);
|
||||
const tokenType = getLocalStorage(StorageKeys.TokenType);
|
||||
config.headers.Authorization = `${tokenType} ${accessToken}`;
|
||||
// config.headers['X-Request-id'] = getLocalStorage(StorageKeys.requestId) ?? '';
|
||||
}
|
||||
return finalizeConfig(config);
|
||||
};
|
||||
|
||||
const onRequestRejected = (error: any) => {
|
||||
const { method, url, params, data, headers } = error.config;
|
||||
Sentry.setContext('API Request Detail', {
|
||||
method,
|
||||
url,
|
||||
params,
|
||||
data,
|
||||
headers,
|
||||
});
|
||||
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
const onResponseFulfilled = (response: AxiosResponse) => {
|
||||
extractAccessToken(response);
|
||||
extractRequestId(response);
|
||||
return {
|
||||
...response,
|
||||
data: response.data.data,
|
||||
};
|
||||
};
|
||||
|
||||
const onResponseRejected = (error: AxiosError) => {
|
||||
if (error?.response) {
|
||||
const { data, status } = error.response;
|
||||
extractRequestId(error?.response);
|
||||
Sentry.setContext('API Response Detail', {
|
||||
status,
|
||||
data,
|
||||
});
|
||||
}
|
||||
return new Promise((_resolve, reject) => {
|
||||
if (checkIsAxiosError(error)) {
|
||||
// iOS의 경우 window에서 unload event가 일어날때 네트워크 에러가 발생하곤 해서 이런 케이스를 방지하기 위해 지연시킴
|
||||
// location.href, location.reload 등으로 unload event가 일어나면 자바스크립트 런타임이 초기화되므로 settimeout으로 인한 네트워크 에러가 트리거 x
|
||||
setTimeout(() => reject(error), 300);
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const initAxios = () => {
|
||||
axios.defaults.withCredentials = true;
|
||||
axios.defaults.timeout = 15000;
|
||||
axios.defaults.transitional = {
|
||||
clarifyTimeoutError: true,
|
||||
forcedJSONParsing: true,
|
||||
silentJSONParsing: true,
|
||||
};
|
||||
axios.interceptors.request.use(onRequestFulfilled, onRequestRejected);
|
||||
axios.interceptors.response.use(onResponseFulfilled, onResponseRejected);
|
||||
};
|
||||
28
src/shared/configs/axios/utils.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import { setLocalStorage } from '@/shared/lib';
|
||||
|
||||
export const finalizeConfig = (config: InternalAxiosRequestConfig) => {
|
||||
const { params, data } = config;
|
||||
return {
|
||||
...config,
|
||||
params,
|
||||
data,
|
||||
};
|
||||
};
|
||||
|
||||
export const extractAccessToken = (response: AxiosResponse): void => {
|
||||
const authHeader = response?.headers?.['authorization'];
|
||||
if (authHeader) {
|
||||
const accessToken = authHeader.substring(7);
|
||||
setLocalStorage(StorageKeys.Jwt, accessToken);
|
||||
}
|
||||
};
|
||||
|
||||
export const extractRequestId = (response: AxiosResponse): void => {
|
||||
const requestIdHeader = response?.headers?.['x-request-id'];
|
||||
if (requestIdHeader) {
|
||||
const requestId = requestIdHeader.replaceAll(', *', '');
|
||||
setLocalStorage(StorageKeys.RequestId, requestId);
|
||||
}
|
||||
};
|
||||
9
src/shared/configs/config.development.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { RELEASE_VERSION } from '@/shared/constants/environment';
|
||||
import { ConfigMap } from './types';
|
||||
/**
|
||||
* development
|
||||
*/
|
||||
export const DEVELOPMENT_CONFIG_MAP: ConfigMap = {
|
||||
APP_NAME: 'NICE-Development',
|
||||
WEB_VERSION: RELEASE_VERSION ?? '1',
|
||||
};
|
||||
10
src/shared/configs/config.production.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { RELEASE_VERSION } from '@/shared/constants/environment';
|
||||
import { ConfigMap } from './types';
|
||||
|
||||
/**
|
||||
* production
|
||||
*/
|
||||
export const PRODUCTION_CONFIG_MAP: ConfigMap = {
|
||||
APP_NAME: 'NICE-Production',
|
||||
WEB_VERSION: RELEASE_VERSION ?? '1',
|
||||
};
|
||||
28
src/shared/configs/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Env } from './types';
|
||||
import { RELEASE_VERSION } from '@/shared/constants/environment';
|
||||
import { PRODUCTION_CONFIG_MAP } from './config.production';
|
||||
import { DEVELOPMENT_CONFIG_MAP } from './config.development';
|
||||
|
||||
|
||||
// env와 bankKey에따라 mockup, 개발계(development), 검증계(production)의 은행 설정 가져오기
|
||||
function getConfig(env: Env) {
|
||||
function getCurrEnvConfigMap() {
|
||||
if (env === 'development') return DEVELOPMENT_CONFIG_MAP;
|
||||
if (env === 'production') return PRODUCTION_CONFIG_MAP;
|
||||
return DEVELOPMENT_CONFIG_MAP;
|
||||
}
|
||||
return getCurrEnvConfigMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 환경변수에서 전달받은 환경값 ( mockup | development | production )
|
||||
* 이 값으로 api 호출 URL 세팅
|
||||
*/
|
||||
const ENV: Env = (import.meta.env.VITE_APP_ENV ?? 'development') as Env;
|
||||
|
||||
const WEB_VERSION = RELEASE_VERSION ?? '1';
|
||||
|
||||
export const config = getConfig(ENV);
|
||||
console.log(`%cENV: %c${ENV}`, 'font-weight:bold;', 'color:red;font-weight:bold;');
|
||||
console.log(`%cWEB_VERSION: %c${WEB_VERSION}`, 'font-weight:bold;', 'color:red;font-weight:bold;');
|
||||
console.log('config', config);
|
||||
20
src/shared/configs/query.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
const globalQueryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
// 사용자에게 에러상태를 즉시 보여주기 위해 retry false로 지정
|
||||
retry: false,
|
||||
refetchOnWindowFocus: true,
|
||||
throwOnError: true,
|
||||
// suspense: true,
|
||||
networkMode: 'always',
|
||||
},
|
||||
mutations: {
|
||||
throwOnError: true,
|
||||
networkMode: 'always',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const getGlobalQueryClient = (): QueryClient => globalQueryClient;
|
||||
196
src/shared/configs/sentry/index.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { OverlayProvider } from 'overlay-kit';
|
||||
import React, { lazy } from 'react';
|
||||
import {
|
||||
createRoutesFromChildren,
|
||||
matchRoutes,
|
||||
Navigate,
|
||||
Route,
|
||||
Routes,
|
||||
useLocation,
|
||||
useNavigationType,
|
||||
} from 'react-router';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
|
||||
import { AppChildren } from '@/app/app-children';
|
||||
import { GlobalAPIErrorBoundary } from '@/widgets/error-boundaries';
|
||||
import { NotFoundError } from '@/widgets/fallbacks';
|
||||
import { ProtectedRoute } from '@/widgets/protected-route';
|
||||
import { PullToRefreshRoute } from '@/widgets/pull-to-refresh/pull-to-refresh-route';
|
||||
import { SubLayout } from '@/widgets/sub-layout';
|
||||
import { IS_PROD } from '@/shared/constants/environment';
|
||||
import { ROUTE_NAMES } from '@/shared/constants/route-names';
|
||||
import { toCamelCase } from '@/shared/lib/to-camel-case';
|
||||
|
||||
export const initSentry = () => {
|
||||
if (IS_PROD) {
|
||||
Sentry.init({
|
||||
environment: import.meta.env.VITE_APP_ENV,
|
||||
dsn: 'https://ddd8755ce025f753e8521af5b1034a93@o4507569039867904.ingest.us.sentry.io/4507569041244160',
|
||||
integrations: [
|
||||
Sentry.browserTracingIntegration(),
|
||||
Sentry.browserProfilingIntegration(),
|
||||
Sentry.replayIntegration(),
|
||||
Sentry.reactRouterV6BrowserTracingIntegration({
|
||||
useEffect: React.useEffect,
|
||||
useLocation,
|
||||
useNavigationType,
|
||||
createRoutesFromChildren,
|
||||
matchRoutes,
|
||||
}),
|
||||
],
|
||||
normalizeDepth: 6,
|
||||
// Performance Monitoring
|
||||
tracesSampleRate: 1.0, // Capture 100% of the transactions
|
||||
// Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
|
||||
tracePropagationTargets: [
|
||||
'http://3.35.79.250:8090'
|
||||
],
|
||||
// Session Replay
|
||||
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
|
||||
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
|
||||
|
||||
profilesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6(createBrowserRouter);
|
||||
|
||||
const modules = import.meta.glob('~/pages/**/*.tsx');
|
||||
|
||||
const lazyLoad = (path: string) => {
|
||||
const module = modules[`${path}.tsx`];
|
||||
if (!module) {
|
||||
throw new Error(`Module not found: ${path}`);
|
||||
}
|
||||
|
||||
const namedExportKebab = path.split('/').pop()?.replace('.tsx', '') as string;
|
||||
const namedExportCamel = toCamelCase(namedExportKebab);
|
||||
return lazy(() => module().then((load: any) => ({
|
||||
default: load[namedExportCamel]
|
||||
})));
|
||||
};
|
||||
|
||||
const HomePage = lazyLoad('/src/pages/home/home-page');
|
||||
const TransactionPages = lazyLoad('/src/pages/transaction/transaction-pages');
|
||||
const SettlementPages = lazyLoad('/src/pages/settlement/settlement-pages');
|
||||
const BusinessMemberPages = lazyLoad('/src/pages/business-member/business-member-pages');
|
||||
const PaymentPages = lazyLoad('/src/pages/payment/payment-pages');
|
||||
const AccountPages = lazyLoad('/src/pages/account/account-pages');
|
||||
const TaxPages = lazyLoad('/src/pages/tax/tax-pages');
|
||||
const AdditionalServicePages = lazyLoad('/src/pages/additional-service/additional-service-pages');
|
||||
const SupportPages = lazyLoad('/src/pages/support/support-pages');
|
||||
const SettingPage = lazyLoad('/src/pages/setting/setting-page');
|
||||
const AlarmPages = lazyLoad('/src/pages/alarm/alarm-pages');
|
||||
/*
|
||||
const IntroPage = lazyLoad('/src/pages/intro/intro-page');
|
||||
const StartPage = lazyLoad('/src/pages/sign-up/start/start-page');
|
||||
const AppAuthPage = lazyLoad('/src/pages/sign-up/app-auth/app-auth-page');
|
||||
const MobileVerificationPage = lazyLoad('/src/pages/sign-up/mobile-verification/mobile-verification-page');
|
||||
const SignUpPages = lazyLoad('/src/pages/sign-up/sign-up-pages');
|
||||
const IssueWalletPages = lazyLoad('/src/pages/issue-wallet/issue-wallet-pages');
|
||||
const MyWalletPages = lazyLoad('/src/pages/my-wallet/my-wallet-pages');
|
||||
const ExchangePages = lazyLoad('/src/pages/exchange/exchange-pages');
|
||||
const MyBankAccountPage = lazyLoad('/src/pages/my-bank-account/my-bank-account-page');
|
||||
const SecurityPage = lazyLoad('/src/pages/security/security-page');
|
||||
const AppVersionPage = lazyLoad('/src/pages/app-version/app-version-page');
|
||||
const SettingFontSizePage = lazyLoad('/src/pages/setting-font-size/setting-font-size-page');
|
||||
const CommunityPages = lazyLoad('/src/pages/community/community-pages');
|
||||
const MenuPage = lazyLoad('/src/pages/menu/menu-page');
|
||||
const LoginPage = lazyLoad('/src/pages/login/login-page');
|
||||
const ReLoginPage = lazyLoad('/src/pages/login/re-login-page');
|
||||
const HomePage = lazyLoad('/src/pages/home/home-page');
|
||||
const ShopListPage = lazyLoad('/src/pages/shop-list/shop-list-page');
|
||||
const NotificationPages = lazyLoad('/src/pages/notification/notification-pages');
|
||||
const PaymentPages = lazyLoad('/src/pages/payment/payment-pages');
|
||||
const VoucherPages = lazyLoad('/src/pages/voucher/voucher-pages');
|
||||
const PaymentGuidePage = lazyLoad('/src/pages/payment/payment-guide-page');
|
||||
const InputShopWalletAddressPage = lazyLoad('/src/pages/payment/input-shop-wallet-address-page');
|
||||
*/
|
||||
|
||||
export const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
|
||||
const Pages = () => {
|
||||
return (
|
||||
<OverlayProvider>
|
||||
<GlobalAPIErrorBoundary>
|
||||
<AppChildren />
|
||||
<SentryRoutes>
|
||||
<Route element={<SubLayout />}>
|
||||
<Route element={<ProtectedRoute />}>
|
||||
<Route path={ROUTE_NAMES.home} element={<HomePage />} />
|
||||
<Route path={ROUTE_NAMES.transaction.base} element={<TransactionPages />} />
|
||||
<Route path={ROUTE_NAMES.settlement.base} element={<SettlementPages />} />
|
||||
<Route path={ROUTE_NAMES.businessMember.base} element={<BusinessMemberPages />} />
|
||||
<Route path={ROUTE_NAMES.payment.base} element={<PaymentPages />} />
|
||||
<Route path={ROUTE_NAMES.account.base} element={<AccountPages />} />
|
||||
<Route path={ROUTE_NAMES.tax.base} element={<TaxPages />} />
|
||||
<Route path={ROUTE_NAMES.additionalService.base} element={<AdditionalServicePages />} />
|
||||
<Route path={ROUTE_NAMES.support.base} element={<SupportPages />} />
|
||||
<Route path={ROUTE_NAMES.setting} element={<SettingPage />} />
|
||||
<Route path={ROUTE_NAMES.alarm.base} element={<AlarmPages />} />
|
||||
</Route>
|
||||
<Route path="*" element={<NotFoundError />} />
|
||||
</Route>
|
||||
<Route element={<PullToRefreshRoute />}>
|
||||
<Route element={<ProtectedRoute />}>
|
||||
|
||||
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
|
||||
{
|
||||
/*
|
||||
<Route path={ROUTE_NAMES.intro} element={<IntroPage />} />
|
||||
<Route path={ROUTE_NAMES.start} element={<StartPage />} />
|
||||
<Route path={ROUTE_NAMES.appAuth} element={<AppAuthPage />} />
|
||||
<Route element={<SubLayout />}>
|
||||
<Route path={ROUTE_NAMES.mobileVerification} element={<MobileVerificationPage />} />
|
||||
<Route path={ROUTE_NAMES.signUp.base} element={<SignUpPages />} />
|
||||
<Route element={<ProtectedRoute />}>
|
||||
<Route path={ROUTE_NAMES.issueWallet.base} element={<IssueWalletPages />} />
|
||||
<Route path={ROUTE_NAMES.myWallet.base} element={<MyWalletPages />} />
|
||||
<Route path={ROUTE_NAMES.exchange.base} element={<ExchangePages />} />
|
||||
<Route path={ROUTE_NAMES.myBankAccount} element={<MyBankAccountPage />} />
|
||||
<Route path={ROUTE_NAMES.security} element={<SecurityPage />} />
|
||||
<Route path={ROUTE_NAMES.appVersion} element={<AppVersionPage />} />
|
||||
<Route path={ROUTE_NAMES.settingFontSize} element={<SettingFontSizePage />} />
|
||||
<Route path={ROUTE_NAMES.community.base} element={<CommunityPages />} />
|
||||
<Route path={ROUTE_NAMES.shopList} element={<ShopListPage />} />
|
||||
<Route path={ROUTE_NAMES.notification.base} element={<NotificationPages />} />
|
||||
<Route path={ROUTE_NAMES.payment.base} element={<PaymentPages />} />
|
||||
<Route path={ROUTE_NAMES.menu} element={<MenuPage />} />
|
||||
<Route path={ROUTE_NAMES.voucher.base} element={<VoucherPages />} />
|
||||
</Route>
|
||||
<Route path="*" element={<NotFoundError />} />
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.login} element={<LoginPage />} />
|
||||
<Route path={ROUTE_NAMES.reLogin} element={<ReLoginPage />} />
|
||||
<Route element={<PullToRefreshRoute />}>
|
||||
<Route element={<ProtectedRoute />}>
|
||||
<Route path={ROUTE_NAMES.home} element={<HomePage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path={ROUTE_NAMES.paymentGuide} element={<PaymentGuidePage />} />
|
||||
<Route path={ROUTE_NAMES.inputShopWalletAddr} element={<InputShopWalletAddressPage />} />
|
||||
*/
|
||||
}
|
||||
|
||||
</SentryRoutes>
|
||||
</GlobalAPIErrorBoundary>
|
||||
</OverlayProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const router = sentryCreateBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <Navigate to={ROUTE_NAMES.home} />,
|
||||
},
|
||||
{
|
||||
path: '/*',
|
||||
element: <Pages />,
|
||||
},
|
||||
]);
|
||||
56
src/shared/configs/test/render.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { render, RenderOptions } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { FC, ReactElement, useEffect } from 'react';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router';
|
||||
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { SubLayout } from '@/widgets/sub-layout';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
throwOnError: true,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
interface RouterParams {
|
||||
path?: string;
|
||||
entry?: string;
|
||||
state?: any;
|
||||
}
|
||||
|
||||
const AllTheProviders: FC<{ children: React.ReactNode; routerParams?: RouterParams }> = ({
|
||||
children,
|
||||
routerParams,
|
||||
}) => {
|
||||
const { path = '/', entry = '/', state } = routerParams ?? {};
|
||||
const initialEntry = { pathname: entry, state };
|
||||
useEffect(() => {
|
||||
return () => queryClient.clear();
|
||||
}, []);
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MemoryRouter initialEntries={[initialEntry]}>
|
||||
<Routes>
|
||||
<Route element={<SubLayout />}>
|
||||
<Route path={path} element={<>{children}</>} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</QueryClientProvider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
const user = userEvent.setup();
|
||||
const customRender = (ui: ReactElement, routerParams?: RouterParams, options?: Omit<RenderOptions, 'wrapper'>) => {
|
||||
return render(ui, { wrapper: (props) => <AllTheProviders {...props} routerParams={routerParams} />, ...options });
|
||||
};
|
||||
|
||||
export * from '@testing-library/react';
|
||||
export { customRender as render, user };
|
||||
7
src/shared/configs/types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type Env = 'development' | 'production'; // 환경변수로 전달받을 환경값 stage(개발계) production(검증계)
|
||||
|
||||
interface Config {
|
||||
APP_NAME: string;
|
||||
WEB_VERSION: string; // 웹 버전
|
||||
}
|
||||
export type ConfigMap = Config;
|
||||
2
src/shared/constants/colors.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const DEFAULT_BACKGROUND_COLOR = '#FFFFFF';
|
||||
export const HOME_BACKGROUND_COLOR = '#EEF3F8';
|
||||
1
src/shared/constants/common.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const IMAGE_ROOT = '/src/shared/ui/assets/images';
|
||||
14
src/shared/constants/environment.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import packageInfo from '../../../package.json';
|
||||
|
||||
// 어플리케이션 running environment (development, production, test)
|
||||
export const IS_LOCAL = import.meta.env.VITE_APP_ENV === 'local';
|
||||
export const IS_TEST = import.meta.env.VITE_APP_ENV === 'test';
|
||||
export const IS_DEV = import.meta.env.VITE_APP_ENV === 'development';
|
||||
export const IS_PROD = import.meta.env.VITE_APP_ENV === 'production';
|
||||
export const IS_STORYBOOK = !!import.meta.env.STORYBOOK;
|
||||
|
||||
export const IS_DEV_PHASE = IS_LOCAL || IS_DEV;
|
||||
|
||||
export const IS_MOCK_PHASE = import.meta.env.VITE_APP_ENV === 'mock';
|
||||
|
||||
export const RELEASE_VERSION = packageInfo.version;
|
||||
55
src/shared/constants/form.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { FieldValues, RegisterOptions } from 'react-hook-form';
|
||||
|
||||
export const createFormOptions = <T extends FieldValues>(overrides?: Partial<Record<string, RegisterOptions<T>>>) => {
|
||||
const defaultOptions: Partial<Record<string, RegisterOptions<T>>> = {
|
||||
NAME: {
|
||||
required: '이름을 입력해 주세요.',
|
||||
pattern: { value: /^[가-힣]{2,}$/, message: '이름을 두 글자 이상 입력해 주세요.' },
|
||||
},
|
||||
BIRTH: {
|
||||
minLength: { value: 8, message: '생년월일을 8자리로 입력해 주세요.' },
|
||||
pattern: {
|
||||
value: /^\d{4}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$/,
|
||||
message: '생년월일 형식에 맞게 입력해 주세요.',
|
||||
},
|
||||
validate: {
|
||||
isBeforeToday: (value: string | null | undefined) => {
|
||||
if (!value) return '생년월일을 입력해 주세요.';
|
||||
const birthDate = new Date(
|
||||
Number(value.slice(0, 4)),
|
||||
Number(value.slice(4, 6)) - 1,
|
||||
Number(value.slice(6, 8)),
|
||||
);
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
return birthDate < today || '오늘 이전 날짜로 입력해 주세요.';
|
||||
},
|
||||
},
|
||||
required: '생년월일을 입력해 주세요.',
|
||||
},
|
||||
CARRIER: {
|
||||
required: '',
|
||||
},
|
||||
PHONE: {
|
||||
required: '휴대폰 번호를 입력해 주세요.',
|
||||
pattern: { value: /^01[016789][0-9]{7,8}$/, message: '올바르지 않은 휴대폰 형식입니다.' },
|
||||
},
|
||||
BANK_ACCOUNT: {
|
||||
required: '계좌번호를 입력해 주세요.',
|
||||
pattern: { value: /^[0-9]+$/, message: '숫자만 입력 가능합니다.' },
|
||||
},
|
||||
BANK_CODE: {
|
||||
required: '은행을 선택해 주세요.',
|
||||
},
|
||||
ALIAS_NM: {
|
||||
required: true,
|
||||
pattern: { value: /^[0-9]+$/, message: '숫자만 입력 가능합니다.' },
|
||||
},
|
||||
};
|
||||
return {
|
||||
...defaultOptions,
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
export const MAX_INPUT_NUMBER = 999999999999999;
|
||||
33
src/shared/constants/local-storage.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export enum StorageKeys {
|
||||
TokenType = 'TOKEN_TYPE',
|
||||
AccessToken = 'ACCESS_TOKEN',
|
||||
RefreshToken = 'REFRESH_TOKEN',
|
||||
AccessTokenExpiresIn = 'ACCESS_TOKEN_EXPIRE_IN',
|
||||
RefreshTokenExpiresIn = 'REFRESH_TOKEN_EXPIRES_IN',
|
||||
MenuGrants = 'MENU_GRANTS',
|
||||
Usrid = 'USRID',
|
||||
ClientAddressIP = 'ClIENT_ADDRESS_IP',
|
||||
Requires2FA = 'REQUIRES_2FA',
|
||||
BottomBannerClose = 'BOTTOM_BANNER_CLOSE',
|
||||
RootStore = 'ROOT_STORE',
|
||||
|
||||
|
||||
requestId = 'REQUEST_ID',
|
||||
|
||||
Jwt = 'JWT',
|
||||
RequestId = 'REQUEST_ID',
|
||||
|
||||
HasBioHardware = 'HAS_BIO_HARDWARE',
|
||||
DeviceUniqueId = 'DEVICE_UNIQUE_ID',
|
||||
AppVersion = 'APP_VERSION',
|
||||
AppColor = 'APP_COLOR',
|
||||
RedirectPath = 'REDIRECT_PATH',
|
||||
Platform = 'PLATFORM',
|
||||
AppPagingSpeed = 'APP_PAGING_SPEED', // deprecated
|
||||
ScannedQrCode = 'SCANNED_QR_CODE',
|
||||
HasPushTokenSent = 'HAS_PUSH_TOKEN_SENT',
|
||||
SetFont = 'SET_FONT',
|
||||
|
||||
LogOut = 'LOGOUT',
|
||||
|
||||
}
|
||||
1
src/shared/constants/locales/en/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './start';
|
||||
5
src/shared/constants/locales/en/start.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const start = {
|
||||
alreadyRegistered: 'already Registered?',
|
||||
startConvenientSpending: 'Start enjoying convenient spending with an e-wallet.',
|
||||
start: 'Start',
|
||||
};
|
||||
5
src/shared/constants/locales/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import * as localeEn from './en';
|
||||
import * as localeKr from './kr';
|
||||
|
||||
export const kr = { ...localeKr };
|
||||
export const en = { ...localeEn };
|
||||
128
src/shared/constants/locales/kr/common.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
export const common = {
|
||||
confirm: '확인',
|
||||
depositToken: '예금 토큰',
|
||||
eMoneyToken: '이머니 토큰',
|
||||
voucher: '바우처',
|
||||
transferDeposit: '전환 입금',
|
||||
payment: '결제',
|
||||
remittance: '송금',
|
||||
switchDeposit: '예금 전환',
|
||||
topUp: '충전',
|
||||
conversion: '예금 토큰 전환',
|
||||
transactionNotAllowed: '결제/송금/예금 전환 불가',
|
||||
transferDepositTokenConversionNotAllowed: '송금/예금 토큰 전환 불가',
|
||||
paymentRemittanceNotAllowed: '결제/송금 불가',
|
||||
hideBalance: '잔액 숨기기',
|
||||
name: '이름',
|
||||
idNumber7Digits: '주민등록번호 7자리(생년월일/성별)',
|
||||
birthDate8Digits: '생년월일 (8자리)',
|
||||
selectCarrier: '통신사 선택',
|
||||
phoneNumberWithoutHyphens: '휴대폰 번호 (-없이 입력)',
|
||||
lastLoginDate: '최근 접속 일시',
|
||||
userSuffix: '님',
|
||||
transactionRestriction: '거래제한',
|
||||
issuedAccountNumber: '발급 계좌 번호',
|
||||
manageMyWallet: '내 지갑 관리',
|
||||
authenticationAndSecurity: '인증/보안',
|
||||
notice: '공지사항',
|
||||
serviceSettings: '서비스 설정',
|
||||
checkLinkedAccount: '연동 계좌 확인',
|
||||
notificationSettings: '알림설정',
|
||||
myVoucherStatus: '내 바우처 현황',
|
||||
serviceGuide: '서비스 안내',
|
||||
usageGuide: '이용 안내',
|
||||
usageLocationGuide: '사용처 안내',
|
||||
customerSupport: '고객 지원',
|
||||
frequentlyAskedQuestions: '자주 묻는 질문',
|
||||
oneOnOneInquiry: '1:1 문의',
|
||||
oneOnOneInquirySubmit: '1:1 문의하기',
|
||||
phoneInquiry: '전화 문의',
|
||||
termsAndConditions: '약관 및 이용 동의',
|
||||
privacyPolicy: '개인정보 처리방침',
|
||||
appVersion: '앱 버전',
|
||||
largeFontSetting: '큰 폰트설정',
|
||||
logout: '로그아웃',
|
||||
cancel: '취소',
|
||||
logoutAction: '로그아웃 하기',
|
||||
customerCenter: '고객센터',
|
||||
weekdays: '평일',
|
||||
register: '등록하기',
|
||||
edit: '수정',
|
||||
delete: '삭제',
|
||||
total: '전체',
|
||||
detail: '상세',
|
||||
search: '검색',
|
||||
resetPin: '간편 비밀번호 재설정',
|
||||
shuffle: '재배열',
|
||||
close: '닫기',
|
||||
reset: '재설정',
|
||||
proceedWithRegistration: '가입진행',
|
||||
selectBank: '은행 선택',
|
||||
next: ' 다음',
|
||||
deleteAll: '전체삭제',
|
||||
start: '시작하기',
|
||||
allUsageHistoryView: '전체 이용 내역 조회',
|
||||
selectInquiryPeriod: '조회 기간 선택',
|
||||
previousMonth: '이전달',
|
||||
nextMonth: '다음달',
|
||||
selectMonthArrow: '월 선택 화살표',
|
||||
tokenSelect: '토큰 선택',
|
||||
selectArrow: '선택 화살표',
|
||||
totalOwnedTokens: '총 보유 토큰',
|
||||
viewAllUsageHistory: '전체 이용 내역 보기',
|
||||
transactionType: '거래 구분',
|
||||
usageToken: '이용 토큰',
|
||||
availableAmount: '{{pageLabel}} 가능 금액',
|
||||
transfer: '전송',
|
||||
scanWalletQRCode: '지갑번호 QR 스캔하기',
|
||||
bankLogo: '은행로고',
|
||||
recentRecipient: '최근 보는 사람',
|
||||
enterWalletNumberOrScanQRCode: '지갑 번호 입력 또는 QR코드 스캔',
|
||||
insufficientBalanceConversionNotAllowed: '잔액 부족 전환 불가',
|
||||
answerComplete: '답변완료',
|
||||
inProgress: '확인중',
|
||||
newInquiry: '새로운 문의 작성하기',
|
||||
selectCategory: '카테고리 선택',
|
||||
verificationRequest: '인증요청',
|
||||
inquiryTitle: '문의 제목',
|
||||
resend: '재요청',
|
||||
newPost: '새글',
|
||||
latestVersion: '최신 버전',
|
||||
tokenInfo: '토큰 정보',
|
||||
update: '업데이트',
|
||||
recentUsageHistory: '최근이용내역',
|
||||
linkedAccount: '연동계좌',
|
||||
voucherIcon: '바우처아이콘',
|
||||
notification: '알림',
|
||||
qrNfcScan: 'QR/NFC 스캔',
|
||||
showQr: 'QR 보여주기',
|
||||
qrScan: 'QR 스캔하기',
|
||||
qrPayment: 'QR 결제하기',
|
||||
applyVoucherBenefits: '바우처 혜택 적용',
|
||||
noApply: '미적용',
|
||||
validTime: '유효시간',
|
||||
reissue: '재발급',
|
||||
retry: '재시도',
|
||||
goBack: '돌아가기',
|
||||
back: '뒤로가기',
|
||||
agreeToAllTerms: '약관 전체 동의하기',
|
||||
viewTerms: '약관보기',
|
||||
doNotShowAgainToday: '오늘은 다시 보지 않기',
|
||||
oneWonVerification: '1원 인증하기',
|
||||
change: '변경하기',
|
||||
changeAccount: '계좌변경',
|
||||
deleteWaller: '지갑만들기',
|
||||
viewQRCode: 'QR코드보기',
|
||||
createWallet: '지갑 만들기',
|
||||
hideTokenBalance: '토큰 잔액 숨기기',
|
||||
tokenAndPayment: '토큰ㆍ결제',
|
||||
security: '보안',
|
||||
activityAndNews: '활동ㆍ소식',
|
||||
receiveAllNotifications: '모든 알림 받기',
|
||||
pushNotificationSettings: '푸시 알림 설정',
|
||||
pushNotificationInfo: '푸시알림정보',
|
||||
fontSizeSetting: '큰폰트 설정',
|
||||
useLargeFont: '큰폰트 사용',
|
||||
myTokenList: '내 트콘 목록',
|
||||
help: '도움말',
|
||||
};
|
||||
1
src/shared/constants/locales/kr/glossary.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const glossary = {};
|
||||
4
src/shared/constants/locales/kr/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './validation';
|
||||
export * from './common';
|
||||
export * from './text';
|
||||
export * from './pin';
|
||||
31
src/shared/constants/locales/kr/pin.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export const pin = {
|
||||
titleSetPin: `간편 비밀번호 설정`,
|
||||
messageSetPin: `생년월일, 휴대폰 번호 또는 \n동일하거나 연속된 숫자는 등록이 제한됩니다.`,
|
||||
titleSetPinConfirm: `간편 비밀번호 확인`,
|
||||
messageSetPinConfirm: `동일한 비밀번호를 한번 더 입력해 주세요.`,
|
||||
|
||||
titleResetInput: `비밀번호 입력`,
|
||||
messageResetInput: `사용 중인 비밀번호를 입력해 주세요.`,
|
||||
|
||||
titleResetPin: `신규 비밀번호 설정`,
|
||||
messageResetPin: `새롭게 사용할 비밀번호를 입력해 주세요.`,
|
||||
titleResetPinConfirm: `신규 비밀번호 다시 입력`,
|
||||
messageResetPinConfirm: `동일한 비밀번호를 한번 더 입력해 주세요.`,
|
||||
completeResetPin: `간편 비밀번호를 변경했습니다.`,
|
||||
|
||||
titleInputPin: `간편 비밀번호 입력`,
|
||||
messageInputPin: `비밀번호 6자리를 입력해 주세요.`,
|
||||
|
||||
messageInputBio: `생체인증 사용을 위해 비밀번호를 입력해 주세요.`,
|
||||
|
||||
titlePin: `비밀번호 입력`,
|
||||
|
||||
errorResetInput: `간편 비밀번호가 초기화되었습니다. \n비밀번호를 다시 설정해 주세요.`,
|
||||
errorResetPin: `생년월일, 휴대폰 번호 또는 동일하거나 연속된 숫자는 등록이 제한됩니다.`,
|
||||
errorMaxPin: `다시 입력할 수 있는 횟수를 초과했습니다.\n비멀번호 재설정 후 다시 시도해 주세요.`,
|
||||
errorMaxIncorrectPin: `다시 입력할 수 있는 횟수를 초과했습니다. \n간편 비밀번호를 처음부터 설정해 주세요.`,
|
||||
errorResetMaxIncorrectPin: `다시 입력할 수 있는 횟수를 초과했습니다. \n신규 비밀번호를 처음부터 설정해 주세요.`,
|
||||
errorDifferentInputPin: `{{cnt}}회 틀렸습니다. ({{cnt}}/5)\n5회 이상 틀리면 정보가 초기화됩니다.`,
|
||||
errorDifferentPin: `먼저 입력한 비밀번호와 다릅니다. \n다시 확인해 주세요.({{cnt}}/5)`,
|
||||
errorCheckBio: `생체 정보를 확인할 수 없습니다. \n처음부터 다시 시도해 주세요.`,
|
||||
};
|
||||
28
src/shared/constants/locales/kr/text.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export const text = {
|
||||
alreadyRegistered: '이미 가입하셨나요?',
|
||||
startConvenientSpending: '전자지갑으로\n편리한 소비를 시작해 보세요.',
|
||||
searchPlaceholder: '사용처, 사람 이름으로 검색해 보세요.',
|
||||
recentYearTransactionsOnly: '최근 1년 간의 거래 기록만 조회할 수 있습니다.',
|
||||
transactionInfo: '이용 내역 반영까지 최대 5분이 소용될 수 있습니다.',
|
||||
enterAmount: '{{pageLabel}}할 금액을 입력해 주세요.',
|
||||
enterWalletNumberForBankRecommendation: '지갑 번호를 입력하시면 은행을 추천해 드려요.',
|
||||
howCanWeHelp: '무엇을 도와드릴까요?',
|
||||
fmTokenCopied: 'FCM 토큰이 복사되었습니다.',
|
||||
unableToResolveIssue: '문제를 해결하지 못하셨나요?',
|
||||
whereToSend: '어디로 보낼까요?',
|
||||
linkedAccountChangeConfirmation: '연동 꼐좌 정보를 변경하시겠습니까?',
|
||||
walletAddressCopied: '지갑 주소가 복사되었습니다.',
|
||||
accountNumberCopied: '계좌번호가 복사되었습니다.',
|
||||
walletCreationRequired: '지갑 생성 후\n메뉴를 사용할 수 있습니다.\n\n지갑을 만들고 다시 시도해 주세요.',
|
||||
hideBalanceDescription: '설정 시, 앱 화면에서 잔액이 숨겨집니다.',
|
||||
tokenAndPaymentDescription: '결제, 송금 등 토큰 이용 시 앱 푸시 알림을 받습니다.',
|
||||
activityNewsDescription: '공지사항, 1:1 문의 답변 등 앱 내 활동/소식 관련 앱 푸시 알림을 받습니다.',
|
||||
usefulServiceNotifications:
|
||||
'서비스 이용에 유용한 알림을 앱 푸시로 받아보세요.\n받고 싶지 않은 알림은 언제든 끌 수 있어요.',
|
||||
securityNotifications: '보안 관련 앱 푸시 알림을 받습니다.',
|
||||
essentialServiceInfoPushNotification:
|
||||
'서비스 이용에 필수적인 정보에 대한 알림은 설정과 관계없이 앱 푸시 알림이 발송됩니다.',
|
||||
largeFontWalletAndTransfer: '지갑조회, 이체화면 등의 글자를 크게 볼 수 있습니다.',
|
||||
tokenOverviewMessage:
|
||||
'가지고 있는 모든 토큰을 확인할 수 있어요.\nㆍ 예금 토큰: 예금 기반의 토큰\nㆍ 이머니토큰: 예금 토큰 기반의 토큰',
|
||||
};
|
||||
62
src/shared/constants/locales/kr/validation.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
export const validation = {
|
||||
authenticationComplete: '본인 인증이 완료되었습니다.',
|
||||
authenticationFail: '본인 인증이 실패하였습니다.',
|
||||
verificationCodeSent: '인증번호가 발송되었습니다.',
|
||||
authenticationMismatch: '본인인증 요청 정보와 \n기존 회원정보가 불일치 합니다.',
|
||||
verificationCodeReset: '인증번호가 재요청 되었습니다.',
|
||||
timeExceeded: '시간이 초과되었어요. 재요청 버튼을 눌러주세요.',
|
||||
enterNamePrompt: '이름을 입력해 주세요.',
|
||||
enterAtLeastTwoCharacters: '이름을 구 글자 이상 입력해 주세요.',
|
||||
enterKorean: '한글을 입력해 주세요',
|
||||
enterUpTo20Characters: '최대 20자까지 입력해 주세요.',
|
||||
enterBirthdate: '생년월일을 입력해 주세요.',
|
||||
enterBirthdateIn8Digits: '생년월일을 8자리로 입력ㄱ해 주세요.',
|
||||
enterBirthdateInCorrectFormat: '생년월일 형식에 맞게 입력해 주세요.',
|
||||
enterDateAfter1900: '1900-01-01 이후 날짜로 입력해 주세요.',
|
||||
enterDateBeforeToday: '오늘 이전 날짜로 입력해 주세요.',
|
||||
enterPhoneNumber: '휴대폰 번호를 입력해 주세요.',
|
||||
invalidPhoneNumberFormat: '올바르지 않은 휴대폰 형식입니다.',
|
||||
didNotReceiveVerificationText: '인증 문자를 받지 못하셨나요?',
|
||||
invalidVerificationCode: '올바르지 않은 인증번호입니다. 다시 시도해 주세요.',
|
||||
numbersOnlyAllowed: '숫자만 입력 가능합니다.',
|
||||
enterSixDigits: '인증번호 6자리를 입력해 주세요.',
|
||||
enterVerificationCode: '인증번호를 입력해 주세요.',
|
||||
enter6DigitVerificationCode: '인증번호 6자리 입력',
|
||||
enterResidentNumber: '주민등록번호를 입력해 주세요.',
|
||||
enterResidentNumberIn7Digit: '주민등록번호 7자리를 입력해 주세요.',
|
||||
enterResidentNumberInCorrectFormat: '주민등록번호 형식에 맞게 입력해 주세요.',
|
||||
alreadyRegisteredUser: '이미 가입된 사용자 정보입니다.\n간편 비밀번호를 재설정 하는 화면으로\n이동하시겠습니까?',
|
||||
noRegisteredInformation: '가입된 정보가 ㅇ벗습니다.\n회원가입을 진행하시겠습니까?',
|
||||
enterShopNumberDirectly: '사용처 번호를\n직접 입력해 주세요.',
|
||||
enterInquiryDetails: '문의 내용을 입력해주세요.',
|
||||
chooseCategory: '카테고리를 선택해 주세요.',
|
||||
deleteInquiryConfirmation: '작성하신\n1:1 문의를 삭제하시겠습니까?',
|
||||
onlyNumbersAllowed: '숫자만 입력 가능합니다.',
|
||||
enterNumber: '번호를 입력해 주세요.',
|
||||
logoutConfirmation: '로그아웃 하시겠습니까?\n로그아웃 후\n인증을 통해 다시 로그인 할 수 있습니다.',
|
||||
noPostAvailable: '등록된 게시물이 없습니다.',
|
||||
noRemittanceHistory: '송금 내역이 없습니다.',
|
||||
noHistoryAvailable: '이용내역이 없습니다.',
|
||||
noInquiries: '등록하신 1:1 문의가 없습니다.',
|
||||
noNoticesAvailable: '등록된 공지사항이 없습니다.',
|
||||
noVouchersAvailable: '보유하고 계신 바우처가 없습니다.',
|
||||
verificationTimeExpired: '인증 시간이 만료되었습니다.',
|
||||
authInfoResetWarning: '지금 페이지를 벗어나시면 인증된 계좌정보가 초기화 됩니다.',
|
||||
returnToBeginning: '처음으로 돌아가시겠습니까?',
|
||||
accountInfoResetWarning: '지금 페이지를 벗어나시면 인증된 계좌정보가 초기화 됩니다.',
|
||||
invalidAuthInfo: '인증정보가 올바르지 않스빈다.\n다시 시도해 주세요.',
|
||||
usingLatestVersion: '현재 최신 버전을 사용 중입니다.',
|
||||
newVersionAvailable: '새로운 버전이 사용 가능합니다.',
|
||||
blockedWalletMessage:
|
||||
'현재 일부 거래가 제한된 상태입니다.\n관련 문의 사항을 1:1 문의 또는\n고객센터를 이용해 주세요.',
|
||||
accountInfoCheckError:
|
||||
'계좌 정보를 확인할 수 없습니다.\n잘못된 정보로 5회 이상 실패 시\n하루동안 인증이 제한될 수 있습니다.',
|
||||
accountAuthAttemptExceeded:
|
||||
'계좌 인증 확인 횟수를 초과하였습니다.\n내일 다시 시도해 주세요.\n\n지속적으로 오류가 발생하거나\n추가 문의 사항이 있으시면 1:1문의 또는 고객센터를 이용해 주세요.',
|
||||
accountInfoInvalid: '계좌 정보를 확인할 수 없습니다.\n입력하신 정보를\n다시 한 번 확인해 주세요.',
|
||||
walletDeletionConfirmation: '지갑을 삭제하시겠습니까?\n지갑을 삭제 후에도\n인증을 통해 다시 만들 수 있습니다.',
|
||||
temporaryErrorOccurred: '일시적인 오류가 발생하였습니다.',
|
||||
tryAgainLater: '잠시 후 다시 시도해주세요.',
|
||||
networkConnectionError: '현재 네트워크 연결이 원활하지 않습니다.\n데이터 또는 Wi-Fi 연결 상태를\n확인해 주세요.',
|
||||
inaccessiblePage: '접근할 수 없는 페이지입니다.',
|
||||
};
|
||||
28
src/shared/constants/native-function.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export enum NativeFunction {
|
||||
ExitApp = 'exitApp',
|
||||
Biometrics = 'biometrics',
|
||||
PaymentQRScan = 'PaymentQRScan',
|
||||
WalletQRScan = 'WalletQRScan',
|
||||
IDScan = 'IDScan',
|
||||
PassportScan = 'PassportScan',
|
||||
BusinessLicenseScan = 'BusinessLicenseScan',
|
||||
SetStorage = 'setStorage',
|
||||
GetStorage = 'getStorage',
|
||||
ClearStorage = 'clearStorage',
|
||||
BeginNfcReaderMode = 'beginNfcReaderMode',
|
||||
EndNfcReaderMode = 'endNfcReaderMode',
|
||||
TransmitNFCBytes = 'transmitNFCBytes',
|
||||
SetAppColor = 'setAppColor',
|
||||
CopyToClipboard = 'copyToClipboard',
|
||||
OpenAppSetting = 'openAppSetting',
|
||||
OpenSecuritySetting = 'openSecuritySetting',
|
||||
OpenWebview = 'openWebview',
|
||||
GoBack = 'goBack',
|
||||
GetNotificationToken = 'getNotificationToken',
|
||||
CanGoBack = 'canGoBack',
|
||||
PhoneCall = 'phoneCall',
|
||||
LocationPermission = 'locationPermission',
|
||||
CheckLocationPermission = 'checkLocationPermission',
|
||||
RootNavigation = 'rootNavigation',
|
||||
RequestNotification = 'requestNotification',
|
||||
}
|
||||
4
src/shared/constants/native-message.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum NativeMessage {
|
||||
HardwareBackPress = 'hardwareBackPress',
|
||||
WebViewNavigate = 'webViewNavigate',
|
||||
}
|
||||
229
src/shared/constants/paths.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
import { ROUTE_NAMES, RouteNamesType } from '@/shared/constants/route-names';
|
||||
|
||||
export type PathType = RouteNamesType[keyof RouteNamesType];
|
||||
const generatePath = (base: string, path?: PathType): string => {
|
||||
base = base.replace(/\/\*/g, '');
|
||||
if (typeof path === 'string') {
|
||||
path = path?.replace(/:.*/, '');
|
||||
}
|
||||
let rs = path ? `${base}/${path as string}` : base;
|
||||
return rs;
|
||||
};
|
||||
|
||||
export const PATHS: RouteNamesType = {
|
||||
home: generatePath(ROUTE_NAMES.home),
|
||||
transaction: {
|
||||
base: generatePath(ROUTE_NAMES.transaction.base),
|
||||
allTransaction: {
|
||||
base: generatePath(`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.allTransaction.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.allTransaction.base}`,
|
||||
ROUTE_NAMES.transaction.allTransaction.list,
|
||||
),
|
||||
detail: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.allTransaction.base}`,
|
||||
ROUTE_NAMES.transaction.allTransaction.detail,
|
||||
),
|
||||
cancel: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.allTransaction.base}`,
|
||||
ROUTE_NAMES.transaction.allTransaction.cancel,
|
||||
),
|
||||
},
|
||||
cashReceit: {
|
||||
base: generatePath(`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.cashReceit.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.cashReceit.base}`,
|
||||
ROUTE_NAMES.transaction.cashReceit.list,
|
||||
),
|
||||
detail: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.cashReceit.base}`,
|
||||
ROUTE_NAMES.transaction.cashReceit.detail,
|
||||
),
|
||||
handWrittenIssuance: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.cashReceit.base}`,
|
||||
ROUTE_NAMES.transaction.cashReceit.handWrittenIssuance,
|
||||
),
|
||||
},
|
||||
escro: {
|
||||
base: generatePath(`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.escro.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.escro.base}`,
|
||||
ROUTE_NAMES.transaction.escro.list,
|
||||
),
|
||||
detail: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.escro.base}`,
|
||||
ROUTE_NAMES.transaction.escro.detail,
|
||||
),
|
||||
},
|
||||
billing: {
|
||||
base: generatePath(`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.billing.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.billing.base}`,
|
||||
ROUTE_NAMES.transaction.billing.list,
|
||||
),
|
||||
detail: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.billing.base}`,
|
||||
ROUTE_NAMES.transaction.billing.detail,
|
||||
),
|
||||
paymentRequest: generatePath(
|
||||
`${ROUTE_NAMES.transaction.base}${ROUTE_NAMES.transaction.billing.base}`,
|
||||
ROUTE_NAMES.transaction.billing.paymentRequest,
|
||||
),
|
||||
}
|
||||
},
|
||||
settlement: {
|
||||
base: generatePath(ROUTE_NAMES.settlement.base),
|
||||
calendar: generatePath(ROUTE_NAMES.settlement.base, ROUTE_NAMES.settlement.calendar),
|
||||
list: generatePath(ROUTE_NAMES.settlement.base, ROUTE_NAMES.settlement.list),
|
||||
detail: generatePath(ROUTE_NAMES.settlement.base, ROUTE_NAMES.settlement.detail),
|
||||
},
|
||||
businessMember: {
|
||||
base: generatePath(ROUTE_NAMES.businessMember.base),
|
||||
info: generatePath(ROUTE_NAMES.businessMember.base, ROUTE_NAMES.businessMember.info),
|
||||
registrationStatus: generatePath(ROUTE_NAMES.businessMember.base, ROUTE_NAMES.businessMember.registrationStatus),
|
||||
},
|
||||
payment: {
|
||||
base: generatePath(ROUTE_NAMES.payment.base),
|
||||
info: generatePath(ROUTE_NAMES.payment.base, ROUTE_NAMES.payment.info),
|
||||
dataNotification: generatePath(ROUTE_NAMES.payment.base, ROUTE_NAMES.payment.dataNotification),
|
||||
},
|
||||
account: {
|
||||
base: generatePath(ROUTE_NAMES.account.base),
|
||||
user: {
|
||||
base: generatePath(`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.user.base}`),
|
||||
manage: generatePath(
|
||||
`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.user.base}`,
|
||||
ROUTE_NAMES.account.user.manage,
|
||||
),
|
||||
loginAuthInfo: generatePath(
|
||||
`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.user.base}`,
|
||||
ROUTE_NAMES.account.user.loginAuthInfo,
|
||||
),
|
||||
accountAuth: generatePath(
|
||||
`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.user.base}`,
|
||||
ROUTE_NAMES.account.user.accountAuth,
|
||||
),
|
||||
menuAuth: generatePath(
|
||||
`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.user.base}`,
|
||||
ROUTE_NAMES.account.user.menuAuth,
|
||||
),
|
||||
addAccount: generatePath(
|
||||
`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.user.base}`,
|
||||
ROUTE_NAMES.account.user.addAccount,
|
||||
),
|
||||
},
|
||||
password: {
|
||||
base: generatePath(`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.password.base}`),
|
||||
manage: generatePath(
|
||||
`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.password.base}`,
|
||||
ROUTE_NAMES.account.password.manage,
|
||||
),
|
||||
modifyLoginPassword: generatePath(
|
||||
`${ROUTE_NAMES.account.base}${ROUTE_NAMES.account.password.base}`,
|
||||
ROUTE_NAMES.account.password.modifyLoginPassword,
|
||||
),
|
||||
}
|
||||
},
|
||||
tax: {
|
||||
base: generatePath(ROUTE_NAMES.tax.base),
|
||||
invoice: {
|
||||
base: generatePath(`${ROUTE_NAMES.tax.base}${ROUTE_NAMES.tax.invoice.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.tax.base}${ROUTE_NAMES.tax.invoice.base}`,
|
||||
ROUTE_NAMES.tax.invoice.list,
|
||||
),
|
||||
detail: generatePath(
|
||||
`${ROUTE_NAMES.tax.base}${ROUTE_NAMES.tax.invoice.base}`,
|
||||
ROUTE_NAMES.tax.invoice.detail,
|
||||
),
|
||||
},
|
||||
vatReference: generatePath(ROUTE_NAMES.tax.base, ROUTE_NAMES.tax.vatReference),
|
||||
},
|
||||
additionalService: {
|
||||
base: generatePath(ROUTE_NAMES.additionalService.base),
|
||||
intro: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.intro),
|
||||
arsCardPayment: {
|
||||
base: generatePath(`${ROUTE_NAMES.additionalService.base}${ROUTE_NAMES.additionalService.arsCardPayment.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.additionalService.base}${ROUTE_NAMES.additionalService.arsCardPayment.base}`,
|
||||
ROUTE_NAMES.additionalService.arsCardPayment.list,
|
||||
),
|
||||
request: generatePath(
|
||||
`${ROUTE_NAMES.additionalService.base}${ROUTE_NAMES.additionalService.arsCardPayment.base}`,
|
||||
ROUTE_NAMES.additionalService.arsCardPayment.request,
|
||||
),
|
||||
requestSuccess: generatePath(
|
||||
`${ROUTE_NAMES.additionalService.base}${ROUTE_NAMES.additionalService.arsCardPayment.base}`,
|
||||
ROUTE_NAMES.additionalService.arsCardPayment.requestSuccess,
|
||||
),
|
||||
},
|
||||
keyInPayment: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.keyInPayment),
|
||||
smsPaymentNotification: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.smsPaymentNotification),
|
||||
accountHolderSearch: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.accountHolderSearch),
|
||||
accountHolderAuth: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.accountHolderAuth),
|
||||
linkPayment: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.linkPayment),
|
||||
kakaoPaymentNotification: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.kakaoPaymentNotification),
|
||||
fundTransfer: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.fundTransfer),
|
||||
settlementAgency: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.settlementAgency),
|
||||
paymentAgency: generatePath(ROUTE_NAMES.additionalService.base, ROUTE_NAMES.additionalService.paymentAgency),
|
||||
},
|
||||
support: {
|
||||
base: generatePath(ROUTE_NAMES.support.base),
|
||||
notice: {
|
||||
base: generatePath(`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.notice.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.notice.base}`,
|
||||
ROUTE_NAMES.support.notice.list,
|
||||
),
|
||||
detail: generatePath(
|
||||
`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.notice.base}`,
|
||||
ROUTE_NAMES.support.notice.detail,
|
||||
),
|
||||
},
|
||||
faq: {
|
||||
base: generatePath(`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.faq.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.faq.base}`,
|
||||
ROUTE_NAMES.support.notice.list,
|
||||
),
|
||||
detail: generatePath(
|
||||
`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.faq.base}`,
|
||||
ROUTE_NAMES.support.faq.detail,
|
||||
),
|
||||
},
|
||||
qna: {
|
||||
base: generatePath(`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.qna.base}`),
|
||||
list: generatePath(
|
||||
`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.qna.base}`,
|
||||
ROUTE_NAMES.support.qna.list,
|
||||
),
|
||||
detail: generatePath(
|
||||
`${ROUTE_NAMES.support.base}${ROUTE_NAMES.support.qna.base}`,
|
||||
ROUTE_NAMES.support.qna.detail,
|
||||
),
|
||||
},
|
||||
},
|
||||
setting: generatePath(ROUTE_NAMES.setting),
|
||||
alarm: {
|
||||
base: generatePath(ROUTE_NAMES.alarm.base),
|
||||
list: generatePath(ROUTE_NAMES.alarm.base, ROUTE_NAMES.alarm.list),
|
||||
},
|
||||
};
|
||||
|
||||
export const BACK_BLOCKED_PATHS = {
|
||||
/*
|
||||
[PATHS.home]: [
|
||||
PATHS.intro,
|
||||
PATHS.start,
|
||||
PATHS.appAuth,
|
||||
PATHS.mobileVerification,
|
||||
PATHS.signUp.bioPinNumber,
|
||||
PATHS.signUp.inputPinNumber,
|
||||
PATHS.signUp.setPinNumber,
|
||||
PATHS.signUp.reSetBiometricAuth,
|
||||
PATHS.signUp.setBiometricAuth,
|
||||
|
||||
],
|
||||
*/
|
||||
//[PATHS.appAuth]: [PATHS.intro],
|
||||
};
|
||||
198
src/shared/constants/route-names.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
export const ROUTE_NAMES = {
|
||||
home: '/home',
|
||||
transaction: {
|
||||
base: '/transaction/*',
|
||||
allTransaction: {
|
||||
base: '/all-transaction/*',
|
||||
list: 'list',
|
||||
detail: 'detail/:tid',
|
||||
cancel: 'cancel',
|
||||
},
|
||||
cashReceit: {
|
||||
base: '/cash-receit/*',
|
||||
list: 'list',
|
||||
detail: 'detail/:tid',
|
||||
handWrittenIssuance: 'hand-written-issuance',
|
||||
},
|
||||
escro: {
|
||||
base: '/escro/*',
|
||||
list: 'list',
|
||||
detail: 'detail/:tid',
|
||||
},
|
||||
billing: {
|
||||
base: '/billing/*',
|
||||
list: 'list',
|
||||
detail: 'detail/:tid',
|
||||
paymentRequest: 'payment-request',
|
||||
}
|
||||
},
|
||||
settlement: {
|
||||
base: '/settlement/*',
|
||||
calendar: 'calendar',
|
||||
list: 'list',
|
||||
detail: 'detail/:tid',
|
||||
},
|
||||
businessMember: {
|
||||
base: '/business-member/*',
|
||||
info: 'info',
|
||||
registrationStatus: 'registration-status'
|
||||
},
|
||||
payment: {
|
||||
base: '/payment/*',
|
||||
info: 'info',
|
||||
dataNotification: 'data-notification'
|
||||
},
|
||||
account: {
|
||||
base: '/account/*',
|
||||
user: {
|
||||
base: '/user/*',
|
||||
manage: 'manage',
|
||||
loginAuthInfo: 'login-auth-info',
|
||||
accountAuth: 'account-auth',
|
||||
menuAuth: 'menu-auth',
|
||||
addAccount: 'add-account',
|
||||
},
|
||||
password: {
|
||||
base: '/password/*',
|
||||
manage: 'manage',
|
||||
modifyLoginPassword: 'modifyLoginPassword'
|
||||
}
|
||||
},
|
||||
tax: {
|
||||
base: '/tax/*',
|
||||
invoice: {
|
||||
base: '/invoice/*',
|
||||
list: 'list',
|
||||
detail: 'detail',
|
||||
},
|
||||
vatReference: 'vatReference'
|
||||
},
|
||||
additionalService: {
|
||||
base: '/additional-service/*',
|
||||
intro: 'intro',
|
||||
arsCardPayment: {
|
||||
base: '/ars-card-payment/*',
|
||||
list: 'list',
|
||||
request: 'request',
|
||||
requestSuccess: 'request-success',
|
||||
},
|
||||
keyInPayment: 'key-in-payment',
|
||||
smsPaymentNotification: 'sms-payment-notification',
|
||||
accountHolderSearch: 'account-holder-search',
|
||||
accountHolderAuth: 'account-holder-auth',
|
||||
linkPayment: 'link-payment',
|
||||
kakaoPaymentNotification: 'kakao-payment-notification',
|
||||
fundTransfer: 'fund-transfer',
|
||||
settlementAgency: 'settlement-agency',
|
||||
paymentAgency: 'payment-agency',
|
||||
},
|
||||
support: {
|
||||
base: '/support/*',
|
||||
notice: {
|
||||
base: '/notice/*',
|
||||
list: 'list',
|
||||
detail: 'detail/:noticeId',
|
||||
},
|
||||
faq: {
|
||||
base: '/faq/*',
|
||||
list: 'list',
|
||||
detail: 'detail/:faqId',
|
||||
},
|
||||
qna: {
|
||||
base: '/qna/*',
|
||||
list: 'list',
|
||||
detail: 'detail/:qnaId',
|
||||
}
|
||||
},
|
||||
setting: '/setting',
|
||||
alarm: {
|
||||
base: '/alarm/*',
|
||||
list: 'list',
|
||||
},
|
||||
|
||||
/*
|
||||
intro: '/intro',
|
||||
appAuth: '/appAuth',
|
||||
start: '/start',
|
||||
mobileVerification: '/mobileVerification',
|
||||
pwFail: '/pwFail',
|
||||
signUp: {
|
||||
base: '/signUp/*',
|
||||
termsAndConditions: 'termsAndConditions',
|
||||
setPinNumber: 'setPinNumber',
|
||||
reSetBiometricAuth: 'reSetBiometricAuth',
|
||||
setBiometricAuth: 'setBiometricAuth',
|
||||
reSetPinNumber: 'reSetPinNumber',
|
||||
bioPinNumber: 'bioPinNumber',
|
||||
inputPinNumber: 'inputPinNumber',
|
||||
},
|
||||
issueWallet: {
|
||||
base: '/issueWallet/*',
|
||||
intro: 'intro',
|
||||
inputBankAccount: 'inputBankAccount',
|
||||
authBankAccount: 'authBankAccount',
|
||||
chooseIDCardType: 'chooseIDCardType',
|
||||
captureBusinessLicense: 'captureBusinessLicense',
|
||||
confirm: {
|
||||
base: '/confirm/*',
|
||||
IDCard: 'IDCard',
|
||||
driverLicense: 'driverLicense',
|
||||
passport: 'passport',
|
||||
businessLicense: 'businessLicense',
|
||||
},
|
||||
},
|
||||
myWallet: {
|
||||
base: '/myWallet/*',
|
||||
history: 'history',
|
||||
historyDetail: 'history/:historyId',
|
||||
refundQRCode: 'refundQRCode/:historyId',
|
||||
tokenDetail: 'token/:tokenId',
|
||||
},
|
||||
exchange: {
|
||||
base: '/exchange/*',
|
||||
selectRecipient: 'selectRecipient',
|
||||
inputRecipient: 'inputRecipient',
|
||||
inputAmount: 'inputAmount',
|
||||
inputConfirm: 'inputConfirm',
|
||||
process: 'process',
|
||||
complete: 'complete',
|
||||
},
|
||||
myBankAccount: '/myBankAccount',
|
||||
security: '/security',
|
||||
appVersion: '/appVersion',
|
||||
settingFontSize: '/settingFontSize',
|
||||
community: {
|
||||
base: '/community/*',
|
||||
faq: 'faq',
|
||||
faqDetail: 'faqDetail',
|
||||
notice: 'notice',
|
||||
noticeDetail: 'noticeDetail',
|
||||
inquiry: 'inquiry',
|
||||
inquiryDetail: 'inquiryDetail',
|
||||
inquiryModify: 'inquiryModify',
|
||||
},
|
||||
menu: '/menu',
|
||||
login: '/login',
|
||||
reLogin: '/reLogin',
|
||||
home: '/home',
|
||||
shopList: '/shopList',
|
||||
notification: {
|
||||
base: '/notification/*',
|
||||
setting: 'setting',
|
||||
},
|
||||
payment: {
|
||||
base: '/payment/*',
|
||||
myPaymentQRCode: 'myPaymentQRCode',
|
||||
payWithNFCManual: 'payWithNFCManual',
|
||||
payWithNFCAuto: 'payWithNFCAuto',
|
||||
},
|
||||
voucher: {
|
||||
base: '/voucher/*',
|
||||
voucherDetail: ':voucherId',
|
||||
},
|
||||
paymentGuide: 'paymentGuide',
|
||||
inputShopWalletAddr: 'inputShopWalletAddr',
|
||||
*/
|
||||
};
|
||||
|
||||
export type RouteNamesType = typeof ROUTE_NAMES;
|
||||
20
src/shared/constants/url.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
const { origin } = window.location;
|
||||
export const CURRENT_URL = `${origin}`;
|
||||
|
||||
const getAPIBaseUrl = () => {
|
||||
return CURRENT_URL;
|
||||
};
|
||||
const getHeaderUserAgent = () => {
|
||||
let os = 'Android';
|
||||
let deviceType = 'Galaxy Flip 6';
|
||||
let deviceID = 'uuid';
|
||||
let appVersion = '1.0.0';
|
||||
let browserInformation = 'Chrome135';
|
||||
return `${os} ${deviceType} ${deviceID} ${appVersion} ${browserInformation}`;
|
||||
};
|
||||
|
||||
export const API_BASE_URL = getAPIBaseUrl();
|
||||
export const API_URL_KEY = 'nmsa';
|
||||
export const HEADER_USER_AGENT = getHeaderUserAgent();
|
||||
export const API_PARAM = {};
|
||||
|
||||
4
src/shared/lib/convert-currency-string-to-number.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const convertCurrencyStringToNumber = (currencyString: string): number => {
|
||||
const cleanedString = currencyString.replace(/[^\d]/g, '');
|
||||
return parseInt(cleanedString, 10);
|
||||
};
|
||||
1
src/shared/lib/delay.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
11
src/shared/lib/error.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import axios from 'axios';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
import { IS_MOCK_PHASE } from '@/shared/constants/environment';
|
||||
|
||||
// CBDCAxiosError가 아니라면 상위 Error Boundary로 위임
|
||||
export const checkIsAxiosError = (error: Error): error is CBDCAxiosError => axios.isAxiosError(error);
|
||||
|
||||
export const checkIsKickOutError = (error: CBDCAxiosError) => {
|
||||
const status = error.response?.status.toString();
|
||||
return !IS_MOCK_PHASE && (status === '401' || status === '403');
|
||||
};
|
||||
16
src/shared/lib/format-korean-number.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export const formatKoreanNumber = (num: number): string => {
|
||||
const units = ['', '만', '억', '조', '경', '해'];
|
||||
let result = '';
|
||||
let unitIndex = 0;
|
||||
|
||||
while (num > 0) {
|
||||
const part = num % 10000;
|
||||
if (part != 0) {
|
||||
result = `${part.toLocaleString()}${units[unitIndex]}${result}`;
|
||||
}
|
||||
num = Math.floor(num / 10000);
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
12
src/shared/lib/hooks/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export * from './use-navigate';
|
||||
export * from './use-app-version';
|
||||
export * from './use-change-bg-color';
|
||||
export * from './use-device-uid';
|
||||
export * from './use-fix-scroll-view-safari';
|
||||
export * from './use-has-bio-hardware';
|
||||
export * from './use-navigate';
|
||||
export * from './use-location-permission';
|
||||
export * from './use-window-focus-change';
|
||||
export * from './use-router-listener';
|
||||
export * from './use-scroll-to-top';
|
||||
export * from './use-app-page-speed';
|
||||
15
src/shared/lib/hooks/use-app-page-speed.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useEffectOnce } from 'react-use';
|
||||
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import { setAfterPageRendered } from '@/shared/lib';
|
||||
|
||||
export const useAppPagingSpeed = () => {
|
||||
return useLocalStorageState(StorageKeys.AppPagingSpeed, { defaultValue: '250' });
|
||||
};
|
||||
|
||||
export const useEffectOnceAfterPageRendered = (callback: () => void) => {
|
||||
useEffectOnce(() => {
|
||||
setAfterPageRendered(callback);
|
||||
});
|
||||
};
|
||||
17
src/shared/lib/hooks/use-app-version.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useMemo } from 'react';
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { config } from '@/shared/configs';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
|
||||
export const DEFAULT_APP_VERSION = '1.0';
|
||||
export const useAppVersion = () => {
|
||||
const [appVersion] = useLocalStorageState(StorageKeys.AppVersion, { defaultValue: DEFAULT_APP_VERSION });
|
||||
return appVersion;
|
||||
};
|
||||
|
||||
export const webVersion = config.WEB_VERSION.replace(/\b(\d+)(?:\.0\.0)?\b/g, '$1');
|
||||
export const useFullVersion = () => {
|
||||
const appVersion = useAppVersion();
|
||||
const fullVersion = useMemo(() => `${appVersion}.${webVersion}`, [appVersion]);
|
||||
return { fullVersion };
|
||||
};
|
||||
10
src/shared/lib/hooks/use-block-back.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { useBlocker } from '@use-blocker';
|
||||
|
||||
export const useBlockBack = (blockPaths: string[]) => {
|
||||
useBlocker(({ nextLocation }) => {
|
||||
if (blockPaths.includes(nextLocation.pathname)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
2
src/shared/lib/hooks/use-blocker.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import { useBlocker as _useBlocker } from 'react-router';
|
||||
export const useBlocker = _useBlocker;
|
||||
44
src/shared/lib/hooks/use-change-bg-color.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useLayoutEffect } from 'react';
|
||||
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { DEFAULT_BACKGROUND_COLOR } from '@/shared/constants/colors';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import { NativeFunction } from '@/shared/constants/native-function';
|
||||
import { useRouterListener } from './use-router-listener';
|
||||
|
||||
export const useChangeBgColor = (color: string) => {
|
||||
const [, setAppColor] = useAppColor();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setAppColor(color);
|
||||
window.webViewBridge.send(NativeFunction.SetAppColor, color);
|
||||
document.body.style.backgroundColor = color;
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useResetBgColor = () => {
|
||||
const [, setAppColor] = useAppColor();
|
||||
const defaultColor = DEFAULT_BACKGROUND_COLOR;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setAppColor(defaultColor);
|
||||
window.webViewBridge.send(NativeFunction.SetAppColor, defaultColor);
|
||||
document.body.style.backgroundColor = defaultColor;
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useResetBgColorOnLeave = () => {
|
||||
const [, setAppColor] = useAppColor();
|
||||
const defaultColor = DEFAULT_BACKGROUND_COLOR;
|
||||
|
||||
useRouterListener(() => {
|
||||
setAppColor(defaultColor);
|
||||
document.body.style.backgroundColor = defaultColor;
|
||||
window.webViewBridge.send(NativeFunction.SetAppColor, defaultColor);
|
||||
});
|
||||
};
|
||||
|
||||
export const useAppColor = () => {
|
||||
return useLocalStorageState(StorageKeys.AppColor, { defaultValue: DEFAULT_BACKGROUND_COLOR });
|
||||
};
|
||||
31
src/shared/lib/hooks/use-device-uid.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @cspell/spellchecker */
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
// import { useUpdateUserInfo } from '@/entities/user/lib/use-update-user-info';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import { useStore } from '@/shared/model/store';
|
||||
|
||||
export const useDeviceUid = () => {
|
||||
let defaultUid = '';
|
||||
if (!window.ReactNativeWebView) {
|
||||
defaultUid = '1234567890';
|
||||
}
|
||||
const userInfo = useStore((state: any) => state.userSlice.userInfo);
|
||||
const [deviceUid] = useLocalStorageState(StorageKeys.DeviceUniqueId, { defaultValue: defaultUid });
|
||||
//const { updateUserInfo } = useUpdateUserInfo();
|
||||
|
||||
useEffect(() => {
|
||||
if (deviceUid.length < 1) {
|
||||
return;
|
||||
}
|
||||
if (userInfo?.appEsntlNo && userInfo?.appEsntlNo?.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// updateUserInfo((prev: any) => ({ ...prev, appEsntlNo: deviceUid }));
|
||||
}, [deviceUid]);
|
||||
|
||||
return deviceUid;
|
||||
};
|
||||
12
src/shared/lib/hooks/use-fix-scroll-view-safari.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useFixScrollViewSafari = () => {
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
window.scrollTo(0, 1);
|
||||
}, 100);
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
45
src/shared/lib/hooks/use-fixed-button-position.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { CSSProperties, useEffect, useState } from 'react';
|
||||
|
||||
export const useFixedButtonPosition = () => {
|
||||
const [buttonStyle, setButtonStyle] = useState<CSSProperties | undefined>();
|
||||
|
||||
const updateButtonPosition = () => {
|
||||
const viewport = window.visualViewport;
|
||||
const viewportHeight = viewport?.height;
|
||||
const viewportOffsetTop = viewport?.offsetTop ?? 0;
|
||||
|
||||
if (viewportHeight && viewportHeight < window.innerHeight) {
|
||||
setButtonStyle({ bottom: `${window.innerHeight - viewportHeight - viewportOffsetTop + 15}px` });
|
||||
} else {
|
||||
setButtonStyle(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
updateButtonPosition();
|
||||
};
|
||||
|
||||
visualViewport?.addEventListener('resize', handleResize);
|
||||
window.addEventListener('resize', handleResize); // fallback for window resize
|
||||
|
||||
return () => {
|
||||
visualViewport?.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
updateButtonPosition();
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return buttonStyle;
|
||||
};
|
||||
14
src/shared/lib/hooks/use-hardware-back-press-listener.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect } from 'react';
|
||||
import { NativeMessage } from '@/shared/constants/native-message';
|
||||
import { bridge } from '@/bridge';
|
||||
|
||||
export const useHardwareBackPressListener = (callback: () => void) => {
|
||||
useEffect(() => {
|
||||
// Subscribe to events from react native.
|
||||
return bridge.addEventListener(NativeMessage.HardwareBackPress, (message: any) => {
|
||||
callback?.();
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
7
src/shared/lib/hooks/use-has-bio-hardware.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
|
||||
export const useHasBioHardware = () => {
|
||||
const [hasBioHardware] = useLocalStorageState(StorageKeys.HasBioHardware, { defaultValue: false });
|
||||
return hasBioHardware;
|
||||
};
|
||||
46
src/shared/lib/hooks/use-location-permission.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable @cspell/spellchecker */
|
||||
import { useEffect } from 'react';
|
||||
import { PERMISSION_RESULTS, PermissionResultValues, WebViewBridgeResponse } from '@/shared/@types/webview-bridge';
|
||||
|
||||
import { NativeFunction } from '@/shared/constants/native-function';
|
||||
|
||||
interface CheckLocationPermissionResponse extends WebViewBridgeResponse {
|
||||
data: PermissionResultValues;
|
||||
}
|
||||
|
||||
export const useRequestLocationPermission = () => {
|
||||
useEffect(() => {
|
||||
window.webViewBridge.send(NativeFunction.LocationPermission, {
|
||||
success: async () => {
|
||||
// console.log('res', res);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useCheckLocationPermission = () => {
|
||||
useEffect(() => {
|
||||
window.webViewBridge.send(NativeFunction.CheckLocationPermission, {
|
||||
success: async (res: CheckLocationPermissionResponse) => {
|
||||
// console.log('res', res.data);
|
||||
switch (res.data) {
|
||||
case PERMISSION_RESULTS.UNAVAILABLE:
|
||||
console.log('This feature is not available (on this device / in this context)');
|
||||
break;
|
||||
case PERMISSION_RESULTS.DENIED:
|
||||
console.log('The permission has not been requested / is denied but requestable');
|
||||
break;
|
||||
case PERMISSION_RESULTS.LIMITED:
|
||||
console.log('The permission is limited: some actions are possible');
|
||||
break;
|
||||
case PERMISSION_RESULTS.GRANTED:
|
||||
console.log('The permission is granted');
|
||||
break;
|
||||
case PERMISSION_RESULTS.BLOCKED:
|
||||
console.log('The permission is denied and not requestable anymore');
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
37
src/shared/lib/hooks/use-navigate.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { PathType } from '@/shared/constants/paths';
|
||||
import {
|
||||
NavigateOptions,
|
||||
To,
|
||||
useNavigate as _useNavigate
|
||||
} from 'react-router';
|
||||
|
||||
export type NavigateTo = PathType | -1 | 0;
|
||||
|
||||
export const goBackWebview = (goBack: () => void) => {
|
||||
if (!window.ReactNativeWebView) {
|
||||
if (window.history.state?.idx > 0) {
|
||||
goBack();
|
||||
return;
|
||||
} else {
|
||||
window.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export const useNavigate = () => {
|
||||
const _navigate = _useNavigate();
|
||||
|
||||
const navigate = (to: NavigateTo, options?: NavigateOptions): void => {
|
||||
const path = typeof to === 'number' ? to : to.toString();
|
||||
_navigate(path as To, options);
|
||||
};
|
||||
|
||||
const reload = async () => {
|
||||
navigate(0);
|
||||
};
|
||||
|
||||
const navigateBack = () => goBackWebview(() => _navigate(-1));
|
||||
|
||||
return { navigate, navigateBack, reload };
|
||||
};
|
||||
13
src/shared/lib/hooks/use-request-notification.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { NativeFunction } from '@/shared/constants/native-function';
|
||||
|
||||
export const useRequestNotification = () => {
|
||||
useEffect(() => {
|
||||
window.webViewBridge.send(NativeFunction.RequestNotification, {
|
||||
success: async () => {
|
||||
// console.log('res', res);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
20
src/shared/lib/hooks/use-router-listener.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { RouterState } from '@remix-run/router';
|
||||
import { useEffect } from 'react';
|
||||
import { NavigationType } from 'react-router';
|
||||
|
||||
import { router } from '@/shared/configs/sentry';
|
||||
|
||||
type IRouterListener = (state: RouterState) => void;
|
||||
|
||||
export const useRouterListener = (callback: IRouterListener, actionType?: NavigationType) => {
|
||||
useEffect(() => {
|
||||
const unsubscribe = router.subscribe((state: any) => {
|
||||
if (actionType && state.historyAction !== actionType) return;
|
||||
if (!actionType || state.historyAction === actionType) {
|
||||
callback?.(state);
|
||||
}
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, [callback, actionType]);
|
||||
};
|
||||
37
src/shared/lib/hooks/use-script.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function useScript(src: string) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
let script = document.querySelector(`script[src="${src}"]`);
|
||||
|
||||
const handleLoad = () => setLoading(false);
|
||||
const handleError = (err: any) => {
|
||||
setError(err);
|
||||
setLoading(false);
|
||||
};
|
||||
if(!script){
|
||||
script = document.createElement('script');
|
||||
(script as any).src = src;
|
||||
(script as any).type = 'text/javascript';
|
||||
(script as any).charset = 'utf-8';
|
||||
|
||||
script.addEventListener('load', handleLoad);
|
||||
script.addEventListener('error', handleError);
|
||||
console.log(script);
|
||||
// document.getElementsByTagName('head')[0].appendChild(script);
|
||||
}
|
||||
else{
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
return () => {
|
||||
script.removeEventListener('load', handleLoad);
|
||||
script.removeEventListener('error', handleError);
|
||||
}
|
||||
}, [src]);
|
||||
|
||||
return [loading, error];
|
||||
}
|
||||
7
src/shared/lib/hooks/use-scroll-to-top.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useScrollToTop = () => {
|
||||
useEffect(() => {
|
||||
document.body.scrollTop = 0;
|
||||
}, []);
|
||||
};
|
||||
50
src/shared/lib/hooks/use-select-bottom-sheet.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { overlay } from 'overlay-kit';
|
||||
import { BottomSheet } from '@/shared/ui/bottom-sheets/bottom-sheet';
|
||||
import {
|
||||
SelectTemplate,
|
||||
ElementType,
|
||||
RadioChangeProps,
|
||||
SelectTemplateProps,
|
||||
} from '@/shared/ui/selects/select-template';
|
||||
import { JSX } from 'react/jsx-runtime';
|
||||
|
||||
export interface SelectBottomSheetProps<T> extends SelectTemplateProps<T> {
|
||||
title?: string | JSX.Element;
|
||||
description?: string | JSX.Element;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
export const useSelectBottomSheet = <T,>() => {
|
||||
const openSelectBottomSheet = (selectBottomSheet: SelectBottomSheetProps<T>) => {
|
||||
const {
|
||||
values = [],
|
||||
labels = [],
|
||||
valueKey = undefined,
|
||||
selectedLabel = '',
|
||||
selectedValue = '',
|
||||
title = '',
|
||||
description = '',
|
||||
} = selectBottomSheet;
|
||||
|
||||
const handleRadioChange = ({ event, label, value }: RadioChangeProps<ElementType<typeof values>>) => {
|
||||
selectBottomSheet?.onRadioChange?.({ event, label, value });
|
||||
};
|
||||
|
||||
overlay.open(({ isOpen, close, unmount }) => {
|
||||
return (
|
||||
<BottomSheet title={title} description={description} afterLeave={unmount} open={isOpen} onClose={close}>
|
||||
<SelectTemplate
|
||||
values={values}
|
||||
valueKey={valueKey}
|
||||
labels={labels}
|
||||
selectedLabel={selectedLabel}
|
||||
selectedValue={selectedValue}
|
||||
onRadioChange={handleRadioChange}
|
||||
/>
|
||||
</BottomSheet>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return { openSelectBottomSheet, closeBottomSheet: overlay.closeAll };
|
||||
};
|
||||
28
src/shared/lib/hooks/use-window-focus-change.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface WindowFocusProps {
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
args?: any[];
|
||||
}
|
||||
|
||||
export const useWindowFocusChange = ({ onFocus, onBlur, ...args }: WindowFocusProps) => {
|
||||
const dependencies: any[] = [];
|
||||
if (Array.isArray(args)) {
|
||||
dependencies.push(...args);
|
||||
}
|
||||
const handleEvent = () => {
|
||||
if (document.hidden) {
|
||||
// the page is hidden
|
||||
onBlur?.();
|
||||
} else {
|
||||
// the page is visible
|
||||
onFocus?.();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
window.document.addEventListener('visibilitychange', handleEvent);
|
||||
return () => window.document.removeEventListener('visibilitychange', handleEvent);
|
||||
}, [onFocus, onBlur, ...dependencies]);
|
||||
};
|
||||
5
src/shared/lib/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './error';
|
||||
export * from './toast';
|
||||
export * from './web-view-bridge';
|
||||
export * from './format-korean-number';
|
||||
export * from './set-after-page-rendered';
|
||||
17
src/shared/lib/resultify.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { CBDCAxiosError } from '@/shared/@types/error';
|
||||
|
||||
export const resultify = async <T = any>(promiseObj: AxiosPromise<T>): Promise<T> => {
|
||||
try {
|
||||
const result: any = await promiseObj;
|
||||
return result?.data !== undefined && result?.data !== null ? result.data : result;
|
||||
} catch (error: any) {
|
||||
const axiosError = error as CBDCAxiosError;
|
||||
console.error(
|
||||
`${
|
||||
axiosError.response?.status ? `[${axiosError.response.status}], ` : ''
|
||||
}${JSON.stringify(axiosError.response?.data)}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
9
src/shared/lib/set-after-page-rendered.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
import { getLocalStorage } from './web-view-bridge';
|
||||
|
||||
export const setAfterPageRendered = (callback: () => void) => {
|
||||
setTimeout(callback, PAGING_SPEED);
|
||||
};
|
||||
export const PAGING_SPEED = getLocalStorage(StorageKeys.AppPagingSpeed)
|
||||
? Number(getLocalStorage(StorageKeys.AppPagingSpeed))
|
||||
: 250;
|
||||
6
src/shared/lib/to-camel-case.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const toCamelCase = (str: string) => {
|
||||
return str
|
||||
.split('-')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join('');
|
||||
};
|
||||
11
src/shared/lib/toast.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
/* eslint-disable @cspell/spellchecker */
|
||||
import { toast } from 'react-toastify';
|
||||
export const snackBar = (text: string) => {
|
||||
toast.dismiss({ containerId: 'snackbar' });
|
||||
toast(text, { containerId: 'snackbar' });
|
||||
};
|
||||
|
||||
export const notiBar = (text: string) => {
|
||||
toast.dismiss({ containerId: 'notibar' });
|
||||
toast(text);
|
||||
};
|
||||
79
src/shared/lib/web-view-bridge.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
export const setLocalStorage = (key: string, value: any) => {
|
||||
if (!key) return false;
|
||||
if (value) {
|
||||
if (typeof value === 'object') {
|
||||
window?.localStorage?.setItem(key, JSON.stringify(value));
|
||||
} else {
|
||||
window?.localStorage?.setItem(key, value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const getLocalStorage = (key: string) => {
|
||||
if (!key) return;
|
||||
const ret = window?.localStorage?.getItem(key);
|
||||
|
||||
try {
|
||||
return ret && JSON.parse(ret);
|
||||
} catch (error) {
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
export const parseValueToDate = (
|
||||
date: string | number | Date | string[] | number[] | null | undefined,
|
||||
type?: string,
|
||||
fixType?: string,
|
||||
): string => {
|
||||
if (!date) return '';
|
||||
let parse = '';
|
||||
switch (typeof date) {
|
||||
case 'string':
|
||||
return date;
|
||||
case 'number':
|
||||
case 'object':
|
||||
let dDate = null;
|
||||
if (date instanceof Date || typeof date === 'number') {
|
||||
dDate = new Date(date);
|
||||
} else if (date instanceof Array) {
|
||||
if (date.length === 6)
|
||||
dDate = new Date(
|
||||
Number(date[0]),
|
||||
Number(date[1]) - 1,
|
||||
Number(date[2]),
|
||||
Number(date[3]),
|
||||
Number(date[4]),
|
||||
Number(date[5]),
|
||||
);
|
||||
else if (date.length === 5)
|
||||
dDate = dDate = new Date(
|
||||
Number(date[0]),
|
||||
Number(date[1]) - 1,
|
||||
Number(date[2]),
|
||||
Number(date[3]),
|
||||
Number(date[4]),
|
||||
);
|
||||
else if (date.length === 4)
|
||||
dDate = dDate = new Date(Number(date[0]), Number(date[1]) - 1, Number(date[2]), Number(date[3]));
|
||||
else if (date.length === 3) dDate = dDate = new Date(Number(date[0]), Number(date[1]) - 1, Number(date[2]));
|
||||
else if (date.length === 2) dDate = dDate = new Date(Number(date[0]), Number(date[1]) - 1);
|
||||
else if (date.length === 1) dDate = new Date(Number(date[0]));
|
||||
}
|
||||
|
||||
if (dDate) {
|
||||
if (type !== 'time') {
|
||||
parse = dDate.getFullYear() + (fixType ? fixType : '-');
|
||||
parse += String(dDate.getMonth() + 1).padStart(2, '0') + (fixType ? fixType : '-');
|
||||
parse += String(dDate.getDate()).padStart(2, '0');
|
||||
}
|
||||
if (type !== 'date') {
|
||||
if (type !== 'time') parse += ' ';
|
||||
parse += String(dDate.getHours()).padStart(2, '0') + ':';
|
||||
parse += String(dDate.getMinutes()).padStart(2, '0') + ':';
|
||||
parse += String(dDate.getSeconds()).padStart(2, '0');
|
||||
}
|
||||
}
|
||||
return parse;
|
||||
}
|
||||
};
|
||||
46
src/shared/model/store.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { persistOptions, withLenses } from '@dhmk/zustand-lens';
|
||||
import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import { createUtilEventSlice, UtilEventState } from '@/entities/redirect/model/store';
|
||||
import { createUserInfoStore, UserInfoState } from '@/entities/user/model/store';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
|
||||
export type RootStore = {
|
||||
UserStore: UserInfoState;
|
||||
};
|
||||
export const useStore = create<RootStore>()(
|
||||
devtools(
|
||||
persist(
|
||||
immer(
|
||||
withLenses(() => ({
|
||||
UserStore: createUserInfoStore,
|
||||
utilEventSlice: createUtilEventSlice,
|
||||
})),
|
||||
),
|
||||
{
|
||||
name: StorageKeys.RootStore,
|
||||
version: 1,
|
||||
...persistOptions,
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
type StoreWithPersist = typeof useStore;
|
||||
|
||||
export const withStorageDOMEvents = (store: StoreWithPersist) => {
|
||||
const storageEventCallback = (e: StorageEvent) => {
|
||||
if (e.key === store.persist.getOptions().name && e.newValue) {
|
||||
store.persist.rehydrate();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('storage', storageEventCallback);
|
||||
return () => {
|
||||
window.removeEventListener('storage', storageEventCallback);
|
||||
};
|
||||
};
|
||||
|
||||
withStorageDOMEvents(useStore);
|
||||
window.store = useStore;
|
||||
63
src/shared/ui/assets/css/fonts.css
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: local('Pretendard Black'), url(../font/Pretendard-Black.woff2) format('woff2'), url(../font/Pretendard-Black.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: local('Pretendard ExtraBold'), url(../font/Pretendard-ExtraBold.woff2) format('woff2'), url(../font/Pretendard-ExtraBold.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local('Pretendard Bold'), url(../font/Pretendard-Bold.woff2) format('woff2'), url(../font/Pretendard-Bold.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: local('Pretendard SemiBold'), url(../font/Pretendard-SemiBold.woff2) format('woff2'), url(../font/Pretendard-SemiBold.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: local('Pretendard Medium'), url(../font/Pretendard-Medium.woff2) format('woff2'), url(../font/Pretendard-Medium.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Pretendard Regular'), url(../font/Pretendard-Regular.woff2) format('woff2'), url(../font/Pretendard-Regular.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: local('Pretendard Light'), url(../font/Pretendard-Light.woff2) format('woff2'), url(../font/Pretendard-Light.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: local('Pretendard ExtraLight'), url(../font/Pretendard-ExtraLight.woff2) format('woff2'), url(../font/Pretendard-ExtraLight.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pretendard';
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: local('Pretendard Thin'), url(../font/Pretendard-Thin.woff2) format('woff2'), url(../font/Pretendard-Thin.woff) format('woff');
|
||||
}
|
||||
54
src/shared/ui/assets/css/reset.css
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
/* style reset */
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre, a,
|
||||
abbr, acsronym, address, big, cite, code, del,
|
||||
dfn, em, img, ins, kbd, q, s, samp, small,
|
||||
strike, strong, sub, sup, tt, var, b, u, i,
|
||||
center, dl, dt, dd, ol, ul, li, fieldset, form,
|
||||
label, legend, table, caption, tbody, tfoot,
|
||||
thead, tr, th, td, article, aside, canvas,
|
||||
details, embed, figure, figcaption, footer,
|
||||
header, hgroup, menu, nav, output, ruby, section,
|
||||
summary, time, mark, audio, video, button, a, input {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
-webkit-tap-highlight-color : rgba(0,0,0,0);
|
||||
line-height: 150%;
|
||||
word-break: keep-all;
|
||||
font-family: 'Pretendard', sans-serif;
|
||||
}
|
||||
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block
|
||||
}
|
||||
body {
|
||||
line-height: 1
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0
|
||||
}
|
||||
a {
|
||||
text-decoration: none
|
||||
}
|
||||
21
src/shared/ui/assets/css/style-fix.css
Normal file
@@ -0,0 +1,21 @@
|
||||
main {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.full-menu-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
z-index: 9999;
|
||||
display: unset;
|
||||
/* overflow-y: auto; */
|
||||
overflow-y: scroll;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.ic20.rot-180{
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
4891
src/shared/ui/assets/css/style.css
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Black.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Black.woff2
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Bold.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Bold.woff2
Normal file
BIN
src/shared/ui/assets/font/Pretendard-ExtraBold.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-ExtraBold.woff2
Normal file
BIN
src/shared/ui/assets/font/Pretendard-ExtraLight.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-ExtraLight.woff2
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Light.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Light.woff2
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Medium.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Medium.woff2
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Regular.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Regular.woff2
Normal file
BIN
src/shared/ui/assets/font/Pretendard-SemiBold.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-SemiBold.woff2
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Thin.woff
Normal file
BIN
src/shared/ui/assets/font/Pretendard-Thin.woff2
Normal file
3
src/shared/ui/assets/images/Forward.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="22" height="38" viewBox="0 0 22 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.20503 13.2115C6.93166 13.4936 6.93166 13.9509 7.20503 14.2329L12.3101 19.5L7.20503 24.7671C6.93166 25.0491 6.93166 25.5064 7.20503 25.7885C7.47839 26.0705 7.92161 26.0705 8.19498 25.7885L13.795 20.0107C14.0683 19.7286 14.0683 19.2714 13.795 18.9893L8.19498 13.2115C7.92161 12.9295 7.47839 12.9295 7.20503 13.2115Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 485 B |
3
src/shared/ui/assets/images/alarm.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.2664 2C13.1869 2 13.9334 2.74652 13.9334 3.66699C13.9334 3.68927 13.9303 3.71134 13.9295 3.7334C16.0745 4.34798 17.7613 6.12394 18.1961 8.40625L19.0467 12.873C19.0479 12.8793 19.0502 12.8852 19.0535 12.8906L20.4099 15.0947C21.6393 17.0933 20.2008 19.6667 17.8543 19.667H14.7576C14.6714 20.9696 13.5908 22 12.2664 22C10.9421 21.9999 9.86135 20.9695 9.77518 19.667H6.67947C4.33258 19.667 2.89491 17.0935 4.12479 15.0947L5.48026 12.8906C5.48352 12.8853 5.48588 12.8792 5.48709 12.873L6.33865 8.40625C6.77333 6.12436 8.45892 4.34925 10.6033 3.73438C10.6024 3.712 10.6004 3.68959 10.6004 3.66699C10.6004 2.74661 11.346 2.00015 12.2664 2ZM11.7976 19.667C11.8666 19.8604 12.0493 19.9999 12.2664 20C12.4836 20 12.6671 19.8605 12.7361 19.667H11.7976ZM12.2674 5.5C10.3299 5.5 8.66604 6.87708 8.3035 8.78027L7.45193 13.2471C7.40531 13.4918 7.31484 13.7263 7.18436 13.9385L5.82791 16.1426C5.4179 16.8088 5.89717 17.667 6.67947 17.667H17.8543C18.6363 17.6667 19.1156 16.8087 18.7058 16.1426L17.3504 13.9385C17.2197 13.7261 17.1285 13.492 17.0818 13.2471L16.2312 8.78027C15.8688 6.87728 14.2045 5.50033 12.2674 5.5Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/shared/ui/assets/images/android-icon-192x192.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/shared/ui/assets/images/apple-icon-180x180.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
9
src/shared/ui/assets/images/chart-active.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_1334_385" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">
|
||||
<path d="M24 0H0V24H24V0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1334_385)">
|
||||
<path d="M15.3839 19.7248L18.76 23.101C19.3398 23.6808 20.1111 24 20.9305 24C21.75 24 22.5212 23.6808 23.1 23.101C23.6798 22.5212 24 21.751 24 20.9305C24 20.1111 23.6808 19.3398 23.101 18.76L19.7248 15.3838C18.6993 17.1954 17.1954 18.6993 15.3839 19.7248ZM0 9.76825C0 15.1546 4.38165 19.5354 9.76715 19.5354C15.1527 19.5354 19.5343 15.1546 19.5343 9.76825C19.5354 4.38193 15.1535 0 9.76822 0C4.38272 0 0.00107002 4.38086 0.00107002 9.76718L0 9.76825Z" fill="#3E6AFC"/>
|
||||
<path d="M4.7024 9.35355C4.7024 9.20863 4.7446 9.08361 4.8298 8.97612C4.915 8.86941 5.0639 8.81606 5.27731 8.81606H5.63563L5.33384 7.17494C5.28368 6.8859 5.30995 6.64702 5.41426 6.4583C5.51778 6.26958 5.68579 6.15651 5.9191 6.11909C6.15798 6.08166 6.36661 6.11272 6.54577 6.21305C6.72493 6.31338 6.83959 6.50846 6.88976 6.79751L7.21066 8.81606H8.18211L8.6535 6.81662C8.68456 6.6717 8.73711 6.55385 8.80957 6.46308C8.88203 6.3723 8.96325 6.29984 9.05483 6.24649C9.1464 6.19314 9.24195 6.15571 9.34228 6.13342C9.44261 6.11112 9.53737 6.09998 9.62496 6.09998C9.71255 6.09998 9.8073 6.11112 9.90764 6.13342C10.008 6.15571 10.1043 6.19314 10.1951 6.24649C10.2867 6.29984 10.3679 6.3723 10.4403 6.46308C10.5128 6.55465 10.5646 6.6725 10.5956 6.81662L11.0582 8.81606H12.0385L12.3594 6.79751C12.4095 6.50846 12.5242 6.31338 12.7033 6.21305C12.8825 6.11272 13.0919 6.08086 13.3308 6.11909C13.5569 6.15651 13.7226 6.27038 13.8261 6.4583C13.9296 6.64702 13.9567 6.88271 13.9065 7.16539L13.5952 8.81606H13.963C14.1764 8.81606 14.3261 8.86941 14.4106 8.97612C14.4958 9.08282 14.538 9.20863 14.538 9.35355C14.538 9.49847 14.4942 9.62826 14.4058 9.74452C14.3174 9.86077 14.1701 9.9189 13.9622 9.9189H13.3961L12.8777 12.7481C12.8276 13.0307 12.7097 13.2545 12.5242 13.4177C12.3386 13.581 12.1388 13.6725 11.9254 13.6909C11.8123 13.7036 11.6976 13.6988 11.5814 13.6765C11.4651 13.6542 11.3552 13.6136 11.2509 13.5539C11.1474 13.4942 11.0559 13.4122 10.977 13.3086C10.8982 13.2051 10.8401 13.0745 10.8026 12.9169L10.1234 9.91811H9.12331L8.44408 12.9169C8.40666 13.0737 8.34774 13.2043 8.2697 13.3086C8.19087 13.4122 8.10009 13.4942 7.99658 13.5539C7.89306 13.6136 7.78238 13.6542 7.66613 13.6765C7.54987 13.698 7.43521 13.7028 7.32214 13.6909C7.10794 13.6717 6.90887 13.581 6.72334 13.4177C6.53781 13.2545 6.41996 13.0307 6.36979 12.7481L5.84186 9.9189H5.27571C5.06789 9.9189 4.92057 9.86077 4.83219 9.74452C4.7438 9.62826 4.70001 9.49767 4.70001 9.35355H4.7024ZM7.6072 11.3148L7.9281 9.9189H7.38106L7.6072 11.3148ZM9.37095 8.81606H9.87101L9.62575 7.70287L9.37095 8.81606ZM11.6435 11.306L11.8601 9.9197H11.313L11.6435 11.306Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
11
src/shared/ui/assets/images/chart.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1040_409)">
|
||||
<path d="M15.3839 19.7248L18.76 23.101C19.3398 23.6808 20.1111 24 20.9305 24C21.75 24 22.5212 23.6808 23.1 23.101H23.101C23.6808 22.5212 24 21.751 24 20.9305C24 20.1111 23.6808 19.3398 23.101 18.76L19.7248 15.3838C18.6993 17.1954 17.1954 18.6993 15.3839 19.7248ZM0 9.76825C0 15.1546 4.38165 19.5354 9.76715 19.5354C15.1527 19.5354 19.5343 15.1546 19.5343 9.76825C19.5354 4.38193 15.1535 0 9.76822 0C4.38272 0 0.00107002 4.38086 0.00107002 9.76718L0 9.76825Z" fill="#B3B8CE"/>
|
||||
<path d="M4.70239 9.35355C4.70239 9.20863 4.74459 9.08361 4.82979 8.97612C4.91499 8.86941 5.06389 8.81606 5.2773 8.81606H5.63562L5.33383 7.17494C5.28367 6.8859 5.30994 6.64702 5.41425 6.4583C5.51777 6.26958 5.68578 6.15651 5.91909 6.11909C6.15797 6.08166 6.3666 6.11272 6.54576 6.21305C6.72492 6.31338 6.83958 6.50846 6.88975 6.79751L7.21065 8.81606H8.1821L8.65349 6.81662C8.68455 6.6717 8.7371 6.55385 8.80956 6.46308C8.88202 6.3723 8.96324 6.29984 9.05482 6.24649C9.14639 6.19314 9.24194 6.15571 9.34227 6.13342C9.4426 6.11112 9.53736 6.09998 9.62495 6.09998C9.71254 6.09998 9.80729 6.11112 9.90763 6.13342C10.008 6.15571 10.1043 6.19314 10.1951 6.24649C10.2867 6.29984 10.3679 6.3723 10.4403 6.46308C10.5128 6.55465 10.5646 6.6725 10.5956 6.81662L11.0582 8.81606H12.0385L12.3594 6.79751C12.4095 6.50846 12.5242 6.31338 12.7033 6.21305C12.8825 6.11272 13.0919 6.08086 13.3308 6.11909C13.5569 6.15651 13.7226 6.27038 13.8261 6.4583C13.9296 6.64702 13.9567 6.88271 13.9065 7.16539L13.5952 8.81606H13.963C14.1764 8.81606 14.3261 8.86941 14.4106 8.97612C14.4958 9.08282 14.538 9.20863 14.538 9.35355C14.538 9.49847 14.4942 9.62826 14.4058 9.74452C14.3174 9.86077 14.1701 9.9189 13.9622 9.9189H13.3961L12.8777 12.7481C12.8276 13.0307 12.7097 13.2545 12.5242 13.4177C12.3386 13.581 12.1388 13.6725 11.9254 13.6909C11.8123 13.7036 11.6976 13.6988 11.5814 13.6765C11.4651 13.6542 11.3552 13.6136 11.2509 13.5539C11.1474 13.4942 11.0559 13.4122 10.977 13.3086C10.8982 13.2051 10.8401 13.0745 10.8026 12.9169L10.1234 9.91811H9.1233L8.44407 12.9169C8.40665 13.0737 8.34773 13.2043 8.26969 13.3086C8.19086 13.4122 8.10008 13.4942 7.99657 13.5539C7.89305 13.6136 7.78237 13.6542 7.66612 13.6765C7.54986 13.698 7.4352 13.7028 7.32213 13.6909C7.10793 13.6717 6.90886 13.581 6.72333 13.4177C6.5378 13.2545 6.41995 13.0307 6.36978 12.7481L5.84185 9.9189H5.2757C5.06788 9.9189 4.92056 9.86077 4.83218 9.74452C4.74379 9.62826 4.7 9.49767 4.7 9.35355H4.70239ZM7.60719 11.3148L7.92809 9.9189H7.38105L7.60719 11.3148ZM9.37094 8.81606H9.871L9.62574 7.70287L9.37094 8.81606ZM11.6435 11.306L11.8601 9.9197H11.313L11.6435 11.306Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1040_409">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
3
src/shared/ui/assets/images/chk_off.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="17" height="17" rx="1.5" stroke="#CCCCCC"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 176 B |
4
src/shared/ui/assets/images/chk_on.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="18" height="18" rx="2" fill="#4968BD"/>
|
||||
<path d="M5 8.75L8 12L13.75 6.25" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 273 B |
7
src/shared/ui/assets/images/chk_scrap_off.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-inside-1_1402_1946" fill="white">
|
||||
<path d="M20.8901 7.48949C21.7048 8.30452 21.705 9.62593 20.8901 10.4408C20.2242 11.1065 19.2206 11.227 18.4311 10.8047L15.6504 13.5855C16.2504 15.4738 15.8028 17.6231 14.3052 19.1208C14.1303 19.2958 13.8463 19.2957 13.6713 19.1208L4.87941 10.329C4.70443 10.154 4.70443 9.87003 4.87941 9.69506C6.37686 8.19772 8.52533 7.75031 10.4133 8.3499L13.1948 5.56843C12.7725 4.77893 12.893 3.77538 13.5587 3.10943C14.3737 2.29451 15.695 2.29467 16.5101 3.10943L20.8901 7.48949Z"/>
|
||||
</mask>
|
||||
<path d="M20.8901 7.48949L21.8588 6.52128L21.8585 6.52107L20.8901 7.48949ZM20.8901 10.4408L21.8584 11.4094L21.8585 11.4093L20.8901 10.4408ZM18.4311 10.8047L19.077 9.59706L18.1811 9.11792L17.4627 9.83633L18.4311 10.8047ZM15.6504 13.5855L14.6819 12.6171L14.0928 13.2063L14.3451 14.0003L15.6504 13.5855ZM14.3052 19.1208L15.2736 20.0893L15.2736 20.0892L14.3052 19.1208ZM13.6713 19.1208L12.7029 20.0893L12.7033 20.0897L13.6713 19.1208ZM4.87941 9.69506L3.91102 8.7266L3.91099 8.72664L4.87941 9.69506ZM10.4133 8.3499L9.99881 9.65521L10.7927 9.90734L11.3818 9.31832L10.4133 8.3499ZM13.1948 5.56843L14.1632 6.53684L14.8817 5.81841L14.4025 4.92248L13.1948 5.56843ZM13.5587 3.10943L12.5903 2.14102L12.5901 2.1412L13.5587 3.10943ZM16.5101 3.10943L17.4785 2.14102L17.4783 2.14086L16.5101 3.10943ZM20.8901 7.48949L19.9215 8.4577C20.2018 8.73815 20.2016 9.19256 19.9217 9.47242L20.8901 10.4408L21.8585 11.4093C23.2085 10.0593 23.2078 7.8709 21.8588 6.52128L20.8901 7.48949ZM20.8901 10.4408L19.9219 9.47223C19.6955 9.69851 19.3508 9.74347 19.077 9.59706L18.4311 10.8047L17.7853 12.0124C19.0905 12.7105 20.7529 12.5145 21.8584 11.4094L20.8901 10.4408ZM18.4311 10.8047L17.4627 9.83633L14.6819 12.6171L15.6504 13.5855L16.6188 14.5539L19.3996 11.7732L18.4311 10.8047ZM15.6504 13.5855L14.3451 14.0003C14.7965 15.4206 14.458 17.0311 13.3367 18.1525L14.3052 19.1208L15.2736 20.0892C17.1476 18.2151 17.7044 15.527 16.9556 13.1707L15.6504 13.5855ZM14.3052 19.1208L13.3368 18.1524C13.6972 17.792 14.28 17.793 14.6392 18.152L13.6713 19.1208L12.7033 20.0897C13.4126 20.7983 14.5634 20.7995 15.2736 20.0893L14.3052 19.1208ZM13.6713 19.1208L14.6397 18.1524L5.84782 9.36055L4.87941 10.329L3.91099 11.2974L12.7029 20.0893L13.6713 19.1208ZM4.87941 10.329L5.84782 9.36055C6.20769 9.72042 6.20769 10.3036 5.84782 10.6635L4.87941 9.69506L3.91099 8.72664C3.20117 9.43646 3.20117 10.5876 3.91099 11.2974L4.87941 10.329ZM4.87941 9.69506L5.84779 10.6635C6.96886 9.54252 8.57859 9.20418 9.99881 9.65521L10.4133 8.3499L10.8279 7.04459C8.47207 6.29644 5.78486 6.85291 3.91102 8.7266L4.87941 9.69506ZM10.4133 8.3499L11.3818 9.31832L14.1632 6.53684L13.1948 5.56843L12.2264 4.60001L9.44493 7.38148L10.4133 8.3499ZM13.1948 5.56843L14.4025 4.92248C14.2561 4.64879 14.301 4.3041 14.5273 4.07767L13.5587 3.10943L12.5901 2.1412C11.4851 3.24666 11.289 4.90906 11.9872 6.21437L13.1948 5.56843ZM13.5587 3.10943L14.5271 4.07785C14.8071 3.79793 15.2614 3.79773 15.5418 4.07801L16.5101 3.10943L17.4783 2.14086C16.1287 0.791621 13.9402 0.791092 12.5903 2.14102L13.5587 3.10943ZM16.5101 3.10943L15.5417 4.07785L19.9217 8.45791L20.8901 7.48949L21.8585 6.52107L17.4785 2.14102L16.5101 3.10943Z" fill="#BEBEBE" mask="url(#path-1-inside-1_1402_1946)"/>
|
||||
<path d="M9.09684 15.1136C8.95481 15.2529 8.78661 15.4186 8.59689 15.6025C8.00149 16.1795 7.20296 16.9409 6.38787 17.683C6.15804 17.8923 5.92715 18.0985 5.70079 18.2983C5.90063 18.0719 6.10681 17.8411 6.31606 17.6112C7.05816 16.7961 7.81956 15.9976 8.39663 15.4022C8.58052 15.2125 8.74621 15.0443 8.88553 14.9023L9.09684 15.1136Z" stroke="#BEBEBE" stroke-width="1.35764"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
4
src/shared/ui/assets/images/chk_scrap_on.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.8901 7.48949C21.7048 8.30452 21.705 9.62593 20.8901 10.4408C20.2242 11.1065 19.2206 11.227 18.4311 10.8047L15.6504 13.5855C16.2504 15.4738 15.8028 17.6231 14.3052 19.1208C14.1303 19.2958 13.8463 19.2957 13.6713 19.1208L4.87941 10.329C4.70443 10.154 4.70443 9.87003 4.87941 9.69506C6.37686 8.19772 8.52533 7.75031 10.4133 8.3499L13.1948 5.56843C12.7725 4.77893 12.893 3.77538 13.5587 3.10943C14.3737 2.29451 15.695 2.29467 16.5101 3.10943L20.8901 7.48949Z" fill="#999999"/>
|
||||
<path d="M9.09684 15.1136C8.95481 15.2529 8.78661 15.4186 8.59689 15.6025C8.00149 16.1795 7.20296 16.9409 6.38787 17.683C6.15804 17.8923 5.92715 18.0985 5.70079 18.2983C5.90063 18.0719 6.10681 17.8411 6.31606 17.6112C7.05816 16.7961 7.81956 15.9976 8.39663 15.4022C8.58052 15.2125 8.74621 15.0443 8.88553 14.9023L9.09684 15.1136Z" stroke="#999999" stroke-width="1.35764"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 962 B |
BIN
src/shared/ui/assets/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |