diff --git a/package-lock.json b/package-lock.json index ea2919e1d..944d0f2e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@stripe/stripe-js": "^3.4.0", "ai": "^2.2.11", "analytics": "^0.8.9", + "cron-parser": "^4.9.0", "dayjs": "^1.11.9", "deep-equal": "^2.2.2", "dotenv": "^16.3.1", @@ -4020,6 +4021,17 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7019,6 +7031,14 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", diff --git a/package.json b/package.json index c49a3eae4..0c931eee5 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@stripe/stripe-js": "^3.4.0", "ai": "^2.2.11", "analytics": "^0.8.9", + "cron-parser": "^4.9.0", "dayjs": "^1.11.9", "deep-equal": "^2.2.2", "dotenv": "^16.3.1", diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 744a4e351..40a7d83bd 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -233,6 +233,7 @@ export enum Submit { DeploymentDelete = 'submit_deployment_delete', DeploymentUpdate = 'submit_deployment_update', ExecutionCreate = 'submit_execution_create', + ExecutionDelete = 'submit_execution_delete', VariableCreate = 'submit_variable_create', VariableDelete = 'submit_variable_delete', VariableUpdate = 'submit_variable_update', diff --git a/src/lib/components/billing/creditsApplied.svelte b/src/lib/components/billing/creditsApplied.svelte index 6cf231e57..1010f0f5e 100644 --- a/src/lib/components/billing/creditsApplied.svelte +++ b/src/lib/components/billing/creditsApplied.svelte @@ -42,9 +42,7 @@ {/if} {#if couponData.credits >= 100} -

- Credits applied -

+

Credits applied

{:else} -{formatCurrency(couponData.credits)} {/if} diff --git a/src/lib/components/billing/estimatedTotalBox.svelte b/src/lib/components/billing/estimatedTotalBox.svelte index 52ac4faba..3ff31041c 100644 --- a/src/lib/components/billing/estimatedTotalBox.svelte +++ b/src/lib/components/billing/estimatedTotalBox.svelte @@ -32,9 +32,10 @@
+

{currentPlan.name} plan

{formatCurrency(currentPlan.price)}

diff --git a/src/lib/elements/forms/inputText.svelte b/src/lib/elements/forms/inputText.svelte index ac354e3f2..0da7f7b38 100644 --- a/src/lib/elements/forms/inputText.svelte +++ b/src/lib/elements/forms/inputText.svelte @@ -108,7 +108,7 @@
- import { Heading } from '$lib/components'; + import { Avatar, AvatarInitials, Card, Heading } from '$lib/components'; import AppwriteLogoDark from '$lib/images/appwrite-logo-dark.svg'; import AppwriteLogoLight from '$lib/images/appwrite-logo-light.svg'; import LoginDark from '$lib/images/login/login-dark-mode.png'; import LoginLight from '$lib/images/login/login-light-mode.png'; import type { Coupon } from '$lib/sdk/billing'; import { app } from '$lib/stores/app'; - import { campaigns } from '$lib/stores/campaigns'; + import { campaigns, type CampaignData } from '$lib/stores/campaigns'; import { user } from '$lib/stores/user'; export const imgLight = LoginLight; @@ -15,8 +15,13 @@ export let campaign: string = null; export let coupon: Coupon = null; - $: variation = coupon?.campaign ?? campaign ? 'card' : 'default'; $: selectedCampaign = campaigns.get(coupon?.campaign ?? campaign); + $: variation = (coupon?.campaign ?? campaign ? selectedCampaign?.template : 'default') as + | 'default' + | CampaignData['template']; + + let currentReviewNumber = 0; + $: currentReview = selectedCampaign?.data?.reviews?.[currentReviewNumber]; function generateTitle() { if (coupon?.credits) { @@ -37,17 +42,17 @@
- {#if variation === 'card'} + {#if variation !== 'default'}
+ {:else if variation === 'review'} +
+
+
+ + {generateTitle()} + +
+
+ + {generateTitle()} + +
+
+ +

{generateDesc()}

+
+ +

+ {currentReview.review} +

+
+ {#if currentReview?.img} + + {:else} + + {/if} +
+

{currentReview.name}

+

{currentReview.desc}

+
+
+
+
+ {#if selectedCampaign?.footer} + + {/if} +
{/if}
@@ -116,7 +180,7 @@

+ class:u-padding-block-start-24={variation !== 'default'}>

@@ -303,6 +367,9 @@ .auth-container { max-inline-size: 27.5rem; } + .review-container { + max-inline-size: 30rem; + } .logo-variation { padding-block-start: 2rem; @@ -313,4 +380,11 @@ } } } + + .review-footer-container { + padding-block-start: 10rem; + @media #{$break1} { + padding-block-start: 5rem; + } + } diff --git a/src/lib/stores/campaigns.ts b/src/lib/stores/campaigns.ts index cf381f16d..6ffc9e0f1 100644 --- a/src/lib/stores/campaigns.ts +++ b/src/lib/stores/campaigns.ts @@ -1,40 +1,119 @@ //campaign welcome and startup -type CampaignData = { +export type CampaignData = { title: string; description: string; + template: 'card' | 'review'; + data?: Record; + onlyNewOrgs?: boolean; + footer?: boolean; }; export const campaigns: Map = new Map(); campaigns .set('welcome', { + template: 'card', title: "You've received $VALUE in credits", description: 'Get $VALUE in credits when you upgrade or create an organization with a Pro plan.' }) .set('startup', { + template: 'card', title: 'Welcome to the Startups program!', description: "We're excited to have you on board. Add the coupon code to your Appwrite Pro account to join." }) .set('RenderATL2024', { + template: 'card', title: 'Claim your $100 RenderATL credits', description: 'Get $100 in Cloud credits when you upgrade or create an organization with a Pro plan.' }) .set('dealsfordevs', { + template: 'card', title: 'Claim your $50 Deals For Devs credits', description: 'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan.' }) .set('WelcomeManual', { + template: 'card', title: 'Here is your $VALUE welcome credit!', description: 'Upgrade to Appwrite Pro and add the credits to enjoy the full capabilities of Cloud.' }) .set('InactiveAccounts', { + template: 'card', title: 'Get everything out of Cloud. Claim your $VALUE credits.', description: 'Get $VALUE in Cloud credits when you upgrade or create an organization with a Pro plan.' + }) + .set('StartupsProgram', { + template: 'review', + title: 'Welcome to the Startups program', + description: + "We're excited to have you on board. Add your credit code to your Appwrite Pro account to join.", + onlyNewOrgs: true, + data: { + cta: 'Get everything out of Cloud with Pro', + claimed: 'Your credits will be valid for 12 months.', + unclaimed: 'Apply your code to join the Startups program.', + reviews: [ + { + name: 'David Foster', + img: '1.jpeg', + desc: 'Managing director', + review: 'We really loved working with Appwrite for launching our bootstrapped "Open Mind" App. It was saving us a lot of money in comparison to Firebase since the amount of users grew quite fast and we needed a quick switch.' + } + ] + } + }) + .set('JSMastery', { + template: 'review', + title: 'Claim your JavaScript Mastery $50 credits.', + description: + 'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan', + data: { + reviews: [ + { + name: 'Marius Bloki', + desc: 'CTO // mySHOEFITTER', + img: 'js_mastery_reviewer_1.png', + review: 'Appwrite has been a tremendous asset in implementing our IT infrastructure. Not only is the software an absolute game-changer, but the team is always there when you need them.' + } + ] + } + }) + .set('FusionVC', { + template: 'review', + title: 'Welcome to Appwrite!', + description: + 'Upgrade to Appwrite Pro and add the credits to enjoy the full capabilities of Cloud. Provided to you by Fusion VC.', + footer: true, + data: { + reviews: [ + { + name: 'David Foster', + img: '1.jpeg', + desc: 'Managing director', + review: 'We really loved working with Appwrite for launching our bootstrapped "Open Mind" App. It was saving us a lot of money in comparison to Firebase since the amount of users grew quite fast and we needed a quick switch.' + } + ] + } + }) + .set('carbon', { + template: 'review', + title: 'Build your entire backend within minutes.', + description: + 'Get $50 in Cloud credits when you upgrade or create an organization with a Pro plan.', + data: { + reviews: [ + { + name: 'David Foster', + img: '1.jpeg', + desc: 'Managing director', + review: 'We really loved working with Appwrite for launching our bootstrapped "Open Mind" App. It was saving us a lot of money in comparison to Firebase since the amount of users grew quite fast and we needed a quick switch.' + } + ] + } }); diff --git a/src/routes/console/apply-credit/+page.svelte b/src/routes/console/apply-credit/+page.svelte index 4395538f1..d1dd1ce47 100644 --- a/src/routes/console/apply-credit/+page.svelte +++ b/src/routes/console/apply-credit/+page.svelte @@ -67,10 +67,11 @@ let name: string; let coupon: string; let couponData = data?.couponData; + let campaign = campaigns.get(data?.couponData?.campaign ?? data?.campaign); onMount(async () => { await loadPaymentMethods(); - if (!$organizationList?.total) { + if (!$organizationList?.total || campaign?.onlyNewOrgs) { selectedOrgId = newOrgId; } }); @@ -183,7 +184,6 @@ $: isButtonDisabled = checkButtonDisabled(selectedOrgId, name, paymentMethodId) || couponData?.status !== 'active'; - $: campaign = campaigns.get(data?.couponData?.campaign ?? data?.campaign); function checkButtonDisabled(id: string | null, name: string | null, method: string | null) { if (id === newOrgId) { @@ -207,7 +207,7 @@
- {#if $organizationList?.total} + {#if $organizationList?.total && !campaign?.onlyNewOrgs} -
-
-
- promo -

- {#if couponData?.credits} - {campaign.title.replace('VALUE', couponData.credits.toString())} - {:else} - {campaign.title} - {/if} -

+ {#if campaign?.template === 'card'} +
+
+
+ promo +

+ {#if couponData?.credits} + {campaign.title.replace('VALUE', couponData.credits.toString())} + {:else} + {campaign.title} + {/if} +

+
-
+ {/if} {#if selectedOrg?.billingPlan === BillingPlan.PRO}
{:else if selectedOrgId} - +
+ + {#if campaign?.template === 'review' && (campaign?.data?.cta || campaign?.data?.claimed || campaign?.data?.unclaimed)} +
+

{campaign?.data?.cta}

+

+ {#if couponData?.code && couponData?.status === 'active' && campaign?.data?.claimed} + {campaign?.data?.claimed} + {:else if campaign?.data?.unclaimed} + {campaign?.data?.unclaimed} + {/if} +

+
+ {/if} +
+
{/if} diff --git a/src/routes/console/project-[project]/functions/+page.svelte b/src/routes/console/project-[project]/functions/+page.svelte index 989f8ebab..2a86c9150 100644 --- a/src/routes/console/project-[project]/functions/+page.svelte +++ b/src/routes/console/project-[project]/functions/+page.svelte @@ -1,24 +1,25 @@ + + +

Are you sure you want to delete this execution?

+ + + + +
diff --git a/static/images/campaigns/FusionVC/footer/dark.png b/static/images/campaigns/FusionVC/footer/dark.png new file mode 100644 index 000000000..6fe0f03b0 Binary files /dev/null and b/static/images/campaigns/FusionVC/footer/dark.png differ diff --git a/static/images/campaigns/FusionVC/footer/light.png b/static/images/campaigns/FusionVC/footer/light.png new file mode 100644 index 000000000..a21263326 Binary files /dev/null and b/static/images/campaigns/FusionVC/footer/light.png differ diff --git a/static/images/campaigns/FusionVC/reviewers/1.jpeg b/static/images/campaigns/FusionVC/reviewers/1.jpeg new file mode 100644 index 000000000..db56c928c Binary files /dev/null and b/static/images/campaigns/FusionVC/reviewers/1.jpeg differ diff --git a/static/images/campaigns/JSMastery/reviewers/js_mastery_reviewer_1.png b/static/images/campaigns/JSMastery/reviewers/js_mastery_reviewer_1.png new file mode 100644 index 000000000..ce5189b59 Binary files /dev/null and b/static/images/campaigns/JSMastery/reviewers/js_mastery_reviewer_1.png differ diff --git a/static/images/campaigns/StartupsProgram/reviewers/1.jpeg b/static/images/campaigns/StartupsProgram/reviewers/1.jpeg new file mode 100644 index 000000000..db56c928c Binary files /dev/null and b/static/images/campaigns/StartupsProgram/reviewers/1.jpeg differ diff --git a/static/images/campaigns/carbon/reviewers/1.jpeg b/static/images/campaigns/carbon/reviewers/1.jpeg new file mode 100644 index 000000000..db56c928c Binary files /dev/null and b/static/images/campaigns/carbon/reviewers/1.jpeg differ