mirror of
https://github.com/appwrite/console.git
synced 2026-04-07 19:17:46 +00:00
Merge branch 'main' into feat-prop-1297-login-for-github-education-program
This commit is contained in:
@@ -983,7 +983,7 @@ export class Billing {
|
||||
};
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return await this.client.call(
|
||||
'post',
|
||||
'patch',
|
||||
uri,
|
||||
{
|
||||
'content-type': 'application/json'
|
||||
|
||||
@@ -42,7 +42,7 @@ export const ResourcesFriendly = {
|
||||
file: { singular: 'File', plural: 'Files' },
|
||||
bucket: { singular: 'Bucket', plural: 'Buckets' },
|
||||
function: { singular: 'Function', plural: 'Functions' },
|
||||
'environment variable': { singular: 'Environment Variable', plural: 'Environment Variables' },
|
||||
'environment-variable': { singular: 'Environment Variable', plural: 'Environment Variables' },
|
||||
deployment: { singular: 'Deployment', plural: 'Deployments' },
|
||||
database: { singular: 'Database', plural: 'Databases' },
|
||||
collection: { singular: 'Collection', plural: 'Collections' },
|
||||
@@ -102,7 +102,7 @@ export const migrationFormToResources = (
|
||||
addResource('function');
|
||||
}
|
||||
if (formData.functions.env) {
|
||||
addResource('environment variable');
|
||||
addResource('environment-variable');
|
||||
}
|
||||
if (formData.functions.inactive) {
|
||||
addResource('deployment');
|
||||
@@ -156,7 +156,7 @@ export const resourcesToMigrationForm = (
|
||||
if (resources.includes('function') && isVersionAtLeast(version, '1.4.0')) {
|
||||
formData.functions.root = true;
|
||||
}
|
||||
if (resources.includes('environment variable') && isVersionAtLeast(version, '1.4.0')) {
|
||||
if (resources.includes('environment-variable') && isVersionAtLeast(version, '1.4.0')) {
|
||||
formData.functions.env = true;
|
||||
}
|
||||
if (resources.includes('deployment') && isVersionAtLeast(version, '1.4.0')) {
|
||||
|
||||
@@ -344,7 +344,7 @@
|
||||
<div />
|
||||
<span>Import all functions and their active deployment</span>
|
||||
<ul>
|
||||
{#if resources?.includes('environment variable')}
|
||||
{#if resources?.includes('environment-variable')}
|
||||
<li class="checkbox-field">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
let currentInvoice: Invoice;
|
||||
let extraMembers = 0;
|
||||
let currentPlan;
|
||||
let availableCredit = 0;
|
||||
|
||||
const today = new Date();
|
||||
onMount(async () => {
|
||||
@@ -30,6 +31,11 @@
|
||||
extraMembers = members.total > 1 ? members.total - 1 : 0;
|
||||
|
||||
currentPlan = await sdk.forConsole.billing.getPlan($organization?.$id);
|
||||
|
||||
const creditList = await sdk.forConsole.billing.listCredits($organization.$id, [
|
||||
Query.offset(0)
|
||||
]);
|
||||
availableCredit = creditList.available;
|
||||
});
|
||||
|
||||
$: extraUsage = (currentInvoice?.amount ?? 0) - (currentPlan?.price ?? 0);
|
||||
@@ -141,6 +147,33 @@
|
||||
</CollapsibleItem>
|
||||
{/if}
|
||||
|
||||
{#if $organization?.billingPlan !== BillingPlan.FREE && availableCredit > 0}
|
||||
<CollapsibleItem noContent gap={4}>
|
||||
<span class="body-text-2 u-flex u-cross-center u-gap-2"
|
||||
><svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M14.166 7.93434C14.4784 8.24676 14.4784 8.75329 14.166 9.06571L8.56603 14.6657C8.25361 14.9781 7.74708 14.9781 7.43466 14.6657L1.83466 9.06571C1.67842 8.90947 1.60032 8.70469 1.60034 8.49992V4.50002C1.60034 3.17454 2.67486 2.10002 4.00034 2.10002H8.00056C8.20523 2.10008 8.40987 2.17818 8.56603 2.33434L14.166 7.93434ZM4.00034 5.30002C4.44217 5.30002 4.80034 4.94185 4.80034 4.50002C4.80034 4.05819 4.44217 3.70002 4.00034 3.70002C3.55851 3.70002 3.20034 4.05819 3.20034 4.50002C3.20034 4.94185 3.55851 5.30002 4.00034 5.30002Z"
|
||||
fill="#00BC5D" />
|
||||
</svg>
|
||||
Credits to be applied</span>
|
||||
|
||||
<div
|
||||
class="body-text-2 u-margin-inline-start-auto"
|
||||
style="color: var(--web-green-500, #10B981)">
|
||||
-{formatCurrency(
|
||||
Math.min(availableCredit, currentInvoice?.amount ?? 0)
|
||||
)}
|
||||
</div>
|
||||
</CollapsibleItem>
|
||||
{/if}
|
||||
|
||||
<CollapsibleItem noContent gap={4}>
|
||||
<span class="body-text-2">Current total (USD)</span>
|
||||
<span class="tooltip u-cross-center" aria-label="total info">
|
||||
@@ -156,7 +189,13 @@
|
||||
{$organization?.billingPlan === BillingPlan.FREE ||
|
||||
$organization?.billingPlan === BillingPlan.GITHUB_EDUCATION
|
||||
? formatCurrency(0)
|
||||
: formatCurrency(currentInvoice?.amount ?? 0)}
|
||||
: formatCurrency(
|
||||
Math.max(
|
||||
(currentInvoice?.amount ?? 0) -
|
||||
Math.min(availableCredit, currentInvoice?.amount ?? 0),
|
||||
0
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import { Query, type Models } from '@appwrite.io/console';
|
||||
import type { PageLoad } from './$types';
|
||||
import type { Organization } from '$lib/stores/organization';
|
||||
import type { Invoice } from '$lib/sdk/billing';
|
||||
@@ -48,22 +48,33 @@ export const load: PageLoad = async ({ params, parent }) => {
|
||||
sdk.forConsole.teams.listMemberships(params.organization)
|
||||
]);
|
||||
|
||||
const queries: string[] = [];
|
||||
|
||||
const projectNames: { [key: string]: Models.Project } = {};
|
||||
if (usage?.projects?.length > 0) {
|
||||
queries.push(
|
||||
Query.equal(
|
||||
'$id',
|
||||
usage.projects.map((p) => p.projectId)
|
||||
)
|
||||
);
|
||||
}
|
||||
// in batches of 100 (the max number of values in a query)
|
||||
const requests = [];
|
||||
const chunk = 100;
|
||||
for (let i = 0; i < usage.projects.length; i += chunk) {
|
||||
const queries = [
|
||||
Query.limit(chunk),
|
||||
Query.equal(
|
||||
'$id',
|
||||
usage.projects.slice(i, i + chunk).map((p) => p.projectId)
|
||||
)
|
||||
];
|
||||
requests.push(sdk.forConsole.projects.list(queries));
|
||||
}
|
||||
|
||||
const projectNames = await sdk.forConsole.projects.list(queries);
|
||||
const responses = await Promise.all(requests);
|
||||
for (const response of responses) {
|
||||
for (const project of response.projects) {
|
||||
projectNames[project.$id] = project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
organizationUsage: usage,
|
||||
projectNames: projectNames.projects,
|
||||
projectNames,
|
||||
invoices,
|
||||
currentInvoice,
|
||||
organizationMembers
|
||||
|
||||
+1
-5
@@ -21,10 +21,6 @@
|
||||
export let projects: OrganizationUsage['projects'];
|
||||
export let metric: Metric;
|
||||
|
||||
function getProjectName(projectId: string): string {
|
||||
return data.projectNames.find((project) => project.$id === projectId)?.name;
|
||||
}
|
||||
|
||||
function getProjectUsageLink(projectId: string): string {
|
||||
return `${base}/project-${projectId}/settings/usage`;
|
||||
}
|
||||
@@ -69,7 +65,7 @@
|
||||
{#each groupByProject(metric).sort((a, b) => b.usage - a.usage) as project}
|
||||
<TableRow>
|
||||
<TableCellText title="Project" style="padding-left: 0;">
|
||||
{getProjectName(project.projectId)}
|
||||
{data.projectNames[project.projectId]?.name ?? 'Unknown'}
|
||||
</TableCellText>
|
||||
<TableCellText title="Usage">{format(project.usage)}</TableCellText>
|
||||
{#if $canSeeProjects}
|
||||
|
||||
Reference in New Issue
Block a user