diff --git a/config/language-translations.php b/config/language-translations.php index 974d1fa9..91ea4009 100644 --- a/config/language-translations.php +++ b/config/language-translations.php @@ -208,14 +208,15 @@ return [ 'interval.year' => 'Yearly', // v2 - 'bandwidth' => 'Bandwidth', - 'storage' => 'Storage', - 'flatFee' => 'Flat Fee', - 'feature_usage_desc_flatFee' => 'Price for the service.', - 'feature_usage_desc_bandwidth' => 'Data amount you transferred to/from your account.', - 'feature_usage_desc_storage' => 'Total storage amount you are using.', - 'feature_usage_desc_members' => 'Total members you invited to your team folders.', - 'feature_usage_desc_platform' => 'Total storage amount you are using.', + 'bandwidth' => 'Bandwidth', + 'storage' => 'Storage', + 'flatFee' => 'Flat Fee', + 'member' => 'Members', + 'feature_usage_desc_flatFee' => 'Price for the service.', + 'feature_usage_desc_bandwidth' => 'Data amount you transferred to/from your account.', + 'feature_usage_desc_storage' => 'Total storage amount you are using.', + 'feature_usage_desc_member' => 'Total members you invited to your team folders.', + 'feature_usage_desc_platform' => 'Total storage amount you are using.', ], 'regular' => [ 'actions.close' => 'Close', diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 7f7a00a8..ac3da90c 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -13,7 +13,7 @@ "/chunks/admin~chunks/files~chunks/my-shared-items~chunks/platform~chunks/recent-uploads~chunks/shared~1bec6fe4.js": "/chunks/admin~chunks/files~chunks/my-shared-items~chunks/platform~chunks/recent-uploads~chunks/shared~1bec6fe4.js?id=510e6c1b1017a73a40a6", "/chunks/admin~chunks/platform.js": "/chunks/admin~chunks/platform.js?id=917aab9de16d3eb7039a", "/chunks/admin~chunks/platform~chunks/settings.js": "/chunks/admin~chunks/platform~chunks/settings.js?id=4bde434e3ed10f3f29b2", - "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.js": "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.js?id=946723d9a3185230f2a6", + "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.js": "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.js?id=9bd311cf42f02c0709e8", "/chunks/admin~chunks/platform~chunks/shared.js": "/chunks/admin~chunks/platform~chunks/shared.js?id=afeba4ebd13af7e995be", "/chunks/app-appearance.js": "/chunks/app-appearance.js?id=6035ca411b2c4239d964", "/chunks/app-appearance~chunks/app-billings~chunks/app-email~chunks/app-index~chunks/app-others~chunks~5acee76d.js": "/chunks/app-appearance~chunks/app-billings~chunks/app-email~chunks/app-index~chunks/app-others~chunks~5acee76d.js?id=75bbb477bf92edb65799", @@ -56,10 +56,10 @@ "/chunks/pages.js": "/chunks/pages.js?id=7db66df0453135bf4e51", "/chunks/plan.js": "/chunks/plan.js?id=f62a5bd64fb706b2f0e2", "/chunks/plan-create.js": "/chunks/plan-create.js?id=995a9a5ae9cc43f35d2c", - "/chunks/plan-delete.js": "/chunks/plan-delete.js?id=c87a14fce5ffb4ea0451", - "/chunks/plan-settings.js": "/chunks/plan-settings.js?id=d58857bc3d9c578a1538", + "/chunks/plan-delete.js": "/chunks/plan-delete.js?id=2308117f74ce905c1c2a", + "/chunks/plan-settings.js": "/chunks/plan-settings.js?id=48f57902fbac955c7c99", "/chunks/plan-subscribers.js": "/chunks/plan-subscribers.js?id=afa7c5328893c5d16e3b", - "/chunks/plans.js": "/chunks/plans.js?id=3c1f3d5a9cfe4dcf5237", + "/chunks/plans.js": "/chunks/plans.js?id=854c232c29d88c5b541a", "/chunks/platform.js": "/chunks/platform.js?id=426da7075ef9a88ea088", "/chunks/platform~chunks/shared.js": "/chunks/platform~chunks/shared.js?id=5734e9333fc67c706853", "/chunks/platform~chunks/shared~chunks/shared-with-me~chunks/team-folders.js": "/chunks/platform~chunks/shared~chunks/shared-with-me~chunks/team-folders.js?id=7d983dfdc91de607d737", @@ -73,7 +73,7 @@ "/chunks/settings-payment-methods.js": "/chunks/settings-payment-methods.js?id=13d23c92a535d7c9a4ff", "/chunks/settings-storage.js": "/chunks/settings-storage.js?id=76b45c336e8e12b23e81", "/chunks/settings-storage~chunks/settings-subscription~chunks/user-storage~chunks/user-subscription.js": "/chunks/settings-storage~chunks/settings-subscription~chunks/user-storage~chunks/user-subscription.js?id=cd797256cb819aac4d24", - "/chunks/settings-subscription.js": "/chunks/settings-subscription.js?id=5cbb63c9ce73aed42a38", + "/chunks/settings-subscription.js": "/chunks/settings-subscription.js?id=53a0c043797fd9a719bb", "/chunks/settings~chunks/settings-password.js": "/chunks/settings~chunks/settings-password.js?id=743bf9cb1e62af56c04e", "/chunks/setup-wizard.js": "/chunks/setup-wizard.js?id=651d5accf401908724c5", "/chunks/shared.js": "/chunks/shared.js?id=6230d050545cd1bd9b87", @@ -100,7 +100,7 @@ "/chunks/user-detail.js": "/chunks/user-detail.js?id=fab2eae409831e768b0d", "/chunks/user-password.js": "/chunks/user-password.js?id=6aeb19839b38f287953d", "/chunks/user-storage.js": "/chunks/user-storage.js?id=936f120357a4480e1bd5", - "/chunks/user-subscription.js": "/chunks/user-subscription.js?id=b868c2c3f6dce432c076", + "/chunks/user-subscription.js": "/chunks/user-subscription.js?id=781e3ce396f5daf7bba4", "/chunks/users.js": "/chunks/users.js?id=ab7eeac6e8559dc1eb2b", "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~35bc7519.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~35bc7519.js?id=ae06aafc3749254fe4aa", "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~629342a0.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~629342a0.js?id=cdefaa7800d04dafb07b", @@ -391,7 +391,7 @@ "/chunks/app-setup.bfb51d46fe9500c09f69.hot-update.js": "/chunks/app-setup.bfb51d46fe9500c09f69.hot-update.js", "/chunks/plan.bfb51d46fe9500c09f69.hot-update.js": "/chunks/plan.bfb51d46fe9500c09f69.hot-update.js", "/chunks/plan-create/fixed.js": "/chunks/plan-create/fixed.js?id=25ad60a594f9e25d7621", - "/chunks/plan-create/metered.js": "/chunks/plan-create/metered.js?id=e3ccb9876029e03fb932", + "/chunks/plan-create/metered.js": "/chunks/plan-create/metered.js?id=d41465e6cd6ff830e8c1", "/chunks/settings-storage.bfb51d46fe9500c09f69.hot-update.js": "/chunks/settings-storage.bfb51d46fe9500c09f69.hot-update.js", "/chunks/settings-subscription.bfb51d46fe9500c09f69.hot-update.js": "/chunks/settings-subscription.bfb51d46fe9500c09f69.hot-update.js", "/chunks/user-create.bfb51d46fe9500c09f69.hot-update.js": "/chunks/user-create.bfb51d46fe9500c09f69.hot-update.js", @@ -611,5 +611,23 @@ "/chunks/plan-settings.3a65ee9da570c90f6297.hot-update.js": "/chunks/plan-settings.3a65ee9da570c90f6297.hot-update.js", "/chunks/plan-settings.dcff32efc576de996ee2.hot-update.js": "/chunks/plan-settings.dcff32efc576de996ee2.hot-update.js", "/chunks/plan-create/metered.50f807a0d4bed310eb40.hot-update.js": "/chunks/plan-create/metered.50f807a0d4bed310eb40.hot-update.js", - "/chunks/plan-settings.9a034df6b5ef5adba55b.hot-update.js": "/chunks/plan-settings.9a034df6b5ef5adba55b.hot-update.js" + "/chunks/plan-settings.9a034df6b5ef5adba55b.hot-update.js": "/chunks/plan-settings.9a034df6b5ef5adba55b.hot-update.js", + "/chunks/plan-create/metered.4ef19a20e19e61a31348.hot-update.js": "/chunks/plan-create/metered.4ef19a20e19e61a31348.hot-update.js", + "/chunks/plan-create/metered.274a2e2a39cbc3e24a72.hot-update.js": "/chunks/plan-create/metered.274a2e2a39cbc3e24a72.hot-update.js", + "/chunks/plan-create/metered.c99a3c2c838cd5aa1252.hot-update.js": "/chunks/plan-create/metered.c99a3c2c838cd5aa1252.hot-update.js", + "/chunks/plan-settings.841a91b4b1caff6d3752.hot-update.js": "/chunks/plan-settings.841a91b4b1caff6d3752.hot-update.js", + "/chunks/plan-settings.e27dc14dc57c3b35f180.hot-update.js": "/chunks/plan-settings.e27dc14dc57c3b35f180.hot-update.js", + "/chunks/plan-settings.1288a4bc847c61c97b62.hot-update.js": "/chunks/plan-settings.1288a4bc847c61c97b62.hot-update.js", + "/chunks/plan-settings.dd51e493c7d1e5935096.hot-update.js": "/chunks/plan-settings.dd51e493c7d1e5935096.hot-update.js", + "/chunks/plan-settings.9e7d7d22a4787b317d00.hot-update.js": "/chunks/plan-settings.9e7d7d22a4787b317d00.hot-update.js", + "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.471864fd47b6ed9bbe5a.hot-update.js": "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.471864fd47b6ed9bbe5a.hot-update.js", + "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.d291161f2184732e0c45.hot-update.js": "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.d291161f2184732e0c45.hot-update.js", + "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.bb83d799f88f9277821f.hot-update.js": "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.bb83d799f88f9277821f.hot-update.js", + "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.beb17b0ad621e24fa9c7.hot-update.js": "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.beb17b0ad621e24fa9c7.hot-update.js", + "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.1c63a4090e8c7c009c73.hot-update.js": "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.1c63a4090e8c7c009c73.hot-update.js", + "/chunks/plans.3f36cf58fc7b3994de9e.hot-update.js": "/chunks/plans.3f36cf58fc7b3994de9e.hot-update.js", + "/chunks/plan-delete.d73a5950c032edd90159.hot-update.js": "/chunks/plan-delete.d73a5950c032edd90159.hot-update.js", + "/chunks/plan-create/metered.3eee477efc68c1983af0.hot-update.js": "/chunks/plan-create/metered.3eee477efc68c1983af0.hot-update.js", + "/chunks/settings-subscription.3eee477efc68c1983af0.hot-update.js": "/chunks/settings-subscription.3eee477efc68c1983af0.hot-update.js", + "/chunks/user-subscription.3eee477efc68c1983af0.hot-update.js": "/chunks/user-subscription.3eee477efc68c1983af0.hot-update.js" } diff --git a/resources/js/components/Spotlight/Spotlight.vue b/resources/js/components/Spotlight/Spotlight.vue index db2ff534..dabb9055 100644 --- a/resources/js/components/Spotlight/Spotlight.vue +++ b/resources/js/components/Spotlight/Spotlight.vue @@ -82,7 +82,7 @@ - + @@ -90,7 +90,7 @@ - + @@ -255,6 +255,7 @@ export default { computed: { ...mapGetters([ 'isDarkMode', + 'config', 'user', ]), actionList() { @@ -270,7 +271,9 @@ export default { title: this.$t('Create Plan'), action: { type: 'route', - value: 'CreateFixedPlan', + value: this.config.subscriptionType === 'fixed' + ? 'CreateFixedPlan' + : 'CreateMeteredPlan', }, }, ] @@ -405,7 +408,9 @@ export default { title: this.$t('Show my Subscription'), action: { type: 'route', - value: 'Subscription', + value: this.config.subscriptionType === 'fixed' + ? 'Subscription' + : 'MeteredSubscription', }, }, { diff --git a/resources/js/views/Admin/Plans.vue b/resources/js/views/Admin/Plans.vue index f665f3ee..38d02fba 100644 --- a/resources/js/views/Admin/Plans.vue +++ b/resources/js/views/Admin/Plans.vue @@ -37,10 +37,16 @@
- + - +
diff --git a/resources/js/views/Admin/Plans/Create/CreateMeteredPlan.vue b/resources/js/views/Admin/Plans/Create/CreateMeteredPlan.vue index c7fdbaa8..16604219 100644 --- a/resources/js/views/Admin/Plans/Create/CreateMeteredPlan.vue +++ b/resources/js/views/Admin/Plans/Create/CreateMeteredPlan.vue @@ -59,6 +59,19 @@ + +
+ + + +
+ + + + + + +
@@ -143,6 +156,12 @@ first_unit: 1, aggregate_strategy: 'maximum_usage', }, + member: { + active: false, + per_unit: undefined, + first_unit: 1, + aggregate_strategy: 'maximum_usage', + }, flatFee: { active: false, per_unit: undefined, @@ -202,7 +221,7 @@ }) .catch(error => { events.$emit('toaster', { - type: 'error', + type: 'danger', message: this.$t('popup_error.title'), }) }) diff --git a/resources/js/views/Admin/Plans/Tabs/PlanDelete.vue b/resources/js/views/Admin/Plans/Tabs/PlanDelete.vue index f4e30671..d6d8b8cc 100644 --- a/resources/js/views/Admin/Plans/Tabs/PlanDelete.vue +++ b/resources/js/views/Admin/Plans/Tabs/PlanDelete.vue @@ -76,7 +76,7 @@ }) .catch(() => { events.$emit('toaster', { - type: 'success', + type: 'danger', message: this.$t('popup_error.title'), }) }) diff --git a/resources/js/views/Admin/Plans/Tabs/PlanMeteredSettings.vue b/resources/js/views/Admin/Plans/Tabs/PlanMeteredSettings.vue index 437e5556..105d44b1 100644 --- a/resources/js/views/Admin/Plans/Tabs/PlanMeteredSettings.vue +++ b/resources/js/views/Admin/Plans/Tabs/PlanMeteredSettings.vue @@ -30,6 +30,11 @@ + + + + + @@ -70,6 +75,7 @@ }, methods: { formatCurrency(currency, amount) { + // TODO: add user locale let formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: currency, diff --git a/resources/js/views/Admin/Users/UserTabs/UserMeteredSubscription.vue b/resources/js/views/Admin/Users/UserTabs/UserMeteredSubscription.vue index 382ac8b0..5fc1ce6d 100644 --- a/resources/js/views/Admin/Users/UserTabs/UserMeteredSubscription.vue +++ b/resources/js/views/Admin/Users/UserTabs/UserMeteredSubscription.vue @@ -133,7 +133,7 @@ }) .catch(() => { events.$emit('toaster', { - type: 'success', + type: 'danger', message: this.$t('popup_error.title'), }) }) diff --git a/resources/js/views/User/MeteredSubscription.vue b/resources/js/views/User/MeteredSubscription.vue index 7b3cebbe..bb3a63f7 100644 --- a/resources/js/views/User/MeteredSubscription.vue +++ b/resources/js/views/User/MeteredSubscription.vue @@ -287,7 +287,7 @@ }) .catch(() => { events.$emit('toaster', { - type: 'success', + type: 'danger', message: this.$t('popup_error.title'), }) }) @@ -318,7 +318,7 @@ }) .catch(() => { events.$emit('toaster', { - type: 'success', + type: 'danger', message: this.$t('popup_error.title'), }) }) diff --git a/src/App/Users/Actions/FormatUsageEstimatesAction.php b/src/App/Users/Actions/FormatUsageEstimatesAction.php index 1aaeea21..f01c5b7b 100644 --- a/src/App/Users/Actions/FormatUsageEstimatesAction.php +++ b/src/App/Users/Actions/FormatUsageEstimatesAction.php @@ -12,15 +12,15 @@ class FormatUsageEstimatesAction return $usage->mapWithKeys(function ($estimate) use ($currency) { // Format usage $usage = match ($estimate['feature']) { - 'bandwidth' => Metric::megabytes($estimate['usage'])->format(), - 'storage' => Metric::megabytes($estimate['usage'])->format(), + 'bandwidth', 'storage' => Metric::megabytes($estimate['usage'])->format(), 'flatFee' => intval($estimate['usage']) . ' ' . __('Pcs.'), + 'member' => intval($estimate['usage']) . ' ' . __('Mem.'), }; // Normalize units $amount = match ($estimate['feature']) { 'bandwidth', 'storage' => $estimate['amount'] / 1000, - 'flatFee' => $estimate['amount'], + 'flatFee', 'member' => $estimate['amount'], }; return [ diff --git a/src/Support/Scheduler/Actions/ReportUsageAction.php b/src/Support/Scheduler/Actions/ReportUsageAction.php index 9c060e1d..5b4d8812 100644 --- a/src/Support/Scheduler/Actions/ReportUsageAction.php +++ b/src/Support/Scheduler/Actions/ReportUsageAction.php @@ -1,6 +1,7 @@ plan->meteredFeatures()->where('key', 'flatFee')->exists()) { $this->recordFlatFee($subscription); } + + if ($subscription->plan->meteredFeatures()->where('key', 'member')->exists()) { + $this->recordMemberUsage($subscription); + } }); } @@ -60,4 +65,35 @@ class ReportUsageAction // Record flat fee $subscription->recordUsage('flatFee', 1); } + + // TODO: Refactor this function to get total used team members, same function here UserLimitation.php@getMaxTeamMembers + private function recordMemberUsage(Subscription $subscription): void + { + $userTeamFolderIds = DB::table('team_folder_members') + ->where('user_id', $subscription->user_id) + ->pluck('parent_id'); + + $memberIds = DB::table('team_folder_members') + ->where('user_id', '!=', $subscription->user_id) + ->whereIn('parent_id', $userTeamFolderIds) + ->pluck('user_id') + ->unique(); + + // Get member emails + $memberEmails = User::whereIn('id', $memberIds) + ->pluck('email'); + + // Get active invitation emails + $InvitationEmails = DB::table('team_folder_invitations') + ->where('status', 'pending') + ->where('inviter_id', $subscription->user_id) + ->pluck('email') + ->unique(); + + // Get allowed emails in the limit + $totalUsedEmails = $memberEmails->merge($InvitationEmails) + ->unique(); + + $subscription->recordUsage('member', $totalUsedEmails->count()); + } } diff --git a/tests/App/Users/UserSubscriptionTest.php b/tests/App/Users/UserSubscriptionTest.php index 77e5e5e6..328031e5 100644 --- a/tests/App/Users/UserSubscriptionTest.php +++ b/tests/App/Users/UserSubscriptionTest.php @@ -120,6 +120,10 @@ class UserSubscriptionTest extends TestCase 'feature' => 'flatFee', 'amount' => 2.49, 'usage' => 1, + ], [ + 'feature' => 'member', + 'amount' => 0.20, + 'usage' => 2, ], ]); @@ -146,6 +150,12 @@ class UserSubscriptionTest extends TestCase 'cost' => '$2.49', 'usage' => '1 Pcs.', ], + 'member' => [ + 'feature' => 'member', + 'amount' => 0.20, + 'cost' => '$0.20', + 'usage' => '2 Mem.', + ], ]; $this->assertEquals($expected, $estimates); diff --git a/tests/Support/Scheduler/SchedulerTest.php b/tests/Support/Scheduler/SchedulerTest.php index afe65fe5..23f5f2f8 100644 --- a/tests/Support/Scheduler/SchedulerTest.php +++ b/tests/Support/Scheduler/SchedulerTest.php @@ -32,11 +32,12 @@ class SchedulerTest extends TestCase ]); PlanMeteredFeature::factory() - ->count(3) + ->count(4) ->sequence( ['key' => 'storage'], ['key' => 'bandwidth'], ['key' => 'flatFee'], + ['key' => 'member'], ) ->create([ 'plan_id' => $plan->id,