update bill query and pay

This commit is contained in:
LittleBoy 2024-06-05 00:10:25 +08:00
parent 081e96243d
commit aabbd75a2f
24 changed files with 201 additions and 55 deletions

View File

@ -18,6 +18,7 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@mui/system": "^5.15.15",
"@sentry/react": "^8.7.0",
"ahooks": "^3.7.11",
"axios": "^1.7.2",
"clsx": "^2.1.1",

View File

@ -196,7 +196,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
formatPageText: (params) => (
<div className="bill-list-pagination">
{props.tableFooter}
<span> {params?.currentStart} - {params?.currentEnd} {params?.total} </span>
{props.source?.pagination.recordTotal && <span>{t('page.record-show',params)}</span>}
</div>
)
}}

View File

@ -4,6 +4,7 @@ import {Card} from "@/components/card";
import {useTranslation} from "react-i18next";
import {BillQueryParams} from "@/service/api/bill.ts";
import dayjs from "dayjs";
import useAuth from "@/hooks/useAuth.ts";
type SearchFormProps = {
onSearch?: (params: BillQueryParams) => void;
@ -15,14 +16,19 @@ type SearchFormProps = {
type SearchFormFields = {
dateRange?: Date[];
student_number?: string;
application_number?: string;
bill_number?: string;
pay_method?: string;
payment_channel?: string;
bill_status?: string;
apply_status?: string;
}
const SearchForm: React.FC<SearchFormProps> = (props) => {
const {user} = useAuth();
const formSubmit = (value: SearchFormFields) => {
const params: BillQueryParams = {}
const params: BillQueryParams = {
department: user?.department == 'RO' ? 'RO' : 'FO',
}
if (value.dateRange && value.dateRange.length == 2) {
params.start_date = dayjs(value.dateRange[0]).format('YYYY-MM-DD');
params.end_date = dayjs(value.dateRange[1]).format('YYYY-MM-DD');
@ -30,6 +36,9 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
if (value.student_number) {
params.student_number = value.student_number;
}
if (value.application_number) {
params.application_number = value.application_number;
}
// 对账状态
if (value.apply_status) {
params.apply_status = value.apply_status;
@ -39,8 +48,8 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
params.status = value.bill_status;
}
// 支付方式
if (value.pay_method) {
params.payment_method = value.pay_method;
if (value.payment_channel) {
params.payment_channel = value.payment_channel;
}
props.onSearch?.(params);
}
@ -48,7 +57,7 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
// 根据语言变化更新订单状态options
const billStatusOptions = useMemo(() => {
return [
{value: `PENDING`, label: t('bill.pay_status_pending')},
{value: `PENDING`, label: t('bill.pay_status_pending')},
{value: 'PAID', label: t('bill.pay_status_paid')},
{value: 'CANCELLED', label: t('bill.pay_status_canceled')}
]
@ -75,28 +84,28 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
placeholder={t('base.please_enter')}/>
</Col>
<Col span={4}>
<Form.Input field='bill_number' label={t('base.bill_number')} trigger='blur'
<Form.Input field='application_number' label={t('base.bill_number')} trigger='blur'
placeholder={t('base.please_enter')}/>
</Col>
<Col span={4}>
<Form.Select showClear field="pay_method" label={t('bill.title_pay_method')}
<Form.Select showClear field="payment_channel" label={t('bill.title_pay_method')}
placeholder={t('base.please_select')} style={{width: '100%'}}>
<Form.Select.Option value="ASIAPAY">AsiaPay</Form.Select.Option>
<Form.Select.Option value="FLYWIRE">FlyWire</Form.Select.Option>
<Form.Select.Option value="ASIAPAY">ASIAPAY</Form.Select.Option>
<Form.Select.Option value="FLYWIRE">FLYWIRE</Form.Select.Option>
<Form.Select.Option value="PPS">PPS</Form.Select.Option>
</Form.Select>
</Col>
<Col span={4}>
<Form.Select showClear field="bill_status" label={t('bill.pay_status')}
placeholder={t('base.please_select')} style={{width: '100%'}}>
{billStatusOptions.map((item,index) => (
{billStatusOptions.map((item, index) => (
<Form.Select.Option key={index} value={item.value}>{item.label}</Form.Select.Option>))}
</Form.Select>
</Col>
{props.showApply && <Col span={4}>
<Form.Select showClear field="apply_status" label={t('bill.title_reconciliation_status')}
placeholder={t('base.please_select')} style={{width: '100%'}}>
{applyStatusOptions.map((item,index) => (
{applyStatusOptions.map((item, index) => (
<Form.Select.Option key={index} value={item.value}>{item.label}</Form.Select.Option>))}
</Form.Select>
</Col>}

View File

@ -51,7 +51,11 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
payload: {
isInitialized: true,
isLoggedIn: !!user,
user
user:{
...user,
// TODO 等待接口返回
department:'root'
}
}
})
}).finally(() => {
@ -73,7 +77,11 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
action: 'login',
payload: {
isLoggedIn: true,
user
user:{
...user,
// TODO 等待接口返回
department:'root'
}
}
})
}
@ -98,6 +106,7 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
id: 1,
token: 'test-123123',
email: 'test@qq.com',
department: 'root',
exp: 1,
iat: 1,
iss: "Hong Kong Chu Hai College",

View File

@ -78,6 +78,9 @@
"student_number_required": "required student number",
"success": "Create bill success"
},
"page": {
"record-show": "Showing {{currentStart}} - {{currentEnd}} of {{total}}"
},
"pay": {
"amount": "Amount",
"bill_error": "Bills to be paid do not exist or are overdue",

View File

@ -78,6 +78,9 @@
"student_number_required": "请填写学号",
"success": "创建订单成功"
},
"page": {
"record-show": "显示第 {{currentStart}} 条 - 第 {{currentEnd}} 条,共 {{total}} 条"
},
"pay": {
"amount": "应付金额",
"bill_error": "待支付账单不存在或已过期",

View File

@ -78,6 +78,9 @@
"student_number_required": "請填入學號",
"success": "創建訂單成功"
},
"page": {
"record-show": "顯示第 {{currentStart}} 條 - 第 {{currentEnd}} 條,共 {{total}} 條"
},
"pay": {
"amount": "應付金額",
"bill_error": "待支付帳單不存在或已過期",

View File

@ -1,9 +1,22 @@
// import React from "react";
import ReactDOM from 'react-dom/client'
import * as Sentry from '@sentry/react';
import App from './App.tsx'
import '@/assets/index.less'
Sentry.init({
dsn: "https://571faf95edca40ac981a573eff1b17fe@logs.1688cd.cn/1",
//dsn: "https://5224dbb04d4a6a521edeb67552310836@o4505757500309504.ingest.us.sentry.io/4507373697564672",
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration(),
],
tracesSampleRate: 1.0,
tracePropagationTargets: ["localhost", ], // /^https:\/\/yourserver\.io\/api/,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<App/>,
// <React.StrictMode></React.StrictMode>,

View File

@ -27,9 +27,9 @@ const SubmitContainer = styled.div({
margin: '70px auto 20px'
})
const LoginURL = getAppUrl() + '/login?action=auth'
const AuthLogin = () => {
const {login, mockLogin} = useAuth();
const LoginURL = getAppUrl() + '/login/auth'
const AuthLogin = ({type}:{type?:'auth'|'login'}) => {
const {login, mockLogin,user} = useAuth();
const navigate = useNavigate();
const [state, setState] = useSetState({
showLogin: false,
@ -40,11 +40,19 @@ const AuthLogin = () => {
const {appName} = useConfig()
const [query] = useSearchParams()
useEffect(()=>{
if(user){
navigate('/dashboard')
}
},[user]);
useEffect(() => {
const code = query.get('code'),
state = query.get('state');
if (query.get('action') == 'auth' && state && code) {
if(user){
return;
}
if (type == 'auth' && state && code) {
setState({loading: true})
// 授权回调
login(code, state).then(() => {

View File

@ -2,13 +2,13 @@ import {BillList} from "@/components/bill/list.tsx";
import {Button, Modal, Notification, Popconfirm, Space} from "@douyinfe/semi-ui";
import {useState} from "react";
import {useRequest} from "ahooks";
import {useTranslation} from "react-i18next";
import SearchForm from "@/components/bill/search-form.tsx";
import BillDetail from "@/components/bill/detail.tsx";
import {billList, BillQueryParams, cancelBill} from "@/service/api/bill.ts";
import {BillStatus} from "@/service/types.ts";
import {useDownloadReceiptPDF} from "@/service/generate-pdf.ts";
import {useTranslation} from "react-i18next";
const BillQuery = () => {
const [showBill, setShowBill] = useState<BillModel>()
@ -51,8 +51,11 @@ const BillQuery = () => {
<BillList
type={'query'} loading={loading}
operationRender={operation} source={data}
onPageChange={function (): void {
throw new Error("Function not implemented.");
onPageChange={(page_number) =>{
setBillQueryParams({
...queryParams,
page_number
})
}}
/>
<Modal

View File

@ -46,13 +46,13 @@ const BillReconciliation = () => {
)} loading={loading} onSearch={setBillQueryParams}/>
<BillList
source={data} type={'reconciliation'}
operationRender={queryParams.apply_status == 'confirmed' ? undefined : operation}
onRowSelection={queryParams.apply_status == 'confirmed' ? undefined : (keys: (number|string)[]) => {
operationRender={queryParams.apply_status == 'CHECKED' ? undefined : operation}
onRowSelection={queryParams.apply_status == 'CHECKED' ? undefined : (keys: (number|string)[]) => {
setSelectedKeys(keys);
}}
loading={loading}
onPageChange={(page_number) => setBillQueryParams({page_number})}
tableFooter={queryParams.apply_status != 'confirmed' && selectKeys?.length > 0 && (
tableFooter={queryParams.apply_status != 'CHECKED' && selectKeys?.length > 0 && (
<Popconfirm
title={'Notice'}
content={`${t('bill.cancel_confirm_bills')}?`}

View File

@ -28,12 +28,12 @@ export const StartAsiaPay: React.FC<{ bill: BillModel }> = ({bill}) => {
<input type="hidden" name="merchantId" value={state.pay?.merchantId || ''}/>
{/*state.pay?.payment_amount || */}
<input type="hidden" name="amount" value={'0.01'}/>
<input type="hidden" name="orderRef" value={`hkchc-pay-${bill.id}-${Date.now().toString(16)}`}/>
<input type="hidden" name="orderRef" value={state.pay?.merchant_ref}/>
<input type="hidden" name="currCode" value={state.pay?.currCode || ''}/>
<input type="hidden" name="mpsMode" value={state.pay?.mpsMode || ''}/>
<input type="hidden" name="successUrl" value={`${appUrl}/pay/success?from=asia_pay&bill=${bill.id}`}/>
<input type="hidden" name="failUrl" value={`${appUrl}/pay/fail?from=asia_pay&bill=${bill.id}`}/>
<input type="hidden" name="cancelUrl" value={`${appUrl}/pay/cancel?from=asia_pay&bill=${bill.id}`}/>
<input type="hidden" name="successUrl" value={`${appUrl}/pay/success?from=ASIAPAY&bill=${bill.id}`}/>
<input type="hidden" name="failUrl" value={`${appUrl}/pay/fail?from=ASIAPAY&bill=${bill.id}`}/>
<input type="hidden" name="cancelUrl" value={`${appUrl}/pay/cancel?from=ASIAPAY&bill=${bill.id}`}/>
<input type="hidden" name="payType" value={state.pay?.payType || ''}/>
<input type="hidden" name="lang" value={lang || ''}/>
<input type="hidden" name="payMethod" value={state.pay?.payMethod || ''}/>

View File

@ -13,11 +13,7 @@ import {updateBillPaymentSuccess} from "@/service/api/bill.ts";
type PayResult = 'fail' | 'success' | 'cancel' | 'error' | string;
function updateBillSuccess(billId: number, type: string, ref: string) {
updateBillPaymentSuccess(billId, ref, type).then(res => {
console.log(res)
})
}
const PayIndex = () => {
const {t} = useTranslation()
@ -31,6 +27,13 @@ const PayIndex = () => {
}>({});
const param = useParams<{ result: PayResult }>();
const [search] = useSearchParams(); // 参数有: from: asia_spay || flywire
const updateBillSuccess = (billId: number, type: string, ref: string) =>{
updateBillPaymentSuccess(billId, ref, type).then(bill => {
setState({bill})
}).finally(()=>{
setState({result:'fail'})
})
}
useEffect(() => {
const result = param.result,
from = search.get('from'),
@ -44,7 +47,7 @@ const PayIndex = () => {
return;
}
if (result == 'success') {
updateBillSuccess(Number(bill), from, (from == 'asia_pay' ? search.get('Ref')! : search.get('callback_id')!));
updateBillSuccess(Number(bill), from , (from == 'asia_pay' ? search.get('Ref')! : search.get('callback_id')!));
}
}
setState({result, status})

View File

@ -34,7 +34,11 @@ const routes: RouteObject[] = [
},
{
path: 'login',
element: <AuthLogin/>
element: <AuthLogin type={'login'}/>
},
{
path: 'login/auth',
element: <AuthLogin type={'auth'}/>
},
{
path: 'pay',

View File

@ -8,7 +8,7 @@ import AppLogo from "@/assets/AppLogo.tsx";
import useAuth from "@/hooks/useAuth.ts";
import {I18nSwitcher} from "@/i18n";
import {AllDashboardMenu, DashboardNavigation} from "@/routes/layout/dashboard-navigation.tsx";
import {IconExit} from "@douyinfe/semi-icons";
import {IconExit, IconUser} from "@douyinfe/semi-icons";
import styled from "@emotion/styled";
import useConfig from "@/hooks/useConfig.ts";
@ -36,12 +36,12 @@ export const HeaderUserAvatar = () => {
<>
<HeaderUserProfile>
<Space>
<Avatar color="orange" size="small">{user?.username.substring(0, 3)}</Avatar>
<Avatar color="orange" size="small"><IconUser /></Avatar>
<div>
<Typography.Title heading={6}>{user?.username}</Typography.Title>
<Typography.Text
type="quaternary"
size={'small'}>{user?.iss?.toUpperCase()}</Typography.Text>
size={'small'}>Department:{user?.department?.toUpperCase()}</Typography.Text>
</div>
</Space>
</HeaderUserProfile>
@ -53,7 +53,7 @@ export const HeaderUserAvatar = () => {
}
>
<Avatar color="orange" size="small">{user?.username.substring(0, 3)}</Avatar>
<Avatar color="orange" size="small"><IconUser /></Avatar>
</Dropdown>)
}
export const CommonHeader: React.FC<CommonHeaderProps> = ({children, title, rightExtra}) => {

View File

@ -33,7 +33,7 @@ export function DashboardNavigation() {
if (!user) return [];
return AllDashboardMenu.filter(it => {
return !it.role || it.role.includes(user.iss)
return !it.role || it.role.includes(user.department)
});
}, [user])
return (<div className={'dashboard-menu-container'}>

View File

@ -3,6 +3,7 @@ import {get, post, put} from "@/service/request.ts";
export type BillQueryResult = {
result: BillModel[];
total_count: number;
query_total_count: number;
query_total_amount: number;
}
export type BillQueryParams = Partial<BillQueryParam>;
@ -13,7 +14,7 @@ function formatBillQueryResult(params: BillQueryParams, result: BillQueryResult)
const formatData: RecordList<BillModel> = {
list: result.result,
pagination: {
total: result.total_count,
total: result.query_total_count,
pageSize: params.page_size || DEFAULT_PAGE_SIZE,
current: params.page_number || 1,
recordTotal: Number(result.query_total_amount || 0)
@ -50,6 +51,6 @@ export function confirmBills(bill_ids: number[]) {
return post(`/bills/apply`, {bill_ids})
}
export function updateBillPaymentSuccess(billId: number, merchant_ref: string,payment_channel:string) {
return post(`/bills/${billId}/finish`, {merchant_ref,payment_channel})
export function updateBillPaymentSuccess(bill_id: number, merchant_ref: string,payment_channel:string) {
return post<BillModel>(`/bills/finish`, {merchant_ref,payment_channel,bill_id})
}

View File

@ -1,4 +1,4 @@
import {get} from "@/service/request.ts";
import {get,post} from "@/service/request.ts";
export function getUserInfo() {
return get<UserProfile>('/userinfo')
@ -10,5 +10,5 @@ export function getUserInfo() {
* @param state
*/
export function auth(code:string,state:string){
return get<UserProfile>('/auth', {code, state})
return post<UserProfile>('/auth', {code, state})
}

View File

@ -35,7 +35,7 @@ export function GeneratePdf(bill: BillModel) {
title: 'Programme:',
content: bill.programme_english_name
}, 56)
drawItem(doc, {title: 'Mode of Study:', content: 'FULL-TIME'}, 70)
drawItem(doc, {title: 'Mode of Study:', content: bill.attendance_mode}, 70)
// draw table
autoTable(doc, {
startY: 80,
@ -69,7 +69,7 @@ export function GeneratePdf(bill: BillModel) {
colSpan: 4,
content: 'TOTAL:',
styles: {valign: 'middle', halign: 'right'},
}, `${bill.payment_amount}`],
}, `${bill.amount}`],
],
})
// draw foot

View File

@ -17,7 +17,7 @@ const Axios = axios.create({
Axios.interceptors.request.use(config => {
const token = getAuthToken();
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
config.headers['Token'] = `${token}`;
}
if (config.data && config.data instanceof FormData) {
config.headers['Content-Type'] = 'multipart/form-data';

3
src/types/auth.d.ts vendored
View File

@ -2,12 +2,13 @@ declare type UserProfile = {
id: string | number;
token: string;
email: string;
username: string;
department: string;
exp: number;
iat: number;
iss: string;
nbf: number;
type: string;
username: string;
}
declare interface AuthProps {

7
src/types/bill.d.ts vendored
View File

@ -22,9 +22,11 @@ declare type BillQueryParam = {
status:string;
apply_status:string;
student_number:string;
payment_method:string;
application_number:string;
payment_channel:string;
start_date:string;
end_date:string;
department:string;
}
/**
*
@ -40,6 +42,7 @@ declare type BillModel = {
student_english_name: string;
programme_chinese_name: string;
programme_english_name: string;
attendance_mode: string;
department_chinese_name: string;
department_english_name: string;
intake_year: string;
@ -70,7 +73,7 @@ declare type AsiaPayModel = {
amount: string;
payment_amount: string;
service_charge: string;
orderRef: string;
merchant_ref: string;
currCode: number;
mpsMode: string;
payType: string;

View File

@ -14,7 +14,7 @@ export default defineConfig(({mode}) => {
API_PREFIX: process.env.APP_API_PREFIX || '/api',
FIY_WIRE_GATEWAY: process.env.FIY_WIRE_GATEWAY || 'https://gateway.flywire.com/v1/transfers',
SSO_AUTH_URL: process.env.SSO_AUTH_URL || 'https://portal.chuhai.edu.hk',
SSO_AUTH_CLIENT_KEY: process.env.AUTH_CLIENT_KEY || '',
SSO_AUTH_CLIENT_KEY: process.env.AUTH_CLIENT_KEY || 'test_client_id',
AUTH_TOKEN_KEY: process.env.AUTH_TOKEN_KEY || 'payment-auth-token',
}),
AppMode: JSON.stringify(mode)
@ -28,7 +28,7 @@ export default defineConfig(({mode}) => {
port:10086,
proxy: {
'/api': {
target: 'http://43.136.175.109:50000',
target: 'http://43.136.175.109:50000', //
changeOrigin: true,
//rewrite: (path) => path.replace(/^\/api/, '')
}

View File

@ -788,6 +788,88 @@
resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz#5a2d08b81e8064b34242d5cc9973ef8dd1e60503"
integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==
"@sentry-internal/browser-utils@8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry-internal/browser-utils/-/browser-utils-8.7.0.tgz#8a75560c80c50e023db58faf055dacde670e9d78"
integrity sha512-RFBK1sYBwV5qGMEwWF0rjOTqQpp4/SvE+qHkOJNRUTVYmfjM+Y9lcxwn4B6lu3aboxePpBw/i1PlP6XwX4UnGA==
dependencies:
"@sentry/core" "8.7.0"
"@sentry/types" "8.7.0"
"@sentry/utils" "8.7.0"
"@sentry-internal/feedback@8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry-internal/feedback/-/feedback-8.7.0.tgz#fa632c70de93b9c6626951f62c732984242097d8"
integrity sha512-qcGtWCtRB4eP7NVQoxW936oPkU4qu9otMLYELPGmOJPnuAG0lujlJXW7BucaM7ADyJgJTE75hG849bHecfnbmQ==
dependencies:
"@sentry/core" "8.7.0"
"@sentry/types" "8.7.0"
"@sentry/utils" "8.7.0"
"@sentry-internal/replay-canvas@8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry-internal/replay-canvas/-/replay-canvas-8.7.0.tgz#78316619cc57b8d81cacabacdf0f5167eb805ef9"
integrity sha512-FOnvBPbq6MJVHPduc0hcsdE3PeeovQ2z5WJnZDGhvp/Obehxqe+XgX7K/595vRIknv4EokRn/3Kw0mFwG8E+ZQ==
dependencies:
"@sentry-internal/replay" "8.7.0"
"@sentry/core" "8.7.0"
"@sentry/types" "8.7.0"
"@sentry/utils" "8.7.0"
"@sentry-internal/replay@8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry-internal/replay/-/replay-8.7.0.tgz#2898303529bb2273129e2e86c861d03f69e95622"
integrity sha512-bQzOkWplaWTe3u+aDBhxWY3Qy0aT7ss2A3VR8iC6N8ZIEP9PxqyJwTNoouhinfgmlnCguI7RDOO4f3r3e2M80Q==
dependencies:
"@sentry-internal/browser-utils" "8.7.0"
"@sentry/core" "8.7.0"
"@sentry/types" "8.7.0"
"@sentry/utils" "8.7.0"
"@sentry/browser@8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry/browser/-/browser-8.7.0.tgz#78844ca196315327979ef7cfbad71788e95c9888"
integrity sha512-4EEp+PlcktsMN0p+MdCPl/lghTkq7eOtZjQG9NGhWzfyWrJ3tuL1nsDr2SSivJ1V277F01KtKYo6BFwP2NtBZA==
dependencies:
"@sentry-internal/browser-utils" "8.7.0"
"@sentry-internal/feedback" "8.7.0"
"@sentry-internal/replay" "8.7.0"
"@sentry-internal/replay-canvas" "8.7.0"
"@sentry/core" "8.7.0"
"@sentry/types" "8.7.0"
"@sentry/utils" "8.7.0"
"@sentry/core@8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry/core/-/core-8.7.0.tgz#c98bc47020cd48d899806baaebc7a4b6fe10b9ab"
integrity sha512-Sq/46B+5nWmgnCD6dEMZ6HTkKbV/KAdgaSvT8oXDb9OWoPy1jJ/gbLrhLs62KbjuDQk4/vWnOgHiKQbcslSzMw==
dependencies:
"@sentry/types" "8.7.0"
"@sentry/utils" "8.7.0"
"@sentry/react@^8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry/react/-/react-8.7.0.tgz#fc3f4303abb2ccd1e23f75f9f5bb6e648e095fa0"
integrity sha512-JFo8QW8JB4eaFC8RdkOBO96JvlGgstywmyMZ39qWfFbD735vGl8PnOa0AnrC/5Auc86dZ98/I4OEPboqUE9q1w==
dependencies:
"@sentry/browser" "8.7.0"
"@sentry/core" "8.7.0"
"@sentry/types" "8.7.0"
"@sentry/utils" "8.7.0"
hoist-non-react-statics "^3.3.2"
"@sentry/types@8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry/types/-/types-8.7.0.tgz#92731af32318d6abb8759216cf6c3c5035894e6e"
integrity sha512-11KLOKumP6akugVGLvSoEig+JlP0ZEzW3nN9P+ppgdIx9HAxMIh6UvumbieG4/DWjAh2kh6NPNfUw3gk2Gfq1A==
"@sentry/utils@8.7.0":
version "8.7.0"
resolved "https://registry.npmmirror.com/@sentry/utils/-/utils-8.7.0.tgz#26893acc5bca9bfd4998d2eafe724491e3ca78a2"
integrity sha512-aWmcbSoOmrbzll/FkNQFJcCtLAuJLvTYbRKiCSkV3FScA7UaA742HkTZAPFiioALFIESWk/fcGZqtN0s4I281Q==
dependencies:
"@sentry/types" "8.7.0"
"@types/babel__core@^7.20.5":
version "7.20.5"
resolved "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
@ -1763,7 +1845,7 @@ hasown@^2.0.0:
dependencies:
function-bind "^1.1.2"
hoist-non-react-statics@^3.3.1:
hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==