mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'appwrite:main' into remove-cloud-endpoints
This commit is contained in:
Generated
+639
-625
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
export let value: number = null;
|
||||
</script>
|
||||
|
||||
<fieldset class="u-margin-block-start-8">
|
||||
<fieldset class="u-margin-block-start-8 u-min-width-0">
|
||||
<legend class="label is-required u-margin-0 u-line-height-1-5 u-bold">
|
||||
<slot />
|
||||
</legend>
|
||||
<ul class="u-flex u-main-space-between u-margin-block-start-16">
|
||||
<ol
|
||||
class="u-flex u-main-space-between u-margin-block-start-16 u-overflow-x-auto"
|
||||
style="padding-block: 0.13rem">
|
||||
{#each Array(11) as _, i}
|
||||
<li>
|
||||
<input
|
||||
@@ -18,9 +20,9 @@
|
||||
checked={value === i} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</ol>
|
||||
<div class="u-flex u-main-space-between u-margin-block-start-8">
|
||||
<span>Not at all likely</span>
|
||||
<span>Extremely likely</span>
|
||||
<span class:u-color-text-gray={value === null}>Not at all likely</span>
|
||||
<span class:u-color-text-gray={value === null}>Extremely likely</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Feedback submitted successfully'
|
||||
message: 'Thank you for your feedback'
|
||||
});
|
||||
feedback.toggleFeedback();
|
||||
feedbackData.reset();
|
||||
@@ -33,15 +33,15 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<section class="drop-section">
|
||||
<section class="drop-section u-padding-24">
|
||||
<header class="u-flex u-main-space-between u-gap-16">
|
||||
<h4 class="body-text-1 u-bold">{$selectedFeedback.title}</h4>
|
||||
<h4 class="label u-bold" style:font-size="var(--font-size-1)">{$selectedFeedback.title}</h4>
|
||||
<button
|
||||
type="button"
|
||||
class="button is-text is-only-icon u-margin-inline-start-auto"
|
||||
style="--button-size:1.5rem;"
|
||||
aria-label="Close Modal"
|
||||
title="Close Modal"
|
||||
aria-label="Close feedback"
|
||||
title="Close feedback"
|
||||
on:click={() => feedback.toggleFeedback()}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
import Evaluation from './evaluation.svelte';
|
||||
</script>
|
||||
|
||||
<Evaluation bind:value={$feedbackData.value}>
|
||||
How likely are you to recommend Appwrite to a friend or colleague?
|
||||
</Evaluation>
|
||||
{#if $feedbackData.value}
|
||||
<FormList>
|
||||
<FormList>
|
||||
<Evaluation bind:value={$feedbackData.value}>
|
||||
How likely are you to recommend Appwrite to a friend or colleague?
|
||||
</Evaluation>
|
||||
{#if $feedbackData.value !== null}
|
||||
<InputTextarea
|
||||
id="feedback"
|
||||
placeholder="Your message here"
|
||||
@@ -26,5 +26,5 @@
|
||||
id="email"
|
||||
bind:value={$feedbackData.email}
|
||||
placeholder="Enter email" />
|
||||
</FormList>
|
||||
{/if}
|
||||
{/if}
|
||||
</FormList>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -124,6 +124,11 @@
|
||||
{/if}
|
||||
<DropList show={$feedback.show} scrollable on:blur={toggleFeedback}>
|
||||
<button class="button is-small is-text" on:click={toggleFeedback}>
|
||||
{#if $feedback.notification}
|
||||
<span
|
||||
class="notification u-position-absolute u-inset-block-start-8 u-inset-inline-end-8"
|
||||
></span>
|
||||
{/if}
|
||||
<span class="text">Feedback</span>
|
||||
</button>
|
||||
<svelte:fragment slot="other">
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
<svelte:component this={component} />
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="form-footer">
|
||||
<div class="u-z-index-20 form-footer">
|
||||
<div class="u-flex u-main-end u-gap-12">
|
||||
{#if !isLastStep && currentStep?.optional}
|
||||
<Button text on:click={() => dispatch('finish')}>
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -30,14 +30,14 @@ export type FeedbackOption = {
|
||||
export const feedbackOptions: FeedbackOption[] = [
|
||||
{
|
||||
type: 'general',
|
||||
title: 'How can we improve?',
|
||||
desc: 'Your feedback is important to us. Please be honest and tell us what you think.',
|
||||
title: 'Help us improve Appwrite',
|
||||
desc: 'Appwrite evolves with your input. Share your thoughts and help us improve Appwrite.',
|
||||
component: FeedbackGeneral
|
||||
},
|
||||
{
|
||||
type: 'nps',
|
||||
title: 'How are we doing?',
|
||||
desc: "At Appwrite, we value the feedback of our users. That means you! We'd love to hear what you think.",
|
||||
title: 'Help us improve Appwrite',
|
||||
desc: 'Appwrite evolves with your input. Share your thoughts and help us improve Appwrite. If you would like to be contacted regarding your feedback, please share your contact details below.',
|
||||
component: FeedbackNps
|
||||
}
|
||||
];
|
||||
@@ -49,7 +49,7 @@ function createFeedbackDataStore() {
|
||||
message: '',
|
||||
name: '',
|
||||
email: '',
|
||||
value: 0
|
||||
value: null
|
||||
});
|
||||
return {
|
||||
set,
|
||||
@@ -60,7 +60,7 @@ function createFeedbackDataStore() {
|
||||
feedbackData.message = '';
|
||||
feedbackData.name = '';
|
||||
feedbackData.email = '';
|
||||
feedbackData.value = 0;
|
||||
feedbackData.value = null;
|
||||
return feedbackData;
|
||||
});
|
||||
}
|
||||
@@ -71,8 +71,8 @@ export const feedbackData = createFeedbackDataStore();
|
||||
|
||||
function createFeedbackStore() {
|
||||
const { subscribe, update } = writable<Feedback>({
|
||||
elapsed: browser ? parseInt(localStorage.getItem('feedbackElapsed')) : 0,
|
||||
visualized: browser ? parseInt(localStorage.getItem('feedbackVisualized')) : 0,
|
||||
elapsed: browser ? parseInt(localStorage.getItem('feedbackElapsed')) ?? 0 : 0,
|
||||
visualized: browser ? parseInt(localStorage.getItem('feedbackVisualized')) ?? 0 : 0,
|
||||
notification: false,
|
||||
type: 'general',
|
||||
show: false
|
||||
|
||||
@@ -70,7 +70,13 @@
|
||||
'/hackathon',
|
||||
'/mfa'
|
||||
];
|
||||
const acceptedAuthenticatedRoutes = ['/console', '/invite', '/card', '/hackathon'];
|
||||
const acceptedAuthenticatedRoutes = [
|
||||
'/console',
|
||||
'/invite',
|
||||
'/card',
|
||||
'/hackathon',
|
||||
'/recover'
|
||||
];
|
||||
|
||||
const pathname = $page.url.pathname;
|
||||
const user = $page.data.account as Models.User<Record<string, string>>;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
checkForMarkedForDeletion,
|
||||
checkForMandate,
|
||||
checkForMissingPaymentMethod,
|
||||
checkForNewDevUpgradePro,
|
||||
plansInfo
|
||||
} from '$lib/stores/billing';
|
||||
import { goto } from '$app/navigation';
|
||||
@@ -235,7 +236,9 @@
|
||||
let isOpen = false;
|
||||
onMount(async () => {
|
||||
loading.set(false);
|
||||
|
||||
if (!localStorage.getItem('feedbackElapsed')) {
|
||||
localStorage.setItem('feedbackElapsed', '0');
|
||||
}
|
||||
setInterval(() => {
|
||||
checkForFeedback(INTERVAL);
|
||||
}, INTERVAL);
|
||||
@@ -266,10 +269,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);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Feedback submitted successfully'
|
||||
message: 'Thank you for your feedback'
|
||||
});
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
|
||||
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 |
Reference in New Issue
Block a user