-
Notifications
You must be signed in to change notification settings - Fork 236
feat: add Premium Geo DB addon to project settings #2981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
470b047
5954b75
7e73d36
0e53210
66e615e
afb13fc
13b0478
0cba797
6fc000f
304142c
67566db
e5a62c5
a54c530
4376702
da68f75
0889ace
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -295,10 +295,6 @@ | |
| }; | ||
|
|
||
| // addons (additional members, projects, etc.) | ||
| const billingAddonNames: Record<string, string> = { | ||
| addon_baa: 'HIPAA BAA' | ||
| }; | ||
|
|
||
| const addons = (currentAggregation?.resources || []) | ||
| .filter( | ||
| (r) => | ||
|
|
@@ -315,8 +311,8 @@ | |
| ? 'Additional members' | ||
| : addon.resourceId === 'projects' | ||
| ? 'Additional projects' | ||
| : (billingAddonNames[addon.resourceId] ?? | ||
| `${addon.resourceId} overage (${formatNum(addon.value)})`), | ||
| : addon.name || | ||
| `${addon.resourceId} overage (${formatNum(addon.value)})`, | ||
| usage: '', | ||
| price: formatCurrency(addon.amount) | ||
| }, | ||
|
|
@@ -462,6 +458,18 @@ | |
| priceFormatter: ({ amount }) => formatCurrency(amount), | ||
| includeProgress: false | ||
| }), | ||
| ...resources | ||
| .filter((r) => r.resourceId?.startsWith('addon_') && (r.amount ?? 0) > 0) | ||
| .map((addon) => | ||
| createRow({ | ||
| id: `addon-${addon.resourceId}`, | ||
| label: addon.name || addon.resourceId, | ||
| resource: addon, | ||
| usageFormatter: ({ value }) => formatNum(value), | ||
| priceFormatter: ({ amount }) => formatCurrency(amount), | ||
| includeProgress: false | ||
| }) | ||
| ), | ||
|
Comment on lines
+461
to
+472
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The top-level Before shipping, confirm whether the cloud billing API places project-level addon charges exclusively in |
||
| createRow({ | ||
| id: 'usage-details', | ||
| label: `<a href="${base}/project-${String(projectData.region || 'default')}-${projectData.$id}/settings/usage" style="text-decoration: underline; color: var(--fgcolor-accent-neutral);">Usage details</a>`, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| <script lang="ts"> | ||
| import { Box, CardGrid } from '$lib/components'; | ||
| import { Button } from '$lib/elements/forms'; | ||
| import { getChangePlanUrl, plansInfo } from '$lib/stores/billing'; | ||
| import { currentPlan, organization } from '$lib/stores/organization'; | ||
| import { page } from '$app/state'; | ||
| import { get } from 'svelte/store'; | ||
| import { invalidate } from '$app/navigation'; | ||
| import { Dependencies } from '$lib/constants'; | ||
| import { addNotification } from '$lib/stores/notifications'; | ||
| import { sdk } from '$lib/stores/sdk'; | ||
| import { formatCurrency } from '$lib/helpers/numbers'; | ||
| import { Badge } from '@appwrite.io/pink-svelte'; | ||
| import type { Models } from '@appwrite.io/console'; | ||
| import PremiumGeoDBEnableModal from './premiumGeoDBEnableModal.svelte'; | ||
| import PremiumGeoDBDisableModal from './premiumGeoDBDisableModal.svelte'; | ||
|
|
||
| export let addons: Models.AddonList | null = null; | ||
| export let addonPrice: Models.AddonPrice | null = null; | ||
|
|
||
| let showEnable = false; | ||
| let showDisable = false; | ||
| let reEnabling = false; | ||
| let cancelling = false; | ||
|
|
||
| $: planSupportsPremiumGeoDB = $currentPlan?.supportedAddons?.premiumGeoDB === true; | ||
| $: canUpgradeToPremiumGeoDB = | ||
| !planSupportsPremiumGeoDB && hasUpgradeablePlanWithPremiumGeoDB($currentPlan); | ||
| $: premiumGeoDBAddon = addons?.addons?.find( | ||
| (a) => a.key === 'premiumGeoDB' && (a.status === 'active' || a.status === 'pending') | ||
| ); | ||
| $: isPending = premiumGeoDBAddon?.status === 'pending'; | ||
| $: isActive = premiumGeoDBAddon?.status === 'active'; | ||
| $: isScheduledForRemoval = isActive && premiumGeoDBAddon?.nextValue === 0; | ||
|
greptile-apps[bot] marked this conversation as resolved.
|
||
| $: monthlyPriceLabel = addonPrice ? formatCurrency(addonPrice.monthlyPrice) : null; | ||
|
|
||
| function hasUpgradeablePlanWithPremiumGeoDB(plan: Models.BillingPlan): boolean { | ||
| if (!plan) return false; | ||
| const plans = get(plansInfo); | ||
| for (const [, p] of plans) { | ||
| if (p.order > plan.order && p.supportedAddons?.premiumGeoDB) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| async function handleCancelAndRetry() { | ||
| cancelling = true; | ||
| try { | ||
| await sdk.forConsoleIn(page.params.region).projects.deleteAddon({ | ||
| projectId: page.params.project, | ||
| addonId: premiumGeoDBAddon.$id | ||
| }); | ||
| await invalidate(Dependencies.ADDONS); | ||
| showEnable = true; | ||
| } catch (e) { | ||
| addNotification({ | ||
| message: e.message, | ||
| type: 'error' | ||
| }); | ||
| } finally { | ||
| cancelling = false; | ||
| } | ||
| } | ||
|
|
||
| async function handleReEnable() { | ||
| reEnabling = true; | ||
| try { | ||
| await sdk.forConsoleIn(page.params.region).projects.createPremiumGeoDBAddon({ | ||
| projectId: page.params.project | ||
| }); | ||
| await Promise.all([invalidate(Dependencies.ADDONS), invalidate(Dependencies.PROJECT)]); | ||
| addNotification({ | ||
| message: 'Premium Geo DB addon has been re-enabled', | ||
| type: 'success' | ||
| }); | ||
| } catch (e) { | ||
| addNotification({ | ||
| message: e.message, | ||
| type: 'error' | ||
| }); | ||
| } finally { | ||
| reEnabling = false; | ||
| } | ||
| } | ||
|
Comment on lines
+67
to
+86
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The enable modal handles this correctly via the |
||
| </script> | ||
|
greptile-apps[bot] marked this conversation as resolved.
|
||
|
|
||
| <CardGrid> | ||
| <svelte:fragment slot="title">Premium Geo DB</svelte:fragment> | ||
| Know exactly where your users are and how they're connecting. Tailor pricing, currency and language | ||
| by region, meet local compliance requirements, and catch risky sign-ins before they cause damage — | ||
| all without writing a line of geo lookup code. | ||
| <svelte:fragment slot="aside"> | ||
| <Box> | ||
| <h6> | ||
| <b>Premium Geo DB</b> | ||
| </h6> | ||
| {#if !planSupportsPremiumGeoDB && canUpgradeToPremiumGeoDB} | ||
| <p class="text u-margin-block-start-8"> | ||
| Premium Geo DB is not available on your current plan. Upgrade your plan to | ||
| enable it. | ||
| </p> | ||
| <Button | ||
| secondary | ||
| class="u-margin-block-start-16" | ||
| href={getChangePlanUrl($organization?.$id)}> | ||
| <span class="text">Upgrade plan</span> | ||
| </Button> | ||
| {:else if !planSupportsPremiumGeoDB} | ||
| <p class="text u-margin-block-start-8"> | ||
| Premium Geo DB is not available on your current plan. | ||
| </p> | ||
| {:else if isPending} | ||
| <div class="u-flex u-cross-center u-gap-8 u-margin-block-start-8"> | ||
| <Badge variant="secondary" type="warning" content="Payment pending" /> | ||
| </div> | ||
| <p class="text u-margin-block-start-8"> | ||
| A payment is awaiting confirmation. If the payment was interrupted, you can | ||
| cancel and retry. | ||
| </p> | ||
| <Button | ||
| secondary | ||
| class="u-margin-block-start-16" | ||
| disabled={cancelling} | ||
| on:click={handleCancelAndRetry}> | ||
| <span class="text">Cancel & retry</span> | ||
| </Button> | ||
| {:else if isActive} | ||
| <div class="u-flex u-cross-center u-gap-8 u-margin-block-start-8"> | ||
| {#if isScheduledForRemoval} | ||
| <Badge variant="secondary" type="warning" content="Scheduled for removal" /> | ||
| {:else} | ||
| <Badge variant="secondary" type="success" content="Active" /> | ||
| {/if} | ||
| </div> | ||
| <p class="text u-margin-block-start-8"> | ||
| {#if monthlyPriceLabel} | ||
| Premium Geo DB is enabled for this project at {monthlyPriceLabel}/month. | ||
| {:else} | ||
| Premium Geo DB is enabled for this project. | ||
| {/if} | ||
| </p> | ||
| {#if isScheduledForRemoval} | ||
| <p class="text u-margin-block-start-8"> | ||
| Premium Geo DB will be removed at the end of your current billing cycle. | ||
| </p> | ||
| <Button | ||
| secondary | ||
| class="u-margin-block-start-16" | ||
| disabled={reEnabling} | ||
| on:click={handleReEnable}> | ||
| <span class="text">Keep Premium Geo DB</span> | ||
| </Button> | ||
| {:else} | ||
| <Button | ||
| secondary | ||
| class="u-margin-block-start-16" | ||
| on:click={() => (showDisable = true)}> | ||
| <span class="text">Disable Premium Geo DB</span> | ||
| </Button> | ||
| {/if} | ||
| {:else} | ||
| <p class="text u-margin-block-start-8"> | ||
| Enable Premium Geo DB for this project to collect detailed geolocation data on | ||
| every request.{#if monthlyPriceLabel} | ||
| This addon costs {monthlyPriceLabel}/month, prorated for your current | ||
| billing cycle. | ||
| {:else} | ||
| Billed prorated for your current cycle. | ||
| {/if} | ||
| </p> | ||
| <Button | ||
| secondary | ||
| class="u-margin-block-start-16" | ||
| on:click={() => (showEnable = true)}> | ||
| <span class="text">Enable Premium Geo DB</span> | ||
| </Button> | ||
| {/if} | ||
| </Box> | ||
| </svelte:fragment> | ||
| </CardGrid> | ||
|
|
||
| <PremiumGeoDBEnableModal bind:show={showEnable} {addonPrice} /> | ||
|
|
||
| {#if premiumGeoDBAddon} | ||
| <PremiumGeoDBDisableModal bind:show={showDisable} addonId={premiumGeoDBAddon.$id} /> | ||
| {/if} | ||
Uh oh!
There was an error while loading. Please reload this page.