알림함 카테고리 셋팅

This commit is contained in:
focp212@naver.com
2025-10-27 11:30:02 +09:00
parent 9eba765957
commit 9be67b403e
12 changed files with 242 additions and 130 deletions

View File

@@ -93,6 +93,7 @@
"swiper": "^11.2.10",
"ts-loader": "^9.5.4",
"tsconfig-paths-webpack-plugin": "^4.2.0",
"ua-parser-js": "^2.0.6",
"use-local-storage-state": "^19.5.0",
"vite-plugin-checker": "^0.10.3",
"vite-tsconfig-paths": "^5.1.4",

28
pnpm-lock.yaml generated
View File

@@ -254,6 +254,9 @@ importers:
tsconfig-paths-webpack-plugin:
specifier: ^4.2.0
version: 4.2.0
ua-parser-js:
specifier: ^2.0.6
version: 2.0.6
use-local-storage-state:
specifier: ^19.5.0
version: 19.5.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -3115,6 +3118,9 @@ packages:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
detect-europe-js@0.1.2:
resolution: {integrity: sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==}
detect-libc@1.0.3:
resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
engines: {node: '>=0.10'}
@@ -3898,6 +3904,9 @@ packages:
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
engines: {node: '>= 0.4'}
is-standalone-pwa@0.1.1:
resolution: {integrity: sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==}
is-string@1.1.1:
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
engines: {node: '>= 0.4'}
@@ -5801,10 +5810,17 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
ua-is-frozen@0.1.2:
resolution: {integrity: sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==}
ua-parser-js@1.0.41:
resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==}
hasBin: true
ua-parser-js@2.0.6:
resolution: {integrity: sha512-EmaxXfltJaDW75SokrY4/lXMrVyXomE/0FpIIqP2Ctic93gK7rlme55Cwkz8l3YZ6gqf94fCU7AnIkidd/KXPg==}
hasBin: true
unbox-primitive@1.1.0:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'}
@@ -9363,6 +9379,8 @@ snapshots:
destroy@1.2.0: {}
detect-europe-js@0.1.2: {}
detect-libc@1.0.3:
optional: true
@@ -10292,6 +10310,8 @@ snapshots:
dependencies:
call-bound: 1.0.4
is-standalone-pwa@0.1.1: {}
is-string@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -12425,8 +12445,16 @@ snapshots:
typescript@5.9.2: {}
ua-is-frozen@0.1.2: {}
ua-parser-js@1.0.41: {}
ua-parser-js@2.0.6:
dependencies:
detect-europe-js: 0.1.2
is-standalone-pwa: 0.1.1
ua-is-frozen: 0.1.2
unbox-primitive@1.1.0:
dependencies:
call-bound: 1.0.4

View File

@@ -4,6 +4,7 @@ import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import moment from 'moment';
export interface AlarmItemProps {
appNotificationSequence?: number;
appNotificationCategory?: string;
notificationReceivedDate?: string;
appNotificationTitle?: string;
@@ -12,6 +13,7 @@ export interface AlarmItemProps {
};
export const AlarmItem = ({
appNotificationSequence,
appNotificationCategory,
notificationReceivedDate,
appNotificationTitle,
@@ -19,9 +21,10 @@ export const AlarmItem = ({
appNotificationLink
}: AlarmItemProps) => {
const { navigate } = useNavigate();
const onClickToNavigate = (alarmId: number) => {
let path = PATHS.support.notice.detail + alarmId;
navigate(path);
const onClickToNavigate = () => {
let path = PATHS.support.notice.detail + appNotificationSequence;
// navigate(path);
};
return (
@@ -35,7 +38,7 @@ export const AlarmItem = ({
</div>
<div
className="notice-arrow"
onClick={ () => onClickToNavigate(4) }
onClick={ () => onClickToNavigate() }
>
<img
src={ IMAGE_ROOT + '/Forward.svg' }

View File

@@ -11,11 +11,11 @@ import { useStore } from '@/shared/model/store';
import { DefaultRequestPagination } from '@/entities/common/model/types';
export interface AlarmListProps {
category: string;
appNotificationCategory: string;
};
export const AlarmList = ({
category
appNotificationCategory
}: AlarmListProps) => {
const { navigate } = useNavigate();
const { t } = useTranslation();
@@ -24,28 +24,10 @@ export const AlarmList = ({
const [onActionIntersect, setOnActionIntersect] = useState<boolean>(false);
const [pageParam, setPageParam] = useState<DefaultRequestPagination>(DEFAULT_PAGE_PARAM);
const [appCl, setAppCl] = useState<MERCHANT_ADMIN_APP>(MERCHANT_ADMIN_APP.MERCHANT_ADMIN_APP)
const [appNotificationCategory, setAppNotificationCategory] = useState<string>('');
const [resultList, setResultList] = useState<Array<AlarmListContent>>([]);
const [selectedCategory, setSelectedCategory] = useState<string>('');
const { mutateAsync: appAlarmList } = useAppAlarmListMutation();
const getAlarmItems = () => {
let rs = [];
for(let i=0;i<resultList.length;i++){
rs.push(
<AlarmItem
appNotificationCategory={ resultList[i]?.appNotificationCategory }
notificationReceivedDate={ resultList[i]?.notificationReceivedDate }
appNotificationTitle={ resultList[i]?.appNotificationTitle }
appNotificationContent={ resultList[i]?.appNotificationContent }
appNotificationLink={ resultList[i]?.appNotificationLink }
></AlarmItem>
)
}
return rs;
};
const onIntersect: IntersectionObserverCallback = (entries: Array<IntersectionObserverEntry>) => {
entries.forEach((entry: IntersectionObserverEntry) => {
if(entry.isIntersecting){
@@ -73,7 +55,7 @@ export const AlarmList = ({
appCl: appCl,
appNotificationCategory: appNotificationCategory,
page: pageParam
}
};
if(type !== 'page' && params.page){
params.page.cursor = null;
@@ -115,11 +97,27 @@ export const AlarmList = ({
}
};
const getAlarmItems = () => {
let rs = [];
for(let i=0;i<resultList.length;i++){
rs.push(
<AlarmItem
appNotificationSequence={ resultList[i]?.appNotificationSequence }
appNotificationCategory={ resultList[i]?.appNotificationCategory }
notificationReceivedDate={ resultList[i]?.notificationReceivedDate }
appNotificationTitle={ resultList[i]?.appNotificationTitle }
appNotificationContent={ resultList[i]?.appNotificationContent }
appNotificationLink={ resultList[i]?.appNotificationLink }
></AlarmItem>
);
}
return rs;
};
useEffect(() => {
callList();
}, [selectedCategory]);
}, [appNotificationCategory]);
return (
<>
<div className="notice-box sub">

View File

@@ -16,6 +16,8 @@ export interface CommonState {
setBankList: (update: SetStateAction<Array<any>>) => void;
virtualBankList: Array<any>;
setVirtualBankList: (update: SetStateAction<Array<any>>) => void;
appNotificationCategories: Array<any>;
setAppNotificationCategories: (update: SetStateAction<Array<any>>) => void;
};
const initialBannerInfoState = {
@@ -24,6 +26,10 @@ const initialBannerInfoState = {
const initialCommonState = {
serviceCodes: [] as Array<any>,
creditCardList: [] as Array<any>,
bankList: [] as Array<any>,
virtualBankList: [] as Array<any>,
appNotificationCategories: [] as Array<any>
} as CommonState;
export const createBannerInfoStore = lens<BannerInfoState>((set, get) => ({
@@ -94,4 +100,16 @@ export const createCommonStore = lens<CommonState>((set, get) => ({
};
});
},
setAppNotificationCategories: (update) => {
set((state: CommonState) => {
const newAppNotificationCategories = (typeof update === 'function')
? update(state.appNotificationCategories): update;
return {
...state,
appNotificationCategories: [
...newAppNotificationCategories
]
};
});
},
}));

View File

@@ -12,6 +12,7 @@ import {
} from '@tanstack/react-query';
export const allTransactionList = (params: AllTransactionListParams) => {
//axios.AxiosHeaders[]
return resultify(
axios.post<AllTransactionListResponse>(API_URL_TRANSACTION.allTransactionList(), params),
);

View File

@@ -4,6 +4,7 @@ import { NumericFormat } from 'react-number-format';
import { AllTransactionCancelSectionPasswordGroup } from './section/all-transaction-cancel-section-password-group';
import { AllTransactionCancelSectionBankGroup } from './section/all-transaction-cancel-section-bank-group';
import { BankCode } from '@/shared/@types/banking-code';
import { ChangeEvent, useEffect, useState } from 'react';
export interface AllTransactionPartCancelProps extends AllTransactionCancelInfoResponse {
serviceCode: string;
@@ -15,6 +16,14 @@ export interface AllTransactionPartCancelProps extends AllTransactionCancelInfoR
setAccountNo?: (accountNo: string) => void;
accountHolder?: string;
setAccountHolder?: (accountHolder: string) => void;
cancelSupplyAmount: number;
setCancelSupplyAmount: (cancelSupplyAmount: number) => void;
cancelGoodsVat: number;
setCancelGoodsVat: (cancelGoodsVat: number) => void;
cancelTaxFreeAmount: number;
setCancelTaxFreeAmount: (cancelTaxFreeAmount: number) => void;
cancelServiceAmount: number;
setCancelServiceAmount: (cancelServiceAmount: number) => void;
};
export const AllTransactionPartCancel = ({
@@ -40,99 +49,115 @@ export const AllTransactionPartCancel = ({
accountNo,
setAccountNo,
accountHolder,
setAccountHolder
setAccountHolder,
cancelSupplyAmount,
setCancelSupplyAmount,
cancelGoodsVat,
setCancelGoodsVat,
cancelTaxFreeAmount,
setCancelTaxFreeAmount,
cancelServiceAmount,
setCancelServiceAmount
}: AllTransactionPartCancelProps) => {
return (
<>
{ !!isCompoundTax &&
<div className="tb_both">
<table className="partial-cancel-table">
<colgroup>
<col width="30%" />
<col width="30%" />
<col width="40%" />
</colgroup>
<thead>
<tr>
<th className="header-empty"></th>
<th className="header-balance"></th>
<th className="header-cancel"></th>
</tr>
</thead>
<tbody>
<tr>
<td className="row-label"></td>
<td className="row-balance">
<NumericFormat
value={ supplyAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</td>
<td className="row-cancel">
<input
className="cancel-input"
type="text"
value=""
/>
</td>
</tr>
<div className="tb_both">
<table className="partial-cancel-table">
<colgroup>
<col width="30%" />
<col width="30%" />
<col width="40%" />
</colgroup>
<thead>
<tr>
<td className="row-label"></td>
<th className="header-empty"></th>
<th className="header-balance"></th>
<th className="header-cancel"></th>
</tr>
</thead>
<tbody>
<tr>
<td className="row-label"></td>
<td className="row-balance">
<NumericFormat
value={ goodsVat }
value={ supplyAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</td>
<td className="row-cancel">
<input
className="cancel-input"
type="text"
placeholder=""
/>
</td>
</tr>
<tr>
<td className="row-label"></td>
<td className="row-balance">
<NumericFormat
value={ taxFreeAmount }
thousandSeparator
displayType="text"
className="cancel-input"
value={ cancelSupplyAmount }
allowNegative={ false }
displayType="input"
onChange={(e: ChangeEvent<HTMLInputElement>) => setCancelServiceAmount(parseInt(e.target.value)) }
></NumericFormat>
</td>
<td className="row-cancel">
<input
className="cancel-input"
type="text"
placeholder=""
/>
</td>
</tr>
<tr>
<td className="row-label"></td>
<td className="row-balance">
<NumericFormat
value={ serviceAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</td>
<td className="row-cancel">
<input
className="cancel-input"
type="text"
placeholder=""
/>
</td>
</tr>
</tbody>
</table>
</div>
<tr>
<td className="row-label"></td>
<td className="row-balance">
<NumericFormat
value={ goodsVat }
thousandSeparator
displayType="text"
></NumericFormat>
</td>
<td className="row-cancel">
<NumericFormat
className="cancel-input"
value={ cancelGoodsVat }
allowNegative={ false }
displayType="input"
onChange={(e: ChangeEvent<HTMLInputElement>) => setCancelGoodsVat(parseInt(e.target.value)) }
></NumericFormat>
</td>
</tr>
<tr>
<td className="row-label"></td>
<td className="row-balance">
<NumericFormat
value={ taxFreeAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</td>
<td className="row-cancel">
<NumericFormat
className="cancel-input"
value={ cancelTaxFreeAmount }
allowNegative={ false }
displayType="input"
onChange={(e: ChangeEvent<HTMLInputElement>) => setCancelTaxFreeAmount(parseInt(e.target.value)) }
></NumericFormat>
</td>
</tr>
<tr>
<td className="row-label"></td>
<td className="row-balance">
<NumericFormat
value={ serviceAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</td>
<td className="row-cancel">
<NumericFormat
className="cancel-input"
value={ cancelServiceAmount }
allowNegative={ false }
displayType="input"
onChange={(e: ChangeEvent<HTMLInputElement>) => setCancelServiceAmount(parseInt(e.target.value)) }
></NumericFormat>
</td>
</tr>
</tbody>
</table>
</div>
}
<div className="form-section">

View File

@@ -1,45 +1,56 @@
import { AlarmList } from '@/entities/alarm/ui/alarm-list';
import { HeaderType } from '@/entities/common/model/types';
import { useStore } from '@/shared/model/store';
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode
} from '@/widgets/sub-layout/use-sub-layout';
import { useState } from 'react';
import { useEffect, useState } from 'react';
export const ListPage = () => {
let totalAppNotificationCategories = useStore.getState().CommonStore.appNotificationCategories;
const [appNotificationCategories, setAppNotificationCategories] = useState<Array<any>>(totalAppNotificationCategories);
useSetHeaderTitle('알림함');
useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false);
const [category, setCategory] = useState<string>('all');
const [appNotificationCategory, setAppNotificationCategory] = useState<string>('');
let btnList = [
{name: '전체', category: 'all'},
{name: '혜택/이벤트', category: 'event'},
{name: '공지사항', category: 'notice'}
];
const onClickToCategory = (value: any) => {
setCategory(value.category);
const resetAlarmCategories = () => {
if(appNotificationCategories){
let newAppNotificationCategories = appNotificationCategories.filter((value, index) => {
return value.code1 !== '****' && value.code2 === '*';
});
setAppNotificationCategories(newAppNotificationCategories);
}
};
useEffect(() => {
resetAlarmCategories();
}, [totalAppNotificationCategories])
return (
<>
<main className="pop">
<div className="sub-wrap">
<div className="notice-tabs">
{
btnList.map((value, index) => (
<button
className={ `tab36 ${(appNotificationCategory === '')? 'on': ''}`}
onClick={ () => setAppNotificationCategory('') }
></button>
{ !!appNotificationCategories &&
appNotificationCategories.map((value, index) => (
<button
className={ `tab36 ${(category === value.category)? 'on': ''}`}
onClick={ () => onClickToCategory(value) }
>{ value.name }</button>
className={ `tab36 ${(appNotificationCategory === value.code1)? 'on': ''}`}
onClick={ () => setAppNotificationCategory(value.code1) }
>{ value.desc1 }</button>
))
}
</div>
<AlarmList
category={ category }
appNotificationCategory={ appNotificationCategory }
></AlarmList>
</div>
</main>

View File

@@ -50,6 +50,10 @@ export const AllTransactionCancelPage = () => {
const [vatAutoCalcSummary, setVatAutoCalcSummary] = useState<number>(0);
const [totalCancelAmount, setTotalCancelAmount] = useState<number>(0);
const [cancelPassword, setCancelPassword] = useState<string>('');
const [cancelSupplyAmount, setCancelSupplyAmount] = useState<number>(0);
const [cancelGoodsVat, setCancelGoodsVat] = useState<number>(0);
const [cancelTaxFreeAmount, setCancelTaxFreeAmount] = useState<number>(0);
const [cancelServiceAmount, setCancelServiceAmount] = useState<number>(0);
const [bankCode, setBankCode] = useState<string>('');
const [accountNo, setAccountNo] = useState<string>('');
@@ -105,10 +109,10 @@ export const AllTransactionCancelPage = () => {
bankCode: bankCode,
accountNo: accountNo,
accountHolder: accountHolder,
supplyAmount: supplyAmount,
goodsVatAmount: goodsVat,
taxFreeAmount: taxFreeAmount,
serviceAmount: serviceAmount,
supplyAmount: (!!partCancelCl)? cancelSupplyAmount: supplyAmount,
goodsVatAmount: (!!partCancelCl)? cancelGoodsVat: goodsVat,
taxFreeAmount: (!!partCancelCl)? cancelTaxFreeAmount: taxFreeAmount,
serviceAmount: (!!partCancelCl)? cancelServiceAmount: serviceAmount,
clientIp: userInfo.clientAddressIP,
partCancelCl: partCancelCl,
isNpg: isNpg,
@@ -195,7 +199,7 @@ export const AllTransactionCancelPage = () => {
setAccountHolder={ setAccountHolder }
></AllTransactionAllCancel>
}
{ partCancelCl && (tabAction === CancelTabKeys.Part) &&
{ !!partCancelCl && (tabAction === CancelTabKeys.Part) &&
<AllTransactionPartCancel
serviceCode={ serviceCode }
debtPreventionCancelDisplayInfo={ debtPreventionCancelDisplayInfo }
@@ -220,6 +224,14 @@ export const AllTransactionCancelPage = () => {
setAccountNo={ setAccountNo }
accountHolder={ accountHolder }
setAccountHolder={ setAccountHolder }
cancelSupplyAmount={ cancelSupplyAmount }
setCancelSupplyAmount={ setCancelSupplyAmount }
cancelGoodsVat={ cancelGoodsVat }
setCancelGoodsVat={ setCancelGoodsVat }
cancelTaxFreeAmount={ cancelTaxFreeAmount}
setCancelTaxFreeAmount={ setCancelTaxFreeAmount }
cancelServiceAmount={ cancelServiceAmount }
setCancelServiceAmount={ setCancelServiceAmount }
></AllTransactionPartCancel>
}
</div>

View File

@@ -1,3 +1,5 @@
import { UAParser } from 'ua-parser-js';
const { origin } = window.location;
export const CURRENT_URL = `${origin}`;
@@ -17,13 +19,20 @@ const getAPIAuthBaseUrl = () => {
}
return rs;
}
const getHeaderUserAgent = () => {
let os = 'Android';
let deviceType = 'Galaxy Flip 6';
const getHeaderUserAgent = (options?: {
menuId?: number,
apiType?: string
}) => {
let parser = new UAParser();
let result = parser.getResult();
let os = result.os.name;
let deviceType = result.device.type;
let deviceID = 'uuid';
let appVersion = '1.0.0';
let browserInformation = 'Chrome135';
return `${os} ${deviceType} ${deviceID} ${appVersion} ${browserInformation}`;
let browserInformation = result.browser.name;
return `${os} ${deviceType} ${deviceID} ${appVersion} ${browserInformation} ${options?.menuId} ${options?.apiType}`;
};
export const API_BASE_URL = getAPIBaseUrl();

View File

@@ -391,4 +391,7 @@ main.home-main{
text-overflow: ellipsis;
overflow-x: hidden;
width: 100%;
}
.cancel-input{
width: calc(100% - 5px);
}

View File

@@ -246,7 +246,7 @@ export const SubLayout = () => {
codeCl: '0022',
code1Filter: filter0022
});
callCodesSelect({ codeCl: '0325' });
callCodesSelect({ codeCl: '0074' });
}).catch((error: any) => {
@@ -309,6 +309,9 @@ export const SubLayout = () => {
});
useStore.getState().CommonStore.setServiceCodes(options);
}
else if(codeCl === '0325'){
useStore.getState().CommonStore.setAppNotificationCategories(data);
}
else if(codeCl === '0074'){
useStore.getState().CommonStore.setVirtualBankList(data);
}