Merge pull request #1038 from appwrite/feat-gradient-alert-banner

feat: gradient alert banner
This commit is contained in:
Torsten Dittmann
2024-07-12 13:43:59 +02:00
committed by GitHub
15 changed files with 787 additions and 635 deletions
+639 -625
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -20,8 +20,8 @@
},
"dependencies": {
"@appwrite.io/console": "^0.6.2",
"@appwrite.io/pink": "0.23.0",
"@appwrite.io/pink-icons": "0.23.0",
"@appwrite.io/pink": "0.25.0",
"@appwrite.io/pink-icons": "0.25.0",
"@popperjs/core": "^2.11.8",
"@sentry/svelte": "^7.66.0",
"@sentry/tracing": "^7.66.0",
@@ -0,0 +1,44 @@
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/stores';
import { trackEvent } from '$lib/actions/analytics';
import { BillingPlan } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { organization } from '$lib/stores/organization';
import { activeHeaderAlert } from '$routes/console/store';
import GradientBanner from '../gradientBanner.svelte';
let show = true;
function handleClose() {
show = false;
const now = new Date().getTime();
localStorage.setItem($activeHeaderAlert.id, now.toString());
trackEvent('close_upgrade_banner', {
source: 'cloud_credits_banner',
campaign: 'WelcomeManual'
});
}
</script>
{#if show && $organization?.$id && $organization?.billingPlan === BillingPlan.FREE && !$page.url.pathname.includes('/console/account')}
<GradientBanner on:close={handleClose}>
<div class="u-flex u-gap-24 u-main-center u-cross-center u-flex-vertical-mobile">
<span class="body-text-1">Get $50 Cloud credits for Appwrite Pro.</span>
<Button
secondary
fullWidthMobile
class="u-line-height-1"
href={`${base}/console/apply-credit?code=appw50&org=${$organization.$id}`}
on:click={() => {
trackEvent('click_credits_redeem', {
from: 'button',
source: 'cloud_credits_banner',
campaign: 'WelcomeManual'
});
}}>
Claim credits
</Button>
</div>
</GradientBanner>
{/if}
@@ -0,0 +1,26 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
</script>
<div class="top-banner">
<div class="top-banner-bg">
<div class="top-banner-bg-1">
<img src="/images/top-banner/bg-pink-desktop.svg" width="1283" height="1278" alt="" />
</div>
<div class="top-banner-bg-2">
<img src="/images/top-banner/bg-mint-desktop.svg" width="1051" height="1271" alt="" />
</div>
</div>
<div class="top-banner-content u-color-text-primary">
<slot />
</div>
<button
on:click|preventDefault={() => dispatch('close')}
type="button"
class="top-banner-button"
aria-label="close upgrade message">
<span class="icon-x" aria-hidden="true"></span>
</button>
</div>
+2 -2
View File
@@ -58,11 +58,11 @@
// TODO: remove once pink is updated
.collapsible-item {
.collapsible-wrapper.is-disabled {
cursor: not-allowed;
&[open] .icon-cheveron-down {
rotate: unset;
}
cursor: not-allowed;
* {
cursor: not-allowed;
}
+5 -5
View File
@@ -121,11 +121,6 @@
--p-drop-bg-color: var(--color-neutral-105);
--p-drop-border-color: var(--color-neutral-85);
body.theme-light & {
--p-drop-bg-color: var(--color-neutral-0);
--p-drop-border-color: var(--color-neutral-10);
}
inset-inline-start: -0.625rem;
inset-block-end: calc(100% + 0.625rem);
background-color: hsl(var(--p-drop-bg-color));
@@ -138,6 +133,11 @@
margin-inline: auto;
line-height: 1.5;
body.theme-light & {
--p-drop-bg-color: var(--color-neutral-0);
--p-drop-border-color: var(--color-neutral-10);
}
.tippy-content {
padding: 1rem;
}
+27
View File
@@ -27,8 +27,11 @@ import PaymentMandate from '$lib/components/billing/alerts/paymentMandate.svelte
import MissingPaymentMethod from '$lib/components/billing/alerts/missingPaymentMethod.svelte';
import LimitReached from '$lib/components/billing/alerts/limitReached.svelte';
import { trackEvent } from '$lib/actions/analytics';
import newDevUpgradePro from '$lib/components/billing/alerts/newDevUpgradePro.svelte';
import { last } from '$lib/helpers/array';
import { sizeToBytes, type Size } from '$lib/helpers/sizeConvertion';
import { user } from './user';
import { browser } from '$app/environment';
export type Tier = 'tier-0' | 'tier-1' | 'tier-2';
@@ -388,6 +391,30 @@ export async function checkForMissingPaymentMethod() {
}
}
// Display upgrade banner for new users after 1 week for 30 days
export async function checkForNewDevUpgradePro(org: Organization) {
if (org?.billingPlan !== BillingPlan.FREE || !browser) return;
const orgs = await sdk.forConsole.billing.listOrganization([
Query.notEqual('billingPlan', BillingPlan.FREE)
]);
if (orgs?.total) return;
const now = new Date().getTime();
const account = get(user);
const accountCreated = new Date(account.$createdAt).getTime();
if (now - accountCreated < 1000 * 60 * 60 * 24 * 7) return;
const isDismissed = !!localStorage.getItem('newDevUpgradePro');
if (isDismissed) return;
if (now - accountCreated < 1000 * 60 * 60 * 24 * 37) {
headerAlert.add({
id: 'newDevUpgradePro',
component: newDevUpgradePro,
show: true,
importance: 1
});
}
}
export const upgradeURL = derived(
page,
($page) => `${base}/console/organization-${$page.data?.organization?.$id}/change-plan`
+4
View File
@@ -22,6 +22,7 @@
checkForMarkedForDeletion,
checkForMandate,
checkForMissingPaymentMethod,
checkForNewDevUpgradePro,
plansInfo
} from '$lib/stores/billing';
import { goto } from '$app/navigation';
@@ -266,10 +267,13 @@
if (isCloud) {
await checkForUsageLimit(org);
checkForMarkedForDeletion(org);
await checkForNewDevUpgradePro(org);
if (org?.billingPlan !== BillingPlan.FREE) {
await paymentExpired(org);
await checkPaymentAuthorizationRequired(org);
await checkForMandate(org);
if ($plansInfo.get(org.billingPlan)?.trialDays) {
calculateTrialDay(org);
}
+6 -1
View File
@@ -32,6 +32,7 @@
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/i;
let previousPage: string = `${base}/console`;
let showExitModal = false;
let canSelectOrg = true;
afterNavigate(({ from }) => {
if (from?.url?.pathname) {
@@ -75,6 +76,10 @@
if (!$organizationList?.total || campaign?.onlyNewOrgs) {
selectedOrgId = newOrgId;
}
if ($page.url.searchParams.has('org')) {
selectedOrgId = $page.url.searchParams.get('org');
canSelectOrg = false;
}
});
async function loadPaymentMethods() {
@@ -197,7 +202,7 @@
<WizardSecondaryContent>
<Form bind:this={formComponent} onSubmit={handleSubmit} bind:isSubmitting>
<FormList>
{#if $organizationList?.total && !campaign?.onlyNewOrgs}
{#if $organizationList?.total && !campaign?.onlyNewOrgs && canSelectOrg}
<InputSelect
bind:value={selectedOrgId}
label="Select organization"
Binary file not shown.

After

Width:  |  Height:  |  Size: 988 KiB

@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1051" height="1271" viewBox="0 0 1051 1271" fill="none">
<g filter="url(#filter0_f_2147_2948)">
<path d="M712.439 968.914C643.17 1007.71 645.862 391.902 460.132 633.57C274.401 875.238 273.104 347.057 342.373 308.256C411.642 269.454 550.638 385.893 652.829 568.329C755.02 750.765 781.708 930.113 712.439 968.914Z" fill="url(#paint0_radial_2147_2948)"/>
</g>
<defs>
<filter id="filter0_f_2147_2948" x="0.219421" y="0.797729" width="1050.74" height="1269.88" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_2147_2948"/>
</filter>
<radialGradient id="paint0_radial_2147_2948" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(496.859 629.666) rotate(137.143) scale(145.953 366.505)">
<stop stop-color="#E7F8F7"/>
<stop offset="1" stop-color="#85DBD8"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 KiB

@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1283" height="1278" viewBox="0 0 1283 1278" fill="none">
<g filter="url(#filter0_f_2147_2911)">
<path d="M422.869 374.715C278.243 495.214 258.684 710.14 379.183 854.766C499.683 999.392 714.609 1018.95 859.235 898.452C930.123 839.389 651.054 705.413 667.842 602.647C685.305 495.749 1036.75 504.514 975.315 430.777C854.815 286.15 567.495 254.215 422.869 374.715Z" fill="url(#paint0_radial_2147_2911)"/>
</g>
<defs>
<filter id="filter0_f_2147_2911" x="0.192719" y="0.607666" width="1282.32" height="1276.83" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_2147_2911"/>
</filter>
<radialGradient id="paint0_radial_2147_2911" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(641.052 636.583) rotate(140.2) scale(340.85 340.851)">
<stop offset="0.281696" stop-color="#FE9567"/>
<stop offset="0.59375" stop-color="#FD366E"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB