Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa1d42651d | |||
| 0c8f0911c7 | |||
| db0d9e003e | |||
| f219c52306 | |||
| 3f7b6d16ed | |||
| 6dbebd152d | |||
| bb3bd7b50c | |||
| fbad3b0e51 | |||
| e551232564 | |||
| bf58f25072 | |||
| 85f55def2e | |||
| 29bc3c7722 | |||
| f72df3a5e8 | |||
| 48a1b97051 | |||
| 786ba0e41c | |||
| ce3197298d | |||
| 092477b2d3 | |||
| b8316bba33 | |||
| dddc21c686 | |||
| d10ecc9fdf | |||
| 94822d902f |
+124
-31
@@ -1,41 +1,134 @@
|
|||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
# Changelog
|
# 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)
|
# [1.0.0-beta.31](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.30...v1.0.0-beta.31) (2025-03-15)
|
||||||
|
|
||||||
|
|
||||||
### ♻ 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))
|
|
||||||
|
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
* **affiliate**: Update user identifier ([35f92c9](https://github.com/perfect-panel/ppanel-web/commit/35f92c9))
|
* **site**: Se ref to store site configuration for updates ([0c8f091](https://github.com/perfect-panel/ppanel-web/commit/0c8f091))
|
||||||
* **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))
|
# [1.0.0-beta.30](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.29...v1.0.0-beta.30) (2025-03-15)
|
||||||
* **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))
|
### ✨ Features
|
||||||
* **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))
|
* **api**: Add CheckoutOrder request and response types, and update user purchase request parameters ([48a1b97](https://github.com/perfect-panel/ppanel-web/commit/48a1b97))
|
||||||
* **purchasing**: Update payment type to lowercase and add optional chaining for discounts ([c06ea49](https://github.com/perfect-panel/ppanel-web/commit/c06ea49))
|
* **email**: Add traffic exhaustion template ([bb3bd7b](https://github.com/perfect-panel/ppanel-web/commit/bb3bd7b))
|
||||||
* **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))
|
* **formatting**: Update differenceInDays function to return whole days or two decimal places ([bf58f25](https://github.com/perfect-panel/ppanel-web/commit/bf58f25))
|
||||||
* **ui**: Multiple display bugs ([f5d8fd3](https://github.com/perfect-panel/ppanel-web/commit/f5d8fd3))
|
* **global**: Add custom data ([6dbebd1](https://github.com/perfect-panel/ppanel-web/commit/6dbebd1))
|
||||||
|
* **input**: Add minimum value constraint and enhance number handling in EnhancedInput ([ce31972](https://github.com/perfect-panel/ppanel-web/commit/ce31972))
|
||||||
|
* **loading**: Replace loading animation with a simpler spinner and loading text ([f72df3a](https://github.com/perfect-panel/ppanel-web/commit/f72df3a))
|
||||||
|
* **node-form**: Update number input fields to enforce step, min, and max values ([3f7b6d1](https://github.com/perfect-panel/ppanel-web/commit/3f7b6d1))
|
||||||
|
* **payment**: Add isEdit prop to PaymentForm and disable fields when editing ([85f55de](https://github.com/perfect-panel/ppanel-web/commit/85f55de))
|
||||||
|
* **timeline**: Simplify timeline component layout and remove commented-out code ([fbad3b0](https://github.com/perfect-panel/ppanel-web/commit/fbad3b0))
|
||||||
|
|
||||||
|
|
||||||
|
### 🎫 Chores
|
||||||
|
|
||||||
|
* **release**: V1.0.0-beta.27 [skip ci] ([092477b](https://github.com/perfect-panel/ppanel-web/commit/092477b))
|
||||||
|
* **release**: V1.0.0-beta.28 [skip ci] ([786ba0e](https://github.com/perfect-panel/ppanel-web/commit/786ba0e))
|
||||||
|
* Merge branch 'beta' into develop ([f219c52](https://github.com/perfect-panel/ppanel-web/commit/f219c52))
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
* **dashboard**: Update date display to use start_time if available ([e551232](https://github.com/perfect-panel/ppanel-web/commit/e551232))
|
||||||
|
|
||||||
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
# [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
|
||||||
|
|
||||||
|
- **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))
|
||||||
|
|
||||||
|
### 🐛 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))
|
||||||
|
|
||||||
|
<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))
|
||||||
|
|
||||||
|
### 🐛 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))
|
||||||
|
|
||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
|
|||||||
@@ -286,31 +286,34 @@ export default function Page() {
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value='template'>
|
<TabsContent value='template'>
|
||||||
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3'>
|
<div className='grid grid-cols-1 gap-4 md:grid-cols-2'>
|
||||||
{['verify_email_template', 'expiration_email_template', 'maintenance_email_template'].map(
|
{[
|
||||||
(templateKey) => (
|
'verify_email_template',
|
||||||
<Card key={templateKey}>
|
'expiration_email_template',
|
||||||
<CardHeader>
|
'maintenance_email_template',
|
||||||
<CardTitle>{t(`${templateKey}`)}</CardTitle>
|
'traffic_exceed_email_template',
|
||||||
<CardDescription>
|
].map((templateKey) => (
|
||||||
{t(`${templateKey}Description`, { after: '{{', before: '}}' })}
|
<Card key={templateKey}>
|
||||||
</CardDescription>
|
<CardHeader>
|
||||||
</CardHeader>
|
<CardTitle>{t(`${templateKey}`)}</CardTitle>
|
||||||
<CardContent>
|
<CardDescription>
|
||||||
<HTMLEditor
|
{t(`${templateKey}Description`, { after: '{{', before: '}}' })}
|
||||||
placeholder={t('inputPlaceholder')}
|
</CardDescription>
|
||||||
value={data?.config?.[templateKey] as string}
|
</CardHeader>
|
||||||
onBlur={(value) =>
|
<CardContent>
|
||||||
updateConfig('config', {
|
<HTMLEditor
|
||||||
...data?.config,
|
placeholder={t('inputPlaceholder')}
|
||||||
[templateKey]: value,
|
value={data?.config?.[templateKey] as string}
|
||||||
})
|
onBlur={(value) =>
|
||||||
}
|
updateConfig('config', {
|
||||||
/>
|
...data?.config,
|
||||||
</CardContent>
|
[templateKey]: value,
|
||||||
</Card>
|
})
|
||||||
),
|
}
|
||||||
)}
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ interface PaymentFormProps<T> {
|
|||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
initialValues?: T;
|
initialValues?: T;
|
||||||
onSubmit: (values: T) => Promise<boolean>;
|
onSubmit: (values: T) => Promise<boolean>;
|
||||||
|
isEdit?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PaymentForm<T>({
|
export default function PaymentForm<T>({
|
||||||
@@ -53,6 +54,7 @@ export default function PaymentForm<T>({
|
|||||||
loading,
|
loading,
|
||||||
initialValues,
|
initialValues,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
isEdit,
|
||||||
}: PaymentFormProps<T>) {
|
}: PaymentFormProps<T>) {
|
||||||
const t = useTranslations('payment');
|
const t = useTranslations('payment');
|
||||||
const { common } = useGlobalStore();
|
const { common } = useGlobalStore();
|
||||||
@@ -317,6 +319,7 @@ export default function PaymentForm<T>({
|
|||||||
}}
|
}}
|
||||||
defaultValue={field.value}
|
defaultValue={field.value}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
|
disabled={isEdit}
|
||||||
>
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ export default function PaymentTable() {
|
|||||||
actions={{
|
actions={{
|
||||||
render: (row) => [
|
render: (row) => [
|
||||||
<PaymentForm<API.UpdatePaymentMethodRequest>
|
<PaymentForm<API.UpdatePaymentMethodRequest>
|
||||||
|
isEdit
|
||||||
key='edit'
|
key='edit'
|
||||||
trigger={<Button>{t('edit')}</Button>}
|
trigger={<Button>{t('edit')}</Button>}
|
||||||
title={t('editPayment')}
|
title={t('editPayment')}
|
||||||
|
|||||||
@@ -362,9 +362,10 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
|||||||
<EnhancedInput
|
<EnhancedInput
|
||||||
{...field}
|
{...field}
|
||||||
type='number'
|
type='number'
|
||||||
placeholder='1-65535'
|
step={1}
|
||||||
min={1}
|
min={1}
|
||||||
max={65535}
|
max={65535}
|
||||||
|
placeholder='1-65535'
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
form.setValue(field.name, value);
|
form.setValue(field.name, value);
|
||||||
}}
|
}}
|
||||||
@@ -418,6 +419,9 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
|||||||
<EnhancedInput
|
<EnhancedInput
|
||||||
{...field}
|
{...field}
|
||||||
type='number'
|
type='number'
|
||||||
|
step={1}
|
||||||
|
min={1}
|
||||||
|
max={65535}
|
||||||
placeholder='1-65535'
|
placeholder='1-65535'
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
form.setValue(field.name, value);
|
form.setValue(field.name, value);
|
||||||
@@ -899,6 +903,7 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
|||||||
{
|
{
|
||||||
name: 'port',
|
name: 'port',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
step: 1,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 65535,
|
max: 65535,
|
||||||
placeholder: t('form.relayPort'),
|
placeholder: t('form.relayPort'),
|
||||||
|
|||||||
@@ -5,18 +5,21 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { Label } from '@workspace/ui/components/label';
|
import { Label } from '@workspace/ui/components/label';
|
||||||
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
|
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
|
||||||
import { Textarea } from '@workspace/ui/components/textarea';
|
import { Textarea } from '@workspace/ui/components/textarea';
|
||||||
|
import { JSONEditor } from '@workspace/ui/custom-components/editor';
|
||||||
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
|
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
|
||||||
import { UploadImage } from '@workspace/ui/custom-components/upload-image';
|
import { UploadImage } from '@workspace/ui/custom-components/upload-image';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { useRef } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
export default function Site() {
|
export default function Site() {
|
||||||
const t = useTranslations('system.site');
|
const t = useTranslations('system.site');
|
||||||
|
const ref = useRef<API.SiteConfig | undefined>(undefined);
|
||||||
const { data, refetch } = useQuery({
|
const { data, refetch } = useQuery({
|
||||||
queryKey: ['getSiteConfig'],
|
queryKey: ['getSiteConfig'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await getSiteConfig();
|
const { data } = await getSiteConfig();
|
||||||
|
ref.current = data.data;
|
||||||
return data.data;
|
return data.data;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -25,7 +28,7 @@ export default function Site() {
|
|||||||
if (data?.[key] === value) return;
|
if (data?.[key] === value) return;
|
||||||
try {
|
try {
|
||||||
await updateSiteConfig({
|
await updateSiteConfig({
|
||||||
...data,
|
...ref.current,
|
||||||
[key]: value,
|
[key]: value,
|
||||||
} as API.SiteConfig);
|
} as API.SiteConfig);
|
||||||
toast.success(t('saveSuccess'));
|
toast.success(t('saveSuccess'));
|
||||||
@@ -130,6 +133,49 @@ export default function Site() {
|
|||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<Label>{t('customData')}</Label>
|
||||||
|
<p className='text-muted-foreground text-xs'>{t('customDataDescription')}</p>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className='w-1/2 text-right'>
|
||||||
|
<JSONEditor
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: true,
|
||||||
|
properties: {
|
||||||
|
website: { type: 'string', title: 'Website' },
|
||||||
|
contacts: {
|
||||||
|
type: 'object',
|
||||||
|
title: 'Contacts',
|
||||||
|
additionalProperties: true,
|
||||||
|
properties: {
|
||||||
|
email: { type: 'string', title: 'Email' },
|
||||||
|
telephone: { type: 'string', title: 'Telephone' },
|
||||||
|
address: { type: 'string', title: 'Address' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
community: {
|
||||||
|
type: 'object',
|
||||||
|
title: 'Community',
|
||||||
|
additionalProperties: true,
|
||||||
|
properties: {
|
||||||
|
telegram: { type: 'string', title: 'Telegram' },
|
||||||
|
twitter: { type: 'string', title: 'Twitter' },
|
||||||
|
discord: { type: 'string', title: 'Discord' },
|
||||||
|
instagram: { type: 'string', title: 'Instagram' },
|
||||||
|
linkedin: { type: 'string', title: 'Linkedin' },
|
||||||
|
facebook: { type: 'string', title: 'Facebook' },
|
||||||
|
github: { type: 'string', title: 'Github' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
value={data?.custom_data}
|
||||||
|
onBlur={(value) => updateConfig('custom_data', value)}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<EnhancedInput
|
<EnhancedInput
|
||||||
type='number'
|
type='number'
|
||||||
|
min={0}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
prefix={currency?.currency_symbol ?? '$'}
|
prefix={currency?.currency_symbol ?? '$'}
|
||||||
formatInput={(value) => unitConversion('centsToDollars', value)}
|
formatInput={(value) => unitConversion('centsToDollars', value)}
|
||||||
@@ -136,6 +137,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<EnhancedInput
|
<EnhancedInput
|
||||||
type='number'
|
type='number'
|
||||||
|
min={0}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
prefix={currency?.currency_symbol ?? '$'}
|
prefix={currency?.currency_symbol ?? '$'}
|
||||||
formatInput={(value) => unitConversion('centsToDollars', value)}
|
formatInput={(value) => unitConversion('centsToDollars', value)}
|
||||||
@@ -159,6 +161,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () =
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<EnhancedInput
|
<EnhancedInput
|
||||||
type='number'
|
type='number'
|
||||||
|
min={0}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
prefix={currency?.currency_symbol ?? '$'}
|
prefix={currency?.currency_symbol ?? '$'}
|
||||||
formatInput={(value) => unitConversion('centsToDollars', value)}
|
formatInput={(value) => unitConversion('centsToDollars', value)}
|
||||||
|
|||||||
@@ -58,8 +58,21 @@ export default function Page() {
|
|||||||
<Switch
|
<Switch
|
||||||
defaultChecked={row.getValue('enable')}
|
defaultChecked={row.getValue('enable')}
|
||||||
onCheckedChange={async (checked) => {
|
onCheckedChange={async (checked) => {
|
||||||
|
const {
|
||||||
|
auth_methods,
|
||||||
|
user_devices,
|
||||||
|
enable_balance_notify,
|
||||||
|
enable_login_notify,
|
||||||
|
enable_subscribe_notify,
|
||||||
|
enable_trade_notify,
|
||||||
|
updated_at,
|
||||||
|
created_at,
|
||||||
|
id,
|
||||||
|
...rest
|
||||||
|
} = row.original;
|
||||||
await updateUserBasicInfo({
|
await updateUserBasicInfo({
|
||||||
...row.original,
|
user_id: id,
|
||||||
|
...rest,
|
||||||
enable: checked,
|
enable: checked,
|
||||||
} as unknown as API.UpdateUserBasiceInfoRequest);
|
} as unknown as API.UpdateUserBasiceInfoRequest);
|
||||||
toast.success(t('updateSuccess'));
|
toast.success(t('updateSuccess'));
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const useGlobalStore = create<GlobalStore>((set) => ({
|
|||||||
site_logo: '',
|
site_logo: '',
|
||||||
keywords: '',
|
keywords: '',
|
||||||
custom_html: '',
|
custom_html: '',
|
||||||
|
custom_data: '',
|
||||||
},
|
},
|
||||||
verify: {
|
verify: {
|
||||||
turnstile_site_key: '',
|
turnstile_site_key: '',
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Stav",
|
"status": "Stav",
|
||||||
"subject": "Předmět",
|
"subject": "Předmět",
|
||||||
"template": "Šablona",
|
"template": "Šablona",
|
||||||
|
"traffic_exceed_email_template": "Šablona oznámení o překročení provozu",
|
||||||
|
"traffic_exceed_email_templateDescription": "Zástupné symboly {after}.variable{before} budou nahrazeny skutečnými daty. Ujistěte se, že tyto proměnné zachováte.",
|
||||||
"verify_email_template": "Šablona ověřovacího e-mailu",
|
"verify_email_template": "Šablona ověřovacího e-mailu",
|
||||||
"verify_email_templateDescription": "Zástupné symboly {after}.variable{before} budou nahrazeny skutečnými daty. Ujistěte se, že tyto proměnné zachováte.",
|
"verify_email_templateDescription": "Zástupné symboly {after}.variable{before} budou nahrazeny skutečnými daty. Ujistěte se, že tyto proměnné zachováte.",
|
||||||
"whitelistSuffixes": "Povolené přípony",
|
"whitelistSuffixes": "Povolené přípony",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Zásady ochrany osobních údajů"
|
"title": "Zásady ochrany osobních údajů"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Vlastní data",
|
||||||
|
"customDataDescription": "Vlastní data, používaná pro vlastní data webu",
|
||||||
"customHtml": "Vlastní HTML",
|
"customHtml": "Vlastní HTML",
|
||||||
"customHtmlDescription": "Vlastní HTML kód, který bude vložen na konec tagu body na stránce.",
|
"customHtmlDescription": "Vlastní HTML kód, který bude vložen na konec tagu body na stránce.",
|
||||||
"keywords": "Klíčová slova",
|
"keywords": "Klíčová slova",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"subject": "Betreff",
|
"subject": "Betreff",
|
||||||
"template": "Vorlage",
|
"template": "Vorlage",
|
||||||
|
"traffic_exceed_email_template": "Benachrichtigung über überschreitenden Verkehr",
|
||||||
|
"traffic_exceed_email_templateDescription": "Die Platzhalter {after}.variable{before} werden durch tatsächliche Daten ersetzt. Stellen Sie sicher, dass Sie diese Variablen beibehalten.",
|
||||||
"verify_email_template": "E-Mail-Bestätigungsvorlage",
|
"verify_email_template": "E-Mail-Bestätigungsvorlage",
|
||||||
"verify_email_templateDescription": "Die Platzhalter {after}.variable{before} werden durch tatsächliche Daten ersetzt. Stellen Sie sicher, dass diese Variablen beibehalten werden.",
|
"verify_email_templateDescription": "Die Platzhalter {after}.variable{before} werden durch tatsächliche Daten ersetzt. Stellen Sie sicher, dass diese Variablen beibehalten werden.",
|
||||||
"whitelistSuffixes": "Whitelist-Suffixe",
|
"whitelistSuffixes": "Whitelist-Suffixe",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Datenschutzrichtlinie"
|
"title": "Datenschutzrichtlinie"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Benutzerdefinierte Daten",
|
||||||
|
"customDataDescription": "Benutzerdefinierte Daten, die für benutzerdefinierte Daten der Website verwendet werden",
|
||||||
"customHtml": "Benutzerdefiniertes HTML",
|
"customHtml": "Benutzerdefiniertes HTML",
|
||||||
"customHtmlDescription": "Benutzerdefinierter HTML-Code, der am Ende des body-Tags der Seite eingefügt werden soll.",
|
"customHtmlDescription": "Benutzerdefinierter HTML-Code, der am Ende des body-Tags der Seite eingefügt werden soll.",
|
||||||
"keywords": "Schlüsselwörter",
|
"keywords": "Schlüsselwörter",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"subject": "Subject",
|
"subject": "Subject",
|
||||||
"template": "Template",
|
"template": "Template",
|
||||||
|
"traffic_exceed_email_template": "Traffic Exceed Notice Template",
|
||||||
|
"traffic_exceed_email_templateDescription": "The {after}.variable{before} placeholders will be replaced with actual data. Ensure to keep these variables.",
|
||||||
"verify_email_template": "Verification Email Template",
|
"verify_email_template": "Verification Email Template",
|
||||||
"verify_email_templateDescription": "The {after}.variable{before} placeholders will be replaced with actual data. Ensure to keep these variables.",
|
"verify_email_templateDescription": "The {after}.variable{before} placeholders will be replaced with actual data. Ensure to keep these variables.",
|
||||||
"whitelistSuffixes": "Whitelist Suffixes",
|
"whitelistSuffixes": "Whitelist Suffixes",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Privacy Policy"
|
"title": "Privacy Policy"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Custom Data",
|
||||||
|
"customDataDescription": "Custom Data, used for custom data of the site",
|
||||||
"customHtml": "Custom HTML",
|
"customHtml": "Custom HTML",
|
||||||
"customHtmlDescription": "Custom HTML code to be injected into the bottom of the site's body tag.",
|
"customHtmlDescription": "Custom HTML code to be injected into the bottom of the site's body tag.",
|
||||||
"keywords": "Keywords",
|
"keywords": "Keywords",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"subject": "Asunto",
|
"subject": "Asunto",
|
||||||
"template": "Plantilla",
|
"template": "Plantilla",
|
||||||
|
"traffic_exceed_email_template": "Plantilla de Notificación de Exceso de Tráfico",
|
||||||
|
"traffic_exceed_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrate de mantener estas variables.",
|
||||||
"verify_email_template": "Plantilla de Correo Electrónico de Verificación",
|
"verify_email_template": "Plantilla de Correo Electrónico de Verificación",
|
||||||
"verify_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrese de mantener estas variables.",
|
"verify_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrese de mantener estas variables.",
|
||||||
"whitelistSuffixes": "Sufijos de la lista blanca",
|
"whitelistSuffixes": "Sufijos de la lista blanca",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Política de Privacidad"
|
"title": "Política de Privacidad"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Datos Personalizados",
|
||||||
|
"customDataDescription": "Datos Personalizados, utilizados para datos personalizados del sitio",
|
||||||
"customHtml": "HTML personalizado",
|
"customHtml": "HTML personalizado",
|
||||||
"customHtmlDescription": "Código HTML personalizado que se inyectará en la parte inferior de la etiqueta body del sitio.",
|
"customHtmlDescription": "Código HTML personalizado que se inyectará en la parte inferior de la etiqueta body del sitio.",
|
||||||
"keywords": "Palabras clave",
|
"keywords": "Palabras clave",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"subject": "Asunto",
|
"subject": "Asunto",
|
||||||
"template": "Plantilla",
|
"template": "Plantilla",
|
||||||
|
"traffic_exceed_email_template": "Plantilla de Notificación de Exceso de Tráfico",
|
||||||
|
"traffic_exceed_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrate de mantener estas variables.",
|
||||||
"verify_email_template": "Plantilla de Correo Electrónico de Verificación",
|
"verify_email_template": "Plantilla de Correo Electrónico de Verificación",
|
||||||
"verify_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrate de mantener estas variables.",
|
"verify_email_templateDescription": "Los marcadores de posición {after}.variable{before} serán reemplazados con datos reales. Asegúrate de mantener estas variables.",
|
||||||
"whitelistSuffixes": "Sufijos de Lista Blanca",
|
"whitelistSuffixes": "Sufijos de Lista Blanca",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Política de Privacidad"
|
"title": "Política de Privacidad"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Datos Personalizados",
|
||||||
|
"customDataDescription": "Datos Personalizados, utilizados para datos personalizados del sitio",
|
||||||
"customHtml": "HTML personalizado",
|
"customHtml": "HTML personalizado",
|
||||||
"customHtmlDescription": "Código HTML personalizado que se inyectará al final de la etiqueta body del sitio.",
|
"customHtmlDescription": "Código HTML personalizado que se inyectará al final de la etiqueta body del sitio.",
|
||||||
"keywords": "Palabras clave",
|
"keywords": "Palabras clave",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "وضعیت",
|
"status": "وضعیت",
|
||||||
"subject": "موضوع",
|
"subject": "موضوع",
|
||||||
"template": "الگو",
|
"template": "الگو",
|
||||||
|
"traffic_exceed_email_template": "الگوی اعلان ترافیک بیش از حد",
|
||||||
|
"traffic_exceed_email_templateDescription": "جایگزینهای {after}.variable{before} با دادههای واقعی خواهد شد. اطمینان حاصل کنید که این متغیرها را حفظ کنید.",
|
||||||
"verify_email_template": "قالب ایمیل تأیید",
|
"verify_email_template": "قالب ایمیل تأیید",
|
||||||
"verify_email_templateDescription": "جایگذاریهای {after}.variable{before} با دادههای واقعی جایگزین خواهند شد. اطمینان حاصل کنید که این متغیرها را حفظ کنید.",
|
"verify_email_templateDescription": "جایگذاریهای {after}.variable{before} با دادههای واقعی جایگزین خواهند شد. اطمینان حاصل کنید که این متغیرها را حفظ کنید.",
|
||||||
"whitelistSuffixes": "پسوندهای لیست سفید",
|
"whitelistSuffixes": "پسوندهای لیست سفید",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "سیاست حفظ حریم خصوصی"
|
"title": "سیاست حفظ حریم خصوصی"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "دادههای سفارشی",
|
||||||
|
"customDataDescription": "دادههای سفارشی، استفاده شده برای دادههای سفارشی سایت",
|
||||||
"customHtml": "HTML سفارشی",
|
"customHtml": "HTML سفارشی",
|
||||||
"customHtmlDescription": "کد HTML سفارشی که باید در انتهای تگ body سایت وارد شود.",
|
"customHtmlDescription": "کد HTML سفارشی که باید در انتهای تگ body سایت وارد شود.",
|
||||||
"keywords": "کلمات کلیدی",
|
"keywords": "کلمات کلیدی",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Tila",
|
"status": "Tila",
|
||||||
"subject": "Aihe",
|
"subject": "Aihe",
|
||||||
"template": "Malline",
|
"template": "Malline",
|
||||||
|
"traffic_exceed_email_template": "Liikenteen Ylitysilmoitusmalli",
|
||||||
|
"traffic_exceed_email_templateDescription": "{after}.variable{before} -paikat korvataan todellisilla tiedoilla. Varmista, että säilytät nämä muuttujat.",
|
||||||
"verify_email_template": "Sähköpostin Vahvistusmalli",
|
"verify_email_template": "Sähköpostin Vahvistusmalli",
|
||||||
"verify_email_templateDescription": "Paikkamerkit {after}.variable{before} korvataan todellisilla tiedoilla. Varmista, että pidät nämä muuttujat.",
|
"verify_email_templateDescription": "Paikkamerkit {after}.variable{before} korvataan todellisilla tiedoilla. Varmista, että pidät nämä muuttujat.",
|
||||||
"whitelistSuffixes": "Sallittujen luetteloiden päätteet",
|
"whitelistSuffixes": "Sallittujen luetteloiden päätteet",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Tietosuojakäytäntö"
|
"title": "Tietosuojakäytäntö"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Mukautettu tieto",
|
||||||
|
"customDataDescription": "Mukautettu tieto, jota käytetään sivuston mukautettuun tietoon",
|
||||||
"customHtml": "Mukautettu HTML",
|
"customHtml": "Mukautettu HTML",
|
||||||
"customHtmlDescription": "Mukautettu HTML-koodi, joka lisätään sivuston body -tagin loppuun.",
|
"customHtmlDescription": "Mukautettu HTML-koodi, joka lisätään sivuston body -tagin loppuun.",
|
||||||
"keywords": "Avainsanat",
|
"keywords": "Avainsanat",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Statut",
|
"status": "Statut",
|
||||||
"subject": "Sujet",
|
"subject": "Sujet",
|
||||||
"template": "Modèle",
|
"template": "Modèle",
|
||||||
|
"traffic_exceed_email_template": "Modèle de notification de dépassement de trafic",
|
||||||
|
"traffic_exceed_email_templateDescription": "Les espaces réservés {after}.variable{before} seront remplacés par des données réelles. Assurez-vous de conserver ces variables.",
|
||||||
"verify_email_template": "Modèle d'Email de Vérification",
|
"verify_email_template": "Modèle d'Email de Vérification",
|
||||||
"verify_email_templateDescription": "Les espaces réservés {after}.variable{before} seront remplacés par des données réelles. Assurez-vous de conserver ces variables.",
|
"verify_email_templateDescription": "Les espaces réservés {after}.variable{before} seront remplacés par des données réelles. Assurez-vous de conserver ces variables.",
|
||||||
"whitelistSuffixes": "Suffixes de la liste blanche",
|
"whitelistSuffixes": "Suffixes de la liste blanche",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Politique de confidentialité"
|
"title": "Politique de confidentialité"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Données personnalisées",
|
||||||
|
"customDataDescription": "Données personnalisées, utilisées pour les données personnalisées du site",
|
||||||
"customHtml": "HTML personnalisé",
|
"customHtml": "HTML personnalisé",
|
||||||
"customHtmlDescription": "Code HTML personnalisé à injecter dans le bas de la balise body du site.",
|
"customHtmlDescription": "Code HTML personnalisé à injecter dans le bas de la balise body du site.",
|
||||||
"keywords": "Mots-clés",
|
"keywords": "Mots-clés",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "स्थिति",
|
"status": "स्थिति",
|
||||||
"subject": "विषय",
|
"subject": "विषय",
|
||||||
"template": "टेम्पलेट",
|
"template": "टेम्पलेट",
|
||||||
|
"traffic_exceed_email_template": "ट्रैफिक अधिकता सूचना टेम्पलेट",
|
||||||
|
"traffic_exceed_email_templateDescription": "{after}.variable{before} प्लेसहोल्डर्स को वास्तविक डेटा के साथ प्रतिस्थापित किया जाएगा। सुनिश्चित करें कि इन वेरिएबल्स को बनाए रखें।",
|
||||||
"verify_email_template": "सत्यापन ईमेल टेम्पलेट",
|
"verify_email_template": "सत्यापन ईमेल टेम्पलेट",
|
||||||
"verify_email_templateDescription": "{after}.variable{before} प्लेसहोल्डर्स को वास्तविक डेटा से बदला जाएगा। सुनिश्चित करें कि इन वेरिएबल्स को बनाए रखें।",
|
"verify_email_templateDescription": "{after}.variable{before} प्लेसहोल्डर्स को वास्तविक डेटा से बदला जाएगा। सुनिश्चित करें कि इन वेरिएबल्स को बनाए रखें।",
|
||||||
"whitelistSuffixes": "श्वेतसूची प्रत्यय",
|
"whitelistSuffixes": "श्वेतसूची प्रत्यय",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "गोपनीयता नीति"
|
"title": "गोपनीयता नीति"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "कस्टम डेटा",
|
||||||
|
"customDataDescription": "कस्टम डेटा, साइट के कस्टम डेटा के लिए उपयोग किया जाता है",
|
||||||
"customHtml": "कस्टम HTML",
|
"customHtml": "कस्टम HTML",
|
||||||
"customHtmlDescription": "साइट के bodyैग के नीचे इंजेक्ट करने के लिए कस्टम HTML कोड।",
|
"customHtmlDescription": "साइट के bodyैग के नीचे इंजेक्ट करने के लिए कस्टम HTML कोड।",
|
||||||
"keywords": "कीवर्ड",
|
"keywords": "कीवर्ड",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Állapot",
|
"status": "Állapot",
|
||||||
"subject": "Tárgy",
|
"subject": "Tárgy",
|
||||||
"template": "Sablon",
|
"template": "Sablon",
|
||||||
|
"traffic_exceed_email_template": "Forgalmi Túllépés Értesítő Sablon",
|
||||||
|
"traffic_exceed_email_templateDescription": "A {after}.variable{before} helyettesítők tényleges adatokkal lesznek helyettesítve. Győződjön meg róla, hogy ezeket a változókat megőrzi.",
|
||||||
"verify_email_template": "Ellenőrző Email Sablon",
|
"verify_email_template": "Ellenőrző Email Sablon",
|
||||||
"verify_email_templateDescription": "A {after}.variable{before} helyőrzőket tényleges adatokkal fogjuk helyettesíteni. Ügyeljen arra, hogy ezeket a változókat megtartsa.",
|
"verify_email_templateDescription": "A {after}.variable{before} helyőrzőket tényleges adatokkal fogjuk helyettesíteni. Ügyeljen arra, hogy ezeket a változókat megtartsa.",
|
||||||
"whitelistSuffixes": "Engedélyezett utótagok",
|
"whitelistSuffixes": "Engedélyezett utótagok",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Adatvédelmi irányelvek"
|
"title": "Adatvédelmi irányelvek"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Egyedi adatok",
|
||||||
|
"customDataDescription": "Egyedi adatok, a webhely egyedi adatainak használatára",
|
||||||
"customHtml": "Egyedi HTML",
|
"customHtml": "Egyedi HTML",
|
||||||
"customHtmlDescription": "Egyedi HTML kód, amelyet a webhely bodyímkéjének aljára kell beilleszteni.",
|
"customHtmlDescription": "Egyedi HTML kód, amelyet a webhely bodyímkéjének aljára kell beilleszteni.",
|
||||||
"keywords": "Kulcsszavak",
|
"keywords": "Kulcsszavak",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "ステータス",
|
"status": "ステータス",
|
||||||
"subject": "件名",
|
"subject": "件名",
|
||||||
"template": "テンプレート",
|
"template": "テンプレート",
|
||||||
|
"traffic_exceed_email_template": "トラフィック超過通知テンプレート",
|
||||||
|
"traffic_exceed_email_templateDescription": "{after}と{before}の変数は実際のデータに置き換えられます。これらの変数を保持することを確認してください。",
|
||||||
"verify_email_template": "確認メールテンプレート",
|
"verify_email_template": "確認メールテンプレート",
|
||||||
"verify_email_templateDescription": "{after}.variable{before} のプレースホルダーは実際のデータに置き換えられます。これらの変数を保持してください。",
|
"verify_email_templateDescription": "{after}.variable{before} のプレースホルダーは実際のデータに置き換えられます。これらの変数を保持してください。",
|
||||||
"whitelistSuffixes": "ホワイトリストのサフィックス",
|
"whitelistSuffixes": "ホワイトリストのサフィックス",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "プライバシーポリシー"
|
"title": "プライバシーポリシー"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "カスタムデータ",
|
||||||
|
"customDataDescription": "サイトのカスタムデータに使用されるカスタムデータ",
|
||||||
"customHtml": "カスタムHTML",
|
"customHtml": "カスタムHTML",
|
||||||
"customHtmlDescription": "サイトの body タグの下部に挿入されるカスタム HTML コード。",
|
"customHtmlDescription": "サイトの body タグの下部に挿入されるカスタム HTML コード。",
|
||||||
"keywords": "キーワード",
|
"keywords": "キーワード",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "상태",
|
"status": "상태",
|
||||||
"subject": "제목",
|
"subject": "제목",
|
||||||
"template": "템플릿",
|
"template": "템플릿",
|
||||||
|
"traffic_exceed_email_template": "트래픽 초과 알림 템플릿",
|
||||||
|
"traffic_exceed_email_templateDescription": "{after}.variable{before} 자리표시는 실제 데이터로 대체됩니다. 이러한 변수를 유지해야 합니다.",
|
||||||
"verify_email_template": "이메일 인증 템플릿",
|
"verify_email_template": "이메일 인증 템플릿",
|
||||||
"verify_email_templateDescription": "{after}.variable{before} 자리표시는 실제 데이터로 대체됩니다. 이 변수를 반드시 유지하십시오.",
|
"verify_email_templateDescription": "{after}.variable{before} 자리표시는 실제 데이터로 대체됩니다. 이 변수를 반드시 유지하십시오.",
|
||||||
"whitelistSuffixes": "허용 목록 접미사",
|
"whitelistSuffixes": "허용 목록 접미사",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "개인정보 처리방침"
|
"title": "개인정보 처리방침"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "사용자 정의 데이터",
|
||||||
|
"customDataDescription": "사이트의 사용자 정의 데이터에 사용되는 사용자 정의 데이터",
|
||||||
"customHtml": "사용자 정의 HTML",
|
"customHtml": "사용자 정의 HTML",
|
||||||
"customHtmlDescription": "사이트의 body그 하단에 삽입될 사용자 정의 HTML 코드입니다.",
|
"customHtmlDescription": "사이트의 body그 하단에 삽입될 사용자 정의 HTML 코드입니다.",
|
||||||
"keywords": "키워드",
|
"keywords": "키워드",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"subject": "Emne",
|
"subject": "Emne",
|
||||||
"template": "Mal",
|
"template": "Mal",
|
||||||
|
"traffic_exceed_email_template": "Varsel om overskridelse av trafikk",
|
||||||
|
"traffic_exceed_email_templateDescription": "Plassholderne {after}.variable{before} vil bli erstattet med faktiske data. Sørg for å beholde disse variablene.",
|
||||||
"verify_email_template": "Verifikasjons-e-postmal",
|
"verify_email_template": "Verifikasjons-e-postmal",
|
||||||
"verify_email_templateDescription": "Plassholderne {after}.variable{before} vil bli erstattet med faktiske data. Sørg for å beholde disse variablene.",
|
"verify_email_templateDescription": "Plassholderne {after}.variable{before} vil bli erstattet med faktiske data. Sørg for å beholde disse variablene.",
|
||||||
"whitelistSuffixes": "Hvitelistesuffikser",
|
"whitelistSuffixes": "Hvitelistesuffikser",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Personvernerklæring"
|
"title": "Personvernerklæring"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Egendata",
|
||||||
|
"customDataDescription": "Egendata, brukt for egendata på nettstedet",
|
||||||
"customHtml": "Egendefinert HTML",
|
"customHtml": "Egendefinert HTML",
|
||||||
"customHtmlDescription": "Egendefinert HTML-kode som skal settes inn i bunnen av nettstedets body-tag.",
|
"customHtmlDescription": "Egendefinert HTML-kode som skal settes inn i bunnen av nettstedets body-tag.",
|
||||||
"keywords": "Nøkkelord",
|
"keywords": "Nøkkelord",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"subject": "Temat",
|
"subject": "Temat",
|
||||||
"template": "Szablon",
|
"template": "Szablon",
|
||||||
|
"traffic_exceed_email_template": "Szablon powiadomienia o przekroczeniu ruchu",
|
||||||
|
"traffic_exceed_email_templateDescription": "Miejsca {after}.variable{before} zostaną zastąpione rzeczywistymi danymi. Upewnij się, że te zmienne są zachowane.",
|
||||||
"verify_email_template": "Szablon e-maila weryfikacyjnego",
|
"verify_email_template": "Szablon e-maila weryfikacyjnego",
|
||||||
"verify_email_templateDescription": "Elementy zastępcze {after}.variable{before} zostaną zastąpione rzeczywistymi danymi. Upewnij się, że te zmienne zostaną zachowane.",
|
"verify_email_templateDescription": "Elementy zastępcze {after}.variable{before} zostaną zastąpione rzeczywistymi danymi. Upewnij się, że te zmienne zostaną zachowane.",
|
||||||
"whitelistSuffixes": "Dozwolone Sufiksy",
|
"whitelistSuffixes": "Dozwolone Sufiksy",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Polityka prywatności"
|
"title": "Polityka prywatności"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Dane niestandardowe",
|
||||||
|
"customDataDescription": "Dane niestandardowe, używane do niestandardowych danych witryny",
|
||||||
"customHtml": "Niestandardowy HTML",
|
"customHtml": "Niestandardowy HTML",
|
||||||
"customHtmlDescription": "Niestandardowy kod HTML, który ma być wstrzyknięty na końcu tagu bodyitryny.",
|
"customHtmlDescription": "Niestandardowy kod HTML, który ma być wstrzyknięty na końcu tagu bodyitryny.",
|
||||||
"keywords": "Słowa kluczowe",
|
"keywords": "Słowa kluczowe",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"subject": "Assunto",
|
"subject": "Assunto",
|
||||||
"template": "Modelo",
|
"template": "Modelo",
|
||||||
|
"traffic_exceed_email_template": "Modelo de Notificação de Excesso de Tráfego",
|
||||||
|
"traffic_exceed_email_templateDescription": "Os espaços reservados {after}.variable{before} serão substituídos por dados reais. Certifique-se de manter essas variáveis.",
|
||||||
"verify_email_template": "Modelo de E-mail de Verificação",
|
"verify_email_template": "Modelo de E-mail de Verificação",
|
||||||
"verify_email_templateDescription": "Os marcadores {after}.variable{before} serão substituídos por dados reais. Certifique-se de manter essas variáveis.",
|
"verify_email_templateDescription": "Os marcadores {after}.variable{before} serão substituídos por dados reais. Certifique-se de manter essas variáveis.",
|
||||||
"whitelistSuffixes": "Sufixos de Lista Branca",
|
"whitelistSuffixes": "Sufixos de Lista Branca",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Política de Privacidade"
|
"title": "Política de Privacidade"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Dados Personalizados",
|
||||||
|
"customDataDescription": "Dados personalizados, usados para dados personalizados do site",
|
||||||
"customHtml": "HTML Personalizado",
|
"customHtml": "HTML Personalizado",
|
||||||
"customHtmlDescription": "Código HTML personalizado a ser inserido no final da tag bodyo site.",
|
"customHtmlDescription": "Código HTML personalizado a ser inserido no final da tag bodyo site.",
|
||||||
"keywords": "Palavras-chave",
|
"keywords": "Palavras-chave",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Stare",
|
"status": "Stare",
|
||||||
"subject": "Subiect",
|
"subject": "Subiect",
|
||||||
"template": "Șablon",
|
"template": "Șablon",
|
||||||
|
"traffic_exceed_email_template": "Șablon de notificare a depășirii traficului",
|
||||||
|
"traffic_exceed_email_templateDescription": "Plasamentele {after}.variable{before} vor fi înlocuite cu datele reale. Asigurați-vă că păstrați aceste variabile.",
|
||||||
"verify_email_template": "Șablon de e-mail pentru verificare",
|
"verify_email_template": "Șablon de e-mail pentru verificare",
|
||||||
"verify_email_templateDescription": "Locurile rezervate {after}.variable{before} vor fi înlocuite cu date reale. Asigurați-vă că păstrați aceste variabile.",
|
"verify_email_templateDescription": "Locurile rezervate {after}.variable{before} vor fi înlocuite cu date reale. Asigurați-vă că păstrați aceste variabile.",
|
||||||
"whitelistSuffixes": "Sufixe pe lista albă",
|
"whitelistSuffixes": "Sufixe pe lista albă",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Politica de confidențialitate"
|
"title": "Politica de confidențialitate"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Date personalizate",
|
||||||
|
"customDataDescription": "Date personalizate, utilizate pentru datele personalizate ale site-ului",
|
||||||
"customHtml": "HTML personalizat",
|
"customHtml": "HTML personalizat",
|
||||||
"customHtmlDescription": "Cod HTML personalizat care va fi injectat în partea de jos a etichetei body a site-ului.",
|
"customHtmlDescription": "Cod HTML personalizat care va fi injectat în partea de jos a etichetei body a site-ului.",
|
||||||
"keywords": "Cuvinte cheie",
|
"keywords": "Cuvinte cheie",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Статус",
|
"status": "Статус",
|
||||||
"subject": "Тема",
|
"subject": "Тема",
|
||||||
"template": "Шаблон",
|
"template": "Шаблон",
|
||||||
|
"traffic_exceed_email_template": "Шаблон уведомления о превышении трафика",
|
||||||
|
"traffic_exceed_email_templateDescription": "Места {after}.variable{before} будут заменены на фактические данные. Убедитесь, что эти переменные сохранены.",
|
||||||
"verify_email_template": "Шаблон письма для подтверждения",
|
"verify_email_template": "Шаблон письма для подтверждения",
|
||||||
"verify_email_templateDescription": "Заполнители {after}.variable{before} будут заменены фактическими данными. Убедитесь, что эти переменные сохранены.",
|
"verify_email_templateDescription": "Заполнители {after}.variable{before} будут заменены фактическими данными. Убедитесь, что эти переменные сохранены.",
|
||||||
"whitelistSuffixes": "Белый список суффиксов",
|
"whitelistSuffixes": "Белый список суффиксов",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Политика конфиденциальности"
|
"title": "Политика конфиденциальности"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Пользовательские данные",
|
||||||
|
"customDataDescription": "Пользовательские данные, используемые для пользовательских данных сайта",
|
||||||
"customHtml": "Пользовательский HTML",
|
"customHtml": "Пользовательский HTML",
|
||||||
"customHtmlDescription": "Пользовательский HTML-код, который будет вставлен в конец тега body сайта.",
|
"customHtmlDescription": "Пользовательский HTML-код, который будет вставлен в конец тега body сайта.",
|
||||||
"keywords": "Ключевые слова",
|
"keywords": "Ключевые слова",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "สถานะ",
|
"status": "สถานะ",
|
||||||
"subject": "เรื่อง",
|
"subject": "เรื่อง",
|
||||||
"template": "แม่แบบ",
|
"template": "แม่แบบ",
|
||||||
|
"traffic_exceed_email_template": "เทมเพลตการแจ้งเตือนการเกินการจราจร",
|
||||||
|
"traffic_exceed_email_templateDescription": "ตัวแปร {after}.variable{before} จะถูกแทนที่ด้วยข้อมูลจริง โปรดตรวจสอบให้แน่ใจว่าได้เก็บตัวแปรเหล่านี้ไว้",
|
||||||
"verify_email_template": "เทมเพลตอีเมลยืนยัน",
|
"verify_email_template": "เทมเพลตอีเมลยืนยัน",
|
||||||
"verify_email_templateDescription": "ตัวแปร {after}.variable{before} จะถูกแทนที่ด้วยข้อมูลจริง โปรดตรวจสอบให้แน่ใจว่าเก็บตัวแปรเหล่านี้ไว้",
|
"verify_email_templateDescription": "ตัวแปร {after}.variable{before} จะถูกแทนที่ด้วยข้อมูลจริง โปรดตรวจสอบให้แน่ใจว่าเก็บตัวแปรเหล่านี้ไว้",
|
||||||
"whitelistSuffixes": "อนุญาตให้ใช้คำต่อท้าย",
|
"whitelistSuffixes": "อนุญาตให้ใช้คำต่อท้าย",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "นโยบายความเป็นส่วนตัว"
|
"title": "นโยบายความเป็นส่วนตัว"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "ข้อมูลที่กำหนดเอง",
|
||||||
|
"customDataDescription": "ข้อมูลที่กำหนดเอง ใช้สำหรับข้อมูลที่กำหนดเองของเว็บไซต์",
|
||||||
"customHtml": "HTML ที่กำหนดเอง",
|
"customHtml": "HTML ที่กำหนดเอง",
|
||||||
"customHtmlDescription": "รหัส HTML ที่กำหนดเองเพื่อแทรกลงในส่วนล่างของแท็ก body ของเว็บไซต์.",
|
"customHtmlDescription": "รหัส HTML ที่กำหนดเองเพื่อแทรกลงในส่วนล่างของแท็ก body ของเว็บไซต์.",
|
||||||
"keywords": "คำสำคัญ",
|
"keywords": "คำสำคัญ",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Durum",
|
"status": "Durum",
|
||||||
"subject": "Konu",
|
"subject": "Konu",
|
||||||
"template": "Şablon",
|
"template": "Şablon",
|
||||||
|
"traffic_exceed_email_template": "Trafik Aşım Bildirimi Şablonu",
|
||||||
|
"traffic_exceed_email_templateDescription": "{after}.variable{before} yer tutucuları gerçek verilerle değiştirilecektir. Bu değişkenleri koruduğunuzdan emin olun.",
|
||||||
"verify_email_template": "Doğrulama E-posta Şablonu",
|
"verify_email_template": "Doğrulama E-posta Şablonu",
|
||||||
"verify_email_templateDescription": "{after}.variable{before} yer tutucuları gerçek verilerle değiştirilecektir. Bu değişkenleri koruduğunuzdan emin olun.",
|
"verify_email_templateDescription": "{after}.variable{before} yer tutucuları gerçek verilerle değiştirilecektir. Bu değişkenleri koruduğunuzdan emin olun.",
|
||||||
"whitelistSuffixes": "Beyaz Liste Sonekleri",
|
"whitelistSuffixes": "Beyaz Liste Sonekleri",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Gizlilik Politikası"
|
"title": "Gizlilik Politikası"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Özel Veri",
|
||||||
|
"customDataDescription": "Özel Veri, sitenin özel verileri için kullanılır",
|
||||||
"customHtml": "Özel HTML",
|
"customHtml": "Özel HTML",
|
||||||
"customHtmlDescription": "Site'in body etiketinin altına yerleştirilecek özel HTML kodu.",
|
"customHtmlDescription": "Site'in body etiketinin altına yerleştirilecek özel HTML kodu.",
|
||||||
"keywords": "Anahtar Kelimeler",
|
"keywords": "Anahtar Kelimeler",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Статус",
|
"status": "Статус",
|
||||||
"subject": "Тема",
|
"subject": "Тема",
|
||||||
"template": "Шаблон",
|
"template": "Шаблон",
|
||||||
|
"traffic_exceed_email_template": "Шаблон сповіщення про перевищення трафіку",
|
||||||
|
"traffic_exceed_email_templateDescription": "Місця для змінних {after}.variable{before} будуть замінені на фактичні дані. Переконайтеся, що ці змінні залишаються.",
|
||||||
"verify_email_template": "Шаблон електронного листа для підтвердження",
|
"verify_email_template": "Шаблон електронного листа для підтвердження",
|
||||||
"verify_email_templateDescription": "Заповнювачі {after}.variable{before} будуть замінені на фактичні дані. Переконайтеся, що зберегли ці змінні.",
|
"verify_email_templateDescription": "Заповнювачі {after}.variable{before} будуть замінені на фактичні дані. Переконайтеся, що зберегли ці змінні.",
|
||||||
"whitelistSuffixes": "Білий список суфіксів",
|
"whitelistSuffixes": "Білий список суфіксів",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Політика конфіденційності"
|
"title": "Політика конфіденційності"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Користувацькі дані",
|
||||||
|
"customDataDescription": "Користувацькі дані, що використовуються для користувацьких даних сайту",
|
||||||
"customHtml": "Користувацький HTML",
|
"customHtml": "Користувацький HTML",
|
||||||
"customHtmlDescription": "Користувацький HTML-код, який буде вставлено в кінець тегу body сайту.",
|
"customHtmlDescription": "Користувацький HTML-код, який буде вставлено в кінець тегу body сайту.",
|
||||||
"keywords": "Ключові слова",
|
"keywords": "Ключові слова",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "Trạng thái",
|
"status": "Trạng thái",
|
||||||
"subject": "Chủ đề",
|
"subject": "Chủ đề",
|
||||||
"template": "Mẫu",
|
"template": "Mẫu",
|
||||||
|
"traffic_exceed_email_template": "Mẫu Thông Báo Vượt Quá Lưu Lượng",
|
||||||
|
"traffic_exceed_email_templateDescription": "Các biến {after}.variable{before} sẽ được thay thế bằng dữ liệu thực tế. Đảm bảo giữ nguyên các biến này.",
|
||||||
"verify_email_template": "Mẫu Email Xác Minh",
|
"verify_email_template": "Mẫu Email Xác Minh",
|
||||||
"verify_email_templateDescription": "Các chỗ trống {after}.variable{before} sẽ được thay thế bằng dữ liệu thực tế. Đảm bảo giữ nguyên các biến này.",
|
"verify_email_templateDescription": "Các chỗ trống {after}.variable{before} sẽ được thay thế bằng dữ liệu thực tế. Đảm bảo giữ nguyên các biến này.",
|
||||||
"whitelistSuffixes": "Danh sách trắng Hậu tố",
|
"whitelistSuffixes": "Danh sách trắng Hậu tố",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "Chính sách bảo mật"
|
"title": "Chính sách bảo mật"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "Dữ liệu tùy chỉnh",
|
||||||
|
"customDataDescription": "Dữ liệu tùy chỉnh, được sử dụng cho dữ liệu tùy chỉnh của trang web",
|
||||||
"customHtml": "HTML tùy chỉnh",
|
"customHtml": "HTML tùy chỉnh",
|
||||||
"customHtmlDescription": "Mã HTML tùy chỉnh để được chèn vào cuối thẻ body của trang.",
|
"customHtmlDescription": "Mã HTML tùy chỉnh để được chèn vào cuối thẻ body của trang.",
|
||||||
"keywords": "Từ khóa",
|
"keywords": "Từ khóa",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "状态",
|
"status": "状态",
|
||||||
"subject": "主题",
|
"subject": "主题",
|
||||||
"template": "模板",
|
"template": "模板",
|
||||||
|
"traffic_exceed_email_template": "流量超限通知模板",
|
||||||
|
"traffic_exceed_email_templateDescription": "{after}和{before}变量将被实际数据替换。请确保保留这些变量。",
|
||||||
"verify_email_template": "验证电子邮件模板",
|
"verify_email_template": "验证电子邮件模板",
|
||||||
"verify_email_templateDescription": "{after}.variable{before} 占位符将被实际数据替换。请确保保留这些变量。",
|
"verify_email_templateDescription": "{after}.variable{before} 占位符将被实际数据替换。请确保保留这些变量。",
|
||||||
"whitelistSuffixes": "白名单后缀",
|
"whitelistSuffixes": "白名单后缀",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "隐私政策"
|
"title": "隐私政策"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "自定义数据",
|
||||||
|
"customDataDescription": "自定义数据,用于网站的自定义数据",
|
||||||
"customHtml": "自定义HTML",
|
"customHtml": "自定义HTML",
|
||||||
"customHtmlDescription": "要注入到网站 body 标签底部的自定义 HTML 代码。",
|
"customHtmlDescription": "要注入到网站 body 标签底部的自定义 HTML 代码。",
|
||||||
"keywords": "关键词",
|
"keywords": "关键词",
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
"status": "狀態",
|
"status": "狀態",
|
||||||
"subject": "主題",
|
"subject": "主題",
|
||||||
"template": "模板",
|
"template": "模板",
|
||||||
|
"traffic_exceed_email_template": "流量超標通知模板",
|
||||||
|
"traffic_exceed_email_templateDescription": "{after}和{before}變量將會被實際數據替換。請確保保留這些變量。",
|
||||||
"verify_email_template": "驗證電郵範本",
|
"verify_email_template": "驗證電郵範本",
|
||||||
"verify_email_templateDescription": "{after}.variable{before} 佔位符將被實際數據替換。請確保保留這些變數。",
|
"verify_email_templateDescription": "{after}.variable{before} 佔位符將被實際數據替換。請確保保留這些變數。",
|
||||||
"whitelistSuffixes": "白名單後綴",
|
"whitelistSuffixes": "白名單後綴",
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"title": "私隱政策"
|
"title": "私隱政策"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
|
"customData": "自定義數據",
|
||||||
|
"customDataDescription": "自定義數據,用於網站的自定義數據",
|
||||||
"customHtml": "自定義HTML",
|
"customHtml": "自定義HTML",
|
||||||
"customHtmlDescription": "自定義 HTML 代碼,將注入到網站 body 標籤的底部。",
|
"customHtmlDescription": "自定義 HTML 代碼,將注入到網站 body 標籤的底部。",
|
||||||
"keywords": "關鍵字",
|
"keywords": "關鍵字",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
||||||
// API 更新时间:
|
// API 更新时间:
|
||||||
// API 唯一标识:
|
// API 唯一标识:
|
||||||
import * as ads from './ads';
|
import * as ads from './ads';
|
||||||
|
|||||||
+23
@@ -116,6 +116,17 @@ declare namespace API {
|
|||||||
ids: number[];
|
ids: number[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CheckoutOrderRequest = {
|
||||||
|
orderNo: string;
|
||||||
|
returnUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CheckoutOrderResponse = {
|
||||||
|
type: string;
|
||||||
|
checkout_url?: string;
|
||||||
|
stripe?: StripePayment;
|
||||||
|
};
|
||||||
|
|
||||||
type CloseOrderRequest = {
|
type CloseOrderRequest = {
|
||||||
orderNo: string;
|
orderNo: string;
|
||||||
};
|
};
|
||||||
@@ -1097,6 +1108,11 @@ declare namespace API {
|
|||||||
total: number;
|
total: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type QueryUserAffiliateListRequest = {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
type QueryUserAffiliateListResponse = {
|
type QueryUserAffiliateListResponse = {
|
||||||
list: UserAffiliate[];
|
list: UserAffiliate[];
|
||||||
total: number;
|
total: number;
|
||||||
@@ -1253,6 +1269,13 @@ declare namespace API {
|
|||||||
site_logo: string;
|
site_logo: string;
|
||||||
keywords: string;
|
keywords: string;
|
||||||
custom_html: string;
|
custom_html: string;
|
||||||
|
custom_data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SiteCustomDataContacts = {
|
||||||
|
email: string;
|
||||||
|
telephone: string;
|
||||||
|
address: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SortItem = {
|
type SortItem = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
||||||
// API 更新时间:
|
// API 更新时间:
|
||||||
// API 唯一标识:
|
// API 唯一标识:
|
||||||
import * as auth from './auth';
|
import * as auth from './auth';
|
||||||
|
|||||||
+23
@@ -94,6 +94,17 @@ declare namespace API {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CheckoutOrderRequest = {
|
||||||
|
orderNo: string;
|
||||||
|
returnUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CheckoutOrderResponse = {
|
||||||
|
type: string;
|
||||||
|
checkout_url?: string;
|
||||||
|
stripe?: StripePayment;
|
||||||
|
};
|
||||||
|
|
||||||
type CheckUserParams = {
|
type CheckUserParams = {
|
||||||
email: string;
|
email: string;
|
||||||
};
|
};
|
||||||
@@ -496,6 +507,11 @@ declare namespace API {
|
|||||||
total: number;
|
total: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type QueryUserAffiliateListRequest = {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
type QueryUserAffiliateListResponse = {
|
type QueryUserAffiliateListResponse = {
|
||||||
list: UserAffiliate[];
|
list: UserAffiliate[];
|
||||||
total: number;
|
total: number;
|
||||||
@@ -643,6 +659,13 @@ declare namespace API {
|
|||||||
site_logo: string;
|
site_logo: string;
|
||||||
keywords: string;
|
keywords: string;
|
||||||
custom_html: string;
|
custom_html: string;
|
||||||
|
custom_data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SiteCustomDataContacts = {
|
||||||
|
email: string;
|
||||||
|
telephone: string;
|
||||||
|
address: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SortItem = {
|
type SortItem = {
|
||||||
|
|||||||
@@ -164,9 +164,7 @@ export default function Content() {
|
|||||||
<CardHeader className='flex flex-row flex-wrap items-center justify-between gap-2 space-y-0'>
|
<CardHeader className='flex flex-row flex-wrap items-center justify-between gap-2 space-y-0'>
|
||||||
<CardTitle className='font-medium'>
|
<CardTitle className='font-medium'>
|
||||||
{item.subscribe.name}
|
{item.subscribe.name}
|
||||||
<p className='text-foreground/50 mt-1 text-sm'>
|
<p className='text-foreground/50 mt-1 text-sm'>{formatDate(item.start_time)}</p>
|
||||||
{formatDate(item.subscribe.created_at)}
|
|
||||||
</p>
|
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<div className='flex flex-wrap gap-2'>
|
<div className='flex flex-wrap gap-2'>
|
||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default function Content({ subscription }: { subscription?: API.Subscribe
|
|||||||
subscribe_id: 0,
|
subscribe_id: 0,
|
||||||
payment: -1,
|
payment: -1,
|
||||||
coupon: '',
|
coupon: '',
|
||||||
platform: 'email',
|
auth_type: 'email',
|
||||||
identifier: '',
|
identifier: '',
|
||||||
password: '',
|
password: '',
|
||||||
});
|
});
|
||||||
@@ -70,12 +70,15 @@ export default function Content({ subscription }: { subscription?: API.Subscribe
|
|||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await purchase(params);
|
const { data } = await purchase(params);
|
||||||
console.log(data);
|
const { order_no } = data.data!;
|
||||||
const { order_no, check_url, type } = data.data!;
|
|
||||||
if (order_no) {
|
if (order_no) {
|
||||||
if (type === 'link') {
|
localStorage.setItem(
|
||||||
window.location.href = check_url!;
|
order_no,
|
||||||
}
|
JSON.stringify({
|
||||||
|
auth_type: params.auth_type,
|
||||||
|
identifier: params.identifier,
|
||||||
|
}),
|
||||||
|
);
|
||||||
router.push(`/purchasing/order?order_no=${order_no}`);
|
router.push(`/purchasing/order?order_no=${order_no}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ export default function Page() {
|
|||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
queryKey: ['queryPurchaseOrder', orderNo],
|
queryKey: ['queryPurchaseOrder', orderNo],
|
||||||
queryFn: async () => {
|
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) {
|
if (data?.data?.status !== 1) {
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
if (data?.data?.token) {
|
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'
|
: 'pointer-events-none bg-transparent opacity-0 backdrop-blur-0'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div className='flex min-h-screen items-center justify-center'>
|
||||||
className={`transform-gpu transition-all duration-500 ${
|
<div className='text-center'>
|
||||||
loading ? 'scale-100 opacity-100' : 'scale-50 opacity-0'
|
<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 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ const PaymentMethods: React.FC<PaymentMethodsProps> = ({ value, onChange, balanc
|
|||||||
queryKey: ['getAvailablePaymentMethods', { balance }],
|
queryKey: ['getAvailablePaymentMethods', { balance }],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await getAvailablePaymentMethods();
|
const { data } = await getAvailablePaymentMethods();
|
||||||
const methods = data.data?.list || [];
|
const list = data.data?.list || [];
|
||||||
if (!value && methods[0]?.id) onChange(methods[0]?.id);
|
const methods = balance ? list : list.filter((item) => item.id !== -1);
|
||||||
if (balance) return methods;
|
const defaultMethod = methods.find((item) => item.id)?.id;
|
||||||
return methods.filter((item) => item.id !== -1);
|
if (defaultMethod) onChange(defaultMethod);
|
||||||
|
return methods;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const useGlobalStore = create<GlobalStore>((set, get) => ({
|
|||||||
site_logo: '',
|
site_logo: '',
|
||||||
keywords: '',
|
keywords: '',
|
||||||
custom_html: '',
|
custom_html: '',
|
||||||
|
custom_data: '',
|
||||||
},
|
},
|
||||||
verify: {
|
verify: {
|
||||||
turnstile_site_key: '',
|
turnstile_site_key: '',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
||||||
// API 更新时间:
|
// API 更新时间:
|
||||||
// API 唯一标识:
|
// API 唯一标识:
|
||||||
import * as auth from './auth';
|
import * as auth from './auth';
|
||||||
|
|||||||
+23
@@ -94,6 +94,17 @@ declare namespace API {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CheckoutOrderRequest = {
|
||||||
|
orderNo: string;
|
||||||
|
returnUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CheckoutOrderResponse = {
|
||||||
|
type: string;
|
||||||
|
checkout_url?: string;
|
||||||
|
stripe?: StripePayment;
|
||||||
|
};
|
||||||
|
|
||||||
type CheckUserParams = {
|
type CheckUserParams = {
|
||||||
email: string;
|
email: string;
|
||||||
};
|
};
|
||||||
@@ -496,6 +507,11 @@ declare namespace API {
|
|||||||
total: number;
|
total: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type QueryUserAffiliateListRequest = {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
type QueryUserAffiliateListResponse = {
|
type QueryUserAffiliateListResponse = {
|
||||||
list: UserAffiliate[];
|
list: UserAffiliate[];
|
||||||
total: number;
|
total: number;
|
||||||
@@ -643,6 +659,13 @@ declare namespace API {
|
|||||||
site_logo: string;
|
site_logo: string;
|
||||||
keywords: string;
|
keywords: string;
|
||||||
custom_html: string;
|
custom_html: string;
|
||||||
|
custom_data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SiteCustomDataContacts = {
|
||||||
|
email: string;
|
||||||
|
telephone: string;
|
||||||
|
address: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SortItem = {
|
type SortItem = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
||||||
// API 更新时间:
|
// API 更新时间:
|
||||||
// API 唯一标识:
|
// API 唯一标识:
|
||||||
import * as announcement from './announcement';
|
import * as announcement from './announcement';
|
||||||
|
|||||||
Vendored
+12
-1
@@ -438,8 +438,8 @@ declare namespace API {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type PortalPurchaseRequest = {
|
type PortalPurchaseRequest = {
|
||||||
|
auth_type: string;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
platform: string;
|
|
||||||
password?: string;
|
password?: string;
|
||||||
payment: number;
|
payment: number;
|
||||||
subscribe_id: number;
|
subscribe_id: number;
|
||||||
@@ -572,10 +572,14 @@ declare namespace API {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type QueryPurchaseOrderParams = {
|
type QueryPurchaseOrderParams = {
|
||||||
|
auth_type: string;
|
||||||
|
identifier: string;
|
||||||
order_no: string;
|
order_no: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type QueryPurchaseOrderRequest = {
|
type QueryPurchaseOrderRequest = {
|
||||||
|
auth_type: string;
|
||||||
|
identifier: string;
|
||||||
order_no: string;
|
order_no: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -773,6 +777,13 @@ declare namespace API {
|
|||||||
site_logo: string;
|
site_logo: string;
|
||||||
keywords: string;
|
keywords: string;
|
||||||
custom_html: string;
|
custom_html: string;
|
||||||
|
custom_data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SiteCustomDataContacts = {
|
||||||
|
email: string;
|
||||||
|
telephone: string;
|
||||||
|
address: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SortItem = {
|
type SortItem = {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ppanel-web",
|
"name": "ppanel-web",
|
||||||
"version": "1.0.0-beta.27",
|
"version": "1.0.0-beta.31",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/perfect-panel/ppanel-web",
|
"homepage": "https://github.com/perfect-panel/ppanel-web",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|||||||
@@ -28,25 +28,25 @@ export const Timeline = ({ data }: { data: TimelineEntry[] }) => {
|
|||||||
const opacityTransform = useTransform(scrollYProgress, [0, 0.1], [0, 1]);
|
const opacityTransform = useTransform(scrollYProgress, [0, 0.1], [0, 1]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full bg-white font-sans md:px-10 dark:bg-neutral-950' ref={containerRef}>
|
<div className='w-full font-sans md:px-10' ref={containerRef}>
|
||||||
<div className='mx-auto max-w-7xl px-4 py-20 md:px-8 lg:px-10'>
|
<div className='mx-auto max-w-7xl px-4 py-10 md:px-8 lg:px-10'>
|
||||||
<h2 className='mb-4 max-w-4xl text-lg text-black md:text-4xl dark:text-white'>
|
{/* <h2 className='mb-4 max-w-4xl text-lg text-black md:text-4xl dark:text-white'>
|
||||||
Changelog from my journey
|
Changelog from my journey
|
||||||
</h2>
|
</h2>
|
||||||
<p className='max-w-sm text-sm text-neutral-700 md:text-base dark:text-neutral-300'>
|
<p className='max-w-sm text-sm text-neutral-700 md:text-base dark:text-neutral-300'>
|
||||||
I've been working on Aceternity for the past 2 years. Here's a timeline of my
|
I've been working on Aceternity for the past 2 years. Here's a timeline of my
|
||||||
journey.
|
journey.
|
||||||
</p>
|
</p> */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref={ref} className='relative mx-auto max-w-7xl pb-20'>
|
<div ref={ref} className='relative mx-auto max-w-7xl pb-20'>
|
||||||
{data.map((item, index) => (
|
{data.map((item, index) => (
|
||||||
<div key={index} className='flex justify-start pt-10 md:gap-10 md:pt-40'>
|
<div key={index} className='flex justify-start pt-10 md:gap-10 md:pt-40'>
|
||||||
<div className='sticky top-40 z-40 flex max-w-xs flex-col items-center self-start md:w-full md:flex-row lg:max-w-sm'>
|
<div className='sticky top-40 z-40 flex max-w-xs flex-col items-center self-start whitespace-nowrap md:w-auto md:flex-row lg:max-w-sm'>
|
||||||
<div className='absolute left-3 flex h-10 w-10 items-center justify-center rounded-full bg-white md:left-3 dark:bg-black'>
|
<div className='absolute left-3 flex h-10 w-10 items-center justify-center rounded-full bg-white md:left-3 dark:bg-black'>
|
||||||
<div className='h-4 w-4 rounded-full border border-neutral-300 bg-neutral-200 p-2 dark:border-neutral-700 dark:bg-neutral-800' />
|
<div className='h-4 w-4 rounded-full border border-neutral-300 bg-neutral-200 p-2 dark:border-neutral-700 dark:bg-neutral-800' />
|
||||||
</div>
|
</div>
|
||||||
<h3 className='hidden text-xl font-bold text-neutral-500 md:block md:pl-20 md:text-5xl dark:text-neutral-500'>
|
<h3 className='hidden text-xl font-bold text-neutral-500 md:block md:pl-20 md:text-xl dark:text-neutral-500'>
|
||||||
{item.title}
|
{item.title}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export interface EnhancedInputProps
|
|||||||
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
|
||||||
prefix?: string | ReactNode;
|
prefix?: string | ReactNode;
|
||||||
suffix?: string | ReactNode;
|
suffix?: string | ReactNode;
|
||||||
formatInput?: (value: string | number) => string;
|
formatInput?: (value: string | number) => string | number;
|
||||||
formatOutput?: (value: string | number) => string | number;
|
formatOutput?: (value: string | number) => string | number;
|
||||||
onValueChange?: (value: string | number) => void;
|
onValueChange?: (value: string | number) => void;
|
||||||
onValueBlur?: (value: string | number) => void;
|
onValueBlur?: (value: string | number) => void;
|
||||||
@@ -26,50 +26,94 @@ export function EnhancedInput({
|
|||||||
...props
|
...props
|
||||||
}: EnhancedInputProps) {
|
}: EnhancedInputProps) {
|
||||||
const getProcessedValue = (inputValue: unknown) => {
|
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;
|
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(() => {
|
useEffect(() => {
|
||||||
if (initialValue !== value) {
|
if (initialValue !== internalValue) {
|
||||||
const newValue = getProcessedValue(initialValue);
|
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
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [initialValue, formatInput]);
|
}, [initialValue, formatInput]);
|
||||||
|
|
||||||
const processValue = (inputValue: string) => {
|
const processValue = (inputValue: string | number) => {
|
||||||
let processedValue: number | string = inputValue?.toString().trim();
|
let processedValue: number | string = inputValue?.toString().trim();
|
||||||
|
|
||||||
|
if (processedValue === '0' && props.type === 'number') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (processedValue && props.type === 'number') processedValue = Number(processedValue);
|
if (processedValue && props.type === 'number') processedValue = Number(processedValue);
|
||||||
return formatOutput ? formatOutput(processedValue) : processedValue;
|
return formatOutput ? formatOutput(processedValue) : processedValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
let inputValue = e.target.value;
|
let inputValue = e.target.value;
|
||||||
if (props.type === 'number' && inputValue) {
|
|
||||||
const numericValue = Number(inputValue);
|
if (props.type === 'number') {
|
||||||
if (!isNaN(numericValue)) {
|
if (inputValue === '0') {
|
||||||
const min = Number.isFinite(props.min) ? props.min : -Infinity;
|
setValue('');
|
||||||
const max = Number.isFinite(props.max) ? props.max : Infinity;
|
setInternalValue(0);
|
||||||
inputValue = String(Math.max(min!, Math.min(max!, numericValue)));
|
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 {
|
} else {
|
||||||
setValue(inputValue);
|
setValue(inputValue);
|
||||||
|
setInternalValue(inputValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputValue = processValue(inputValue);
|
const outputValue = processValue(inputValue);
|
||||||
console.log();
|
|
||||||
onValueChange?.(outputValue);
|
onValueChange?.(outputValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBlur = () => {
|
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);
|
const outputValue = processValue(value);
|
||||||
if ((initialValue || '') !== outputValue) {
|
if ((initialValue || '') !== outputValue) {
|
||||||
onValueBlur?.(outputValue);
|
onValueBlur?.(outputValue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPrefix = () => {
|
const renderPrefix = () => {
|
||||||
return typeof prefix === 'string' ? (
|
return typeof prefix === 'string' ? (
|
||||||
<div className='bg-muted relative mr-px flex h-9 items-center text-nowrap px-3'>{prefix}</div>
|
<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
|
prefix
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSuffix = () => {
|
const renderSuffix = () => {
|
||||||
return typeof suffix === 'string' ? (
|
return typeof suffix === 'string' ? (
|
||||||
<div className='bg-muted relative ml-px flex h-9 items-center text-nowrap px-3'>{suffix}</div>
|
<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()}
|
{renderPrefix()}
|
||||||
<Input
|
<Input
|
||||||
|
step={0.01}
|
||||||
{...props}
|
{...props}
|
||||||
value={value}
|
value={value}
|
||||||
className='block rounded-none border-none'
|
className='block rounded-none border-none'
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ export function formatDate(date?: Date | number, showTime: boolean = true) {
|
|||||||
export function differenceInDays(dateLeft: Date | number, dateRight: Date | number) {
|
export function differenceInDays(dateLeft: Date | number, dateRight: Date | number) {
|
||||||
const diffInMs = differenceInMilliseconds(dateLeft, dateRight);
|
const diffInMs = differenceInMilliseconds(dateLeft, dateRight);
|
||||||
const diffInDays = diffInMs / (1000 * 60 * 60 * 24);
|
const diffInDays = diffInMs / (1000 * 60 * 60 * 24);
|
||||||
if (diffInDays % 1 === 0) return diffInDays;
|
if (diffInDays >= 1) return diffInDays.toFixed(0);
|
||||||
return Number(diffInDays.toFixed(1));
|
return Number(diffInDays.toFixed(2));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,35 @@
|
|||||||
import { evaluate, format } from 'mathjs';
|
import { evaluate, format } from 'mathjs';
|
||||||
|
|
||||||
export function unitConversion(
|
type ConversionType =
|
||||||
type: 'centsToDollars' | 'dollarsToCents' | 'bitsToMb' | 'mbToBits' | 'bytesToGb' | 'gbToBytes',
|
| 'centsToDollars'
|
||||||
value?: number | string,
|
| 'dollarsToCents'
|
||||||
) {
|
| 'bitsToMb'
|
||||||
if (!value) return;
|
| 'mbToBits'
|
||||||
switch (type) {
|
| 'bytesToGb'
|
||||||
case 'centsToDollars':
|
| 'gbToBytes';
|
||||||
return evaluate(`${value} / 100`);
|
|
||||||
case 'dollarsToCents':
|
const conversionConfig: Record<ConversionType, { formula: string; precision: number }> = {
|
||||||
return evaluate(`${value} * 100`);
|
centsToDollars: { formula: 'value / 100', precision: 2 },
|
||||||
case 'bitsToMb':
|
dollarsToCents: { formula: 'value * 100', precision: 0 },
|
||||||
return evaluate(`${value} / 1024 / 1024`);
|
bitsToMb: { formula: 'value / 1024 / 1024', precision: 2 },
|
||||||
case 'mbToBits':
|
mbToBits: { formula: 'value * 1024 * 1024', precision: 0 },
|
||||||
return evaluate(`${value} * 1024 * 1024`);
|
bytesToGb: { formula: 'value / 1024 / 1024 / 1024', precision: 2 },
|
||||||
case 'bytesToGb':
|
gbToBytes: { formula: 'value * 1024 * 1024 * 1024', precision: 0 },
|
||||||
return evaluate(`${value} / 1024 / 1024 / 1024`);
|
};
|
||||||
case 'gbToBytes':
|
|
||||||
return evaluate(`${value} * 1024 * 1024 * 1024`);
|
export function unitConversion(type: ConversionType, value?: number | string) {
|
||||||
default:
|
if (!value) return 0;
|
||||||
throw new Error('Invalid conversion type');
|
|
||||||
}
|
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) {
|
export function evaluateWithPrecision(expression: string) {
|
||||||
const result = evaluate(expression);
|
const result = evaluate(expression);
|
||||||
|
|
||||||
const formatted = format(result, { notation: 'fixed', precision: 2 });
|
const formatted = format(result, { notation: 'fixed', precision: 2 });
|
||||||
|
|
||||||
return Number(formatted);
|
return Number(formatted);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user