Compare commits

..

62 Commits

Author SHA1 Message Date
semantic-release-bot 383638f702 🔖 chore(release): v1.0.0-beta.33 [skip ci]
# [1.0.0-beta.33](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.32...v1.0.0-beta.33) (2025-03-18)

### 🐛 Bug Fixes

* **subscribe**: Handle optional values in price and discount calculations ([5939763](https://github.com/perfect-panel/ppanel-web/commit/5939763))
2025-03-18 07:53:53 +00:00
web@ppanel 5939763b57 🐛 fix(subscribe): Handle optional values in price and discount calculations 2025-03-18 14:50:48 +07:00
semantic-release-bot fa56eb8293 🔖 chore(release): v1.0.0-beta.32 [skip ci]
# [1.0.0-beta.32](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.31...v1.0.0-beta.32) (2025-03-17)

### 🐛 Bug Fixes

* **forms**: Add step attribute to number inputs for better value control ([b8f4f1e](https://github.com/perfect-panel/ppanel-web/commit/b8f4f1e))
* **locales**: Update invite code text to indicate it's optional ([6a34bfb](https://github.com/perfect-panel/ppanel-web/commit/6a34bfb))
2025-03-17 15:53:15 +00:00
web@ppanel 6a34bfb78d 🐛 fix(locales): Update invite code text to indicate it's optional 2025-03-17 22:50:21 +07:00
web@ppanel b8f4f1e694 🐛 fix(forms): Add step attribute to number inputs for better value control 2025-03-17 22:47:42 +07:00
semantic-release-bot aa1d42651d 🔖 chore(release): v1.0.0-beta.31 [skip ci]
# [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)

### 🐛 Bug Fixes

* **site**: Se ref to store site configuration for updates ([0c8f091](https://github.com/perfect-panel/ppanel-web/commit/0c8f091))
2025-03-15 15:51:45 +00:00
web@ppanel 0c8f0911c7 🐛 fix(site): Se ref to store site configuration for updates 2025-03-15 22:47:19 +07:00
semantic-release-bot db0d9e003e 🔖 chore(release): v1.0.0-beta.30 [skip ci]
# [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)

###  Features

* **api**: Add CheckoutOrder request and response types, and update user purchase request parameters ([48a1b97](https://github.com/perfect-panel/ppanel-web/commit/48a1b97))
* **email**: Add traffic exhaustion template ([bb3bd7b](https://github.com/perfect-panel/ppanel-web/commit/bb3bd7b))
* **formatting**: Update differenceInDays function to return whole days or two decimal places ([bf58f25](https://github.com/perfect-panel/ppanel-web/commit/bf58f25))
* **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))
2025-03-15 09:02:24 +00:00
web@ppanel f219c52306 🔧 chore: Merge branch 'beta' into develop 2025-03-15 15:58:37 +07:00
web@ppanel 3f7b6d16ed feat(node-form): Update number input fields to enforce step, min, and max values 2025-03-15 15:48:59 +07:00
web@ppanel 6dbebd152d feat(global): Add custom data 2025-03-15 15:39:55 +07:00
web@ppanel bb3bd7b50c feat(email): Add traffic exhaustion template 2025-03-14 23:28:36 +07:00
web@ppanel fbad3b0e51 feat(timeline): Simplify timeline component layout and remove commented-out code 2025-03-14 23:21:17 +07:00
web@ppanel e551232564 🐛 fix(dashboard): Update date display to use start_time if available 2025-03-14 22:10:24 +07:00
web@ppanel bf58f25072 feat(formatting): Update differenceInDays function to return whole days or two decimal places 2025-03-14 20:51:17 +07:00
web@ppanel 85f55def2e feat(payment): Add isEdit prop to PaymentForm and disable fields when editing 2025-03-14 14:04:10 +07:00
semantic-release-bot 29bc3c7722 🔖 chore(release): v1.0.0-beta.29 [skip ci]
# [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))
2025-03-14 06:43:47 +00:00
web@ppanel f72df3a5e8 feat(loading): Replace loading animation with a simpler spinner and loading text 2025-03-14 13:42:58 +07:00
web@ppanel 48a1b97051 feat(api): Add CheckoutOrder request and response types, and update user purchase request parameters 2025-03-14 13:42:58 +07:00
semantic-release-bot 786ba0e41c 🔖 chore(release): v1.0.0-beta.28 [skip ci]
# [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))
2025-03-14 13:42:58 +07:00
web@ppanel ce3197298d feat(input): Add minimum value constraint and enhance number handling in EnhancedInput 2025-03-14 13:42:58 +07:00
semantic-release-bot 092477b2d3 🔖 chore(release): v1.0.0-beta.27 [skip ci]
* **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))

* **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))

* **merge**: Bump version to 1.0.0-beta.26 and update changelog ([3222016](https://github.com/perfect-panel/ppanel-web/commit/3222016))

* **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))
2025-03-14 13:42:58 +07:00
web@ppanel b8316bba33 feat(loading): Replace loading animation with a simpler spinner and loading text 2025-03-14 13:40:56 +07:00
web@ppanel dddc21c686 feat(api): Add CheckoutOrder request and response types, and update user purchase request parameters 2025-03-14 13:34:36 +07:00
semantic-release-bot d10ecc9fdf 🔖 chore(release): v1.0.0-beta.28 [skip ci]
# [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))
2025-03-13 10:35:19 +00:00
web@ppanel 94822d902f feat(input): Add minimum value constraint and enhance number handling in EnhancedInput 2025-03-13 17:32:36 +07:00
semantic-release-bot 85fdc36c14 🔖 chore(release): v1.0.0-beta.27 [skip ci]
# [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))
2025-03-13 04:27:12 +00:00
web@ppanel cfa3fc024d 🐛 fix(changelog): Update change log style 2025-03-13 11:23:14 +07:00
web@ppanel c06ea49d6f 🐛 fix(purchasing): Update payment type to lowercase and add optional chaining for discounts 2025-03-13 00:06:42 +07:00
web@ppanel c3138a863d 🐛 fix(payment): Fix payment related type definitions and update payment method references 2025-03-12 21:17:04 +07:00
web@ppanel 7fa3a57df4 feat(payment): Add bank card payment 2025-03-12 17:15:32 +07:00
web@ppanel 70d6a38a29 🐛 fix(payment): Update payment information 2025-03-12 13:43:06 +07:00
web@ppanel 5c710e1add 🐛 fix(payment): Add notification URL field to payment management interface 2025-03-11 21:22:18 +07:00
web@ppanel 6752420ba5 🐛 fix(payment): Update payment method update logic to include row data 2025-03-11 21:15:07 +07:00
web@ppanel 710947209a ♻️ refactor(payment): Reconstruct the payment page 2025-03-11 20:06:12 +07:00
web@ppanel fc0da761b5 🐛 fix(ui): Improve dashboard layout and enhance button functionality; open checkout URLs in a new tab 2025-03-10 18:32:07 +07:00
web@ppanel 136a1abd8b 🐛 fix(payment): Update checkout type from 'link' to 'url' for consistency 2025-03-08 20:10:45 +07:00
web@ppanel a5e2079ddc 🐛 fix(payment): Refactor purchaseCheckout usage and remove redundant code 2025-03-08 20:04:33 +07:00
web@ppanel 35f92c9329 🐛 fix(affiliate): Update user identifier 2025-03-08 15:43:20 +07:00
web@ppanel f5d8fd3b65 🐛 fix(ui): Multiple display bugs 2025-03-08 14:46:20 +07:00
web@ppanel 0c907337e1 feat(cdn): Add CDN URL configuration and update related references 2025-03-08 12:54:58 +07:00
web@ppanel e4630f8ca9 feat(subscription): Improve layout and organization of subscription detail tabs 2025-03-08 12:43:57 +07:00
web@ppanel 2215c7f2b9 feat(subscription): Refactor subscription handling and update imports for better organization 2025-03-08 12:36:25 +07:00
semantic-release-bot 3222016799 🔧 chore(merge): Bump version to 1.0.0-beta.26 and update changelog 2025-03-08 12:36:02 +07:00
web@ppanel d0a04679ce Merge pull request #12 from turbolnk-com/develop
♻️ refactor: Enhance user navigation dropdown ui and styling
2025-03-07 11:46:01 +07:00
turbolnk d2732e650b ♻️ refactor: Enhance user navigation dropdown ui and styling 2025-03-06 09:34:39 +08:00
semantic-release-bot 79edea7973 🔖 chore(release): v1.0.0-beta.26 [skip ci]
# [1.0.0-beta.26](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2025-03-02)

### 🐛 Bug Fixes

* **icon**: Comment out unused icon collection imports ([f17bf8d](https://github.com/perfect-panel/ppanel-web/commit/f17bf8d))
2025-03-02 16:40:10 +00:00
web@ppanel f17bf8db6e 🐛 fix(icon): Comment out unused icon collection imports 2025-03-02 23:37:15 +07:00
semantic-release-bot 047a69886e 🔖 chore(release): v1.0.0-beta.25 [skip ci]
# [1.0.0-beta.25](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2025-03-01)

###  Features

* **auth**: Add privacy policy link to the footer ([8e16ef1](https://github.com/perfect-panel/ppanel-web/commit/8e16ef1))

### 🐛 Bug Fixes

* **dashboard**: Display subscription creation date in user dashboard ([d0e6df0](https://github.com/perfect-panel/ppanel-web/commit/d0e6df0))
* **request**: Add error code 40005 to trigger logout ([71bf002](https://github.com/perfect-panel/ppanel-web/commit/71bf002))
* **subscribe**: Update payment return URL ([2b80496](https://github.com/perfect-panel/ppanel-web/commit/2b80496))
2025-03-01 12:59:06 +00:00
web@ppanel 71bf002370 🐛 fix(request): Add error code 40005 to trigger logout 2025-03-01 19:55:55 +07:00
web@ppanel d0e6df044e 🐛 fix(dashboard): Display subscription creation date in user dashboard 2025-03-01 19:55:31 +07:00
web@ppanel 8e16ef17ed feat(auth): Add privacy policy link to the footer 2025-03-01 19:40:45 +07:00
web@ppanel 2b80496637 🐛 fix(subscribe): Update payment return URL 2025-03-01 19:35:32 +07:00
semantic-release-bot 01a3aa02ec 🔖 chore(release): v1.0.0-beta.24 [skip ci]
# [1.0.0-beta.24](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2025-02-27)

### ♻ Code Refactoring

* **ui**: Optimize document display ([2ca2992](https://github.com/perfect-panel/ppanel-web/commit/2ca2992))
* Reduce code complexity and improve readability ([e11f18c](https://github.com/perfect-panel/ppanel-web/commit/e11f18c))

###  Features

* **loading**: Add loading components and integrate them in Providers ([d5847fa](https://github.com/perfect-panel/ppanel-web/commit/d5847fa))

### 🎫 Chores

* **merge**: Add advertising module and device settings ([0130e02](https://github.com/perfect-panel/ppanel-web/commit/0130e02))

### 🐛 Bug Fixes

* **locales**: Order recharge related fields ([35210fe](https://github.com/perfect-panel/ppanel-web/commit/35210fe))
2025-02-27 08:02:53 +00:00
web@ppanel 35210fe8cf 🐛 fix(locales): Order recharge related fields 2025-02-27 14:53:19 +07:00
web@ppanel d5847faeda feat(loading): Add loading components and integrate them in Providers 2025-02-27 14:44:30 +07:00
web@ppanel 0130e02ffd 🔧 chore(merge): Add advertising module and device settings 2025-02-27 13:46:51 +07:00
web@ppanel c2b858e3fe Merge pull request #11 from turbolnk-com/develop
♻️ refactor(ui): Optimize document display
2025-02-25 18:35:05 +07:00
turbolnk e11f18c034 ♻️ refactor: Reduce code complexity and improve readability 2025-02-25 19:31:32 +08:00
turbolnk 2ca299224d ♻️ refactor(ui): Optimize document display 2025-02-25 18:38:25 +08:00
semantic-release-bot cf1d66d07e 🔖 chore(release): v1.0.0-beta.23 [skip ci]
# [1.0.0-beta.23](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.22...v1.0.0-beta.23) (2025-02-24)

### 🐛 Bug Fixes

* **auth**: Update email verification logic to use domain suffix check ([62662bb](https://github.com/perfect-panel/ppanel-web/commit/62662bb))
2025-02-24 05:10:30 +00:00
web@ppanel 62662bb79c 🐛 fix(auth): Update email verification logic to use domain suffix check 2025-02-24 12:07:41 +07:00
382 changed files with 7423 additions and 2824 deletions
+199 -7
View File
@@ -1,26 +1,218 @@
<a name="readme-top"></a> <a name="readme-top"></a>
# Changelog # Changelog
# [1.0.0-beta.22](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.21...v1.0.0-beta.22) (2025-02-23) # [1.0.0-beta.33](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.32...v1.0.0-beta.33) (2025-03-18)
### 🐛 Bug Fixes ### 🐛 Bug Fixes
* **locales**: Removed language file import to clean up unnecessary language support ([68f6ab2](https://github.com/perfect-panel/ppanel-web/commit/68f6ab2)) * **subscribe**: Handle optional values in price and discount calculations ([5939763](https://github.com/perfect-panel/ppanel-web/commit/5939763))
# [1.0.0-beta.21](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2025-02-23) # [1.0.0-beta.32](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.31...v1.0.0-beta.32) (2025-03-17)
### 🐛 Bug Fixes
* **forms**: Add step attribute to number inputs for better value control ([b8f4f1e](https://github.com/perfect-panel/ppanel-web/commit/b8f4f1e))
* **locales**: Update invite code text to indicate it's optional ([6a34bfb](https://github.com/perfect-panel/ppanel-web/commit/6a34bfb))
<a name="readme-top"></a>
# Changelog
# [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)
### 🐛 Bug Fixes
- **site**: Se ref to store site configuration for updates ([0c8f091](https://github.com/perfect-panel/ppanel-web/commit/0c8f091))
# [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)
### ✨ Features ### ✨ Features
* **privacy-policy**: Add privacy policy related text and links ([baa68f0](https://github.com/perfect-panel/ppanel-web/commit/baa68f0)) - **api**: Add CheckoutOrder request and response types, and update user purchase request parameters ([48a1b97](https://github.com/perfect-panel/ppanel-web/commit/48a1b97))
- **email**: Add traffic exhaustion template ([bb3bd7b](https://github.com/perfect-panel/ppanel-web/commit/bb3bd7b))
- **formatting**: Update differenceInDays function to return whole days or two decimal places ([bf58f25](https://github.com/perfect-panel/ppanel-web/commit/bf58f25))
- **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 ### 🐛 Bug Fixes
* **locales**: Removed multilingual files to clean up unnecessary language support ([5b151cd](https://github.com/perfect-panel/ppanel-web/commit/5b151cd)) - **dashboard**: Update date display to use start_time if available ([e551232](https://github.com/perfect-panel/ppanel-web/commit/e551232))
* **locales**: Update custom HTML description in language file, ([87381da](https://github.com/perfect-panel/ppanel-web/commit/87381da))
* **table**: Update privacy policy tab translation key and remove unnecessary requestType from OAuth callback ([14b3af5](https://github.com/perfect-panel/ppanel-web/commit/14b3af5)) <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>
# Changelog
# [1.0.0-beta.26](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2025-03-02)
### 🐛 Bug Fixes
- **icon**: Comment out unused icon collection imports ([f17bf8d](https://github.com/perfect-panel/ppanel-web/commit/f17bf8d))
# [1.0.0-beta.25](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2025-03-01)
### ✨ Features
- **auth**: Add privacy policy link to the footer ([8e16ef1](https://github.com/perfect-panel/ppanel-web/commit/8e16ef1))
### 🐛 Bug Fixes
- **dashboard**: Display subscription creation date in user dashboard ([d0e6df0](https://github.com/perfect-panel/ppanel-web/commit/d0e6df0))
- **request**: Add error code 40005 to trigger logout ([71bf002](https://github.com/perfect-panel/ppanel-web/commit/71bf002))
- **subscribe**: Update payment return URL ([2b80496](https://github.com/perfect-panel/ppanel-web/commit/2b80496))
# [1.0.0-beta.24](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2025-02-27)
### ♻ Code Refactoring
- **ui**: Optimize document display ([2ca2992](https://github.com/perfect-panel/ppanel-web/commit/2ca2992))
- Reduce code complexity and improve readability ([e11f18c](https://github.com/perfect-panel/ppanel-web/commit/e11f18c))
### ✨ Features
- **loading**: Add loading components and integrate them in Providers ([d5847fa](https://github.com/perfect-panel/ppanel-web/commit/d5847fa))
### 🎫 Chores
- **merge**: Add advertising module and device settings ([0130e02](https://github.com/perfect-panel/ppanel-web/commit/0130e02))
### 🐛 Bug Fixes
- **locales**: Order recharge related fields ([35210fe](https://github.com/perfect-panel/ppanel-web/commit/35210fe))
<a name="readme-top"></a>
# Changelog
# [1.0.0-beta.23](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.22...v1.0.0-beta.23) (2025-02-24)
### 🐛 Bug Fixes
- **auth**: Update email verification logic to use domain suffix check ([62662bb](https://github.com/perfect-panel/ppanel-web/commit/62662bb))
# [1.0.0-beta.22](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.21...v1.0.0-beta.22) (2025-02-23)
### 🐛 Bug Fixes
- **locales**: Removed language file import to clean up unnecessary language support ([68f6ab2](https://github.com/perfect-panel/ppanel-web/commit/68f6ab2))
# [1.0.0-beta.21](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2025-02-23)
### ✨ Features
- **privacy-policy**: Add privacy policy related text and links ([baa68f0](https://github.com/perfect-panel/ppanel-web/commit/baa68f0))
### 🐛 Bug Fixes
- **locales**: Removed multilingual files to clean up unnecessary language support ([5b151cd](https://github.com/perfect-panel/ppanel-web/commit/5b151cd))
- **locales**: Update custom HTML description in language file, ([87381da](https://github.com/perfect-panel/ppanel-web/commit/87381da))
- **table**: Update privacy policy tab translation key and remove unnecessary requestType from OAuth callback ([14b3af5](https://github.com/perfect-panel/ppanel-web/commit/14b3af5))
<a name="readme-top"></a> <a name="readme-top"></a>
@@ -31,7 +31,7 @@ export default function RegisterForm({
const handleCheckUser = async (email: string) => { const handleCheckUser = async (email: string) => {
try { try {
if (!auth.email.enable_verify) return true; if (!auth.email.enable_domain_suffix) return true;
const domain = email.split('@')[1]; const domain = email.split('@')[1];
const isValid = auth.email?.domain_suffix_list.split('\n').includes(domain || ''); const isValid = auth.email?.domain_suffix_list.split('\n').includes(domain || '');
return isValid; return isValid;
+299
View File
@@ -0,0 +1,299 @@
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import { Button } from '@workspace/ui/components/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@workspace/ui/components/form';
import { RadioGroup, RadioGroupItem } from '@workspace/ui/components/radio-group';
import { ScrollArea } from '@workspace/ui/components/scroll-area';
import {
Sheet,
SheetContent,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@workspace/ui/components/sheet';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { Icon } from '@workspace/ui/custom-components/icon';
import { useTranslations } from 'next-intl';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
const formSchema = z.object({
title: z.string(),
type: z.enum(['image', 'video']),
content: z.string(),
description: z.string(),
target_url: z.string().url(),
start_time: z.number(),
end_time: z.number(),
});
interface AdsFormProps<T> {
onSubmit: (data: T) => Promise<boolean> | boolean;
initialValues?: T;
loading?: boolean;
trigger: string;
title: string;
}
export default function AdsForm<T extends Record<string, any>>({
onSubmit,
initialValues,
loading,
trigger,
title,
}: AdsFormProps<T>) {
const t = useTranslations('ads');
const [open, setOpen] = useState(false);
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
...initialValues,
} as any,
});
useEffect(() => {
form?.reset(initialValues);
}, [form, initialValues]);
const type = form.watch('type');
const startTime = form.watch('start_time');
const renderContentField = () => {
return (
<FormField
control={form.control}
name='content'
render={({ field }) => (
<FormItem>
<FormLabel>{t('form.content')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={
type === 'image'
? 'https://example.com/image.jpg'
: 'https://example.com/video.mp4'
}
value={field.value}
onValueChange={(value) => {
form.setValue('content', value);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
};
async function handleSubmit(data: { [x: string]: any }) {
const bool = await onSubmit(data as T);
if (bool) setOpen(false);
}
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button
onClick={() => {
form.reset();
setOpen(true);
}}
>
{trigger}
</Button>
</SheetTrigger>
<SheetContent className='w-[500px] max-w-full md:max-w-screen-md'>
<SheetHeader>
<SheetTitle>{title}</SheetTitle>
</SheetHeader>
<ScrollArea className='-mx-6 h-[calc(100vh-48px-36px-36px-env(safe-area-inset-top))]'>
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className='space-y-4 px-6 pt-4'>
<FormField
control={form.control}
name='title'
render={({ field }) => (
<FormItem>
<FormLabel>{t('form.title')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('form.enterTitle')}
value={field.value}
onValueChange={(value) => {
form.setValue(field.name, value);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='type'
render={({ field }) => (
<FormItem>
<FormLabel>{t('form.type')}</FormLabel>
<FormControl>
<RadioGroup
defaultValue={field.value}
onValueChange={(value) => {
form.setValue(field.name, value);
}}
className='flex gap-4'
>
<FormItem className='flex items-center space-x-3 space-y-0'>
<FormControl>
<RadioGroupItem value='image' />
</FormControl>
<FormLabel className='font-normal'>{t('form.typeImage')}</FormLabel>
</FormItem>
<FormItem className='flex items-center space-x-3 space-y-0'>
<FormControl>
<RadioGroupItem value='video' />
</FormControl>
<FormLabel className='font-normal'>{t('form.typeVideo')}</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{renderContentField()}
<FormField
control={form.control}
name='description'
render={({ field }) => (
<FormItem>
<FormLabel>{t('form.description')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('form.enterDescription')}
value={field.value}
onValueChange={(value) => {
form.setValue(field.name, value);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='target_url'
render={({ field }) => (
<FormItem>
<FormLabel>{t('form.targetUrl')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('form.enterTargetUrl')}
value={field.value}
onValueChange={(value) => {
form.setValue(field.name, value);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='start_time'
render={({ field }) => (
<FormItem>
<FormLabel>{t('form.startTime')}</FormLabel>
<FormControl>
<EnhancedInput
type='datetime-local'
placeholder={t('form.enterStartTime')}
value={field.value ? new Date(field.value).toISOString().slice(0, 16) : ''}
min={Number(new Date().toISOString().slice(0, 16))}
onValueChange={(value) => {
const timestamp = value ? new Date(value).getTime() : 0;
form.setValue(field.name, timestamp);
const endTime = form.getValues('end_time');
if (endTime && timestamp > endTime) {
form.setValue('end_time', '');
}
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='end_time'
render={({ field }) => {
return (
<FormItem>
<FormLabel>{t('form.endTime')}</FormLabel>
<FormControl>
<EnhancedInput
type='datetime-local'
placeholder={t('form.enterEndTime')}
value={
field.value ? new Date(field.value).toISOString().slice(0, 16) : ''
}
min={Number(
startTime
? new Date(startTime).toISOString().slice(0, 16)
: new Date().toISOString().slice(0, 16),
)}
disabled={!startTime}
onValueChange={(value) => {
const timestamp = value ? new Date(value).getTime() : 0;
if (!startTime || timestamp < startTime) return;
form.setValue(field.name, timestamp);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
</form>
</Form>
</ScrollArea>
<SheetFooter className='flex-row justify-end gap-2 pt-3'>
<Button
variant='outline'
disabled={loading}
onClick={() => {
setOpen(false);
}}
>
{t('form.cancel')}
</Button>
<Button disabled={loading} onClick={form.handleSubmit(handleSubmit)}>
{loading && <Icon icon='mdi:loading' className='mr-2 animate-spin' />}
{t('form.confirm')}
</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}
+162
View File
@@ -0,0 +1,162 @@
'use client';
import { ProTable, ProTableActions } from '@/components/pro-table';
import { createAds, deleteAds, getAdsList, updateAds } from '@/services/admin/ads';
import { Badge } from '@workspace/ui/components/badge';
import { Button } from '@workspace/ui/components/button';
import { Switch } from '@workspace/ui/components/switch';
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
import { formatDate } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { useRef, useState } from 'react';
import { toast } from 'sonner';
import AdsForm from './ads-form';
export default function Page() {
const t = useTranslations('ads');
const [loading, setLoading] = useState(false);
const ref = useRef<ProTableActions>(null);
return (
<ProTable<API.Ads, Record<string, unknown>>
action={ref}
header={{
toolbar: (
<AdsForm<API.CreateAdsRequest>
trigger={t('create')}
title={t('createAds')}
loading={loading}
onSubmit={async (values) => {
setLoading(true);
try {
await createAds({
...values,
status: 0,
});
toast.success(t('createSuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
/>
),
}}
params={[
{
key: 'search',
},
{
key: 'status',
placeholder: t('status'),
options: [
{ label: t('enabled'), value: '1' },
{ label: t('disabled'), value: '0' },
],
},
]}
request={async (pagination, filters) => {
const { data } = await getAdsList({
...pagination,
...filters,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
columns={[
{
accessorKey: 'status',
header: t('status'),
cell: ({ row }) => {
return (
<Switch
defaultChecked={row.getValue('status') === 1}
onCheckedChange={async (checked) => {
await updateAds({
...row.original,
status: checked ? 1 : 0,
});
ref.current?.refresh();
}}
/>
);
},
},
{
accessorKey: 'title',
header: t('title'),
},
{
accessorKey: 'type',
header: t('type'),
cell: ({ row }) => {
const type = row.original.type;
return <Badge>{type}</Badge>;
},
},
{
accessorKey: 'target_url',
header: t('targetUrl'),
},
{
accessorKey: 'description',
header: t('form.description'),
},
{
accessorKey: 'period',
header: t('validityPeriod'),
cell: ({ row }) => {
const { start_time, end_time } = row.original;
return (
<>
{formatDate(start_time)} - {formatDate(end_time)}
</>
);
},
},
]}
actions={{
render: (row) => [
<AdsForm<API.UpdateAdsRequest>
key='edit'
trigger={t('edit')}
title={t('editAds')}
loading={loading}
initialValues={row}
onSubmit={async (values) => {
setLoading(true);
try {
await updateAds({ ...row, ...values });
toast.success(t('updateSuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
/>,
<ConfirmButton
key='delete'
trigger={<Button variant='destructive'>{t('delete')}</Button>}
title={t('confirmDelete')}
description={t('deleteWarning')}
onConfirm={async () => {
await deleteAds({ id: row.id });
toast.success(t('deleteSuccess'));
ref.current?.refresh();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
],
}}
/>
);
}
@@ -5,7 +5,10 @@ import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label'; import { Label } from '@workspace/ui/components/label';
import { Switch } from '@workspace/ui/components/switch'; import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table'; import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { DicesIcon } from 'lucide-react';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { uid } from 'radash';
import { toast } from 'sonner'; import { toast } from 'sonner';
export default function Page() { export default function Page() {
@@ -49,6 +52,89 @@ export default function Page() {
/> />
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow>
<TableCell>
<Label>{t('showAds')}</Label>
<p className='text-muted-foreground text-xs'>{t('showAdsDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.config?.show_ads}
onCheckedChange={(checked) => {
updateConfig('config', {
...data?.config,
show_ads: checked,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('blockVirtualMachine')}</Label>
<p className='text-muted-foreground text-xs'>{t('blockVirtualMachineDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.config?.only_real_device}
onCheckedChange={(checked) => {
updateConfig('config', {
...data?.config,
only_real_device: checked,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('enableSecurity')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableSecurityDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.config?.enable_security}
onCheckedChange={(checked) => {
updateConfig('config', {
...data?.config,
enable_security: checked,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('communicationKey')}</Label>
<p className='text-muted-foreground text-xs'>{t('communicationKeyDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
value={data?.config?.security_secret}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
security_secret: value,
});
}}
suffix={
<div className='bg-muted flex h-9 items-center text-nowrap px-3'>
<DicesIcon
onClick={() => {
const id = uid(32).toLowerCase();
const formatted = `${id.slice(0, 8)}-${id.slice(8, 12)}-${id.slice(12, 16)}-${id.slice(16, 20)}-${id.slice(20)}`;
updateConfig('config', {
...data?.config,
security_secret: formatted,
});
}}
className='cursor-pointer'
/>
</div>
}
/>
</TableCell>
</TableRow>
</TableBody> </TableBody>
</Table> </Table>
); );
@@ -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>
@@ -307,6 +307,7 @@ export default function CouponForm<T extends Record<string, any>>({
<EnhancedInput <EnhancedInput
placeholder={t('form.countPlaceholder')} placeholder={t('form.countPlaceholder')}
type='number' type='number'
step={1}
{...field} {...field}
onValueChange={(value) => { onValueChange={(value) => {
form.setValue(field.name, value); form.setValue(field.name, value);
@@ -327,6 +328,7 @@ export default function CouponForm<T extends Record<string, any>>({
<EnhancedInput <EnhancedInput
placeholder={t('form.userLimitPlaceholder')} placeholder={t('form.userLimitPlaceholder')}
type='number' type='number'
step={1}
{...field} {...field}
onValueChange={(value) => { onValueChange={(value) => {
form.setValue(field.name, value); form.setValue(field.name, value);
+1 -1
View File
@@ -116,7 +116,7 @@ export default function Page(props: any) {
<ul className='grid gap-3'> <ul className='grid gap-3'>
<li className='flex items-center justify-between'> <li className='flex items-center justify-between'>
<span className='text-muted-foreground'>{t('method')}</span> <span className='text-muted-foreground'>{t('method')}</span>
<span>{t(`methods.${row.original.method}`)}</span> <span>{row.original?.payment?.name || row.original?.payment?.platform}</span>
</li> </li>
</ul> </ul>
</HoverCardContent> </HoverCardContent>
@@ -1,256 +0,0 @@
'use client';
import { getAlipayF2FPaymentConfig, updateAlipayF2FPaymentConfig } from '@/services/admin/payment';
import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { Textarea } from '@workspace/ui/components/textarea';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export default function AlipayF2F() {
const t = useTranslations('payment');
const { data, refetch } = useQuery({
queryKey: ['getAlipayF2FPaymentConfig'],
queryFn: async () => {
const { data } = await getAlipayF2FPaymentConfig();
return data.data;
},
});
async function updateConfig(key: string, value: unknown) {
if (data?.[key] === value) return;
try {
await updateAlipayF2FPaymentConfig({
...data,
[key]: value,
} as API.UpdateAlipayF2fRequest);
toast.success(t('saveSuccess'));
refetch();
} catch (error) {
/* empty */
}
}
return (
<Table>
<TableBody>
<TableRow>
<TableCell>
<Label>{t('enable')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.enable}
onCheckedChange={(checked) => {
updateConfig('enable', checked);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.sandbox')}</Label>
<p className='text-muted-foreground text-xs'>{t('alipayf2f.sandboxDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.config.sandbox}
onCheckedChange={(checked) => {
updateConfig('config', {
...data?.config,
sandbox: checked,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('showName')}</Label>
<p className='text-muted-foreground text-xs'>{t('showNameDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.name}
onValueBlur={(value) => updateConfig('name', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('iconUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('iconUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.icon_url}
onValueBlur={(value) => updateConfig('icon', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('notifyUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('notifyUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.domain}
onValueBlur={(value) => updateConfig('domain', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feeMode')}</Label>
<p className='text-muted-foreground text-xs'>{t('feeModeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Select
value={String(data?.fee_mode)}
onValueChange={(value) => {
updateConfig('fee_mode', Number(value));
}}
>
<SelectTrigger>
<SelectValue placeholder='请选择' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value='0'>{t('feeModeItems.0')}</SelectItem>
<SelectItem value='1'>{t('feeModeItems.1')}</SelectItem>
<SelectItem value='2'>{t('feeModeItems.2')}</SelectItem>
<SelectItem value='3'>{t('feeModeItems.3')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feePercent')}</Label>
<p className='text-muted-foreground text-xs'>{t('feePercentDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
max={100}
maxLength={3}
value={data?.fee_percent}
onValueBlur={(value) => updateConfig('fee_percent', value)}
suffix='%'
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('fixedFee')}</Label>
<p className='text-muted-foreground text-xs'>{t('fixedFeeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
min={0}
value={data?.fee_amount}
formatInput={(value) => unitConversion('centsToDollars', value)}
formatOutput={(value) => unitConversion('dollarsToCents', value)}
onValueBlur={(value) => updateConfig('fee_amount', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.appId')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.app_id}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
app_id: value,
})
}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.privateKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<Textarea
placeholder={t('inputPlaceholder')}
defaultValue={data?.config.private_key}
onBlur={(e) => {
updateConfig('config', {
...data?.config,
private_key: e.target.value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.publicKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<Textarea
placeholder={t('inputPlaceholder')}
defaultValue={data?.config.public_key}
onBlur={(e) => {
updateConfig('config', {
...data?.config,
public_key: e.target.value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('alipayf2f.invoiceName')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('alipayf2f.invoiceNameDescription')}
value={data?.config.invoice_name}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
invoice_name: value,
})
}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
-221
View File
@@ -1,221 +0,0 @@
'use client';
import { getEpayPaymentConfig, updateEpayPaymentConfig } from '@/services/admin/payment';
import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export default function Epay() {
const t = useTranslations('payment');
const { data, refetch } = useQuery({
queryKey: ['getEpayPaymentConfig'],
queryFn: async () => {
const { data } = await getEpayPaymentConfig();
return data.data;
},
});
async function updateConfig(key: string, value: unknown) {
if (data?.[key] === value) return;
try {
await updateEpayPaymentConfig({
...data,
[key]: value,
} as API.UpdateEpayRequest);
toast.success(t('saveSuccess'));
refetch();
} catch (error) {
/* empty */
}
}
return (
<Table>
<TableBody>
<TableRow>
<TableCell>
<Label>{t('enable')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.enable}
onCheckedChange={(checked) => {
updateConfig('enable', checked);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('showName')}</Label>
<p className='text-muted-foreground text-xs'>{t('showNameDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.name}
onValueBlur={(value) => updateConfig('name', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('iconUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('iconUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.icon}
onValueBlur={(value) => updateConfig('icon', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('notifyUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('notifyUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.domain}
onValueBlur={(value) => updateConfig('domain', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feeMode')}</Label>
<p className='text-muted-foreground text-xs'>{t('feeModeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Select
value={String(data?.fee_mode)}
onValueChange={(value) => {
updateConfig('fee_mode', Number(value));
}}
>
<SelectTrigger>
<SelectValue placeholder='请选择' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value='0'>{t('feeModeItems.0')}</SelectItem>
<SelectItem value='1'>{t('feeModeItems.1')}</SelectItem>
<SelectItem value='2'>{t('feeModeItems.2')}</SelectItem>
<SelectItem value='3'>{t('feeModeItems.3')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feePercent')}</Label>
<p className='text-muted-foreground text-xs'>{t('feePercentDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
max={100}
maxLength={3}
value={data?.fee_percent}
onValueBlur={(value) => updateConfig('fee_percent', value)}
suffix='%'
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('fixedFee')}</Label>
<p className='text-muted-foreground text-xs'>{t('fixedFeeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
value={data?.fee_amount}
formatInput={(value) => unitConversion('centsToDollars', value)}
formatOutput={(value) => unitConversion('dollarsToCents', value)}
onValueBlur={(value) => updateConfig('fee_amount', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('epay.url')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.url}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
url: value,
})
}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('epay.pid')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.pid}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
pid: value,
})
}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('epay.key')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.key}
onValueBlur={(value) =>
updateConfig('config', {
...data?.config,
key: value,
})
}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
+3 -26
View File
@@ -1,34 +1,11 @@
import Billing from '@/components/billing'; import Billing from '@/components/billing';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@workspace/ui/components/tabs'; import PaymentTable from './payment-table';
import AlipayF2F from './alipayf2f';
import Epay from './epay';
import StripeAlipay from './stripe-alipay';
import StripeWeChatPay from './stripe-wechat-pay';
export default async function Page() { export default async function Page() {
return ( return (
<> <>
<Tabs defaultValue='Epay'> <PaymentTable />
<TabsList className='h-full flex-wrap'> <div className='mt-5 flex flex-col gap-3'>
<TabsTrigger value='Epay'>Epay</TabsTrigger>
<TabsTrigger value='Stripe-Alipay'>Stripe(AliPay)</TabsTrigger>
<TabsTrigger value='Strip-WeChatPay'>Stripe(WeChatPay)</TabsTrigger>
<TabsTrigger value='AlipayF2F'>AlipayF2F</TabsTrigger>
</TabsList>
<TabsContent value='Epay'>
<Epay />
</TabsContent>
<TabsContent value='Stripe-Alipay'>
<StripeAlipay />
</TabsContent>
<TabsContent value='Strip-WeChatPay'>
<StripeWeChatPay />
</TabsContent>
<TabsContent value='AlipayF2F'>
<AlipayF2F />
</TabsContent>
</Tabs>
<div className='flex flex-col gap-3'>
<Billing type='payment' /> <Billing type='payment' />
</div> </div>
</> </>
@@ -0,0 +1,419 @@
'use client';
import useGlobalStore from '@/config/use-global';
import { getPaymentPlatform } from '@/services/admin/payment';
import { zodResolver } from '@hookform/resolvers/zod';
import { useQuery } from '@tanstack/react-query';
import { Button } from '@workspace/ui/components/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@workspace/ui/components/form';
import { RadioGroup, RadioGroupItem } from '@workspace/ui/components/radio-group';
import { ScrollArea } from '@workspace/ui/components/scroll-area';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import {
Sheet,
SheetContent,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@workspace/ui/components/sheet';
import { MarkdownEditor } from '@workspace/ui/custom-components/editor';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { Icon } from '@workspace/ui/custom-components/icon';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import * as z from 'zod';
interface PaymentFormProps<T> {
trigger: React.ReactNode;
title: string;
loading?: boolean;
initialValues?: T;
onSubmit: (values: T) => Promise<boolean>;
isEdit?: boolean;
}
export default function PaymentForm<T>({
trigger,
title,
loading,
initialValues,
onSubmit,
isEdit,
}: PaymentFormProps<T>) {
const t = useTranslations('payment');
const { common } = useGlobalStore();
const { currency } = common;
const [open, setOpen] = useState(false);
const { data: platformData } = useQuery({
queryKey: ['getPaymentPlatform'],
queryFn: async () => {
const { data } = await getPaymentPlatform();
return data?.data?.list || [];
},
});
const formSchema = z.object({
name: z.string().min(1, { message: t('nameRequired') }),
platform: z.string().optional(),
icon: z.string().optional(),
domain: z.string().optional(),
config: z.any(),
fee_mode: z.coerce.number().min(0).max(2),
fee_percent: z.coerce.number().optional(),
fee_amount: z.coerce.number().optional(),
description: z.string().optional(),
});
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: '',
platform: '',
icon: '',
domain: '',
config: {},
fee_mode: 0,
fee_percent: 0,
fee_amount: 0,
...(initialValues as any),
},
});
const feeMode = form.watch('fee_mode');
const platformValue = form.watch('platform');
const configValues = form.watch('config');
const currentPlatform = platformData?.find((p) => p.platform === platformValue);
const currentFieldDescriptions = currentPlatform?.platform_field_description || {};
const configFields = Object.keys(currentFieldDescriptions) || [];
const platformUrl = currentPlatform?.platform_url || '';
useEffect(() => {
if (feeMode === 0) {
form.setValue('fee_amount', 0);
form.setValue('fee_percent', 0);
} else if (feeMode === 1) {
form.setValue('fee_amount', 0);
} else if (feeMode === 2) {
form.setValue('fee_percent', 0);
}
}, [feeMode, form]);
const handleClose = () => {
form.reset();
setOpen(false);
};
const handleSubmit = async (values: z.infer<typeof formSchema>) => {
const cleanedValues = { ...values };
if (values.fee_mode === 0) {
cleanedValues.fee_amount = undefined;
cleanedValues.fee_percent = undefined;
} else if (values.fee_mode === 1) {
cleanedValues.fee_amount = undefined;
} else if (values.fee_mode === 2) {
cleanedValues.fee_percent = undefined;
}
const success = await onSubmit(cleanedValues as unknown as T);
if (success) {
handleClose();
}
};
const openPlatformUrl = () => {
if (platformUrl) {
window.open(platformUrl, '_blank');
}
};
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>{trigger}</SheetTrigger>
<SheetContent className='w-[550px] max-w-full md:max-w-screen-md'>
<SheetHeader>
<SheetTitle>{title}</SheetTitle>
</SheetHeader>
<ScrollArea className='-mx-6 h-[calc(100vh-48px-36px-36px-env(safe-area-inset-top))]'>
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className='space-y-6 px-6 pt-4'>
{/* 基本信息分组 */}
<div className='space-y-4'>
<div className='grid grid-cols-1 gap-4 sm:grid-cols-2'>
<FormField
control={form.control}
name='name'
render={({ field }) => (
<FormItem>
<FormLabel>{t('name')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('namePlaceholder')}
value={field.value}
onValueChange={(value) => form.setValue('name', value as string)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='icon'
render={({ field }) => (
<FormItem>
<FormLabel>{t('icon')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('iconPlaceholder')}
value={field.value}
onValueChange={(value) => form.setValue('icon', value as string)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name='domain'
render={({ field }) => (
<FormItem>
<FormLabel>{t('domain')}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('domainPlaceholder', { example: 'http(s)://example.com' })}
value={field.value}
onValueChange={(value) => form.setValue('domain', value as string)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className='space-y-4'>
<FormField
control={form.control}
name='fee_mode'
render={({ field }) => (
<FormItem>
<FormLabel>{t('handlingFee')}</FormLabel>
<FormControl>
<RadioGroup
onValueChange={(value) => field.onChange(parseInt(value))}
value={field.value.toString()}
className='flex flex-wrap gap-4'
>
<FormItem className='flex items-center space-x-2'>
<FormControl>
<RadioGroupItem value='0' />
</FormControl>
<FormLabel className='!mt-0 cursor-pointer'>{t('noFee')}</FormLabel>
</FormItem>
<FormItem className='flex items-center space-x-2'>
<FormControl>
<RadioGroupItem value='1' />
</FormControl>
<FormLabel className='!mt-0 cursor-pointer'>
{t('percentFee')}
</FormLabel>
</FormItem>
<FormItem className='flex items-center space-x-2'>
<FormControl>
<RadioGroupItem value='2' />
</FormControl>
<FormLabel className='!mt-0 cursor-pointer'>{t('fixedFee')}</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{feeMode === 1 && (
<div className='grid grid-cols-1 sm:w-1/2'>
<FormField
control={form.control}
name='fee_percent'
render={({ field }) => (
<FormItem>
<FormLabel>{t('feePercent')}</FormLabel>
<FormControl>
<EnhancedInput
type='number'
step='0.01'
suffix='%'
value={field.value}
onValueChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
{feeMode === 2 && (
<div className='grid grid-cols-1 sm:w-1/2'>
<FormField
control={form.control}
name='fee_amount'
render={({ field }) => (
<FormItem>
<FormLabel>{t('feeAmount')}</FormLabel>
<FormControl>
<EnhancedInput
type='number'
step='0.01'
prefix={currency.currency_symbol}
suffix={currency.currency_unit}
value={unitConversion('centsToDollars', field.value)}
onValueChange={(value) =>
field.onChange(unitConversion('dollarsToCents', value))
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</div>
<div className='space-y-4'>
{(!platformValue || platformData?.find((p) => p.platform === platformValue)) && (
<FormField
control={form.control}
name='platform'
render={({ field }) => (
<FormItem>
<FormLabel>{t('platform')}</FormLabel>
<Select
onValueChange={(value) => {
form.setValue('platform', value as string);
form.setValue('config', {});
}}
defaultValue={field.value}
value={field.value}
// @ts-expect-error
disabled={isEdit && Boolean(initialValues?.platform)}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={t('selectPlatform')} />
</SelectTrigger>
</FormControl>
<SelectContent>
{platformData?.map((platform) => (
<SelectItem key={platform.platform} value={platform.platform}>
{platform.platform}
</SelectItem>
))}
</SelectContent>
</Select>
{platformUrl ? (
<div className='mt-1 flex justify-end'>
<Button
variant='ghost'
size='sm'
className='h-6 px-2 text-xs'
onClick={openPlatformUrl}
>
<Icon icon='tabler:external-link' className='mr-1 h-3 w-3' />
{t('applyForPayment')}
</Button>
</div>
) : (
<div className='mt-1 h-6'></div>
)}
<FormMessage />
</FormItem>
)}
/>
)}
{configFields.length > 0 && (
<div className='mt-4 space-y-4'>
{configFields.map((fieldKey) => (
<FormItem key={fieldKey}>
<FormLabel>{currentFieldDescriptions[fieldKey]}</FormLabel>
<FormControl>
<EnhancedInput
placeholder={t('configPlaceholder', {
field: currentFieldDescriptions[fieldKey],
})}
value={
configValues && configValues[fieldKey] !== undefined
? configValues[fieldKey]
: ''
}
onValueChange={(value) => {
const newConfig = { ...configValues };
newConfig[fieldKey] = value;
form.setValue('config', newConfig);
}}
/>
</FormControl>
</FormItem>
))}
</div>
)}
<FormField
control={form.control}
name='description'
render={({ field }) => (
<FormItem>
<FormLabel>{t('description')}</FormLabel>
<FormControl>
<MarkdownEditor
value={field.value}
onChange={(value) => form.setValue(field.name, value as string)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</form>
</Form>
</ScrollArea>
<SheetFooter className='flex-row justify-end gap-2 pt-3'>
<Button variant='outline' disabled={loading} onClick={handleClose}>
{t('cancel')}
</Button>
<Button disabled={loading} onClick={form.handleSubmit(handleSubmit)}>
{loading && <Icon icon='mdi:loading' className='mr-2 animate-spin' />}
{t('submit')}
</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}
@@ -0,0 +1,223 @@
'use client';
import { Display } from '@/components/display';
import { ProTable, ProTableActions } from '@/components/pro-table';
import {
createPaymentMethod,
deletePaymentMethod,
getPaymentMethodList,
updatePaymentMethod,
} from '@/services/admin/payment';
import { Avatar, AvatarFallback, AvatarImage } from '@workspace/ui/components/avatar';
import { Badge } from '@workspace/ui/components/badge';
import { Button } from '@workspace/ui/components/button';
import { Switch } from '@workspace/ui/components/switch';
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
import { useTranslations } from 'next-intl';
import { useRef, useState } from 'react';
import { toast } from 'sonner';
import PaymentForm from './payment-form';
export default function PaymentTable() {
const t = useTranslations('payment');
const [loading, setLoading] = useState(false);
const ref = useRef<ProTableActions>(null);
return (
<ProTable<API.PaymentConfig, { search: string }>
action={ref}
header={{
title: t('paymentManagement'),
toolbar: (
<PaymentForm<API.CreatePaymentMethodRequest>
trigger={<Button>{t('create')}</Button>}
title={t('createPayment')}
loading={loading}
onSubmit={async (values) => {
setLoading(true);
try {
await createPaymentMethod({
...values,
enable: false,
});
toast.success(t('createSuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
/>
),
}}
columns={[
{
accessorKey: 'enable',
header: t('enable'),
cell: ({ row }) => {
return (
<Switch
checked={Boolean(row.getValue('enable'))}
onCheckedChange={async (checked) => {
await updatePaymentMethod({
...row.original,
enable: checked,
});
ref.current?.refresh();
}}
/>
);
},
},
{
accessorKey: 'icon',
header: t('icon'),
cell: ({ row }) => {
const icon = row.getValue('icon') as string;
return (
<Avatar className='h-8 w-8'>
{icon ? <AvatarImage src={icon} alt={row.getValue('name')} /> : null}
<AvatarFallback>
{(row.getValue('name') as string)?.charAt(0) || '?'}
</AvatarFallback>
</Avatar>
);
},
},
{
accessorKey: 'name',
header: t('name'),
},
{
accessorKey: 'platform',
header: t('platform'),
cell: ({ row }) => <Badge>{t(row.getValue('platform'))}</Badge>,
},
{
accessorKey: 'notify_url',
header: t('notify_url'),
},
{
accessorKey: 'fee',
header: t('handlingFee'),
cell: ({ row }) => {
const feeMode = row.original.fee_mode;
if (feeMode === 1) {
return <Badge>{row.original.fee_percent}%</Badge>;
} else if (feeMode === 2) {
return (
<Badge>
<Display value={row.original.fee_amount} type='currency' />
</Badge>
);
}
return '--';
},
},
]}
params={[
{
key: 'search',
placeholder: t('searchPlaceholder'),
},
]}
request={async (pagination, filter) => {
const { data } = await getPaymentMethodList({
...pagination,
...filter,
});
return {
list: data?.data?.list || [],
total: data?.data?.total || 0,
};
}}
actions={{
render: (row) => [
<PaymentForm<API.UpdatePaymentMethodRequest>
isEdit
key='edit'
trigger={<Button>{t('edit')}</Button>}
title={t('editPayment')}
loading={loading}
initialValues={row}
onSubmit={async (values) => {
setLoading(true);
try {
await updatePaymentMethod({
...row,
...values,
});
toast.success(t('updateSuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
/>,
<ConfirmButton
key='delete'
trigger={<Button variant='destructive'>{t('delete')}</Button>}
title={t('confirmDelete')}
description={t('deleteWarning')}
onConfirm={async () => {
await deletePaymentMethod({
id: row.id,
});
toast.success(t('deleteSuccess'));
ref.current?.refresh();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
<Button
key='copy'
variant='outline'
onClick={async () => {
setLoading(true);
try {
const { id, ...params } = row;
await createPaymentMethod({
...params,
enable: false,
});
toast.success(t('copySuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
>
{t('copy')}
</Button>,
],
batchRender(rows) {
return [
<ConfirmButton
key='delete'
trigger={<Button variant='destructive'>{t('batchDelete')}</Button>}
title={t('confirmDelete')}
description={t('deleteWarning')}
onConfirm={async () => {
for (const row of rows) {
await deletePaymentMethod({ id: row.id });
}
toast.success(t('deleteSuccess'));
ref.current?.refresh();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
];
},
}}
/>
);
}
@@ -1,232 +0,0 @@
'use client';
import {
getStripeAlipayPaymentConfig,
updateStripeAlipayPaymentConfig,
} from '@/services/admin/payment';
import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export default function Stripe() {
const t = useTranslations('payment');
const { data, refetch } = useQuery({
queryKey: ['getStripeAlipayPaymentConfig'],
queryFn: async () => {
const { data } = await getStripeAlipayPaymentConfig();
return data.data;
},
});
async function updateConfig(key: string, value: unknown) {
if (data?.[key] === value) return;
try {
await updateStripeAlipayPaymentConfig({
...data,
mark: 'stripe_alipay',
[key]: value,
} as any);
toast.success(t('saveSuccess'));
refetch();
} catch (error) {
/* empty */
}
}
return (
<Table>
<TableBody>
<TableRow>
<TableCell>
<Label>{t('aliPay')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.enable}
onCheckedChange={async (checked) => {
updateConfig('enable', checked);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('showName')}</Label>
<p className='text-muted-foreground text-xs'>{t('showNameDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.name}
onValueBlur={(value) => {
updateConfig('name', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('iconUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('iconUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.icon}
onValueBlur={(value) => {
updateConfig('icon', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('notifyUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('notifyUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.domain}
onValueBlur={(value) => {
updateConfig('domain', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feeMode')}</Label>
<p className='text-muted-foreground text-xs'>{t('feeModeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Select
value={String(data?.fee_mode)}
onValueChange={(value) => {
updateConfig('fee_mode', Number(value));
}}
>
<SelectTrigger>
<SelectValue placeholder='请选择' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value='0'>{t('feeModeItems.0')}</SelectItem>
<SelectItem value='1'>{t('feeModeItems.1')}</SelectItem>
<SelectItem value='2'>{t('feeModeItems.2')}</SelectItem>
<SelectItem value='3'>{t('feeModeItems.3')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feePercent')}</Label>
<p className='text-muted-foreground text-xs'>{t('feePercentDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
max={100}
value={data?.fee_percent}
onValueBlur={(value) => {
updateConfig('fee_percent', value);
}}
suffix='%'
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('fixedFee')}</Label>
<p className='text-muted-foreground text-xs'>{t('fixedFeeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
value={data?.fee_amount}
formatInput={(value) => unitConversion('centsToDollars', value)}
formatOutput={(value) => unitConversion('dollarsToCents', value)}
onValueBlur={(value) => updateConfig('fee_amount', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.publicKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.public_key}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
public_key: value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.secretKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.secret_key}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
secret_key: value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.webhookSecret')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.webhook_secret}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
webhook_secret: value,
});
}}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
@@ -1,232 +0,0 @@
'use client';
import {
getStripeWeChatPayPaymentConfig,
updateStripeWeChatPayPaymentConfig,
} from '@/services/admin/payment';
import { useQuery } from '@tanstack/react-query';
import { Label } from '@workspace/ui/components/label';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import { Switch } from '@workspace/ui/components/switch';
import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { unitConversion } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { toast } from 'sonner';
export default function StripeWeChatPay() {
const t = useTranslations('payment');
const { data, refetch } = useQuery({
queryKey: ['getStripeWeChatPayPaymentConfig'],
queryFn: async () => {
const { data } = await getStripeWeChatPayPaymentConfig();
return data.data;
},
});
async function updateConfig(key: string, value: unknown) {
if (data?.[key] === value) return;
try {
await updateStripeWeChatPayPaymentConfig({
...data,
mark: 'stripe_wechat_pay',
[key]: value,
} as any);
toast.success(t('saveSuccess'));
refetch();
} catch (error) {
/* empty */
}
}
return (
<Table>
<TableBody>
<TableRow>
<TableCell>
<Label>{t('wechatPay')}</Label>
<p className='text-muted-foreground text-xs'>{t('enableDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Switch
checked={data?.enable}
onCheckedChange={async (checked) => {
updateConfig('enable', checked);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('showName')}</Label>
<p className='text-muted-foreground text-xs'>{t('showNameDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.name}
onValueBlur={(value) => {
updateConfig('name', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('iconUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('iconUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.icon}
onValueBlur={(value) => {
updateConfig('icon', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('notifyUrl')}</Label>
<p className='text-muted-foreground text-xs'>{t('notifyUrlDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.domain}
onValueBlur={(value) => {
updateConfig('domain', value);
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feeMode')}</Label>
<p className='text-muted-foreground text-xs'>{t('feeModeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<Select
value={String(data?.fee_mode)}
onValueChange={(value) => {
updateConfig('fee_mode', Number(value));
}}
>
<SelectTrigger>
<SelectValue placeholder='请选择' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value='0'>{t('feeModeItems.0')}</SelectItem>
<SelectItem value='1'>{t('feeModeItems.1')}</SelectItem>
<SelectItem value='2'>{t('feeModeItems.2')}</SelectItem>
<SelectItem value='3'>{t('feeModeItems.3')}</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('feePercent')}</Label>
<p className='text-muted-foreground text-xs'>{t('feePercentDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
max={100}
value={data?.fee_percent}
onValueBlur={(value) => {
updateConfig('fee_percent', value);
}}
suffix='%'
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('fixedFee')}</Label>
<p className='text-muted-foreground text-xs'>{t('fixedFeeDescription')}</p>
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
type='number'
min={0}
value={data?.fee_amount}
formatInput={(value) => unitConversion('centsToDollars', value)}
formatOutput={(value) => unitConversion('dollarsToCents', value)}
onValueBlur={(value) => updateConfig('fee_amount', value)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.publicKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.public_key}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
public_key: value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.secretKey')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.secret_key}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
secret_key: value,
});
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Label>{t('stripe.webhookSecret')}</Label>
<p className='text-muted-foreground text-xs' />
</TableCell>
<TableCell className='text-right'>
<EnhancedInput
placeholder={t('inputPlaceholder')}
value={data?.config.webhook_secret}
onValueBlur={(value) => {
updateConfig('config', {
...data?.config,
webhook_secret: value,
});
}}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
@@ -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'),
@@ -298,6 +298,7 @@ export default function SubscribeForm<T extends Record<string, any>>({
<EnhancedInput <EnhancedInput
placeholder={t('form.noLimit')} placeholder={t('form.noLimit')}
type='number' type='number'
step={1}
{...field} {...field}
onValueChange={(value) => { onValueChange={(value) => {
form.setValue(field.name, value); form.setValue(field.name, value);
@@ -321,7 +322,8 @@ export default function SubscribeForm<T extends Record<string, any>>({
<EnhancedInput <EnhancedInput
placeholder={t('form.noLimit')} placeholder={t('form.noLimit')}
type='number' type='number'
value={field.value === -1 ? 0 : field.value} step={1}
value={field.value}
min={0} min={0}
onValueChange={(value) => { onValueChange={(value) => {
form.setValue(field.name, value); form.setValue(field.name, value);
@@ -343,6 +345,7 @@ export default function SubscribeForm<T extends Record<string, any>>({
<EnhancedInput <EnhancedInput
placeholder={t('form.noLimit')} placeholder={t('form.noLimit')}
type='number' type='number'
step={1}
{...field} {...field}
onValueChange={(value) => { onValueChange={(value) => {
form.setValue(field.name, value); form.setValue(field.name, value);
@@ -545,6 +548,7 @@ export default function SubscribeForm<T extends Record<string, any>>({
{ {
name: 'quantity', name: 'quantity',
type: 'number', type: 'number',
step: 1,
min: 1, min: 1,
suffix: unit_time && t(`form.${unit_time}`), suffix: unit_time && t(`form.${unit_time}`),
}, },
@@ -560,7 +564,7 @@ export default function SubscribeForm<T extends Record<string, any>>({
return { return {
...data, ...data,
price: evaluateWithPrecision( price: evaluateWithPrecision(
`${unit_price} * ${data.quantity} * ${data.discount} / 100`, `${unit_price || 0} * ${data.quantity || 0} * ${data.discount || 0} / 100`,
), ),
}; };
}, },
@@ -569,7 +573,6 @@ export default function SubscribeForm<T extends Record<string, any>>({
name: 'price', name: 'price',
placeholder: t('form.discount_price'), placeholder: t('form.discount_price'),
type: 'number', type: 'number',
min: 0,
formatInput: (value) => unitConversion('centsToDollars', value), formatInput: (value) => unitConversion('centsToDollars', value),
formatOutput: (value) => unitConversion('dollarsToCents', value), formatOutput: (value) => unitConversion('dollarsToCents', value),
internal: true, internal: true,
@@ -578,7 +581,7 @@ export default function SubscribeForm<T extends Record<string, any>>({
return { return {
...data, ...data,
discount: evaluateWithPrecision( discount: evaluateWithPrecision(
`${data.price} / ${data.quantity} / ${unit_price} * 100`, `${data.price || 0} / ${data.quantity || 0} / ${unit_price || 0} * 100`,
), ),
}; };
}, },
+48 -2
View File
@@ -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)}
@@ -57,150 +57,154 @@ export function SubscriptionDetail({
{t('onlineDevices')} {t('onlineDevices')}
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
<TabsContent value='logs'> <div className='mt-4 max-h-[60dvh] overflow-y-auto'>
<ProTable<API.UserSubscribeLog, Record<string, unknown>> <TabsContent value='logs'>
columns={[ <ProTable<API.UserSubscribeLog, Record<string, unknown>>
{ columns={[
accessorKey: 'ip', {
header: 'IP', accessorKey: 'ip',
}, header: 'IP',
{ },
accessorKey: 'user_agent', {
header: t('userAgent'), accessorKey: 'user_agent',
}, header: t('userAgent'),
{ },
accessorKey: 'token', {
header: t('token'), accessorKey: 'token',
}, header: t('token'),
{ },
accessorKey: 'created_at', {
header: t('time'), accessorKey: 'created_at',
cell: ({ row }) => formatDate(row.getValue('created_at')), header: t('time'),
}, cell: ({ row }) => formatDate(row.getValue('created_at')),
]} },
request={async (pagination) => { ]}
const { data } = await getUserSubscribeLogs({ request={async (pagination) => {
user_id: userId, const { data } = await getUserSubscribeLogs({
subscribe_id: subscriptionId, user_id: userId,
...pagination, subscribe_id: subscriptionId,
}); ...pagination,
return { });
list: data.data?.list || [], return {
total: data.data?.total || 0, list: data.data?.list || [],
}; total: data.data?.total || 0,
}} };
/> }}
</TabsContent> />
<TabsContent value='traffic'> </TabsContent>
<ProTable<API.TrafficLog, Record<string, unknown>> <TabsContent value='traffic'>
columns={[ <ProTable<API.TrafficLog, Record<string, unknown>>
{ columns={[
accessorKey: 'download', {
header: t('download'), accessorKey: 'download',
cell: ({ row }) => <Display type='traffic' value={row.getValue('download')} />, header: t('download'),
}, cell: ({ row }) => (
{ <Display type='traffic' value={row.getValue('download')} />
accessorKey: 'upload', ),
header: t('upload'), },
cell: ({ row }) => <Display type='traffic' value={row.getValue('upload')} />, {
}, accessorKey: 'upload',
{ header: t('upload'),
accessorKey: 'timestamp', cell: ({ row }) => <Display type='traffic' value={row.getValue('upload')} />,
header: t('time'), },
cell: ({ row }) => formatDate(row.getValue('timestamp')), {
}, accessorKey: 'timestamp',
]} header: t('time'),
request={async (pagination) => { cell: ({ row }) => formatDate(row.getValue('timestamp')),
const { data } = await getUserSubscribeTrafficLogs({ },
user_id: userId, ]}
subscribe_id: subscriptionId, request={async (pagination) => {
...pagination, const { data } = await getUserSubscribeTrafficLogs({
}); user_id: userId,
return { subscribe_id: subscriptionId,
list: data.data?.list || [], ...pagination,
total: data.data?.total || 0, } as API.GetUserSubscribeTrafficLogsParams);
}; return {
}} list: data.data?.list || [],
/> total: data.data?.total || 0,
</TabsContent> };
<TabsContent value='devices'> }}
<ProTable<API.UserDevice, Record<string, unknown>> />
columns={[ </TabsContent>
{ <TabsContent value='devices'>
accessorKey: 'enabled', <ProTable<API.UserDevice, Record<string, unknown>>
header: t('enable'), columns={[
cell: ({ row }) => ( {
<Switch accessorKey: 'enabled',
checked={row.getValue('enabled')} header: t('enable'),
onChange={(checked) => { cell: ({ row }) => (
console.log('Switch:', checked); <Switch
}} checked={row.getValue('enabled')}
/> onChange={(checked) => {
), console.log('Switch:', checked);
}, }}
{ />
accessorKey: 'id', ),
header: 'ID', },
}, {
{ accessorKey: 'id',
accessorKey: 'identifier', header: 'ID',
header: 'IMEI', },
}, {
{ accessorKey: 'identifier',
accessorKey: 'user_agent', header: 'IMEI',
header: t('userAgent'), },
}, {
{ accessorKey: 'user_agent',
accessorKey: 'ip', header: t('userAgent'),
header: 'IP', },
}, {
{ accessorKey: 'ip',
accessorKey: 'online', header: 'IP',
header: t('loginStatus'), },
cell: ({ row }) => ( {
<Badge variant={row.getValue('online') ? 'default' : 'destructive'}> accessorKey: 'online',
{row.getValue('online') ? t('online') : t('offline')} header: t('loginStatus'),
</Badge> cell: ({ row }) => (
), <Badge variant={row.getValue('online') ? 'default' : 'destructive'}>
}, {row.getValue('online') ? t('online') : t('offline')}
{ </Badge>
accessorKey: 'updated_at', ),
header: t('lastSeen'), },
cell: ({ row }) => formatDate(row.getValue('updated_at')), {
}, accessorKey: 'updated_at',
]} header: t('lastSeen'),
request={async (pagination) => { cell: ({ row }) => formatDate(row.getValue('updated_at')),
const { data } = await getUserSubscribeDevices({ },
user_id: userId, ]}
subscribe_id: subscriptionId, request={async (pagination) => {
...pagination, const { data } = await getUserSubscribeDevices({
}); user_id: userId,
return { subscribe_id: subscriptionId,
list: data.data?.list || [], ...pagination,
total: data.data?.total || 0, });
}; return {
}} list: data.data?.list || [],
actions={{ total: data.data?.total || 0,
render: (row) => { };
if (!row.identifier) return []; }}
return [ actions={{
<ConfirmButton render: (row) => {
key='offline' if (!row.identifier) return [];
trigger={<Button variant='destructive'>{t('confirmOffline')}</Button>} return [
title={t('confirmOffline')} <ConfirmButton
description={t('kickOfflineConfirm', { ip: row.ip })} key='offline'
onConfirm={async () => { trigger={<Button variant='destructive'>{t('confirmOffline')}</Button>}
await kickOfflineByUserDevice({ id: row.id }); title={t('confirmOffline')}
toast.success(t('kickOfflineSuccess')); description={t('kickOfflineConfirm', { ip: row.ip })}
}} onConfirm={async () => {
cancelText={t('cancel')} await kickOfflineByUserDevice({ id: row.id });
confirmText={t('confirm')} toast.success(t('kickOfflineSuccess'));
/>, }}
]; cancelText={t('cancel')}
}, confirmText={t('confirm')}
}} />,
/> ];
</TabsContent> },
}}
/>
</TabsContent>
</div>
</Tabs> </Tabs>
</div> </div>
</DialogContent> </DialogContent>
+14 -1
View File
@@ -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'));
+6 -1
View File
@@ -61,6 +61,11 @@ export const navs = [
icon: 'flat-color-icons:lock-portrait', icon: 'flat-color-icons:lock-portrait',
items: AuthControl, items: AuthControl,
}, },
{
title: 'ADS Config',
url: '/dashboard/ads',
icon: 'flat-color-icons:electrical-sensor',
},
{ {
title: 'Payment Config', title: 'Payment Config',
url: '/dashboard/payment', url: '/dashboard/payment',
@@ -115,7 +120,7 @@ export const navs = [
{ {
title: 'Announcement Management', title: 'Announcement Management',
url: '/dashboard/announcement', url: '/dashboard/announcement',
icon: 'flat-color-icons:news', icon: 'flat-color-icons:advertising',
}, },
{ {
title: 'Ticket Management', title: 'Ticket Management',
+2
View File
@@ -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: '',
@@ -63,6 +64,7 @@ export const useGlobalStore = create<GlobalStore>((set) => ({
verify_code_interval: 60, verify_code_interval: 60,
}, },
oauth_methods: [], oauth_methods: [],
web_ad: false,
}, },
user: undefined, user: undefined,
setCommon: (common) => setCommon: (common) =>
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "Zrušit",
"confirm": "Potvrdit",
"confirmDelete": "Potvrdit smazání",
"create": "Vytvořit inzerát",
"createAds": "Vytvořit reklamu",
"createSuccess": "Úspěšně vytvořeno",
"delete": "Smazat",
"deleteSuccess": "Úspěšně smazáno",
"deleteWarning": "Opravdu chcete smazat tento inzerát?",
"disabled": "Deaktivováno",
"edit": "Upravit",
"editAds": "Upravit reklamu",
"enabled": "Aktivováno",
"form": {
"cancel": "Zrušit",
"confirm": "Potvrdit",
"content": "Obsah",
"description": "Popis",
"endTime": "Čas ukončení",
"enterContent": "Prosím zadejte obsah inzerátu",
"enterDescription": "Prosím zadejte popis inzerátu",
"enterEndTime": "Prosím vyberte čas ukončení",
"enterStartTime": "Prosím vyberte čas zahájení",
"enterTargetUrl": "Prosím zadejte cílovou URL",
"enterTitle": "Prosím zadejte název inzerátu",
"startTime": "Čas zahájení",
"targetUrl": "Cílová URL",
"title": "Název",
"type": "Typ inzerátu",
"typeImage": "Obrázek",
"typeVideo": "Video"
},
"status": "Stav",
"targetUrl": "Cílová URL",
"title": "Název",
"type": "Typ",
"updateSuccess": "Úspěšně aktualizováno",
"validityPeriod": "Doba platnosti"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "Zadejte platnou e-mailovou adresu.", "email": "Zadejte platnou e-mailovou adresu.",
"existingAccount": "Již máte účet?", "existingAccount": "Již máte účet?",
"get": "Získat", "get": "Získat",
"invite": "Pozvánkový kód", "invite": "Pozvánkový kód (volitelné)",
"message": "#### Vážený uživateli,\n\nDěkujeme za váš zájem a podporu. V důsledku úpravy provozní strategie jsme uzavřeli registraci nových uživatelů. Během této doby nebude používání stávajících účtů nijak ovlivněno.\n\nSnažíme se vám poskytnout lepší služby a zážitky, a proto během uzavření registrace provedeme komplexní optimalizaci systému a aktualizaci funkcí. V budoucnu vás přivítáme s kvalitnějším obsahem a službami.\n\nSledujte naše webové stránky a sociální média pro nejnovější aktualizace a oznámení. Děkujeme za vaše pochopení a podporu.\n\nPokud máte jakékoli dotazy nebo potřebujete pomoc, neváhejte kontaktovat náš zákaznický servis.\n\n**Ještě jednou děkujeme za vaši podporu a pochopení.**", "message": "#### Vážený uživateli,\n\nDěkujeme za váš zájem a podporu. V důsledku úpravy provozní strategie jsme uzavřeli registraci nových uživatelů. Během této doby nebude používání stávajících účtů nijak ovlivněno.\n\nSnažíme se vám poskytnout lepší služby a zážitky, a proto během uzavření registrace provedeme komplexní optimalizaci systému a aktualizaci funkcí. V budoucnu vás přivítáme s kvalitnějším obsahem a službami.\n\nSledujte naše webové stránky a sociální média pro nejnovější aktualizace a oznámení. Děkujeme za vaše pochopení a podporu.\n\nPokud máte jakékoli dotazy nebo potřebujete pomoc, neváhejte kontaktovat náš zákaznický servis.\n\n**Ještě jednou děkujeme za vaši podporu a pochopení.**",
"passwordMismatch": "Hesla se neshodují", "passwordMismatch": "Hesla se neshodují",
"success": "Registrace úspěšná, automaticky přihlášeno!", "success": "Registrace úspěšná, automaticky přihlášeno!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "Blokovat virtuální stroj",
"blockVirtualMachineDescription": "Pokud je povoleno, zařízení nebudou moci běžet na virtuálních strojích nebo emulátorech",
"communicationKey": "Komunikační klíč",
"communicationKeyDescription": "Komunikační klíč se používá pro bezpečnou komunikaci mezi zařízeními a servery",
"enable": "Povolit", "enable": "Povolit",
"enableDescription": "Po povolení jsou podporovány různé identifikátory zařízení, jako jsou IMEI/IDFA/IDFV/AndroidID/Mac adresa pro přihlášení a registraci" "enableDescription": "Po povolení jsou podporovány různé identifikátory zařízení, jako jsou IMEI/IDFA/IDFV/AndroidID/Mac adresa pro přihlášení a registraci",
"enableSecurity": "Povolit šifrování komunikace",
"enableSecurityDescription": "Pokud je povoleno, komunikace mezi zařízeními a servery bude šifrována",
"saveFailed": "Uložení se nezdařilo",
"saveSuccess": "Uložení bylo úspěšné",
"showAds": "Zobrazit reklamy",
"showAdsDescription": "Pokud je povoleno, na zařízeních se budou zobrazovat reklamy"
} }
+2
View File
@@ -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",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "Konfigurace ADS",
"Announcement Management": "Správa oznámení", "Announcement Management": "Správa oznámení",
"Apple": "Apple ID", "Apple": "Apple ID",
"Auth Control": "Řízení ověřování", "Auth Control": "Řízení ověřování",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "Platba Alipay", "AlipayF2F": "Alipay Osobně",
"alipayf2f": { "EPay": "EPay",
"appId": "ID aplikace Alipay", "Stripe": "Stripe",
"invoiceName": "Název vlastního produktu", "applyForPayment": "Žádat o platbu",
"invoiceNameDescription": "Bude zobrazeno na faktuře Alipay", "balance": "Zůstatek",
"privateKey": "Soukromý klíč Alipay", "batchDelete": "Hromadné smazání",
"publicKey": "Veřejný klíč Alipay", "cancel": "Zrušit",
"sandbox": "Režim sandbox", "config": "Nastavení",
"sandboxDescription": "Povolte režim sandbox pro testování plateb" "configInfo": "Informace o nastavení",
}, "configPlaceholder": "Vyplňte prosím poskytnutou konfiguraci {field}",
"confirm": "Potvrdit",
"confirmDelete": "Potvrdit smazání",
"copy": "Kopírovat",
"copySuccess": "Úspěšně zkopírováno",
"create": "Přidat platební metodu",
"createPayment": "Přidat platební metodu",
"createSuccess": "Úspěšně vytvořeno",
"delete": "Smazat",
"deleteSuccess": "Úspěšně smazáno",
"deleteWarning": "Opravdu chcete smazat tuto platební metodu? Tuto akci nelze vrátit zpět.",
"description": "Popis",
"descriptionPlaceholder": "Zadejte popis platební metody",
"domain": "Doména",
"domainPlaceholder": "volitelné, např. {example}",
"edit": "Upravit",
"editPayment": "Upravit platební metodu",
"enable": "Povolit", "enable": "Povolit",
"enableDescription": "Povolit tento způsob platby", "feeAmount": "Pevná částka",
"epay": {
"key": "KLÍČ",
"pid": "PID",
"url": "URL"
},
"feeMode": "Způsob účtování", "feeMode": "Způsob účtování",
"feeModeDescription": "Způsob účtování dodatečných poplatků",
"feeModeItems": ["Bez poplatku", "Procento", "Pevná částka", "Obojí výše uvedené"],
"feePercent": "Procentuální poplatek (volitelně)", "feePercent": "Procentuální poplatek (volitelně)",
"feePercentDescription": "Přidání poplatku k částce objednávky",
"fixedFee": "Pevný poplatek (volitelný)", "fixedFee": "Pevný poplatek (volitelný)",
"fixedFeeDescription": "Přidání pevného poplatku k částce objednávky", "handlingFee": "Poplatek za zpracování",
"iconUrl": "URL ikony (volitelné)", "icon": "Ikona",
"iconUrlDescription": "Používá se pro zobrazení na frontendu", "iconPlaceholder": "Zadejte URL ikony",
"inputPlaceholder": "Prosím zadejte", "mark": "Identifikátor",
"key": "KLÍČ", "markPlaceholder": "Zadejte identifikátor platební metody",
"notifyUrl": "Vlastní oznámení URL (volitelné)", "name": "Název",
"notifyUrlDescription": "Oznámení brány budou odeslána na tuto doménu", "namePlaceholder": "Zadejte název platební metody",
"payForRecommendations": "Zaplatit za doporučení", "nameRequired": "Název je povinný",
"pid": "PID", "noFee": "Žádný poplatek",
"saveSuccess": "Uložení bylo úspěšné", "notify_url": "URL pro oznámení",
"showName": "Zobrazit název", "paymentManagement": "Správa plateb",
"showNameDescription": "Používá se pro zobrazení na frontendu", "percentFee": "Procento",
"stripe": { "platform": "Platforma",
"publicKey": "VEŘEJNÝ KLÍČ", "searchPlaceholder": "Zadejte hledané výrazy",
"secretKey": "TAJNÝ KLÍČ", "selectPlatform": "Vyberte platformu",
"webhookSecret": "PODPIS WEBHOOK KLÍČE" "selectType": "Vyberte typ platby",
}, "submit": "Odeslat",
"url": "URL", "type": "Typ",
"wechatPay": "WeChat Pay" "updateSuccess": "Úspěšně aktualizováno"
} }
+2
View File
@@ -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",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "Abbrechen",
"confirm": "Bestätigen",
"confirmDelete": "Löschen bestätigen",
"create": "Anzeige erstellen",
"createAds": "Anzeige erstellen",
"createSuccess": "Erfolgreich erstellt",
"delete": "Löschen",
"deleteSuccess": "Erfolgreich gelöscht",
"deleteWarning": "Sind Sie sicher, dass Sie diese Anzeige löschen möchten?",
"disabled": "Deaktiviert",
"edit": "Bearbeiten",
"editAds": "Anzeige bearbeiten",
"enabled": "Aktiviert",
"form": {
"cancel": "Abbrechen",
"confirm": "Bestätigen",
"content": "Inhalt",
"description": "Beschreibung",
"endTime": "Endzeit",
"enterContent": "Bitte geben Sie den Anzeigeninhalt ein",
"enterDescription": "Bitte geben Sie die Anzeigenbeschreibung ein",
"enterEndTime": "Bitte wählen Sie die Endzeit aus",
"enterStartTime": "Bitte wählen Sie die Startzeit aus",
"enterTargetUrl": "Bitte geben Sie die Ziel-URL ein",
"enterTitle": "Bitte geben Sie den Anzeigentitel ein",
"startTime": "Startzeit",
"targetUrl": "Ziel-URL",
"title": "Titel",
"type": "Anzeigentyp",
"typeImage": "Bild",
"typeVideo": "Video"
},
"status": "Status",
"targetUrl": "Ziel-URL",
"title": "Titel",
"type": "Typ",
"updateSuccess": "Erfolgreich aktualisiert",
"validityPeriod": "Gültigkeitsdauer"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "Bitte geben Sie eine gültige E-Mail-Adresse ein.", "email": "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
"existingAccount": "Bereits ein Konto?", "existingAccount": "Bereits ein Konto?",
"get": "Erhalten", "get": "Erhalten",
"invite": "Einladungscode", "invite": "Einladungscode (optional)",
"message": "#### Sehr geehrter Nutzer,\n\nVielen Dank für Ihr Interesse und Ihre Unterstützung. Aufgrund einer Anpassung unserer Betriebsstrategie haben wir die Registrierung für neue Nutzer geschlossen. Während dieser Zeit wird die Nutzung für bestehende Nutzer nicht beeinträchtigt.\n\nWir sind bestrebt, Ihnen einen besseren Service und ein besseres Erlebnis zu bieten, und werden daher während der Schließung der Registrierung umfassende Systemoptimierungen und Funktionsupgrades durchführen. In Zukunft werden wir Sie mit hochwertigeren Inhalten und Dienstleistungen willkommen heißen.\n\nBitte folgen Sie unserer Website und unseren Social-Media-Plattformen, um die neuesten Updates und Benachrichtigungen zu erhalten. Vielen Dank für Ihr Verständnis und Ihre Unterstützung.\n\nBei Fragen oder wenn Sie Hilfe benötigen, kontaktieren Sie bitte jederzeit unser Kundenserviceteam.\n\n**Nochmals vielen Dank für Ihre Unterstützung und Ihr Verständnis.**", "message": "#### Sehr geehrter Nutzer,\n\nVielen Dank für Ihr Interesse und Ihre Unterstützung. Aufgrund einer Anpassung unserer Betriebsstrategie haben wir die Registrierung für neue Nutzer geschlossen. Während dieser Zeit wird die Nutzung für bestehende Nutzer nicht beeinträchtigt.\n\nWir sind bestrebt, Ihnen einen besseren Service und ein besseres Erlebnis zu bieten, und werden daher während der Schließung der Registrierung umfassende Systemoptimierungen und Funktionsupgrades durchführen. In Zukunft werden wir Sie mit hochwertigeren Inhalten und Dienstleistungen willkommen heißen.\n\nBitte folgen Sie unserer Website und unseren Social-Media-Plattformen, um die neuesten Updates und Benachrichtigungen zu erhalten. Vielen Dank für Ihr Verständnis und Ihre Unterstützung.\n\nBei Fragen oder wenn Sie Hilfe benötigen, kontaktieren Sie bitte jederzeit unser Kundenserviceteam.\n\n**Nochmals vielen Dank für Ihre Unterstützung und Ihr Verständnis.**",
"passwordMismatch": "Die eingegebenen Passwörter stimmen nicht überein", "passwordMismatch": "Die eingegebenen Passwörter stimmen nicht überein",
"success": "Registrierung erfolgreich, Sie sind automatisch eingeloggt!", "success": "Registrierung erfolgreich, Sie sind automatisch eingeloggt!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "Virtuelle Maschine blockieren",
"blockVirtualMachineDescription": "Wenn aktiviert, wird verhindert, dass Geräte auf virtuellen Maschinen oder Emulatoren ausgeführt werden.",
"communicationKey": "Kommunikationsschlüssel",
"communicationKeyDescription": "Der Kommunikationsschlüssel wird für die sichere Kommunikation zwischen Geräten und Servern verwendet.",
"enable": "Aktivieren", "enable": "Aktivieren",
"enableDescription": "Nach der Aktivierung werden mehrere Geräteidentifikatoren wie IMEI/IDFA/IDFV/AndroidID/Mac-Adresse für die Anmeldung und Registrierung unterstützt." "enableDescription": "Nach der Aktivierung werden mehrere Geräteidentifikatoren wie IMEI/IDFA/IDFV/AndroidID/Mac-Adresse für die Anmeldung und Registrierung unterstützt.",
"enableSecurity": "Kommunikationsverschlüsselung aktivieren",
"enableSecurityDescription": "Wenn aktiviert, wird die Kommunikation zwischen Geräten und Servern verschlüsselt.",
"saveFailed": "Speichern fehlgeschlagen",
"saveSuccess": "Speichern erfolgreich",
"showAds": "Werbung anzeigen",
"showAdsDescription": "Wenn aktiviert, werden auf den Geräten Anzeigen angezeigt."
} }
+2
View File
@@ -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",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "ADS-Konfiguration",
"Announcement Management": "Ankündigungsverwaltung", "Announcement Management": "Ankündigungsverwaltung",
"Apple": "Apple-ID", "Apple": "Apple-ID",
"Auth Control": "Authentifizierungskontrolle", "Auth Control": "Authentifizierungskontrolle",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "Alipay-Zahlung", "AlipayF2F": "Alipay Gesicht zu Gesicht",
"alipayf2f": { "EPay": "EPay",
"appId": "Alipay Anwendungs-ID", "Stripe": "Stripe",
"invoiceName": "Benutzerdefinierter Produktname", "applyForPayment": "Zahlung beantragen",
"invoiceNameDescription": "Wird in der Alipay-Rechnung angezeigt", "balance": "Guthaben",
"privateKey": "Alipay Privatschlüssel", "batchDelete": "Massenlöschung",
"publicKey": "Alipay Öffentlicher Schlüssel", "cancel": "Abbrechen",
"sandbox": "Sandbox-Modus", "config": "Konfiguration",
"sandboxDescription": "Aktivieren Sie den Sandbox-Modus, um Zahlungen zu testen" "configInfo": "Konfigurationsinformationen",
}, "configPlaceholder": "Bitte füllen Sie die bereitgestellte {field} Konfiguration aus",
"confirm": "Bestätigen",
"confirmDelete": "Löschen bestätigen",
"copy": "Kopieren",
"copySuccess": "Erfolgreich kopiert",
"create": "Zahlungsmethode hinzufügen",
"createPayment": "Zahlungsmethode hinzufügen",
"createSuccess": "Erfolgreich erstellt",
"delete": "Löschen",
"deleteSuccess": "Erfolgreich gelöscht",
"deleteWarning": "Sind Sie sicher, dass Sie diese Zahlungsmethode löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
"description": "Beschreibung",
"descriptionPlaceholder": "Geben Sie die Beschreibung der Zahlungsmethode ein",
"domain": "Domain",
"domainPlaceholder": "optional, z.B. {example}",
"edit": "Bearbeiten",
"editPayment": "Zahlungsmethode bearbeiten",
"enable": "Aktivieren", "enable": "Aktivieren",
"enableDescription": "Diese Zahlungsmethode aktivieren", "feeAmount": "Fester Betrag",
"epay": {
"key": "SCHLÜSSEL",
"pid": "PID",
"url": "URL"
},
"feeMode": "Gebührenmodus", "feeMode": "Gebührenmodus",
"feeModeDescription": "Gebührenmodus für zusätzliche Gebühren",
"feeModeItems": ["Keine Gebühr", "Prozentsatz", "Fester Betrag", "Beides"],
"feePercent": "Prozentgebühr (optional)", "feePercent": "Prozentgebühr (optional)",
"feePercentDescription": "Gebühren werden zusätzlich zum Bestellbetrag hinzugefügt",
"fixedFee": "Feste Gebühr (optional)", "fixedFee": "Feste Gebühr (optional)",
"fixedFeeDescription": "Feste Gebühr zusätzlich zum Bestellbetrag hinzufügen", "handlingFee": "Bearbeitungsgebühr",
"iconUrl": "Symbol-URL (optional)", "icon": "Symbol",
"iconUrlDescription": "Zur Anzeige im Frontend verwendet", "iconPlaceholder": "Geben Sie die URL des Symbols ein",
"inputPlaceholder": "Bitte eingeben", "mark": "Kennung",
"key": "SCHLÜSSEL", "markPlaceholder": "Geben Sie die Kennung der Zahlungsmethode ein",
"notifyUrl": "Benutzerdefinierte Benachrichtigungs-URL (optional)", "name": "Name",
"notifyUrlDescription": "Gateway-Benachrichtigungen werden an diese Domain gesendet", "namePlaceholder": "Geben Sie den Namen der Zahlungsmethode ein",
"payForRecommendations": "Für Empfehlungen bezahlen", "nameRequired": "Name ist erforderlich",
"pid": "PID", "noFee": "Keine Gebühr",
"saveSuccess": "Erfolgreich gespeichert", "notify_url": "Benachrichtigungs-URL",
"showName": "Anzeigename", "paymentManagement": "Zahlungsverwaltung",
"showNameDescription": "Zur Anzeige im Frontend verwendet", "percentFee": "Prozentsatz",
"stripe": { "platform": "Plattform",
"publicKey": "ÖFFENTLICHER SCHLÜSSEL", "searchPlaceholder": "Suchbegriffe eingeben",
"secretKey": "GEHEIMER SCHLÜSSEL", "selectPlatform": "Plattform auswählen",
"webhookSecret": "WEBHOOK-SCHLÜSSEL-SIGNATUR" "selectType": "Zahlungsart auswählen",
}, "submit": "Einreichen",
"url": "URL", "type": "Typ",
"wechatPay": "WeChat Pay" "updateSuccess": "Erfolgreich aktualisiert"
} }
+2
View File
@@ -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",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"create": "Create Ad",
"createAds": "Create Advertisement",
"createSuccess": "Successfully created",
"delete": "Delete",
"deleteSuccess": "Successfully deleted",
"deleteWarning": "Are you sure you want to delete this ad?",
"disabled": "Disabled",
"edit": "Edit",
"editAds": "Edit Advertisement",
"enabled": "Enabled",
"form": {
"cancel": "Cancel",
"confirm": "Confirm",
"content": "Content",
"description": "Description",
"endTime": "End Time",
"enterContent": "Please enter ad content",
"enterDescription": "Please enter ad description",
"enterEndTime": "Please select end time",
"enterStartTime": "Please select start time",
"enterTargetUrl": "Please enter target URL",
"enterTitle": "Please enter ad title",
"startTime": "Start Time",
"targetUrl": "Target URL",
"title": "Title",
"type": "Ad Type",
"typeImage": "Image",
"typeVideo": "Video"
},
"status": "Status",
"targetUrl": "Target URL",
"title": "Title",
"type": "Type",
"updateSuccess": "Successfully updated",
"validityPeriod": "Validity Period"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "Please enter a valid email address.", "email": "Please enter a valid email address.",
"existingAccount": "Already have an account?", "existingAccount": "Already have an account?",
"get": "Get", "get": "Get",
"invite": "Invitation code", "invite": "Invitation code (optional)",
"message": "#### Dear User, Hello!\n\nThank you for your attention and support. Due to adjustments in site operation strategy, we have closed the new user registration function. During this period, existing users will not be affected.\n\nWe are committed to providing you with better service and experience, so we will conduct comprehensive system optimization and feature upgrades during the registration closure. In the future, we will welcome you with better content and services.\n\nPlease follow our website and social media platforms to get the latest updates and notifications. Thank you for your understanding and support.\n\nIf you have any questions or need assistance, please feel free to contact our customer service team.\n\n**Thank you again for your support and understanding.**", "message": "#### Dear User, Hello!\n\nThank you for your attention and support. Due to adjustments in site operation strategy, we have closed the new user registration function. During this period, existing users will not be affected.\n\nWe are committed to providing you with better service and experience, so we will conduct comprehensive system optimization and feature upgrades during the registration closure. In the future, we will welcome you with better content and services.\n\nPlease follow our website and social media platforms to get the latest updates and notifications. Thank you for your understanding and support.\n\nIf you have any questions or need assistance, please feel free to contact our customer service team.\n\n**Thank you again for your support and understanding.**",
"passwordMismatch": "Passwords do not match", "passwordMismatch": "Passwords do not match",
"success": "Registration successful, automatically logged in!", "success": "Registration successful, automatically logged in!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "Block Virtual Machine",
"blockVirtualMachineDescription": "When enabled, devices will be prevented from running on virtual machines or emulators",
"communicationKey": "Communication Key",
"communicationKeyDescription": "Communication key is used for secure communication between devices and servers",
"enable": "Enable", "enable": "Enable",
"enableDescription": "After enabling, multiple device identifiers such as IMEI/IDFA/IDFV/AndroidID/Mac address are supported for login and registration" "enableDescription": "After enabling, multiple device identifiers such as IMEI/IDFA/IDFV/AndroidID/Mac address are supported for login and registration",
"enableSecurity": "Enable Communication Encryption",
"enableSecurityDescription": "When enabled, the communication between devices and servers will be encrypted",
"saveFailed": "Save failed",
"saveSuccess": "Save successful",
"showAds": "Show Advertisements",
"showAdsDescription": "When enabled, advertisements will be displayed on devices"
} }
+2
View File
@@ -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",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "ADS Config",
"Announcement Management": "Announcement Management", "Announcement Management": "Announcement Management",
"Apple": "Apple ID", "Apple": "Apple ID",
"Auth Control": "Auth Control", "Auth Control": "Auth Control",
+48 -40
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "Alipay payment", "AlipayF2F": "Alipay Face to Face",
"alipayf2f": { "EPay": "EPay",
"appId": "Alipay App ID", "Stripe": "Stripe",
"invoiceName": "Custom Product Name", "applyForPayment": "Apply for Payment",
"invoiceNameDescription": "Will be displayed on Alipay bill", "balance": "Balance",
"privateKey": "Alipay Private Key", "batchDelete": "Batch Delete",
"publicKey": "Alipay Public Key", "cancel": "Cancel",
"sandbox": "Sandbox Mode", "config": "Configuration",
"sandboxDescription": "Enable sandbox mode to test payments" "configInfo": "Configuration Information",
}, "configPlaceholder": "Please fill in the provided {field} configuration",
"confirm": "Confirm",
"confirmDelete": "Confirm Delete",
"copy": "Copy",
"copySuccess": "Copied successfully",
"create": "Add Payment Method",
"createPayment": "Add Payment Method",
"createSuccess": "Created successfully",
"delete": "Delete",
"deleteSuccess": "Deleted successfully",
"deleteWarning": "Are you sure you want to delete this payment method? This action cannot be undone.",
"description": "Description",
"descriptionPlaceholder": "Enter payment method description",
"domain": "Domain",
"domainPlaceholder": "optional, e.g. {example}",
"edit": "Edit",
"editPayment": "Edit Payment Method",
"enable": "Enable", "enable": "Enable",
"enableDescription": "Enable this payment method", "feeAmount": "Fixed Amount",
"epay": {
"key": "KEY",
"pid": "PID",
"url": "URL"
},
"feeMode": "Fee Mode", "feeMode": "Fee Mode",
"feeModeDescription": "Fee charging method for additional fees", "feePercent": "Fee Percentage",
"feeModeItems": ["No Fee", "Percentage", "Fixed Amount", "Both"], "fixedFee": "Fixed Amount",
"feePercent": "Percentage Fee (optional)", "handlingFee": "Handling Fee",
"feePercentDescription": "Add fee on top of order amount", "icon": "Icon",
"fixedFee": "Fixed Fee (optional)", "iconPlaceholder": "Enter icon URL",
"fixedFeeDescription": "Add fixed fee on top of order amount", "mark": "Identifier",
"iconUrl": "Icon URL (optional)", "markPlaceholder": "Enter payment method identifier",
"iconUrlDescription": "Used for frontend display", "name": "Name",
"inputPlaceholder": "Please enter", "namePlaceholder": "Enter payment method name",
"key": "KEY", "nameRequired": "Name is required",
"notifyUrl": "Custom Notification Domain (optional)", "noFee": "No Fee",
"notifyUrlDescription": "Gateway notifications will be sent to this domain", "notify_url": "Notify URL",
"payForRecommendations": "Pay for Recommendations", "paymentManagement": "Payment Management",
"pid": "PID", "percentFee": "Percentage",
"saveSuccess": "Save Successful", "platform": "Platform",
"showName": "Display Name", "searchPlaceholder": "Enter search terms",
"showNameDescription": "Used for frontend display", "selectPlatform": "Select Platform",
"stripe": { "selectType": "Select payment type",
"publicKey": "PUBLIC KEY", "submit": "Submit",
"secretKey": "SECRET KEY", "type": "Type",
"webhookSecret": "WEBHOOK Secret Key" "updateSuccess": "Updated successfully"
},
"url": "URL",
"wechatPay": "WeChat Pay"
} }
+2
View File
@@ -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",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "Cancelar",
"confirm": "Confirmar",
"confirmDelete": "Confirmar eliminación",
"create": "Crear anuncio",
"createAds": "Crear publicidad",
"createSuccess": "Creado con éxito",
"delete": "Eliminar",
"deleteSuccess": "Eliminado con éxito",
"deleteWarning": "¿Está seguro de que desea eliminar este anuncio?",
"disabled": "Deshabilitado",
"edit": "Editar",
"editAds": "Editar publicidad",
"enabled": "Habilitado",
"form": {
"cancel": "Cancelar",
"confirm": "Confirmar",
"content": "Contenido",
"description": "Descripción",
"endTime": "Hora de finalización",
"enterContent": "Por favor, introduzca el contenido del anuncio",
"enterDescription": "Por favor, introduzca la descripción del anuncio",
"enterEndTime": "Por favor, seleccione la hora de finalización",
"enterStartTime": "Por favor, seleccione la hora de inicio",
"enterTargetUrl": "Por favor, introduzca la URL de destino",
"enterTitle": "Por favor, introduzca el título del anuncio",
"startTime": "Hora de inicio",
"targetUrl": "URL de destino",
"title": "Título",
"type": "Tipo de anuncio",
"typeImage": "Imagen",
"typeVideo": "Vídeo"
},
"status": "Estado",
"targetUrl": "URL de destino",
"title": "Título",
"type": "Tipo",
"updateSuccess": "Actualizado con éxito",
"validityPeriod": "Período de validez"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "Por favor, introduce una dirección de correo electrónico válida.", "email": "Por favor, introduce una dirección de correo electrónico válida.",
"existingAccount": "¿Ya tienes una cuenta?", "existingAccount": "¿Ya tienes una cuenta?",
"get": "Obtener", "get": "Obtener",
"invite": "Código de invitación", "invite": "Código de invitación (opcional)",
"message": "#### ¡Estimado usuario!\n\nGracias por su interés y apoyo. Debido a un ajuste en la estrategia de operación del sitio, hemos cerrado la función de registro para nuevos usuarios. Durante este período, el uso por parte de los usuarios existentes no se verá afectado.\n\nNos comprometemos a brindarle un mejor servicio y experiencia, por lo que realizaremos una optimización completa del sistema y una actualización de funciones durante el cierre del registro. En el futuro, le daremos la bienvenida con contenido y servicios de mayor calidad.\n\nPor favor, siga nuestro sitio web y plataformas de redes sociales para obtener las últimas actualizaciones y notificaciones. Agradecemos su comprensión y apoyo.\n\nSi tiene alguna pregunta o necesita ayuda, no dude en ponerse en contacto con nuestro equipo de atención al cliente.\n\n**Nuevamente, gracias por su apoyo y comprensión.**", "message": "#### ¡Estimado usuario!\n\nGracias por su interés y apoyo. Debido a un ajuste en la estrategia de operación del sitio, hemos cerrado la función de registro para nuevos usuarios. Durante este período, el uso por parte de los usuarios existentes no se verá afectado.\n\nNos comprometemos a brindarle un mejor servicio y experiencia, por lo que realizaremos una optimización completa del sistema y una actualización de funciones durante el cierre del registro. En el futuro, le daremos la bienvenida con contenido y servicios de mayor calidad.\n\nPor favor, siga nuestro sitio web y plataformas de redes sociales para obtener las últimas actualizaciones y notificaciones. Agradecemos su comprensión y apoyo.\n\nSi tiene alguna pregunta o necesita ayuda, no dude en ponerse en contacto con nuestro equipo de atención al cliente.\n\n**Nuevamente, gracias por su apoyo y comprensión.**",
"passwordMismatch": "Las contraseñas no coinciden", "passwordMismatch": "Las contraseñas no coinciden",
"success": "¡Registro exitoso, sesión iniciada automáticamente!", "success": "¡Registro exitoso, sesión iniciada automáticamente!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "Bloquear Máquina Virtual",
"blockVirtualMachineDescription": "Cuando está habilitado, se evitará que los dispositivos se ejecuten en máquinas virtuales o emuladores",
"communicationKey": "Clave de Comunicación",
"communicationKeyDescription": "La clave de comunicación se utiliza para la comunicación segura entre dispositivos y servidores",
"enable": "Habilitar", "enable": "Habilitar",
"enableDescription": "Después de habilitar, se admiten múltiples identificadores de dispositivos como IMEI/IDFA/IDFV/AndroidID/dirección MAC para el inicio de sesión y el registro" "enableDescription": "Después de habilitar, se admiten múltiples identificadores de dispositivos como IMEI/IDFA/IDFV/AndroidID/dirección MAC para el inicio de sesión y el registro",
"enableSecurity": "Habilitar Cifrado de Comunicación",
"enableSecurityDescription": "Cuando está habilitado, la comunicación entre dispositivos y servidores será cifrada",
"saveFailed": "Error al guardar",
"saveSuccess": "Guardado exitoso",
"showAds": "Mostrar Anuncios",
"showAdsDescription": "Cuando está habilitado, se mostrarán anuncios en los dispositivos"
} }
+2
View File
@@ -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",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "Configuración de ADS",
"Announcement Management": "Gestión de Anuncios", "Announcement Management": "Gestión de Anuncios",
"Apple": "ID de Apple", "Apple": "ID de Apple",
"Auth Control": "Control de Autenticación", "Auth Control": "Control de Autenticación",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "Pago con Alipay", "AlipayF2F": "Alipay Cara a Cara",
"alipayf2f": { "EPay": "EPay",
"appId": "ID de la aplicación de Alipay", "Stripe": "Stripe",
"invoiceName": "Nombre personalizado del producto", "applyForPayment": "Solicitar Pago",
"invoiceNameDescription": "Se mostrará en la factura de Alipay", "balance": "Saldo",
"privateKey": "Clave privada de Alipay", "batchDelete": "Eliminar en Lote",
"publicKey": "Clave pública de Alipay", "cancel": "Cancelar",
"sandbox": "Modo sandbox", "config": "Configuración",
"sandboxDescription": "Habilitar el modo sandbox para probar el pago" "configInfo": "Información de Configuración",
}, "configPlaceholder": "Por favor, complete la configuración de {field} proporcionada",
"confirm": "Confirmar",
"confirmDelete": "Confirmar Eliminación",
"copy": "Copiar",
"copySuccess": "Copiado con éxito",
"create": "Agregar Método de Pago",
"createPayment": "Agregar Método de Pago",
"createSuccess": "Creado con éxito",
"delete": "Eliminar",
"deleteSuccess": "Eliminado con éxito",
"deleteWarning": "¿Está seguro de que desea eliminar este método de pago? Esta acción no se puede deshacer.",
"description": "Descripción",
"descriptionPlaceholder": "Ingrese la descripción del método de pago",
"domain": "Dominio",
"domainPlaceholder": "opcional, p. ej. {example}",
"edit": "Editar",
"editPayment": "Editar Método de Pago",
"enable": "Habilitar", "enable": "Habilitar",
"enableDescription": "Habilitar este método de pago", "feeAmount": "Monto Fijo",
"epay": {
"key": "CLAVE",
"pid": "PID",
"url": "URL"
},
"feeMode": "Modo de cobro", "feeMode": "Modo de cobro",
"feeModeDescription": "Modo de cobro de tarifas adicionales",
"feeModeItems": ["Sin tarifa", "Porcentaje", "Cantidad fija", "Ambos anteriores"],
"feePercent": "Porcentaje de tarifa (opcional)", "feePercent": "Porcentaje de tarifa (opcional)",
"feePercentDescription": "Se añade una tarifa sobre el monto del pedido",
"fixedFee": "Tarifa fija (opcional)", "fixedFee": "Tarifa fija (opcional)",
"fixedFeeDescription": "Se añade una tarifa fija sobre el monto del pedido", "handlingFee": "Tarifa de manejo",
"iconUrl": "URL del icono (opcional)", "icon": "Ícono",
"iconUrlDescription": "Usado para mostrar en el frontend", "iconPlaceholder": "Ingrese la URL del ícono",
"inputPlaceholder": "Por favor, introduzca", "mark": "Identificador",
"key": "CLAVE", "markPlaceholder": "Ingrese el identificador del método de pago",
"notifyUrl": "Dominio de notificación personalizado (opcional)", "name": "Nombre",
"notifyUrlDescription": "La notificación del gateway se enviará a este dominio", "namePlaceholder": "Ingrese el nombre del método de pago",
"payForRecommendations": "Pagar por recomendaciones", "nameRequired": "El nombre es obligatorio",
"pid": "PID", "noFee": "Sin Tarifa",
"saveSuccess": "Guardado exitosamente", "notify_url": "URL de notificación",
"showName": "Mostrar nombre", "paymentManagement": "Gestión de Pagos",
"showNameDescription": "Usado para mostrar en el frontend", "percentFee": "Porcentaje",
"stripe": { "platform": "Plataforma",
"publicKey": "CLAVE PÚBLICA", "searchPlaceholder": "Ingrese términos de búsqueda",
"secretKey": "CLAVE SECRETA", "selectPlatform": "Seleccionar Plataforma",
"webhookSecret": "FIRMA DE CLAVE WEBHOOK" "selectType": "Seleccionar tipo de pago",
}, "submit": "Enviar",
"url": "URL", "type": "Tipo",
"wechatPay": "Pago con WeChat" "updateSuccess": "Actualizado con éxito"
} }
+2
View File
@@ -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",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "Cancelar",
"confirm": "Confirmar",
"confirmDelete": "Confirmar Eliminación",
"create": "Crear Anuncio",
"createAds": "Crear Publicidad",
"createSuccess": "Creado con éxito",
"delete": "Eliminar",
"deleteSuccess": "Eliminado con éxito",
"deleteWarning": "¿Está seguro de que desea eliminar este anuncio?",
"disabled": "Deshabilitado",
"edit": "Editar",
"editAds": "Editar Publicidad",
"enabled": "Habilitado",
"form": {
"cancel": "Cancelar",
"confirm": "Confirmar",
"content": "Contenido",
"description": "Descripción",
"endTime": "Hora de Finalización",
"enterContent": "Por favor ingrese el contenido del anuncio",
"enterDescription": "Por favor ingrese la descripción del anuncio",
"enterEndTime": "Por favor seleccione la hora de finalización",
"enterStartTime": "Por favor seleccione la hora de inicio",
"enterTargetUrl": "Por favor ingrese la URL de destino",
"enterTitle": "Por favor ingrese el título del anuncio",
"startTime": "Hora de Inicio",
"targetUrl": "URL de Destino",
"title": "Título",
"type": "Tipo de Anuncio",
"typeImage": "Imagen",
"typeVideo": "Video"
},
"status": "Estado",
"targetUrl": "URL de Destino",
"title": "Título",
"type": "Tipo",
"updateSuccess": "Actualizado con éxito",
"validityPeriod": "Período de Validez"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "Por favor, ingresa una dirección de correo electrónico válida.", "email": "Por favor, ingresa una dirección de correo electrónico válida.",
"existingAccount": "¿Ya tienes una cuenta?", "existingAccount": "¿Ya tienes una cuenta?",
"get": "Obtener", "get": "Obtener",
"invite": "Código de invitación", "invite": "Código de invitación (opcional)",
"message": "#### Estimado usuario,\n\nGracias por su interés y apoyo. Debido a un ajuste en la estrategia de operación del sitio, hemos cerrado la función de registro para nuevos usuarios. Durante este período, el uso por parte de los usuarios existentes no se verá afectado.\n\nNos comprometemos a ofrecerle un mejor servicio y experiencia, por lo que realizaremos una optimización completa del sistema y una actualización de funciones durante el cierre del registro. En el futuro, le daremos la bienvenida con contenido y servicios de mayor calidad.\n\nPor favor, siga nuestro sitio web y plataformas de redes sociales para obtener las últimas actualizaciones y notificaciones. Agradecemos su comprensión y apoyo.\n\nSi tiene alguna pregunta o necesita ayuda, no dude en ponerse en contacto con nuestro equipo de atención al cliente.\n\n**Nuevamente, gracias por su apoyo y comprensión.**", "message": "#### Estimado usuario,\n\nGracias por su interés y apoyo. Debido a un ajuste en la estrategia de operación del sitio, hemos cerrado la función de registro para nuevos usuarios. Durante este período, el uso por parte de los usuarios existentes no se verá afectado.\n\nNos comprometemos a ofrecerle un mejor servicio y experiencia, por lo que realizaremos una optimización completa del sistema y una actualización de funciones durante el cierre del registro. En el futuro, le daremos la bienvenida con contenido y servicios de mayor calidad.\n\nPor favor, siga nuestro sitio web y plataformas de redes sociales para obtener las últimas actualizaciones y notificaciones. Agradecemos su comprensión y apoyo.\n\nSi tiene alguna pregunta o necesita ayuda, no dude en ponerse en contacto con nuestro equipo de atención al cliente.\n\n**Nuevamente, gracias por su apoyo y comprensión.**",
"passwordMismatch": "Las contraseñas no coinciden", "passwordMismatch": "Las contraseñas no coinciden",
"success": "¡Registro exitoso, has iniciado sesión automáticamente!", "success": "¡Registro exitoso, has iniciado sesión automáticamente!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "Bloquear Máquina Virtual",
"blockVirtualMachineDescription": "Cuando está habilitado, se evitará que los dispositivos se ejecuten en máquinas virtuales o emuladores",
"communicationKey": "Clave de Comunicación",
"communicationKeyDescription": "La clave de comunicación se utiliza para la comunicación segura entre dispositivos y servidores",
"enable": "Habilitar", "enable": "Habilitar",
"enableDescription": "Después de habilitar, se admiten múltiples identificadores de dispositivos como IMEI/IDFA/IDFV/AndroidID/dirección Mac para iniciar sesión y registrarse" "enableDescription": "Después de habilitar, se admiten múltiples identificadores de dispositivos como IMEI/IDFA/IDFV/AndroidID/dirección Mac para iniciar sesión y registrarse",
"enableSecurity": "Habilitar Cifrado de Comunicación",
"enableSecurityDescription": "Cuando está habilitado, la comunicación entre dispositivos y servidores será cifrada",
"saveFailed": "Error al guardar",
"saveSuccess": "Guardado exitoso",
"showAds": "Mostrar Anuncios",
"showAdsDescription": "Cuando está habilitado, se mostrarán anuncios en los dispositivos"
} }
+2
View File
@@ -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",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "Configuración de ADS",
"Announcement Management": "Gestión de Anuncios", "Announcement Management": "Gestión de Anuncios",
"Apple": "ID de Apple", "Apple": "ID de Apple",
"Auth Control": "Control de Autenticación", "Auth Control": "Control de Autenticación",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "Pago con Alipay", "AlipayF2F": "Alipay Cara a Cara",
"alipayf2f": { "EPay": "EPay",
"appId": "ID de la aplicación de Alipay", "Stripe": "Stripe",
"invoiceName": "Nombre personalizado del producto", "applyForPayment": "Solicitar Pago",
"invoiceNameDescription": "Se mostrará en la factura de Alipay", "balance": "Saldo",
"privateKey": "Clave privada de Alipay", "batchDelete": "Eliminar en Lote",
"publicKey": "Clave pública de Alipay", "cancel": "Cancelar",
"sandbox": "Modo sandbox", "config": "Configuración",
"sandboxDescription": "Habilitar el modo sandbox para probar el pago" "configInfo": "Información de Configuración",
}, "configPlaceholder": "Por favor, complete la configuración de {field} proporcionada",
"confirm": "Confirmar",
"confirmDelete": "Confirmar Eliminación",
"copy": "Copiar",
"copySuccess": "Copiado con éxito",
"create": "Agregar Método de Pago",
"createPayment": "Agregar Método de Pago",
"createSuccess": "Creado con éxito",
"delete": "Eliminar",
"deleteSuccess": "Eliminado con éxito",
"deleteWarning": "¿Está seguro de que desea eliminar este método de pago? Esta acción no se puede deshacer.",
"description": "Descripción",
"descriptionPlaceholder": "Ingresa la descripción del método de pago",
"domain": "Dominio",
"domainPlaceholder": "opcional, p. ej. {example}",
"edit": "Editar",
"editPayment": "Editar Método de Pago",
"enable": "Habilitar", "enable": "Habilitar",
"enableDescription": "Habilitar este método de pago", "feeAmount": "Monto Fijo",
"epay": {
"key": "CLAVE",
"pid": "PID",
"url": "URL"
},
"feeMode": "Modo de cobro", "feeMode": "Modo de cobro",
"feeModeDescription": "Modo de cobro de tarifas adicionales",
"feeModeItems": ["Sin tarifa", "Porcentaje", "Cantidad fija", "Ambos"],
"feePercent": "Porcentaje de tarifa (opcional)", "feePercent": "Porcentaje de tarifa (opcional)",
"feePercentDescription": "Se añade una tarifa sobre el monto del pedido",
"fixedFee": "Cuota fija (opcional)", "fixedFee": "Cuota fija (opcional)",
"fixedFeeDescription": "Agregar una tarifa fija sobre el monto del pedido", "handlingFee": "Cargo por manejo",
"iconUrl": "URL del ícono (opcional)", "icon": "Ícono",
"iconUrlDescription": "Usado para mostrar en el frontend", "iconPlaceholder": "Ingrese la URL del ícono",
"inputPlaceholder": "Por favor ingrese", "mark": "Identificador",
"key": "LLAVE", "markPlaceholder": "Ingrese el identificador del método de pago",
"notifyUrl": "Dominio de notificación personalizado (opcional)", "name": "Nombre",
"notifyUrlDescription": "La notificación del gateway se enviará a este dominio", "namePlaceholder": "Ingrese el nombre del método de pago",
"payForRecommendations": "Pagar por recomendaciones", "nameRequired": "El nombre es obligatorio",
"pid": "PID", "noFee": "Sin Tarifa",
"saveSuccess": "Guardado exitoso", "notify_url": "URL de notificación",
"showName": "Mostrar nombre", "paymentManagement": "Gestión de Pagos",
"showNameDescription": "Usado para mostrar en el frontend", "percentFee": "Porcentaje",
"stripe": { "platform": "Plataforma",
"publicKey": "CLAVE PÚBLICA", "searchPlaceholder": "Ingrese términos de búsqueda",
"secretKey": "CLAVE SECRETA", "selectPlatform": "Seleccionar Plataforma",
"webhookSecret": "FIRMA DE SECRETO DE WEBHOOK" "selectType": "Seleccionar tipo de pago",
}, "submit": "Enviar",
"url": "URL", "type": "Tipo",
"wechatPay": "WeChat Pay" "updateSuccess": "Actualizado con éxito"
} }
+2
View File
@@ -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",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "لغو",
"confirm": "تأیید",
"confirmDelete": "تأیید حذف",
"create": "ایجاد آگهی",
"createAds": "ایجاد تبلیغ",
"createSuccess": "با موفقیت ایجاد شد",
"delete": "حذف",
"deleteSuccess": "با موفقیت حذف شد",
"deleteWarning": "آیا مطمئن هستید که می‌خواهید این آگهی را حذف کنید؟",
"disabled": "غیرفعال",
"edit": "ویرایش",
"editAds": "ویرایش تبلیغ",
"enabled": "فعال",
"form": {
"cancel": "لغو",
"confirm": "تأیید",
"content": "محتوا",
"description": "توضیحات",
"endTime": "زمان پایان",
"enterContent": "لطفاً محتوای آگهی را وارد کنید",
"enterDescription": "لطفاً توضیحات آگهی را وارد کنید",
"enterEndTime": "لطفاً زمان پایان را انتخاب کنید",
"enterStartTime": "لطفاً زمان شروع را انتخاب کنید",
"enterTargetUrl": "لطفاً URL هدف را وارد کنید",
"enterTitle": "لطفاً عنوان آگهی را وارد کنید",
"startTime": "زمان شروع",
"targetUrl": "URL هدف",
"title": "عنوان",
"type": "نوع آگهی",
"typeImage": "تصویر",
"typeVideo": "ویدیو"
},
"status": "وضعیت",
"targetUrl": "URL هدف",
"title": "عنوان",
"type": "نوع",
"updateSuccess": "با موفقیت به‌روزرسانی شد",
"validityPeriod": "مدت اعتبار"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "لطفاً یک آدرس ایمیل معتبر وارد کنید.", "email": "لطفاً یک آدرس ایمیل معتبر وارد کنید.",
"existingAccount": "آیا قبلاً حساب کاربری دارید؟", "existingAccount": "آیا قبلاً حساب کاربری دارید؟",
"get": "دریافت", "get": "دریافت",
"invite": "کد دعوت", "invite": "کد دعوت (اختیاری)",
"message": "#### کاربر گرامی، سلام!\n\nاز توجه و حمایت شما سپاسگزاریم. به دلیل تغییرات در استراتژی عملیاتی سایت، عملکرد ثبت‌نام کاربران جدید را متوقف کرده‌ایم. در این مدت، کاربران فعلی تحت تأثیر قرار نخواهند گرفت.\n\nما متعهد به ارائه خدمات و تجربه بهتر به شما هستیم، بنابراین در طول این دوره، بهینه‌سازی جامع سیستم و ارتقاء ویژگی‌ها را انجام خواهیم داد. در آینده، با محتوای و خدمات بهتر از شما استقبال خواهیم کرد.\n\nلطفاً برای دریافت آخرین به‌روزرسانی‌ها و اطلاعیه‌ها، وب‌سایت و پلتفرم‌های اجتماعی ما را دنبال کنید. از درک و حمایت شما سپاسگزاریم.\n\nاگر سوالی دارید یا به کمک نیاز دارید، لطفاً با تیم خدمات مشتری ما تماس بگیرید.\n\n**بار دیگر از حمایت و درک شما سپاسگزاریم.**", "message": "#### کاربر گرامی، سلام!\n\nاز توجه و حمایت شما سپاسگزاریم. به دلیل تغییرات در استراتژی عملیاتی سایت، عملکرد ثبت‌نام کاربران جدید را متوقف کرده‌ایم. در این مدت، کاربران فعلی تحت تأثیر قرار نخواهند گرفت.\n\nما متعهد به ارائه خدمات و تجربه بهتر به شما هستیم، بنابراین در طول این دوره، بهینه‌سازی جامع سیستم و ارتقاء ویژگی‌ها را انجام خواهیم داد. در آینده، با محتوای و خدمات بهتر از شما استقبال خواهیم کرد.\n\nلطفاً برای دریافت آخرین به‌روزرسانی‌ها و اطلاعیه‌ها، وب‌سایت و پلتفرم‌های اجتماعی ما را دنبال کنید. از درک و حمایت شما سپاسگزاریم.\n\nاگر سوالی دارید یا به کمک نیاز دارید، لطفاً با تیم خدمات مشتری ما تماس بگیرید.\n\n**بار دیگر از حمایت و درک شما سپاسگزاریم.**",
"passwordMismatch": "رمزهای عبور مطابقت ندارند", "passwordMismatch": "رمزهای عبور مطابقت ندارند",
"success": "ثبت‌نام موفقیت‌آمیز بود، به‌طور خودکار وارد شدید!", "success": "ثبت‌نام موفقیت‌آمیز بود، به‌طور خودکار وارد شدید!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "مسدود کردن ماشین مجازی",
"blockVirtualMachineDescription": "هنگامی که فعال باشد، دستگاه‌ها از اجرای ماشین‌های مجازی یا شبیه‌سازها جلوگیری خواهند شد",
"communicationKey": "کلید ارتباطی",
"communicationKeyDescription": "کلید ارتباطی برای ارتباط امن بین دستگاه‌ها و سرورها استفاده می‌شود",
"enable": "فعال کردن", "enable": "فعال کردن",
"enableDescription": "پس از فعال‌سازی، شناسه‌های دستگاه‌های مختلف مانند IMEI/IDFA/IDFV/AndroidID/آدرس مک برای ورود و ثبت‌نام پشتیبانی می‌شوند" "enableDescription": "پس از فعال‌سازی، شناسه‌های دستگاه‌های مختلف مانند IMEI/IDFA/IDFV/AndroidID/آدرس مک برای ورود و ثبت‌نام پشتیبانی می‌شوند",
"enableSecurity": "فعال‌سازی رمزنگاری ارتباطات",
"enableSecurityDescription": "هنگامی که فعال باشد، ارتباط بین دستگاه‌ها و سرورها رمزنگاری خواهد شد",
"saveFailed": "ذخیره‌سازی ناموفق بود",
"saveSuccess": "ذخیره‌سازی با موفقیت انجام شد",
"showAds": "نمایش تبلیغات",
"showAdsDescription": "هنگامی که فعال باشد، تبلیغات بر روی دستگاه‌ها نمایش داده خواهد شد"
} }
+2
View File
@@ -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": "پسوندهای لیست سفید",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "تنظیمات ADS",
"Announcement Management": "مدیریت اطلاعیه‌ها", "Announcement Management": "مدیریت اطلاعیه‌ها",
"Apple": "شناسه اپل", "Apple": "شناسه اپل",
"Auth Control": "کنترل احراز هویت", "Auth Control": "کنترل احراز هویت",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "پرداخت علی‌پی", "AlipayF2F": "علی‌پی چهره به چهره",
"alipayf2f": { "EPay": "ای‌پی",
"appId": "شناسه برنامه Alipay", "Stripe": "استرایپ",
"invoiceName": "نام محصول سفارشی", "applyForPayment": "درخواست پرداخت",
"invoiceNameDescription": "در صورتحساب Alipay نمایش داده خواهد شد", "balance": "موجودی",
"privateKey": "کلید خصوصی Alipay", "batchDelete": "حذف دسته‌ای",
"publicKey": "کلید عمومی Alipay", "cancel": "لغو",
"sandbox": "حالت Sandbox", "config": "پیکربندی",
"sandboxDescription": "حالت sandbox را برای آزمایش پرداخت‌ها فعال کنید" "configInfo": "اطلاعات پیکربندی",
}, "configPlaceholder": "لطفاً پیکربندی {field} ارائه شده را پر کنید",
"confirm": "تأیید",
"confirmDelete": "تأیید حذف",
"copy": "کپی",
"copySuccess": "با موفقیت کپی شد",
"create": "افزودن روش پرداخت",
"createPayment": "افزودن روش پرداخت",
"createSuccess": "با موفقیت ایجاد شد",
"delete": "حذف",
"deleteSuccess": "با موفقیت حذف شد",
"deleteWarning": "آیا مطمئن هستید که می‌خواهید این روش پرداخت را حذف کنید؟ این عمل قابل بازگشت نیست.",
"description": "توضیحات",
"descriptionPlaceholder": "توضیحات روش پرداخت را وارد کنید",
"domain": "دامنه",
"domainPlaceholder": "اختیاری، به عنوان مثال {example}",
"edit": "ویرایش",
"editPayment": "ویرایش روش پرداخت",
"enable": "فعال کردن", "enable": "فعال کردن",
"enableDescription": "این روش پرداخت را فعال کنید", "feeAmount": "مقدار ثابت",
"epay": {
"key": "کلید",
"pid": "شناسه محصول",
"url": "نشانی وب"
},
"feeMode": "حالت هزینه", "feeMode": "حالت هزینه",
"feeModeDescription": "روش دریافت هزینه برای هزینه‌های اضافی",
"feeModeItems": ["بدون هزینه", "درصدی", "مبلغ ثابت", "هر دو"],
"feePercent": "درصد کارمزد (اختیاری)", "feePercent": "درصد کارمزد (اختیاری)",
"feePercentDescription": "افزودن کارمزد به مبلغ سفارش",
"fixedFee": "هزینه ثابت (اختیاری)", "fixedFee": "هزینه ثابت (اختیاری)",
"fixedFeeDescription": "افزودن هزینه ثابت به مبلغ سفارش", "handlingFee": "هزینه خدمات",
"iconUrl": درس آیکون (اختیاری)", "icon": یکون",
"iconUrlDescription": "برای نمایش در رابط کاربری استفاده می‌شود", "iconPlaceholder": "آدرس URL آیکون را وارد کنید",
"inputPlaceholder": "لطفاً وارد کنید", "mark": "شناسه",
"key": "کلید", "markPlaceholder": "شناسه روش پرداخت را وارد کنید",
"notifyUrl": "دامنه اعلان سفارشی (اختیاری)", "name": "نام",
"notifyUrlDescription": "اعلان‌های درگاه به این دامنه ارسال خواهند شد", "namePlaceholder": "نام روش پرداخت را وارد کنید",
"payForRecommendations": "پرداخت برای توصیه‌ها", "nameRequired": "نام الزامی است",
"pid": "شناسه فرآیند", "noFee": "بدون هزینه",
"saveSuccess": "ذخیره با موفقیت انجام شد", "notify_url": "آدرس اطلاع‌رسانی",
"showName": "نمایش نام", "paymentManagement": "مدیریت پرداخت",
"showNameDescription": "برای نمایش در قسمت جلویی استفاده می‌شود", "percentFee": "درصد",
"stripe": { "platform": "پلتفرم",
"publicKey": "کلید عمومی", "searchPlaceholder": "عبارات جستجو را وارد کنید",
"secretKey": "کلید مخفی", "selectPlatform": "انتخاب پلتفرم",
"webhookSecret": "کلید مخفی وب‌هوک" "selectType": "انتخاب نوع پرداخت",
}, "submit": "ارسال",
"url": "آدرس اینترنتی", "type": "نوع",
"wechatPay": "وی‌چت پی" "updateSuccess": "با موفقیت به‌روزرسانی شد"
} }
+2
View File
@@ -13,6 +13,8 @@
"title": "سیاست حفظ حریم خصوصی" "title": "سیاست حفظ حریم خصوصی"
}, },
"site": { "site": {
"customData": "داده‌های سفارشی",
"customDataDescription": "داده‌های سفارشی، استفاده شده برای داده‌های سفارشی سایت",
"customHtml": "HTML سفارشی", "customHtml": "HTML سفارشی",
"customHtmlDescription": "کد HTML سفارشی که باید در انتهای تگ body سایت وارد شود.", "customHtmlDescription": "کد HTML سفارشی که باید در انتهای تگ body سایت وارد شود.",
"keywords": "کلمات کلیدی", "keywords": "کلمات کلیدی",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "Peruuta",
"confirm": "Vahvista",
"confirmDelete": "Vahvista poisto",
"create": "Luo ilmoitus",
"createAds": "Luo mainos",
"createSuccess": "Luonti onnistui",
"delete": "Poista",
"deleteSuccess": "Poisto onnistui",
"deleteWarning": "Oletko varma, että haluat poistaa tämän ilmoituksen?",
"disabled": "Pois käytöstä",
"edit": "Muokkaa",
"editAds": "Muokkaa mainosta",
"enabled": "Käytössä",
"form": {
"cancel": "Peruuta",
"confirm": "Vahvista",
"content": "Sisältö",
"description": "Kuvaus",
"endTime": "Loppuaika",
"enterContent": "Ole hyvä ja syötä mainoksen sisältö",
"enterDescription": "Ole hyvä ja syötä mainoksen kuvaus",
"enterEndTime": "Ole hyvä ja valitse loppuaika",
"enterStartTime": "Ole hyvä ja valitse aloitusaika",
"enterTargetUrl": "Ole hyvä ja syötä kohde-URL",
"enterTitle": "Ole hyvä ja syötä mainoksen otsikko",
"startTime": "Aloitusaika",
"targetUrl": "Kohde-URL",
"title": "Otsikko",
"type": "Mainostyyppi",
"typeImage": "Kuva",
"typeVideo": "Video"
},
"status": "Tila",
"targetUrl": "Kohde-URL",
"title": "Otsikko",
"type": "Tyyppi",
"updateSuccess": "Päivitys onnistui",
"validityPeriod": "Voimassaoloaika"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "Anna kelvollinen sähköpostiosoite.", "email": "Anna kelvollinen sähköpostiosoite.",
"existingAccount": "Onko sinulla jo tili?", "existingAccount": "Onko sinulla jo tili?",
"get": "Hanki", "get": "Hanki",
"invite": "Kutsukoodi", "invite": "Kutsukoodi (valinnainen)",
"message": "#### Arvoisa käyttäjä,\n\nKiitos mielenkiinnostasi ja tuestasi. Sivuston toimintastrategian muutosten vuoksi olemme sulkeneet uusien käyttäjien rekisteröinnin. Tänä aikana nykyisten käyttäjien käyttö ei vaikutu millään tavalla.\n\nOlemme sitoutuneet tarjoamaan sinulle parempaa palvelua ja kokemusta, joten rekisteröinnin sulkemisen aikana suoritamme kattavan järjestelmän optimoinnin ja toiminnallisuuden päivityksen. Tulevaisuudessa toivotamme sinut tervetulleeksi entistä laadukkaamman sisällön ja palvelun kera.\n\nSeuraa verkkosivustoamme ja sosiaalisen median alustoja saadaksesi uusimmat päivitykset ja ilmoitukset. Kiitos ymmärryksestäsi ja tuestasi.\n\nJos sinulla on kysyttävää tai tarvitset apua, ota yhteyttä asiakaspalvelutiimiimme.\n\n**Kiitos vielä kerran tuestasi ja ymmärryksestäsi.**", "message": "#### Arvoisa käyttäjä,\n\nKiitos mielenkiinnostasi ja tuestasi. Sivuston toimintastrategian muutosten vuoksi olemme sulkeneet uusien käyttäjien rekisteröinnin. Tänä aikana nykyisten käyttäjien käyttö ei vaikutu millään tavalla.\n\nOlemme sitoutuneet tarjoamaan sinulle parempaa palvelua ja kokemusta, joten rekisteröinnin sulkemisen aikana suoritamme kattavan järjestelmän optimoinnin ja toiminnallisuuden päivityksen. Tulevaisuudessa toivotamme sinut tervetulleeksi entistä laadukkaamman sisällön ja palvelun kera.\n\nSeuraa verkkosivustoamme ja sosiaalisen median alustoja saadaksesi uusimmat päivitykset ja ilmoitukset. Kiitos ymmärryksestäsi ja tuestasi.\n\nJos sinulla on kysyttävää tai tarvitset apua, ota yhteyttä asiakaspalvelutiimiimme.\n\n**Kiitos vielä kerran tuestasi ja ymmärryksestäsi.**",
"passwordMismatch": "Salasanat eivät täsmää", "passwordMismatch": "Salasanat eivät täsmää",
"success": "Rekisteröinti onnistui, olet kirjautunut automaattisesti!", "success": "Rekisteröinti onnistui, olet kirjautunut automaattisesti!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "Estä virtuaalikone",
"blockVirtualMachineDescription": "Kun tämä on käytössä, laitteiden käyttö virtuaalikoneissa tai emulaattoreissa estetään",
"communicationKey": "Viestintäavain",
"communicationKeyDescription": "Viestintäavain käytetään turvalliseen viestintään laitteiden ja palvelimien välillä",
"enable": "Ota käyttöön", "enable": "Ota käyttöön",
"enableDescription": "Kun otat käyttöön, useita laiteidentifikaattoreita, kuten IMEI/IDFA/IDFV/AndroidID/Mac-osoite, tuetaan kirjautumiseen ja rekisteröitymiseen." "enableDescription": "Kun otat käyttöön, useita laiteidentifikaattoreita, kuten IMEI/IDFA/IDFV/AndroidID/Mac-osoite, tuetaan kirjautumiseen ja rekisteröitymiseen.",
"enableSecurity": "Ota käyttöön viestinnän salaus",
"enableSecurityDescription": "Kun tämä on käytössä, laitteiden ja palvelimien välinen viestintä salataan",
"saveFailed": "Tallennus epäonnistui",
"saveSuccess": "Tallennus onnistui",
"showAds": "Näytä mainoksia",
"showAdsDescription": "Kun tämä on käytössä, mainoksia näytetään laitteilla"
} }
+2
View File
@@ -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",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "ADS-asetukset",
"Announcement Management": "Ilmoitusten hallinta", "Announcement Management": "Ilmoitusten hallinta",
"Apple": "Apple ID", "Apple": "Apple ID",
"Auth Control": "Todennuksen hallinta", "Auth Control": "Todennuksen hallinta",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "Alipay-maksu", "AlipayF2F": "Alipay Kasvokkain",
"alipayf2f": { "EPay": "EPay",
"appId": "Alipay-sovelluksen ID", "Stripe": "Stripe",
"invoiceName": "Mukautettu tuotteen nimi", "applyForPayment": "Hae maksua",
"invoiceNameDescription": "Näkyy Alipay-laskussa", "balance": "Saldo",
"privateKey": "Alipay-yksityinen avain", "batchDelete": "Ryhmän poisto",
"publicKey": "Alipay-julkinen avain", "cancel": "Peruuta",
"sandbox": "Hiekkalaatikkotila", "config": "Konfiguraatio",
"sandboxDescription": "Ota hiekkalaatikkotila käyttöön maksujen testaamiseksi" "configInfo": "Konfiguraatio tiedot",
}, "configPlaceholder": "Täytä annettu {field} konfiguraatio",
"confirm": "Vahvista",
"confirmDelete": "Vahvista poisto",
"copy": "Kopioi",
"copySuccess": "Kopiointi onnistui",
"create": "Lisää maksutapa",
"createPayment": "Lisää maksutapa",
"createSuccess": "Luonti onnistui",
"delete": "Poista",
"deleteSuccess": "Poisto onnistui",
"deleteWarning": "Oletko varma, että haluat poistaa tämän maksutavan? Tätä toimintoa ei voi peruuttaa.",
"description": "Kuvaus",
"descriptionPlaceholder": "Syötä maksutavan kuvaus",
"domain": "Verkkotunnus",
"domainPlaceholder": "valinnainen, esim. {esimerkki}",
"edit": "Muokkaa",
"editPayment": "Muokkaa maksutapaa",
"enable": "Ota käyttöön", "enable": "Ota käyttöön",
"enableDescription": "Ota tämä maksutapa käyttöön", "feeAmount": "Kiinteä summa",
"epay": {
"key": "AVAIN",
"pid": "PID",
"url": "URL"
},
"feeMode": "Maksutapa", "feeMode": "Maksutapa",
"feeModeDescription": "Lisämaksun veloitustapa",
"feeModeItems": ["Ei käsittelymaksua", "Prosenttiosuus", "Kiinteä summa", "Molemmat yllä olevat"],
"feePercent": "Prosenttiosuusmaksu (valinnainen)", "feePercent": "Prosenttiosuusmaksu (valinnainen)",
"feePercentDescription": "Lisätään maksu tilauksen summan päälle",
"fixedFee": "Kiinteä maksu (valinnainen)", "fixedFee": "Kiinteä maksu (valinnainen)",
"fixedFeeDescription": "Lisää kiinteä maksu tilauksen summan päälle", "handlingFee": "Käsittelymaksu",
"iconUrl": "Kuvakkeen URL (valinnainen)", "icon": "Ikoni",
"iconUrlDescription": "Käytetään etunäytössä", "iconPlaceholder": "Syötä ikonin URL-osoite",
"inputPlaceholder": "Kirjoita tähän", "mark": "Tunniste",
"key": "AVAIN", "markPlaceholder": "Syötä maksutavan tunniste",
"notifyUrl": "Mukautettu ilmoitusalue (valinnainen)", "name": "Nimi",
"notifyUrlDescription": "Yhdyskäytävän ilmoitus lähetetään tähän verkkotunnukseen", "namePlaceholder": "Syötä maksutavan nimi",
"payForRecommendations": "Maksa suosituksista", "nameRequired": "Nimi on pakollinen",
"pid": "PID", "noFee": "Ei maksua",
"saveSuccess": "Tallennus onnistui", "notify_url": "Ilmoitus-URL",
"showName": "Näytä nimi", "paymentManagement": "Maksujen hallinta",
"showNameDescription": "Näytetään käyttöliittymässä", "percentFee": "Prosentti",
"stripe": { "platform": "Alusta",
"publicKey": "JULKINEN AVAIN", "searchPlaceholder": "Syötä hakusanat",
"secretKey": "SALAINEN AVAIN", "selectPlatform": "Valitse alusta",
"webhookSecret": "WEBHOOK-SALAINEN ALLEKIRJOITUS" "selectType": "Valitse maksutyyppi",
}, "submit": "Lähetä",
"url": "URL", "type": "Tyyppi",
"wechatPay": "WeChat-maksu" "updateSuccess": "Päivitys onnistui"
} }
+2
View File
@@ -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",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "Annuler",
"confirm": "Confirmer",
"confirmDelete": "Confirmer la suppression",
"create": "Créer une annonce",
"createAds": "Créer une publicité",
"createSuccess": "Créé avec succès",
"delete": "Supprimer",
"deleteSuccess": "Supprimé avec succès",
"deleteWarning": "Êtes-vous sûr de vouloir supprimer cette annonce ?",
"disabled": "Désactivé",
"edit": "Modifier",
"editAds": "Modifier la publicité",
"enabled": "Activé",
"form": {
"cancel": "Annuler",
"confirm": "Confirmer",
"content": "Contenu",
"description": "Description",
"endTime": "Heure de fin",
"enterContent": "Veuillez entrer le contenu de l'annonce",
"enterDescription": "Veuillez entrer la description de l'annonce",
"enterEndTime": "Veuillez sélectionner l'heure de fin",
"enterStartTime": "Veuillez sélectionner l'heure de début",
"enterTargetUrl": "Veuillez entrer l'URL cible",
"enterTitle": "Veuillez entrer le titre de l'annonce",
"startTime": "Heure de début",
"targetUrl": "URL cible",
"title": "Titre",
"type": "Type d'annonce",
"typeImage": "Image",
"typeVideo": "Vidéo"
},
"status": "Statut",
"targetUrl": "URL cible",
"title": "Titre",
"type": "Type",
"updateSuccess": "Mis à jour avec succès",
"validityPeriod": "Période de validité"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "Veuillez entrer une adresse e-mail valide.", "email": "Veuillez entrer une adresse e-mail valide.",
"existingAccount": "Vous avez déjà un compte ?", "existingAccount": "Vous avez déjà un compte ?",
"get": "Obtenir", "get": "Obtenir",
"invite": "Code d'invitation", "invite": "Code d'invitation (facultatif)",
"message": "#### Cher utilisateur,\n\nMerci pour votre intérêt et votre soutien. En raison d'un ajustement de notre stratégie opérationnelle, nous avons fermé la fonction d'inscription pour les nouveaux utilisateurs. Pendant cette période, l'utilisation par les utilisateurs existants ne sera pas affectée.\n\nNous nous engageons à vous offrir un meilleur service et une meilleure expérience, c'est pourquoi nous procéderons à une optimisation complète du système et à une mise à niveau des fonctionnalités pendant la fermeture des inscriptions. À l'avenir, nous vous accueillerons avec un contenu et des services de meilleure qualité.\n\nVeuillez suivre notre site web et nos plateformes de médias sociaux pour obtenir les dernières nouvelles et notifications. Merci pour votre compréhension et votre soutien.\n\nSi vous avez des questions ou avez besoin d'aide, n'hésitez pas à contacter notre équipe de service client.\n\n**Merci encore pour votre soutien et votre compréhension.**", "message": "#### Cher utilisateur,\n\nMerci pour votre intérêt et votre soutien. En raison d'un ajustement de notre stratégie opérationnelle, nous avons fermé la fonction d'inscription pour les nouveaux utilisateurs. Pendant cette période, l'utilisation par les utilisateurs existants ne sera pas affectée.\n\nNous nous engageons à vous offrir un meilleur service et une meilleure expérience, c'est pourquoi nous procéderons à une optimisation complète du système et à une mise à niveau des fonctionnalités pendant la fermeture des inscriptions. À l'avenir, nous vous accueillerons avec un contenu et des services de meilleure qualité.\n\nVeuillez suivre notre site web et nos plateformes de médias sociaux pour obtenir les dernières nouvelles et notifications. Merci pour votre compréhension et votre soutien.\n\nSi vous avez des questions ou avez besoin d'aide, n'hésitez pas à contacter notre équipe de service client.\n\n**Merci encore pour votre soutien et votre compréhension.**",
"passwordMismatch": "Les mots de passe saisis ne correspondent pas", "passwordMismatch": "Les mots de passe saisis ne correspondent pas",
"success": "Inscription réussie, vous êtes maintenant connecté automatiquement !", "success": "Inscription réussie, vous êtes maintenant connecté automatiquement !",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "Bloquer la machine virtuelle",
"blockVirtualMachineDescription": "Lorsqu'il est activé, les appareils seront empêchés de fonctionner sur des machines virtuelles ou des émulateurs",
"communicationKey": "Clé de communication",
"communicationKeyDescription": "La clé de communication est utilisée pour une communication sécurisée entre les appareils et les serveurs",
"enable": "Activer", "enable": "Activer",
"enableDescription": "Après activation, plusieurs identifiants de périphériques tels que IMEI/IDFA/IDFV/AndroidID/adresse Mac sont pris en charge pour la connexion et l'enregistrement" "enableDescription": "Après activation, plusieurs identifiants de périphériques tels que IMEI/IDFA/IDFV/AndroidID/adresse Mac sont pris en charge pour la connexion et l'enregistrement",
"enableSecurity": "Activer le chiffrement de la communication",
"enableSecurityDescription": "Lorsqu'il est activé, la communication entre les appareils et les serveurs sera chiffrée",
"saveFailed": "Échec de l'enregistrement",
"saveSuccess": "Enregistrement réussi",
"showAds": "Afficher les publicités",
"showAdsDescription": "Lorsqu'il est activé, des publicités seront affichées sur les appareils"
} }
+2
View File
@@ -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",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "Configuration ADS",
"Announcement Management": "Gestion des annonces", "Announcement Management": "Gestion des annonces",
"Apple": "Identifiant Apple", "Apple": "Identifiant Apple",
"Auth Control": "Contrôle d'authentification", "Auth Control": "Contrôle d'authentification",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "Paiement par Alipay", "AlipayF2F": "Alipay Face à Face",
"alipayf2f": { "EPay": "EPay",
"appId": "ID de l'application Alipay", "Stripe": "Stripe",
"invoiceName": "Nom personnalisé du produit", "applyForPayment": "Demander un paiement",
"invoiceNameDescription": "S'affichera sur la facture Alipay", "balance": "Solde",
"privateKey": "Clé privée Alipay", "batchDelete": "Suppression par lot",
"publicKey": "Clé publique Alipay", "cancel": "Annuler",
"sandbox": "Mode bac à sable", "config": "Configuration",
"sandboxDescription": "Activez le mode bac à sable pour tester le paiement" "configInfo": "Informations de configuration",
}, "configPlaceholder": "Veuillez remplir la configuration {field} fournie",
"confirm": "Confirmer",
"confirmDelete": "Confirmer la suppression",
"copy": "Copier",
"copySuccess": "Copié avec succès",
"create": "Ajouter un mode de paiement",
"createPayment": "Ajouter un mode de paiement",
"createSuccess": "Créé avec succès",
"delete": "Supprimer",
"deleteSuccess": "Supprimé avec succès",
"deleteWarning": "Êtes-vous sûr de vouloir supprimer ce mode de paiement ? Cette action ne peut pas être annulée.",
"description": "Description",
"descriptionPlaceholder": "Entrez la description du mode de paiement",
"domain": "Domaine",
"domainPlaceholder": "facultatif, par exemple {exemple}",
"edit": "Modifier",
"editPayment": "Modifier le mode de paiement",
"enable": "Activer", "enable": "Activer",
"enableDescription": "Activer ce mode de paiement", "feeAmount": "Montant fixe",
"epay": {
"key": "CLÉ",
"pid": "PID",
"url": "URL"
},
"feeMode": "Mode de facturation", "feeMode": "Mode de facturation",
"feeModeDescription": "Mode de facturation des frais supplémentaires",
"feeModeItems": ["Pas de frais", "Pourcentage", "Montant fixe", "Les deux ci-dessus"],
"feePercent": "Pourcentage de frais (optionnel)", "feePercent": "Pourcentage de frais (optionnel)",
"feePercentDescription": "Ajouter des frais au montant de la commande",
"fixedFee": "Frais fixes (optionnel)", "fixedFee": "Frais fixes (optionnel)",
"fixedFeeDescription": "Ajouter des frais fixes au montant de la commande", "handlingFee": "Frais de traitement",
"iconUrl": "URL de l'icône (facultatif)", "icon": "Icône",
"iconUrlDescription": "Utilisé pour l'affichage frontal", "iconPlaceholder": "Entrez l'URL de l'icône",
"inputPlaceholder": "Veuillez entrer", "mark": "Identifiant",
"key": "CLÉ", "markPlaceholder": "Entrez l'identifiant du mode de paiement",
"notifyUrl": "Domaine de notification personnalisé (facultatif)", "name": "Nom",
"notifyUrlDescription": "Les notifications de la passerelle seront envoyées à ce domaine", "namePlaceholder": "Entrez le nom du mode de paiement",
"payForRecommendations": "Payer pour les recommandations", "nameRequired": "Le nom est requis",
"pid": "PID", "noFee": "Pas de frais",
"saveSuccess": "Enregistrement réussi", "notify_url": "URL de notification",
"showName": "Afficher le nom", "paymentManagement": "Gestion des paiements",
"showNameDescription": "Utilisé pour l'affichage frontal", "percentFee": "Pourcentage",
"stripe": { "platform": "Plateforme",
"publicKey": "CLÉ PUBLIQUE", "searchPlaceholder": "Entrez des termes de recherche",
"secretKey": "CLÉ SECRÈTE", "selectPlatform": "Sélectionner la plateforme",
"webhookSecret": "SIGNATURE DE CLÉ WEBHOOK" "selectType": "Sélectionner le type de paiement",
}, "submit": "Soumettre",
"url": "URL", "type": "Type",
"wechatPay": "Paiement WeChat" "updateSuccess": "Mis à jour avec succès"
} }
+2
View File
@@ -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",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "रद्द करें",
"confirm": "पुष्टि करें",
"confirmDelete": "हटाने की पुष्टि करें",
"create": "विज्ञापन बनाएं",
"createAds": "विज्ञापन बनाएं",
"createSuccess": "सफलता से बनाया गया",
"delete": "हटाएं",
"deleteSuccess": "सफलता से हटाया गया",
"deleteWarning": "क्या आप वास्तव में इस विज्ञापन को हटाना चाहते हैं?",
"disabled": "अक्षम",
"edit": "संपादित करें",
"editAds": "विज्ञापन संपादित करें",
"enabled": "सक्षम",
"form": {
"cancel": "रद्द करें",
"confirm": "पुष्टि करें",
"content": "सामग्री",
"description": "विवरण",
"endTime": "समाप्ति समय",
"enterContent": "कृपया विज्ञापन सामग्री दर्ज करें",
"enterDescription": "कृपया विज्ञापन विवरण दर्ज करें",
"enterEndTime": "कृपया समाप्ति समय चुनें",
"enterStartTime": "कृपया प्रारंभ समय चुनें",
"enterTargetUrl": "कृपया लक्षित URL दर्ज करें",
"enterTitle": "कृपया विज्ञापन शीर्षक दर्ज करें",
"startTime": "प्रारंभ समय",
"targetUrl": "लक्षित URL",
"title": "शीर्षक",
"type": "विज्ञापन प्रकार",
"typeImage": "छवि",
"typeVideo": "वीडियो"
},
"status": "स्थिति",
"targetUrl": "लक्षित URL",
"title": "शीर्षक",
"type": "प्रकार",
"updateSuccess": "सफलता से अपडेट किया गया",
"validityPeriod": "वैधता अवधि"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "कृपया एक मान्य ईमेल पता दर्ज करें।", "email": "कृपया एक मान्य ईमेल पता दर्ज करें।",
"existingAccount": "पहले से खाता है?", "existingAccount": "पहले से खाता है?",
"get": "प्राप्त करें", "get": "प्राप्त करें",
"invite": "आमंत्रण कोड", "invite": "आमंत्रण कोड (वैकल्पिक)",
"message": "#### प्रिय उपयोगकर्ता, नमस्ते!\n\nहमारे प्रति आपकी रुचि और समर्थन के लिए धन्यवाद। साइट संचालन रणनीति में बदलाव के कारण, हमने नए उपयोगकर्ता पंजीकरण को बंद कर दिया है। इस दौरान, मौजूदा उपयोगकर्ताओं के उपयोग पर कोई प्रभाव नहीं पड़ेगा।\n\nहम आपको बेहतर सेवा और अनुभव प्रदान करने के लिए प्रतिबद्ध हैं, इसलिए पंजीकरण बंद होने के दौरान हम व्यापक प्रणाली अनुकूलन और कार्यक्षमता उन्नयन करेंगे। भविष्य में, हम आपको बेहतर सामग्री और सेवाओं के साथ स्वागत करेंगे।\n\nहमारी वेबसाइट और सोशल मीडिया प्लेटफॉर्म पर नवीनतम अपडेट और सूचनाएं प्राप्त करने के लिए ध्यान दें। आपकी समझ और समर्थन के लिए धन्यवाद।\n\nयदि आपके कोई प्रश्न हैं या सहायता की आवश्यकता है, तो कृपया हमारी ग्राहक सेवा टीम से संपर्क करें।\n\n**आपके समर्थन और समझ के लिए एक बार फिर धन्यवाद।**", "message": "#### प्रिय उपयोगकर्ता, नमस्ते!\n\nहमारे प्रति आपकी रुचि और समर्थन के लिए धन्यवाद। साइट संचालन रणनीति में बदलाव के कारण, हमने नए उपयोगकर्ता पंजीकरण को बंद कर दिया है। इस दौरान, मौजूदा उपयोगकर्ताओं के उपयोग पर कोई प्रभाव नहीं पड़ेगा।\n\nहम आपको बेहतर सेवा और अनुभव प्रदान करने के लिए प्रतिबद्ध हैं, इसलिए पंजीकरण बंद होने के दौरान हम व्यापक प्रणाली अनुकूलन और कार्यक्षमता उन्नयन करेंगे। भविष्य में, हम आपको बेहतर सामग्री और सेवाओं के साथ स्वागत करेंगे।\n\nहमारी वेबसाइट और सोशल मीडिया प्लेटफॉर्म पर नवीनतम अपडेट और सूचनाएं प्राप्त करने के लिए ध्यान दें। आपकी समझ और समर्थन के लिए धन्यवाद।\n\nयदि आपके कोई प्रश्न हैं या सहायता की आवश्यकता है, तो कृपया हमारी ग्राहक सेवा टीम से संपर्क करें।\n\n**आपके समर्थन और समझ के लिए एक बार फिर धन्यवाद।**",
"passwordMismatch": "दोनों बार दर्ज किया गया पासवर्ड मेल नहीं खा रहा है", "passwordMismatch": "दोनों बार दर्ज किया गया पासवर्ड मेल नहीं खा रहा है",
"success": "पंजीकरण सफल, स्वचालित रूप से लॉग इन हो गया!", "success": "पंजीकरण सफल, स्वचालित रूप से लॉग इन हो गया!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "वर्चुअल मशीन को ब्लॉक करें",
"blockVirtualMachineDescription": "जब सक्षम किया जाता है, तो उपकरणों को वर्चुअल मशीनों या एमुलेटर्स पर चलने से रोका जाएगा",
"communicationKey": "संचार कुंजी",
"communicationKeyDescription": "संचार कुंजी का उपयोग उपकरणों और सर्वरों के बीच सुरक्षित संचार के लिए किया जाता है",
"enable": "सक्रिय करें", "enable": "सक्रिय करें",
"enableDescription": "सक्रिय करने के बाद, लॉगिन और पंजीकरण के लिए IMEI/IDFA/IDFV/AndroidID/Mac पता जैसे कई डिवाइस पहचानकर्ताओं का समर्थन किया जाता है" "enableDescription": "सक्रिय करने के बाद, लॉगिन और पंजीकरण के लिए IMEI/IDFA/IDFV/AndroidID/Mac पता जैसे कई डिवाइस पहचानकर्ताओं का समर्थन किया जाता है",
"enableSecurity": "संचार एन्क्रिप्शन सक्षम करें",
"enableSecurityDescription": "जब सक्षम किया जाता है, तो उपकरणों और सर्वरों के बीच का संचार एन्क्रिप्ट किया जाएगा",
"saveFailed": "सहेजना विफल",
"saveSuccess": "सहेजना सफल",
"showAds": "विज्ञापन दिखाएँ",
"showAdsDescription": "जब सक्षम किया जाता है, तो उपकरणों पर विज्ञापन प्रदर्शित किए जाएंगे"
} }
+2
View File
@@ -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": "श्वेतसूची प्रत्यय",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "एडीएस कॉन्फ़िग",
"Announcement Management": "घोषणा प्रबंधन", "Announcement Management": "घोषणा प्रबंधन",
"Apple": "एप्पल आईडी", "Apple": "एप्पल आईडी",
"Auth Control": "प्रमाणिकरण नियंत्रण", "Auth Control": "प्रमाणिकरण नियंत्रण",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "अलीपे भुगतान", "AlipayF2F": "अलीपे फेस टू फेस",
"alipayf2f": { "EPay": "ईपे",
"appId": "अलीपे एप्लिकेशन आईडी", "Stripe": "स्ट्राइप",
"invoiceName": "कस्टम उत्पाद नाम", "applyForPayment": "भुगतान के लिए आवेदन करें",
"invoiceNameDescription": "अलीपे बिल में प्रदर्शित होगा", "balance": "शेष",
"privateKey": "अलीपे निजी कुंजी", "batchDelete": "बैच हटाएं",
"publicKey": "अलीपे सार्वजनिक कुंजी", "cancel": "रद्द करें",
"sandbox": "सैंडबॉक्स मोड", "config": "कॉन्फ़िगरेशन",
"sandboxDescription": "भुगतान का परीक्षण करने के लिए सैंडबॉक्स मोड सक्षम करें" "configInfo": "कॉन्फ़िगरेशन जानकारी",
}, "configPlaceholder": "कृपया प्रदान किए गए {field} कॉन्फ़िगरेशन को भरें",
"confirm": "पुष्टि करें",
"confirmDelete": "हटाने की पुष्टि करें",
"copy": "कॉपी करें",
"copySuccess": "सफलता से कॉपी किया गया",
"create": "भुगतान विधि जोड़ें",
"createPayment": "भुगतान विधि जोड़ें",
"createSuccess": "सफलता से बनाया गया",
"delete": "हटाएं",
"deleteSuccess": "सफलता से हटाया गया",
"deleteWarning": "क्या आप वास्तव में इस भुगतान विधि को हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।",
"description": "विवरण",
"descriptionPlaceholder": "भुगतान विधि का विवरण दर्ज करें",
"domain": "डोमेन",
"domainPlaceholder": "वैकल्पिक, जैसे कि {example}",
"edit": "संपादित करें",
"editPayment": "भुगतान विधि संपादित करें",
"enable": "सक्षम करें", "enable": "सक्षम करें",
"enableDescription": "इस भुगतान विधि को सक्षम करें", "feeAmount": "निश्चित राशि",
"epay": {
"key": "कुंजी",
"pid": "पीआईडी",
"url": "यूआरएल"
},
"feeMode": "शुल्क मोड", "feeMode": "शुल्क मोड",
"feeModeDescription": "अतिरिक्त शुल्क की वसूली का तरीका",
"feeModeItems": ["कोई शुल्क नहीं", "प्रतिशत", "निश्चित राशि", "उपरोक्त दोनों"],
"feePercent": "प्रतिशत शुल्क (वैकल्पिक)", "feePercent": "प्रतिशत शुल्क (वैकल्पिक)",
"feePercentDescription": "ऑर्डर राशि के ऊपर शुल्क जोड़ा जाएगा",
"fixedFee": "निश्चित शुल्क (वैकल्पिक)", "fixedFee": "निश्चित शुल्क (वैकल्पिक)",
"fixedFeeDescription": "ऑर्डर राशि के ऊपर एक निश्चित शुल्क जोड़ा जाता है", "handlingFee": "हैंडलिंग शुल्क",
"iconUrl": "आइकन URL (वैकल्पिक)", "icon": "चिन्ह",
"iconUrlDescription": "फ्रंटएंड प्रदर्शन के लिए उपयोग किया जाता है", "iconPlaceholder": "चिन्ह URL दर्ज करें",
"inputPlaceholder": "कृपया दर्ज करें", "mark": "पहचानकर्ता",
"key": "कुंजी", "markPlaceholder": "भुगतान विधि पहचानकर्ता दर्ज करें",
"notifyUrl": "कस्टम अधिसूचना डोमेन (वैकल्पिक)", "name": "नाम",
"notifyUrlDescription": "गेटवे सूचना इस डोमेन पर भेजी जाएगी", "namePlaceholder": "भुगतान विधि का नाम दर्ज करें",
"payForRecommendations": "सिफारिशों के लिए भुगतान करें", "nameRequired": "नाम आवश्यक है",
"pid": "पीआईडी", "noFee": "कोई शुल्क नहीं",
"saveSuccess": "सहेजा गया", "notify_url": "सूचना URL",
"showName": "नाम दिखाएं", "paymentManagement": "भुगतान प्रबंधन",
"showNameDescription": "फ्रंटएंड प्रदर्शन के लिए उपयोग किया जाता है", "percentFee": "प्रतिशत",
"stripe": { "platform": "प्लेटफ़ॉर्म",
"publicKey": "सार्वजनिक कुंजी", "searchPlaceholder": "खोज शब्द दर्ज करें",
"secretKey": "गुप्त कुंजी", "selectPlatform": "प्लेटफ़ॉर्म चुनें",
"webhookSecret": "वेबहुक कुंजी हस्ताक्षर" "selectType": "भुगतान प्रकार चुनें",
}, "submit": "जमा करें",
"url": "यूआरएल", "type": "प्रकार",
"wechatPay": "वीचैट पे" "updateSuccess": "सफलता से अपडेट किया गया"
} }
+2
View File
@@ -13,6 +13,8 @@
"title": "गोपनीयता नीति" "title": "गोपनीयता नीति"
}, },
"site": { "site": {
"customData": "कस्टम डेटा",
"customDataDescription": "कस्टम डेटा, साइट के कस्टम डेटा के लिए उपयोग किया जाता है",
"customHtml": "कस्टम HTML", "customHtml": "कस्टम HTML",
"customHtmlDescription": "साइट के bodyैग के नीचे इंजेक्ट करने के लिए कस्टम HTML कोड।", "customHtmlDescription": "साइट के bodyैग के नीचे इंजेक्ट करने के लिए कस्टम HTML कोड।",
"keywords": "कीवर्ड", "keywords": "कीवर्ड",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "Mégse",
"confirm": "Megerősítés",
"confirmDelete": "Törlés megerősítése",
"create": "Hirdetés létrehozása",
"createAds": "Hirdetés létrehozása",
"createSuccess": "Sikeresen létrehozva",
"delete": "Törlés",
"deleteSuccess": "Sikeresen törölve",
"deleteWarning": "Biztosan törölni szeretné ezt a hirdetést?",
"disabled": "Letiltva",
"edit": "Szerkesztés",
"editAds": "Hirdetés szerkesztése",
"enabled": "Engedélyezve",
"form": {
"cancel": "Mégse",
"confirm": "Megerősítés",
"content": "Tartalom",
"description": "Leírás",
"endTime": "Befejezési idő",
"enterContent": "Kérjük, adja meg a hirdetés tartalmát",
"enterDescription": "Kérjük, adja meg a hirdetés leírását",
"enterEndTime": "Kérjük, válassza ki a befejezési időt",
"enterStartTime": "Kérjük, válassza ki a kezdési időt",
"enterTargetUrl": "Kérjük, adja meg a cél URL-t",
"enterTitle": "Kérjük, adja meg a hirdetés címét",
"startTime": "Kezdési idő",
"targetUrl": "Cél URL",
"title": "Cím",
"type": "Hirdetés típusa",
"typeImage": "Kép",
"typeVideo": "Videó"
},
"status": "Állapot",
"targetUrl": "Cél URL",
"title": "Cím",
"type": "Típus",
"updateSuccess": "Sikeresen frissítve",
"validityPeriod": "Érvényességi időszak"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "Kérjük, adjon meg egy érvényes e-mail címet.", "email": "Kérjük, adjon meg egy érvényes e-mail címet.",
"existingAccount": "Már van fiókja?", "existingAccount": "Már van fiókja?",
"get": "Megkap", "get": "Megkap",
"invite": "Meghívókód", "invite": "Meghívó kód (opcionális)",
"message": "#### Tisztelt Felhasználó!\n\nKöszönjük érdeklődését és támogatását. Az oldal működési stratégiájának módosítása miatt lezártuk az új felhasználók regisztrációját. Ez idő alatt a meglévő felhasználók használata nem lesz érintett.\n\nElkötelezettek vagyunk abban, hogy jobb szolgáltatást és élményt nyújtsunk Önnek, ezért a regisztráció lezárása alatt átfogó rendszeroptimalizálást és funkciófrissítést hajtunk végre. A jövőben még jobb tartalommal és szolgáltatásokkal várjuk Önt.\n\nKérjük, kövesse weboldalunkat és közösségi média platformjainkat, hogy megkapja a legfrissebb híreket és értesítéseket. Köszönjük megértését és támogatását.\n\nHa bármilyen kérdése van, vagy segítségre van szüksége, kérjük, forduljon ügyfélszolgálati csapatunkhoz.\n\n**Még egyszer köszönjük támogatását és megértését.**", "message": "#### Tisztelt Felhasználó!\n\nKöszönjük érdeklődését és támogatását. Az oldal működési stratégiájának módosítása miatt lezártuk az új felhasználók regisztrációját. Ez idő alatt a meglévő felhasználók használata nem lesz érintett.\n\nElkötelezettek vagyunk abban, hogy jobb szolgáltatást és élményt nyújtsunk Önnek, ezért a regisztráció lezárása alatt átfogó rendszeroptimalizálást és funkciófrissítést hajtunk végre. A jövőben még jobb tartalommal és szolgáltatásokkal várjuk Önt.\n\nKérjük, kövesse weboldalunkat és közösségi média platformjainkat, hogy megkapja a legfrissebb híreket és értesítéseket. Köszönjük megértését és támogatását.\n\nHa bármilyen kérdése van, vagy segítségre van szüksége, kérjük, forduljon ügyfélszolgálati csapatunkhoz.\n\n**Még egyszer köszönjük támogatását és megértését.**",
"passwordMismatch": "A két jelszó nem egyezik meg", "passwordMismatch": "A két jelszó nem egyezik meg",
"success": "Sikeres regisztráció, automatikusan bejelentkezett!", "success": "Sikeres regisztráció, automatikusan bejelentkezett!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "Virtuális gép blokkolása",
"blockVirtualMachineDescription": "Ha engedélyezve van, az eszközök nem fognak tudni virtuális gépeken vagy emulátorokon futni",
"communicationKey": "Kommunikációs kulcs",
"communicationKeyDescription": "A kommunikációs kulcs a biztonságos kommunikációhoz szükséges az eszközök és a szerverek között",
"enable": "Engedélyezés", "enable": "Engedélyezés",
"enableDescription": "Engedélyezés után több eszközazonosító, például IMEI/IDFA/IDFV/AndroidID/Mac-cím támogatott a bejelentkezéshez és a regisztrációhoz." "enableDescription": "Engedélyezés után több eszközazonosító, például IMEI/IDFA/IDFV/AndroidID/Mac-cím támogatott a bejelentkezéshez és a regisztrációhoz.",
"enableSecurity": "Kommunikációs titkosítás engedélyezése",
"enableSecurityDescription": "Ha engedélyezve van, az eszközök és a szerverek közötti kommunikáció titkosítva lesz",
"saveFailed": "Mentés sikertelen",
"saveSuccess": "Mentés sikeres",
"showAds": "Hirdetések megjelenítése",
"showAdsDescription": "Ha engedélyezve van, hirdetések fognak megjelenni az eszközökön"
} }
+2
View File
@@ -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",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "ADS konfiguráció",
"Announcement Management": "Hirdetménykezelés", "Announcement Management": "Hirdetménykezelés",
"Apple": "Apple ID", "Apple": "Apple ID",
"Auth Control": "Hitelesítési vezérlés", "Auth Control": "Hitelesítési vezérlés",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "Alipay fizetés", "AlipayF2F": "Alipay Személyesen",
"alipayf2f": { "EPay": "EPay",
"appId": "Alipay alkalmazásazonosító", "Stripe": "Stripe",
"invoiceName": "Egyedi terméknév", "applyForPayment": "Kérjen kifizetést",
"invoiceNameDescription": "A számlán az Alipay-ben fog megjelenni", "balance": "Egyenleg",
"privateKey": "Alipay privát kulcs", "batchDelete": "Tömeges törlés",
"publicKey": "Alipay nyilvános kulcs", "cancel": "Mégse",
"sandbox": "Tesztkörnyezet", "config": "Beállítások",
"sandboxDescription": "Tesztkörnyezet engedélyezése a fizetések teszteléséhez" "configInfo": "Beállítási információk",
}, "configPlaceholder": "Kérjük, töltse ki a megadott {field} beállítást",
"confirm": "Megerősít",
"confirmDelete": "Törlés megerősítése",
"copy": "Másolás",
"copySuccess": "Sikeresen másolva",
"create": "Fizetési mód hozzáadása",
"createPayment": "Fizetési mód hozzáadása",
"createSuccess": "Sikeresen létrehozva",
"delete": "Törlés",
"deleteSuccess": "Sikeresen törölve",
"deleteWarning": "Biztosan törölni szeretné ezt a fizetési módot? Ez a művelet nem vonható vissza.",
"description": "Leírás",
"descriptionPlaceholder": "Írja be a fizetési mód leírását",
"domain": "Domain",
"domainPlaceholder": "opcionális, pl. {példa}",
"edit": "Szerkesztés",
"editPayment": "Fizetési mód szerkesztése",
"enable": "Engedélyezés", "enable": "Engedélyezés",
"enableDescription": "Engedélyezze ezt a fizetési módot", "feeAmount": "Fix összeg",
"epay": {
"key": "KULCS",
"pid": "PID",
"url": "URL"
},
"feeMode": "Díjfizetési mód", "feeMode": "Díjfizetési mód",
"feeModeDescription": "További díjak felszámításának módja",
"feeModeItems": ["Nincs díj", "Százalékos", "Fix összeg", "Mindkettő"],
"feePercent": "Díjszázalék (opcionális)", "feePercent": "Díjszázalék (opcionális)",
"feePercentDescription": "Díj hozzáadása a rendelés összegéhez",
"fixedFee": "Fix díj (opcionális)", "fixedFee": "Fix díj (opcionális)",
"fixedFeeDescription": "Rögzített díj hozzáadása a rendelés összegéhez", "handlingFee": "Kezelési díj",
"iconUrl": "Ikon URL (opcionális)", "icon": "Ikon",
"iconUrlDescription": "Előnézeti kép megjelenítéséhez", "iconPlaceholder": "Adja meg az ikon URL-jét",
"inputPlaceholder": "Kérjük, írja be", "mark": "Azonosító",
"key": "KULCS", "markPlaceholder": "Adja meg a fizetési mód azonosítóját",
"notifyUrl": "Egyéni értesítési domain (opcionális)", "name": "Név",
"notifyUrlDescription": "A kapu értesítéseket erre a tartományra küldi", "namePlaceholder": "Adja meg a fizetési mód nevét",
"payForRecommendations": "Fizetés ajánlásokért", "nameRequired": "A név megadása kötelező",
"pid": "PID", "noFee": "Nincs díj",
"saveSuccess": "Sikeres mentés", "notify_url": "Értesítési URL",
"showName": "Megjelenítési név", "paymentManagement": "Fizetéskezelés",
"showNameDescription": "Előnézet megjelenítésére használatos", "percentFee": "Százalékos díj",
"stripe": { "platform": "Platform",
"publicKey": "NYILVÁNOS KULCS", "searchPlaceholder": "Adja meg a keresési kifejezéseket",
"secretKey": "TITKOS KULCS", "selectPlatform": "Válassza ki a platformot",
"webhookSecret": "WEBHOOK TITKOS ALÁÍRÁS" "selectType": "Válassza ki a fizetési típust",
}, "submit": "Beküldés",
"url": "URL", "type": "Típus",
"wechatPay": "WeChat fizetés" "updateSuccess": "Sikeresen frissítve"
} }
+2
View File
@@ -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",
+40
View File
@@ -0,0 +1,40 @@
{
"cancel": "キャンセル",
"confirm": "確認",
"confirmDelete": "削除を確認",
"create": "広告を作成",
"createAds": "広告を作成",
"createSuccess": "作成に成功しました",
"delete": "削除",
"deleteSuccess": "削除に成功しました",
"deleteWarning": "この広告を削除してもよろしいですか?",
"disabled": "無効",
"edit": "編集",
"editAds": "広告を編集",
"enabled": "有効",
"form": {
"cancel": "キャンセル",
"confirm": "確認",
"content": "コンテンツ",
"description": "説明",
"endTime": "終了時間",
"enterContent": "広告の内容を入力してください",
"enterDescription": "広告の説明を入力してください",
"enterEndTime": "終了時間を選択してください",
"enterStartTime": "開始時間を選択してください",
"enterTargetUrl": "ターゲットURLを入力してください",
"enterTitle": "広告のタイトルを入力してください",
"startTime": "開始時間",
"targetUrl": "ターゲットURL",
"title": "タイトル",
"type": "広告の種類",
"typeImage": "画像",
"typeVideo": "動画"
},
"status": "ステータス",
"targetUrl": "ターゲットURL",
"title": "タイトル",
"type": "種類",
"updateSuccess": "更新に成功しました",
"validityPeriod": "有効期限"
}
+1 -1
View File
@@ -28,7 +28,7 @@
"email": "有効なメールアドレスを入力してください。", "email": "有効なメールアドレスを入力してください。",
"existingAccount": "すでにアカウントをお持ちですか?", "existingAccount": "すでにアカウントをお持ちですか?",
"get": "取得", "get": "取得",
"invite": "招待コード", "invite": "招待コード(任意)",
"message": "#### 親愛なるユーザーの皆様へ\n\n私たちのサイトにご関心とご支援をいただき、誠にありがとうございます。運営方針の調整に伴い、新規ユーザー登録機能を停止いたしました。この期間中、既存ユーザーのご利用には影響はございません。\n\nより良いサービスと体験を提供するため、登録停止期間中にシステムの全面的な最適化と機能のアップグレードを行います。今後、より質の高いコンテンツとサービスで皆様をお迎えいたします。\n\n最新の情報や通知を得るために、当社のウェブサイトやソーシャルメディアプラットフォームをご覧ください。ご理解とご支援に感謝いたします。\n\nご不明な点やご質問がございましたら、いつでもカスタマーサポートチームにお問い合わせください。\n\n**改めてご支援とご理解に感謝いたします。**", "message": "#### 親愛なるユーザーの皆様へ\n\n私たちのサイトにご関心とご支援をいただき、誠にありがとうございます。運営方針の調整に伴い、新規ユーザー登録機能を停止いたしました。この期間中、既存ユーザーのご利用には影響はございません。\n\nより良いサービスと体験を提供するため、登録停止期間中にシステムの全面的な最適化と機能のアップグレードを行います。今後、より質の高いコンテンツとサービスで皆様をお迎えいたします。\n\n最新の情報や通知を得るために、当社のウェブサイトやソーシャルメディアプラットフォームをご覧ください。ご理解とご支援に感謝いたします。\n\nご不明な点やご質問がございましたら、いつでもカスタマーサポートチームにお問い合わせください。\n\n**改めてご支援とご理解に感謝いたします。**",
"passwordMismatch": "パスワードが一致しません", "passwordMismatch": "パスワードが一致しません",
"success": "登録が成功しました。自動的にログインしました!", "success": "登録が成功しました。自動的にログインしました!",
+11 -1
View File
@@ -1,4 +1,14 @@
{ {
"blockVirtualMachine": "仮想マシンをブロック",
"blockVirtualMachineDescription": "有効にすると、デバイスは仮想マシンやエミュレーター上での実行が禁止されます",
"communicationKey": "通信キー",
"communicationKeyDescription": "通信キーは、デバイスとサーバー間の安全な通信に使用されます",
"enable": "有効化", "enable": "有効化",
"enableDescription": "有効化後、ログインおよび登録のために、IMEI/IDFA/IDFV/AndroidID/Macアドレスなどの複数のデバイス識別子がサポートされます。" "enableDescription": "有効化後、ログインおよび登録のために、IMEI/IDFA/IDFV/AndroidID/Macアドレスなどの複数のデバイス識別子がサポートされます。",
"enableSecurity": "通信暗号化を有効にする",
"enableSecurityDescription": "有効にすると、デバイスとサーバー間の通信が暗号化されます",
"saveFailed": "保存に失敗しました",
"saveSuccess": "保存に成功しました",
"showAds": "広告を表示",
"showAdsDescription": "有効にすると、デバイスに広告が表示されます"
} }
+2
View File
@@ -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": "ホワイトリストのサフィックス",
+1
View File
@@ -1,4 +1,5 @@
{ {
"ADS Config": "ADS設定",
"Announcement Management": "お知らせ管理", "Announcement Management": "お知らせ管理",
"Apple": "Apple ID", "Apple": "Apple ID",
"Auth Control": "認証管理", "Auth Control": "認証管理",
+46 -38
View File
@@ -1,44 +1,52 @@
{ {
"aliPay": "アリペイ決済", "AlipayF2F": "アリペイ対面",
"alipayf2f": { "EPay": "EPay",
"appId": "支付宝アプリID", "Stripe": "ストライプ",
"invoiceName": "カスタム製品名", "applyForPayment": "支払いを申請する",
"invoiceNameDescription": "支付宝の請求書に表示されます", "balance": "残高",
"privateKey": "支付宝プライベートキー", "batchDelete": "一括削除",
"publicKey": "支付宝パブリックキー", "cancel": "キャンセル",
"sandbox": "サンドボックスモード", "config": "設定",
"sandboxDescription": "支払いをテストするためにサンドボックスモードを有効にします" "configInfo": "設定情報",
}, "configPlaceholder": "提供された{field}の設定を入力してください",
"confirm": "確認",
"confirmDelete": "削除を確認",
"copy": "コピー",
"copySuccess": "コピー成功",
"create": "支払い方法を追加",
"createPayment": "支払い方法を追加",
"createSuccess": "作成成功",
"delete": "削除",
"deleteSuccess": "削除成功",
"deleteWarning": "この支払い方法を削除してもよろしいですか?この操作は元に戻せません。",
"description": "説明",
"descriptionPlaceholder": "支払い方法の説明を入力してください",
"domain": "ドメイン",
"domainPlaceholder": "任意、例:{example}",
"edit": "編集",
"editPayment": "支払い方法を編集",
"enable": "有効", "enable": "有効",
"enableDescription": "この支払い方法を有効にする", "feeAmount": "固定額",
"epay": {
"key": "キー",
"pid": "PID",
"url": "URL"
},
"feeMode": "料金方式", "feeMode": "料金方式",
"feeModeDescription": "追加手数料の課金方式",
"feeModeItems": ["手数料なし", "パーセンテージ", "固定金額", "上記の2つ"],
"feePercent": "パーセンテージ手数料(オプション)", "feePercent": "パーセンテージ手数料(オプション)",
"feePercentDescription": "注文金額に追加される手数料",
"fixedFee": "固定料金(オプション)", "fixedFee": "固定料金(オプション)",
"fixedFeeDescription": "注文金額に固定料金を追加する", "handlingFee": "手数料",
"iconUrl": "アイコンURL(任意)", "icon": "アイコン",
"iconUrlDescription": "フロントエンド表示用", "iconPlaceholder": "アイコンのURLを入力してください",
"inputPlaceholder": "入力してください", "mark": "識別子",
"key": "キー", "markPlaceholder": "支払い方法の識別子を入力してください",
"notifyUrl": "カスタム通知ドメイン(オプション)", "name": "名前",
"notifyUrlDescription": "ゲートウェイ通知はこのドメインに送信されます", "namePlaceholder": "支払い方法の名前を入力してください",
"payForRecommendations": "おすすめの支払い", "nameRequired": "名前は必須です",
"pid": "PID", "noFee": "手数料なし",
"saveSuccess": "保存が成功しました", "notify_url": "通知URL",
"showName": "表示名", "paymentManagement": "支払い管理",
"showNameDescription": "フロントエンド表示用", "percentFee": "パーセンテージ",
"stripe": { "platform": "プラットフォーム",
"publicKey": "パブリックキー", "searchPlaceholder": "検索用語を入力してください",
"secretKey": "シークレットキー", "selectPlatform": "プラットフォームを選択",
"webhookSecret": "WEBHOOK シークレットサイン" "selectType": "支払いタイプを選択",
}, "submit": "送信",
"url": "URL", "type": "タイプ",
"wechatPay": "WeChatペイ" "updateSuccess": "更新成功"
} }
+2
View File
@@ -13,6 +13,8 @@
"title": "プライバシーポリシー" "title": "プライバシーポリシー"
}, },
"site": { "site": {
"customData": "カスタムデータ",
"customDataDescription": "サイトのカスタムデータに使用されるカスタムデータ",
"customHtml": "カスタムHTML", "customHtml": "カスタムHTML",
"customHtmlDescription": "サイトの body タグの下部に挿入されるカスタム HTML コード。", "customHtmlDescription": "サイトの body タグの下部に挿入されるカスタム HTML コード。",
"keywords": "キーワード", "keywords": "キーワード",

Some files were not shown because too many files have changed in this diff Show More