Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29bc3c7722 | |||
| b8316bba33 | |||
| dddc21c686 | |||
| d10ecc9fdf | |||
| 94822d902f |
+38
-24
@@ -1,41 +1,55 @@
|
||||
<a name="readme-top"></a>
|
||||
# Changelog
|
||||
|
||||
# [1.0.0-beta.27](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.26...v1.0.0-beta.27) (2025-03-13)
|
||||
|
||||
|
||||
### ♻ Code Refactoring
|
||||
|
||||
* **payment**: Reconstruct the payment page ([7109472](https://github.com/perfect-panel/ppanel-web/commit/7109472))
|
||||
* Enhance user navigation dropdown ui and styling ([d2732e6](https://github.com/perfect-panel/ppanel-web/commit/d2732e6))
|
||||
# [1.0.0-beta.29](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.28...v1.0.0-beta.29) (2025-03-14)
|
||||
|
||||
|
||||
### ✨ Features
|
||||
|
||||
* **cdn**: Add CDN URL configuration and update related references ([0c90733](https://github.com/perfect-panel/ppanel-web/commit/0c90733))
|
||||
* **payment**: Add bank card payment ([7fa3a57](https://github.com/perfect-panel/ppanel-web/commit/7fa3a57))
|
||||
* **subscription**: Improve layout and organization of subscription detail tabs ([e4630f8](https://github.com/perfect-panel/ppanel-web/commit/e4630f8))
|
||||
* **subscription**: Refactor subscription handling and update imports for better organization ([2215c7f](https://github.com/perfect-panel/ppanel-web/commit/2215c7f))
|
||||
* **api**: Add CheckoutOrder request and response types, and update user purchase request parameters ([dddc21c](https://github.com/perfect-panel/ppanel-web/commit/dddc21c))
|
||||
* **loading**: Replace loading animation with a simpler spinner and loading text ([b8316bb](https://github.com/perfect-panel/ppanel-web/commit/b8316bb))
|
||||
|
||||
<a name="readme-top"></a>
|
||||
|
||||
# Changelog
|
||||
|
||||
# [1.0.0-beta.28](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2025-03-13)
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- **input**: Add minimum value constraint and enhance number handling in EnhancedInput ([94822d9](https://github.com/perfect-panel/ppanel-web/commit/94822d9))
|
||||
|
||||
# [1.0.0-beta.27](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.26...v1.0.0-beta.27) (2025-03-13)
|
||||
|
||||
### ♻ Code Refactoring
|
||||
|
||||
- **payment**: Reconstruct the payment page ([7109472](https://github.com/perfect-panel/ppanel-web/commit/7109472))
|
||||
- Enhance user navigation dropdown ui and styling ([d2732e6](https://github.com/perfect-panel/ppanel-web/commit/d2732e6))
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- **cdn**: Add CDN URL configuration and update related references ([0c90733](https://github.com/perfect-panel/ppanel-web/commit/0c90733))
|
||||
- **payment**: Add bank card payment ([7fa3a57](https://github.com/perfect-panel/ppanel-web/commit/7fa3a57))
|
||||
- **subscription**: Improve layout and organization of subscription detail tabs ([e4630f8](https://github.com/perfect-panel/ppanel-web/commit/e4630f8))
|
||||
- **subscription**: Refactor subscription handling and update imports for better organization ([2215c7f](https://github.com/perfect-panel/ppanel-web/commit/2215c7f))
|
||||
|
||||
### 🎫 Chores
|
||||
|
||||
* **merge**: Bump version to 1.0.0-beta.26 and update changelog ([3222016](https://github.com/perfect-panel/ppanel-web/commit/3222016))
|
||||
|
||||
- **merge**: Bump version to 1.0.0-beta.26 and update changelog ([3222016](https://github.com/perfect-panel/ppanel-web/commit/3222016))
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
* **affiliate**: Update user identifier ([35f92c9](https://github.com/perfect-panel/ppanel-web/commit/35f92c9))
|
||||
* **changelog**: Update change log style ([cfa3fc0](https://github.com/perfect-panel/ppanel-web/commit/cfa3fc0))
|
||||
* **payment**: Add notification URL field to payment management interface ([5c710e1](https://github.com/perfect-panel/ppanel-web/commit/5c710e1))
|
||||
* **payment**: Fix payment related type definitions and update payment method references ([c3138a8](https://github.com/perfect-panel/ppanel-web/commit/c3138a8))
|
||||
* **payment**: Refactor purchaseCheckout usage and remove redundant code ([a5e2079](https://github.com/perfect-panel/ppanel-web/commit/a5e2079))
|
||||
* **payment**: Update checkout type from 'link' to 'url' for consistency ([136a1ab](https://github.com/perfect-panel/ppanel-web/commit/136a1ab))
|
||||
* **payment**: Update payment information ([70d6a38](https://github.com/perfect-panel/ppanel-web/commit/70d6a38))
|
||||
* **payment**: Update payment method update logic to include row data ([6752420](https://github.com/perfect-panel/ppanel-web/commit/6752420))
|
||||
* **purchasing**: Update payment type to lowercase and add optional chaining for discounts ([c06ea49](https://github.com/perfect-panel/ppanel-web/commit/c06ea49))
|
||||
* **ui**: Improve dashboard layout and enhance button functionality; open checkout URLs in a new tab ([fc0da76](https://github.com/perfect-panel/ppanel-web/commit/fc0da76))
|
||||
* **ui**: Multiple display bugs ([f5d8fd3](https://github.com/perfect-panel/ppanel-web/commit/f5d8fd3))
|
||||
- **affiliate**: Update user identifier ([35f92c9](https://github.com/perfect-panel/ppanel-web/commit/35f92c9))
|
||||
- **changelog**: Update change log style ([cfa3fc0](https://github.com/perfect-panel/ppanel-web/commit/cfa3fc0))
|
||||
- **payment**: Add notification URL field to payment management interface ([5c710e1](https://github.com/perfect-panel/ppanel-web/commit/5c710e1))
|
||||
- **payment**: Fix payment related type definitions and update payment method references ([c3138a8](https://github.com/perfect-panel/ppanel-web/commit/c3138a8))
|
||||
- **payment**: Refactor purchaseCheckout usage and remove redundant code ([a5e2079](https://github.com/perfect-panel/ppanel-web/commit/a5e2079))
|
||||
- **payment**: Update checkout type from 'link' to 'url' for consistency ([136a1ab](https://github.com/perfect-panel/ppanel-web/commit/136a1ab))
|
||||
- **payment**: Update payment information ([70d6a38](https://github.com/perfect-panel/ppanel-web/commit/70d6a38))
|
||||
- **payment**: Update payment method update logic to include row data ([6752420](https://github.com/perfect-panel/ppanel-web/commit/6752420))
|
||||
- **purchasing**: Update payment type to lowercase and add optional chaining for discounts ([c06ea49](https://github.com/perfect-panel/ppanel-web/commit/c06ea49))
|
||||
- **ui**: Improve dashboard layout and enhance button functionality; open checkout URLs in a new tab ([fc0da76](https://github.com/perfect-panel/ppanel-web/commit/fc0da76))
|
||||
- **ui**: Multiple display bugs ([f5d8fd3](https://github.com/perfect-panel/ppanel-web/commit/f5d8fd3))
|
||||
|
||||
<a name="readme-top"></a>
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='number'
|
||||
min={0}
|
||||
value={field.value}
|
||||
prefix={currency?.currency_symbol ?? '$'}
|
||||
formatInput={(value) => unitConversion('centsToDollars', value)}
|
||||
@@ -136,6 +137,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='number'
|
||||
min={0}
|
||||
value={field.value}
|
||||
prefix={currency?.currency_symbol ?? '$'}
|
||||
formatInput={(value) => unitConversion('centsToDollars', value)}
|
||||
@@ -159,6 +161,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='number'
|
||||
min={0}
|
||||
value={field.value}
|
||||
prefix={currency?.currency_symbol ?? '$'}
|
||||
formatInput={(value) => unitConversion('centsToDollars', value)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
|
||||
|
||||
// API 更新时间:
|
||||
// API 唯一标识:
|
||||
import * as ads from './ads';
|
||||
|
||||
+16
@@ -116,6 +116,17 @@ declare namespace API {
|
||||
ids: number[];
|
||||
};
|
||||
|
||||
type CheckoutOrderRequest = {
|
||||
orderNo: string;
|
||||
returnUrl?: string;
|
||||
};
|
||||
|
||||
type CheckoutOrderResponse = {
|
||||
type: string;
|
||||
checkout_url?: string;
|
||||
stripe?: StripePayment;
|
||||
};
|
||||
|
||||
type CloseOrderRequest = {
|
||||
orderNo: string;
|
||||
};
|
||||
@@ -1097,6 +1108,11 @@ declare namespace API {
|
||||
total: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListResponse = {
|
||||
list: UserAffiliate[];
|
||||
total: number;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
|
||||
|
||||
// API 更新时间:
|
||||
// API 唯一标识:
|
||||
import * as auth from './auth';
|
||||
|
||||
+16
@@ -94,6 +94,17 @@ declare namespace API {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
type CheckoutOrderRequest = {
|
||||
orderNo: string;
|
||||
returnUrl?: string;
|
||||
};
|
||||
|
||||
type CheckoutOrderResponse = {
|
||||
type: string;
|
||||
checkout_url?: string;
|
||||
stripe?: StripePayment;
|
||||
};
|
||||
|
||||
type CheckUserParams = {
|
||||
email: string;
|
||||
};
|
||||
@@ -496,6 +507,11 @@ declare namespace API {
|
||||
total: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListResponse = {
|
||||
list: UserAffiliate[];
|
||||
total: number;
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function Content({ subscription }: { subscription?: API.Subscribe
|
||||
subscribe_id: 0,
|
||||
payment: -1,
|
||||
coupon: '',
|
||||
platform: 'email',
|
||||
auth_type: 'email',
|
||||
identifier: '',
|
||||
password: '',
|
||||
});
|
||||
@@ -70,12 +70,15 @@ export default function Content({ subscription }: { subscription?: API.Subscribe
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const { data } = await purchase(params);
|
||||
console.log(data);
|
||||
const { order_no, check_url, type } = data.data!;
|
||||
const { order_no } = data.data!;
|
||||
if (order_no) {
|
||||
if (type === 'link') {
|
||||
window.location.href = check_url!;
|
||||
}
|
||||
localStorage.setItem(
|
||||
order_no,
|
||||
JSON.stringify({
|
||||
auth_type: params.auth_type,
|
||||
identifier: params.identifier,
|
||||
}),
|
||||
);
|
||||
router.push(`/purchasing/order?order_no=${order_no}`);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -37,7 +37,10 @@ export default function Page() {
|
||||
enabled: enabled,
|
||||
queryKey: ['queryPurchaseOrder', orderNo],
|
||||
queryFn: async () => {
|
||||
const { data } = await queryPurchaseOrder({ order_no: orderNo! });
|
||||
if (!orderNo) return;
|
||||
const params = localStorage.getItem(orderNo);
|
||||
const authParams = params ? JSON.parse(params) : {};
|
||||
const { data } = await queryPurchaseOrder({ order_no: orderNo!, ...authParams });
|
||||
if (data?.data?.status !== 1) {
|
||||
setEnabled(false);
|
||||
if (data?.data?.token) {
|
||||
|
||||
@@ -11,21 +11,10 @@ export default function Loading({ loading = true }: LoadingProps) {
|
||||
: 'pointer-events-none bg-transparent opacity-0 backdrop-blur-0'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`transform-gpu transition-all duration-500 ${
|
||||
loading ? 'scale-100 opacity-100' : 'scale-50 opacity-0'
|
||||
}`}
|
||||
>
|
||||
<div className='relative h-32 w-32 animate-[spin_3s_linear_infinite] overflow-hidden rounded-full shadow-[0_0_15px_rgba(0,0,0,0.1)] dark:shadow-[0_0_15px_rgba(255,255,255,0.1)]'>
|
||||
<div className='absolute inset-0 h-full w-full rounded-full bg-black shadow-inner dark:bg-white'>
|
||||
<div className='absolute right-0 top-0 h-full w-1/2 rounded-r-full bg-white shadow-sm dark:bg-black' />
|
||||
<div className='absolute left-1/2 top-0 h-1/2 w-1/2 -translate-x-1/2 rounded-full bg-white shadow-sm dark:bg-black'>
|
||||
<div className='absolute left-1/2 top-1/2 h-1/4 w-1/4 -translate-x-1/2 -translate-y-1/2 rounded-full bg-black shadow-inner dark:bg-white' />
|
||||
</div>
|
||||
<div className='absolute bottom-0 left-1/2 h-1/2 w-1/2 -translate-x-1/2 rounded-full bg-black shadow-sm dark:bg-white'>
|
||||
<div className='absolute left-1/2 top-1/2 h-1/4 w-1/4 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white shadow-inner dark:bg-black' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex min-h-screen items-center justify-center'>
|
||||
<div className='text-center'>
|
||||
<div className='border-primary mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2 border-t-2'></div>
|
||||
<p className='text-muted-foreground text-lg'>Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,10 +22,11 @@ const PaymentMethods: React.FC<PaymentMethodsProps> = ({ value, onChange, balanc
|
||||
queryKey: ['getAvailablePaymentMethods', { balance }],
|
||||
queryFn: async () => {
|
||||
const { data } = await getAvailablePaymentMethods();
|
||||
const methods = data.data?.list || [];
|
||||
if (!value && methods[0]?.id) onChange(methods[0]?.id);
|
||||
if (balance) return methods;
|
||||
return methods.filter((item) => item.id !== -1);
|
||||
const list = data.data?.list || [];
|
||||
const methods = balance ? list : list.filter((item) => item.id !== -1);
|
||||
const defaultMethod = methods.find((item) => item.id)?.id;
|
||||
if (defaultMethod) onChange(defaultMethod);
|
||||
return methods;
|
||||
},
|
||||
});
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
|
||||
|
||||
// API 更新时间:
|
||||
// API 唯一标识:
|
||||
import * as auth from './auth';
|
||||
|
||||
+16
@@ -94,6 +94,17 @@ declare namespace API {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
type CheckoutOrderRequest = {
|
||||
orderNo: string;
|
||||
returnUrl?: string;
|
||||
};
|
||||
|
||||
type CheckoutOrderResponse = {
|
||||
type: string;
|
||||
checkout_url?: string;
|
||||
stripe?: StripePayment;
|
||||
};
|
||||
|
||||
type CheckUserParams = {
|
||||
email: string;
|
||||
};
|
||||
@@ -496,6 +507,11 @@ declare namespace API {
|
||||
total: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListResponse = {
|
||||
list: UserAffiliate[];
|
||||
total: number;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
|
||||
|
||||
// API 更新时间:
|
||||
// API 唯一标识:
|
||||
import * as announcement from './announcement';
|
||||
|
||||
Vendored
+5
-1
@@ -438,8 +438,8 @@ declare namespace API {
|
||||
};
|
||||
|
||||
type PortalPurchaseRequest = {
|
||||
auth_type: string;
|
||||
identifier: string;
|
||||
platform: string;
|
||||
password?: string;
|
||||
payment: number;
|
||||
subscribe_id: number;
|
||||
@@ -572,10 +572,14 @@ declare namespace API {
|
||||
};
|
||||
|
||||
type QueryPurchaseOrderParams = {
|
||||
auth_type: string;
|
||||
identifier: string;
|
||||
order_no: string;
|
||||
};
|
||||
|
||||
type QueryPurchaseOrderRequest = {
|
||||
auth_type: string;
|
||||
identifier: string;
|
||||
order_no: string;
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ppanel-web",
|
||||
"version": "1.0.0-beta.27",
|
||||
"version": "1.0.0-beta.29",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/perfect-panel/ppanel-web",
|
||||
"bugs": {
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface EnhancedInputProps
|
||||
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
|
||||
prefix?: string | ReactNode;
|
||||
suffix?: string | ReactNode;
|
||||
formatInput?: (value: string | number) => string;
|
||||
formatInput?: (value: string | number) => string | number;
|
||||
formatOutput?: (value: string | number) => string | number;
|
||||
onValueChange?: (value: string | number) => void;
|
||||
onValueBlur?: (value: string | number) => void;
|
||||
@@ -26,50 +26,94 @@ export function EnhancedInput({
|
||||
...props
|
||||
}: EnhancedInputProps) {
|
||||
const getProcessedValue = (inputValue: unknown) => {
|
||||
const newValue = inputValue === '' || inputValue === 0 ? '' : String(inputValue ?? '');
|
||||
if (inputValue === '' || inputValue === 0 || inputValue === '0') return '';
|
||||
const newValue = String(inputValue ?? '');
|
||||
return formatInput ? formatInput(newValue) : newValue;
|
||||
};
|
||||
|
||||
const [value, setValue] = useState<string>(() => getProcessedValue(initialValue));
|
||||
const [value, setValue] = useState<string | number>(() => getProcessedValue(initialValue));
|
||||
// @ts-expect-error - This is a controlled component
|
||||
const [internalValue, setInternalValue] = useState<string | number>(initialValue ?? '');
|
||||
|
||||
useEffect(() => {
|
||||
if (initialValue !== value) {
|
||||
if (initialValue !== internalValue) {
|
||||
const newValue = getProcessedValue(initialValue);
|
||||
if (value !== newValue) setValue(newValue);
|
||||
if (value !== newValue) {
|
||||
setValue(newValue);
|
||||
// @ts-expect-error - This is a controlled component
|
||||
setInternalValue(initialValue ?? '');
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [initialValue, formatInput]);
|
||||
|
||||
const processValue = (inputValue: string) => {
|
||||
const processValue = (inputValue: string | number) => {
|
||||
let processedValue: number | string = inputValue?.toString().trim();
|
||||
|
||||
if (processedValue === '0' && props.type === 'number') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (processedValue && props.type === 'number') processedValue = Number(processedValue);
|
||||
return formatOutput ? formatOutput(processedValue) : processedValue;
|
||||
};
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
let inputValue = e.target.value;
|
||||
if (props.type === 'number' && inputValue) {
|
||||
const numericValue = Number(inputValue);
|
||||
if (!isNaN(numericValue)) {
|
||||
const min = Number.isFinite(props.min) ? props.min : -Infinity;
|
||||
const max = Number.isFinite(props.max) ? props.max : Infinity;
|
||||
inputValue = String(Math.max(min!, Math.min(max!, numericValue)));
|
||||
|
||||
if (props.type === 'number') {
|
||||
if (inputValue === '0') {
|
||||
setValue('');
|
||||
setInternalValue(0);
|
||||
onValueChange?.(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (/^-?\d*\.?\d*$/.test(inputValue) || inputValue === '-' || inputValue === '.') {
|
||||
const numericValue = Number(inputValue);
|
||||
if (!isNaN(numericValue) && inputValue !== '-' && inputValue !== '.') {
|
||||
const min = Number.isFinite(props.min) ? props.min : -Infinity;
|
||||
const max = Number.isFinite(props.max) ? props.max : Infinity;
|
||||
const constrainedValue = Math.max(min!, Math.min(max!, numericValue));
|
||||
inputValue = String(constrainedValue);
|
||||
setInternalValue(constrainedValue);
|
||||
} else {
|
||||
setInternalValue(inputValue);
|
||||
}
|
||||
setValue(inputValue === '0' ? '' : inputValue);
|
||||
}
|
||||
setValue(inputValue === '0' ? '' : inputValue);
|
||||
} else {
|
||||
setValue(inputValue);
|
||||
setInternalValue(inputValue);
|
||||
}
|
||||
|
||||
const outputValue = processValue(inputValue);
|
||||
console.log();
|
||||
onValueChange?.(outputValue);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
if (props.type === 'number' && value) {
|
||||
if (value === '-' || value === '.') {
|
||||
setValue('');
|
||||
setInternalValue('');
|
||||
onValueBlur?.('');
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保0值显示为空
|
||||
if (value === '0') {
|
||||
setValue('');
|
||||
onValueBlur?.(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const outputValue = processValue(value);
|
||||
if ((initialValue || '') !== outputValue) {
|
||||
onValueBlur?.(outputValue);
|
||||
}
|
||||
};
|
||||
|
||||
const renderPrefix = () => {
|
||||
return typeof prefix === 'string' ? (
|
||||
<div className='bg-muted relative mr-px flex h-9 items-center text-nowrap px-3'>{prefix}</div>
|
||||
@@ -77,6 +121,7 @@ export function EnhancedInput({
|
||||
prefix
|
||||
);
|
||||
};
|
||||
|
||||
const renderSuffix = () => {
|
||||
return typeof suffix === 'string' ? (
|
||||
<div className='bg-muted relative ml-px flex h-9 items-center text-nowrap px-3'>{suffix}</div>
|
||||
@@ -95,6 +140,7 @@ export function EnhancedInput({
|
||||
>
|
||||
{renderPrefix()}
|
||||
<Input
|
||||
step={0.01}
|
||||
{...props}
|
||||
value={value}
|
||||
className='block rounded-none border-none'
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
import { evaluate, format } from 'mathjs';
|
||||
|
||||
export function unitConversion(
|
||||
type: 'centsToDollars' | 'dollarsToCents' | 'bitsToMb' | 'mbToBits' | 'bytesToGb' | 'gbToBytes',
|
||||
value?: number | string,
|
||||
) {
|
||||
if (!value) return;
|
||||
switch (type) {
|
||||
case 'centsToDollars':
|
||||
return evaluate(`${value} / 100`);
|
||||
case 'dollarsToCents':
|
||||
return evaluate(`${value} * 100`);
|
||||
case 'bitsToMb':
|
||||
return evaluate(`${value} / 1024 / 1024`);
|
||||
case 'mbToBits':
|
||||
return evaluate(`${value} * 1024 * 1024`);
|
||||
case 'bytesToGb':
|
||||
return evaluate(`${value} / 1024 / 1024 / 1024`);
|
||||
case 'gbToBytes':
|
||||
return evaluate(`${value} * 1024 * 1024 * 1024`);
|
||||
default:
|
||||
throw new Error('Invalid conversion type');
|
||||
}
|
||||
type ConversionType =
|
||||
| 'centsToDollars'
|
||||
| 'dollarsToCents'
|
||||
| 'bitsToMb'
|
||||
| 'mbToBits'
|
||||
| 'bytesToGb'
|
||||
| 'gbToBytes';
|
||||
|
||||
const conversionConfig: Record<ConversionType, { formula: string; precision: number }> = {
|
||||
centsToDollars: { formula: 'value / 100', precision: 2 },
|
||||
dollarsToCents: { formula: 'value * 100', precision: 0 },
|
||||
bitsToMb: { formula: 'value / 1024 / 1024', precision: 2 },
|
||||
mbToBits: { formula: 'value * 1024 * 1024', precision: 0 },
|
||||
bytesToGb: { formula: 'value / 1024 / 1024 / 1024', precision: 2 },
|
||||
gbToBytes: { formula: 'value * 1024 * 1024 * 1024', precision: 0 },
|
||||
};
|
||||
|
||||
export function unitConversion(type: ConversionType, value?: number | string) {
|
||||
if (!value) return 0;
|
||||
|
||||
const config = conversionConfig[type];
|
||||
if (!config) throw new Error('Invalid conversion type');
|
||||
|
||||
const formula = config.formula.replace('value', `${value}`);
|
||||
const result = evaluate(formula);
|
||||
return Number(format(result, { notation: 'fixed', precision: config.precision }));
|
||||
}
|
||||
|
||||
export function evaluateWithPrecision(expression: string) {
|
||||
const result = evaluate(expression);
|
||||
|
||||
const formatted = format(result, { notation: 'fixed', precision: 2 });
|
||||
|
||||
return Number(formatted);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user