mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'img-poc' into mgn-dv
# Conflicts: # src/routes/(console)/+layout.svelte # src/routes/(console)/organization-[organization]/+page.svelte
This commit is contained in:
+3
-1
@@ -24,12 +24,14 @@ ARG PUBLIC_APPWRITE_ENDPOINT
|
||||
ARG PUBLIC_GROWTH_ENDPOINT
|
||||
ARG PUBLIC_STRIPE_KEY
|
||||
ARG SENTRY_AUTH_TOKEN
|
||||
ARG PUBLIC_PROJECT_PROFILE
|
||||
|
||||
ENV PUBLIC_APPWRITE_ENDPOINT=$PUBLIC_APPWRITE_ENDPOINT
|
||||
ENV PUBLIC_GROWTH_ENDPOINT=$PUBLIC_GROWTH_ENDPOINT
|
||||
ENV PUBLIC_CONSOLE_MODE=$PUBLIC_CONSOLE_MODE
|
||||
ENV PUBLIC_STRIPE_KEY=$PUBLIC_STRIPE_KEY
|
||||
ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN
|
||||
ENV PUBLIC_PROJECT_PROFILE=$PUBLIC_PROJECT_PROFILE
|
||||
ENV NODE_OPTIONS=--max_old_space_size=8192
|
||||
|
||||
RUN pnpm run build
|
||||
@@ -38,5 +40,5 @@ FROM nginx:1.25-alpine
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY docker/nginx.$PUBLIC_PROJECT_PROFILE.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/build /usr/share/nginx/html/console
|
||||
@@ -0,0 +1,39 @@
|
||||
map $sent_http_content_type $expires {
|
||||
# cache everything for 1 year
|
||||
default 1y;
|
||||
# html files shouldn't be cached for single-page applications
|
||||
text/html off;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
# serve compressed file if filename.gz exists
|
||||
gzip_static on;
|
||||
|
||||
location /studio {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri /studio/index.html;
|
||||
|
||||
# Add cache headers
|
||||
expires $expires;
|
||||
add_header Pragma public;
|
||||
add_header Cache-Control "public";
|
||||
|
||||
# Deny IE browsers from going into quirks mode
|
||||
add_header X-UA-Compatible "IE=Edge";
|
||||
# X-Frame-Options is to prevent from clickJacking attack
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
# This header enables the Cross-site scripting (XSS) filter
|
||||
add_header X-XSS-Protection "1; mode=block;";
|
||||
# disable content-type sniffing on some browsers.
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
}
|
||||
|
||||
location / {
|
||||
absolute_redirect off;
|
||||
return 301 /studio;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@
|
||||
"cron-parser": "^4.9.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"deep-equal": "^2.2.3",
|
||||
"dotenv": "^16.4.7",
|
||||
"echarts": "^5.6.0",
|
||||
"envfile": "^7.1.0",
|
||||
"ignore": "^6.0.2",
|
||||
@@ -48,6 +49,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.2.7",
|
||||
"@eslint/js": "^9.23.0",
|
||||
"@melt-ui/pp": "^0.3.2",
|
||||
"@melt-ui/svelte": "^0.86.5",
|
||||
"@playwright/test": "^1.51.1",
|
||||
|
||||
Generated
+6
@@ -47,6 +47,9 @@ importers:
|
||||
deep-equal:
|
||||
specifier: ^2.2.3
|
||||
version: 2.2.3
|
||||
dotenv:
|
||||
specifier: ^16.4.7
|
||||
version: 16.4.7
|
||||
echarts:
|
||||
specifier: ^5.6.0
|
||||
version: 5.6.0
|
||||
@@ -84,6 +87,9 @@ importers:
|
||||
'@eslint/compat':
|
||||
specifier: ^1.2.7
|
||||
version: 1.2.7(eslint@9.23.0)
|
||||
'@eslint/js':
|
||||
specifier: ^9.23.0
|
||||
version: 9.23.0
|
||||
'@melt-ui/pp':
|
||||
specifier: ^0.3.2
|
||||
version: 0.3.2(@melt-ui/svelte@0.86.5(svelte@5.25.3))(svelte@5.25.3)
|
||||
|
||||
+3
-3
@@ -5,9 +5,9 @@
|
||||
<meta
|
||||
name="description"
|
||||
content="Appwrite is an open-source platform for building applications at any scale, using your preferred programming languages and tools." />
|
||||
<link rel="icon" type="image/svg+xml" href="/console/logos/appwrite-icon.svg" />
|
||||
<link rel="mask-icon" type="image/png" href="/console/logos/appwrite-icon.png" />
|
||||
<link rel="stylesheet" href="/console/css/loading.css" />
|
||||
<link rel="icon" type="image/svg+xml" href="/{project_profile}/logos/appwrite-icon.svg" />
|
||||
<link rel="mask-icon" type="image/png" href="/{project_profile}/logos/appwrite-icon.png" />
|
||||
<link rel="stylesheet" href="/{project_profile}/css/loading.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
+22
-1
@@ -2,6 +2,7 @@ import { sequence } from '@sveltejs/kit/hooks';
|
||||
import { handleErrorWithSentry, sentryHandle } from '@sentry/sveltekit';
|
||||
import * as Sentry from '@sentry/sveltekit';
|
||||
import { isCloud, isProd } from '$lib/system';
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
|
||||
Sentry.init({
|
||||
enabled: isCloud && isProd,
|
||||
@@ -9,6 +10,26 @@ Sentry.init({
|
||||
tracesSampleRate: 1.0
|
||||
});
|
||||
|
||||
export const handle = sequence(sentryHandle());
|
||||
const projectProfile = process.env.PUBLIC_PROJECT_PROFILE || 'console';
|
||||
|
||||
const dynamicHtmlTransform: Handle = async ({ event, resolve }) => {
|
||||
let response = await resolve(event);
|
||||
|
||||
if (response.headers.get('content-type')?.includes('text/html')) {
|
||||
let html = await response.text();
|
||||
|
||||
// Replace '/console/' dynamically with the project profile
|
||||
html = html.replace(/\/{project_profile}\//g, `/${projectProfile}/`);
|
||||
|
||||
response = new Response(html, {
|
||||
status: response.status,
|
||||
headers: response.headers
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export const handle = sequence(sentryHandle(), dynamicHtmlTransform);
|
||||
|
||||
export const handleError = handleErrorWithSentry();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { BottomSheet } from '$lib/components';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { isCloud, isStudio } from '$lib/system';
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { newOrgModal } from '$lib/stores/organization';
|
||||
@@ -80,7 +80,7 @@
|
||||
title: 'Switch Organization',
|
||||
items: organizations.map((organization) => ({
|
||||
name: organization.name,
|
||||
href: `/console/organization-${organization?.$id}`
|
||||
href: `${base}/organization-${organization?.$id}`
|
||||
}))
|
||||
},
|
||||
bottom: {
|
||||
@@ -101,7 +101,7 @@
|
||||
items: [
|
||||
{
|
||||
name: 'Organization overview',
|
||||
href: `/console/organization-${selectedOrg?.$id}`
|
||||
href: `${base}/organization-${selectedOrg?.$id}`
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -139,12 +139,12 @@
|
||||
if (index < 4) {
|
||||
return {
|
||||
name: project.name,
|
||||
href: `/console/project-${project.$id}/overview`
|
||||
href: `${base}/project-${project.$id}/overview`
|
||||
};
|
||||
} else if (index === 4) {
|
||||
return {
|
||||
name: 'All projects',
|
||||
href: `/console/organization-${selectedOrg?.$id}`
|
||||
href: `${base}/organization-${selectedOrg?.$id}`
|
||||
};
|
||||
}
|
||||
return null;
|
||||
@@ -156,7 +156,7 @@
|
||||
{
|
||||
name: 'Create project',
|
||||
trailingIcon: IconPlus,
|
||||
href: `/console/organization-${selectedOrg?.$id}?create-project`
|
||||
href: `${base}/organization-${selectedOrg?.$id}?create-project`
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -167,7 +167,7 @@
|
||||
{
|
||||
name: 'Create project',
|
||||
trailingIcon: IconPlus,
|
||||
href: `/console/organization-${selectedOrg?.$id}?create-project`
|
||||
href: `${base}/organization-${selectedOrg?.$id}?create-project`
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -185,7 +185,9 @@
|
||||
<svelte:window on:resize={onResize} />
|
||||
<div use:melt={$menubar}>
|
||||
{#if !$isSmallViewport}
|
||||
<span class="breadcrumb-separator">/</span>
|
||||
{#if !isStudio}
|
||||
<span class="breadcrumb-separator">/</span>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="trigger"
|
||||
@@ -219,7 +221,7 @@
|
||||
{#if selectedOrg}
|
||||
<div use:melt={$itemOrganizations}>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor href={`/console/organization-${selectedOrg?.$id}`}
|
||||
<ActionMenu.Item.Anchor href={`${base}/organization-${selectedOrg?.$id}`}
|
||||
>Organization overview</ActionMenu.Item.Anchor
|
||||
></ActionMenu.Root>
|
||||
</div>
|
||||
@@ -239,7 +241,7 @@
|
||||
<div use:melt={$itemOrganizations}>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`/console/organization-${organization?.$id}`}
|
||||
href={`${base}/organization-${organization?.$id}`}
|
||||
>{organization.name}</ActionMenu.Item.Anchor>
|
||||
</ActionMenu.Root>
|
||||
</div>
|
||||
@@ -270,7 +272,7 @@
|
||||
<div use:melt={$itemOrganizations}>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`/console/organization-${organization?.$id}`}
|
||||
href={`${base}/organization-${organization?.$id}`}
|
||||
>{organization.name}</ActionMenu.Item.Anchor
|
||||
></ActionMenu.Root>
|
||||
</div>
|
||||
@@ -315,7 +317,7 @@
|
||||
{#if index < 4}
|
||||
<div use:melt={$itemProjects}>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor href={`/console/project-${project.$id}`}
|
||||
<ActionMenu.Item.Anchor href={`${base}/project-${project.$id}`}
|
||||
>{project.name}</ActionMenu.Item.Anchor
|
||||
></ActionMenu.Root>
|
||||
</div>
|
||||
@@ -323,7 +325,7 @@
|
||||
<div use:melt={$itemProjects}>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`/console/organization-${selectedOrg.$id}`}
|
||||
href={`${base}/organization-${selectedOrg.$id}`}
|
||||
>All projects</ActionMenu.Item.Anchor
|
||||
></ActionMenu.Root>
|
||||
</div>
|
||||
@@ -335,7 +337,7 @@
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
leadingIcon={IconPlusSm}
|
||||
href={`/console/organization-${selectedOrg?.$id}?create-project`}
|
||||
href={`${base}/organization-${selectedOrg?.$id}?create-project`}
|
||||
>Create project</ActionMenu.Item.Anchor
|
||||
></ActionMenu.Root>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import { ActionMenu, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { base } from '$app/paths';
|
||||
import type { Organization } from '$lib/stores/organization';
|
||||
|
||||
export let organization: Organization;
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<Layout.Stack>
|
||||
<Typography.Text color="--fgcolor-neutral-tertiary">{organization.name}</Typography.Text>
|
||||
|
||||
<Layout.Stack gap="xs">
|
||||
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}`}
|
||||
>Projects</ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}/members`}
|
||||
>Members</ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}/usage`}
|
||||
>Usage</ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}/billing`}
|
||||
>Billing</ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor href={`${base}/organization-${organization.$id}/settings`}
|
||||
>Settings</ActionMenu.Item.Anchor>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 48px;
|
||||
width: 200px;
|
||||
height: calc(100vh - 48px);
|
||||
padding: var(--space-4) var(--space-8) var(--space-8) var(--space-8);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { Layout, ActionMenu, Icon, Divider } from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconAnnotation,
|
||||
IconCog,
|
||||
IconDatabase,
|
||||
IconFolder,
|
||||
IconTemplate,
|
||||
IconUserGroup
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
</script>
|
||||
|
||||
<nav style:--icon-fill="var(--fgcolor-neutral-tertiary)">
|
||||
<Layout.Stack gap="xs">
|
||||
<ActionMenu.Item.Anchor href="" leadingIcon={IconTemplate}></ActionMenu.Item.Anchor>
|
||||
<Divider />
|
||||
<ActionMenu.Item.Anchor href="" leadingIcon={IconUserGroup}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor href="" leadingIcon={IconDatabase}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor href="" leadingIcon={IconAnnotation}></ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor href="" leadingIcon={IconFolder}></ActionMenu.Item.Anchor>
|
||||
<Divider />
|
||||
<ActionMenu.Item.Anchor href="" leadingIcon={IconCog}></ActionMenu.Item.Anchor>
|
||||
</Layout.Stack>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 48px;
|
||||
height: calc(100vh - 48px);
|
||||
padding: var(--space-4);
|
||||
width: 52px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { isStudio } from '$lib/system';
|
||||
export let overlapCover = false;
|
||||
export let size: 'small' | 'medium' | 'large' | 'xl' | 'xxl' | 'xxxl' = null;
|
||||
|
||||
@@ -8,13 +9,17 @@
|
||||
: '';
|
||||
</script>
|
||||
|
||||
<div style:container-type="inline-size" class:overlap-cover={overlapCover}>
|
||||
<div class="console-container" {style}>
|
||||
<Layout.Stack gap="l">
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
{#if isStudio}
|
||||
<slot />
|
||||
{:else}
|
||||
<div style:container-type="inline-size" class:overlap-cover={overlapCover}>
|
||||
<div class="console-container" {style}>
|
||||
<Layout.Stack gap="l">
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.overlap-cover {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<script lang="ts">
|
||||
import { AvatarInitials, Breadcrumbs } from '$lib/components/index.js';
|
||||
import { Avatar, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { organization, organizationList } from '$lib/stores/organization';
|
||||
import { user } from '$lib/stores/user';
|
||||
import SidebarProject from '$lib/components/studio/sidebarProject.svelte';
|
||||
import SidebarOrganization from '$lib/components/studio/sidebarOrganization.svelte';
|
||||
import { BillingPlan } from '$lib/constants.js';
|
||||
import { isCloud } from '$lib/system.js';
|
||||
import { tierToPlan } from '$lib/stores/billing.js';
|
||||
import type { NavbarProject } from '$lib/components/navbar.svelte';
|
||||
|
||||
$: hasProjectSidebar = $page.url.pathname.startsWith('/studio/proj');
|
||||
|
||||
export let loadedProjects: Array<NavbarProject> = [];
|
||||
|
||||
$: organizations = $organizationList.teams.map((org) => {
|
||||
const billingPlan = org['billingPlan'];
|
||||
return {
|
||||
name: org.name,
|
||||
$id: org.$id,
|
||||
showUpgrade: billingPlan === BillingPlan.FREE,
|
||||
tierName: isCloud ? (tierToPlan(billingPlan)?.name ?? '') : null,
|
||||
isSelected: $page.data.organization?.$id === org.$id,
|
||||
projects: loadedProjects
|
||||
};
|
||||
});
|
||||
|
||||
$: console.log(organizations);
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<Layout.Stack>
|
||||
<header>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Breadcrumbs {organizations} />
|
||||
<AvatarInitials name={$user?.name ?? ''} size="s" />
|
||||
</Layout.Stack>
|
||||
</header>
|
||||
<div class="studio-content" class:project-sidebar={hasProjectSidebar}><slot /></div>
|
||||
{#if hasProjectSidebar}
|
||||
<SidebarProject />
|
||||
{:else}
|
||||
<SidebarOrganization organization={$page.data.organization} />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
main {
|
||||
min-height: 100vh;
|
||||
background-color: var(--bgcolor-neutral-default);
|
||||
}
|
||||
|
||||
header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
.studio-content {
|
||||
min-height: calc(100vh - 48px);
|
||||
margin-top: 48px;
|
||||
width: calc(100vw - 200px);
|
||||
margin-left: 200px;
|
||||
padding-right: var(--space-4);
|
||||
|
||||
&.project-sidebar {
|
||||
width: calc(100vw - 52px);
|
||||
margin-left: 52px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+9
-1
@@ -6,11 +6,17 @@ export const enum Mode {
|
||||
SELF_HOSTED = 'self-hosted'
|
||||
}
|
||||
|
||||
export const enum Profile {
|
||||
CONSOLE = 'console',
|
||||
STUDIO = 'studio'
|
||||
}
|
||||
|
||||
export const VARS = {
|
||||
CONSOLE_MODE: (env.PUBLIC_CONSOLE_MODE as Mode) ?? undefined,
|
||||
APPWRITE_ENDPOINT: env.PUBLIC_APPWRITE_ENDPOINT ?? undefined,
|
||||
GROWTH_ENDPOINT: env.PUBLIC_GROWTH_ENDPOINT ?? undefined,
|
||||
PUBLIC_STRIPE_KEY: env.PUBLIC_STRIPE_KEY ?? undefined
|
||||
PUBLIC_STRIPE_KEY: env.PUBLIC_STRIPE_KEY ?? undefined,
|
||||
PROJECT_PROFILE: (env.PUBLIC_PROJECT_PROFILE as Profile) ?? undefined
|
||||
};
|
||||
|
||||
export const ENV = {
|
||||
@@ -21,7 +27,9 @@ export const ENV = {
|
||||
};
|
||||
|
||||
export const MODE = VARS.CONSOLE_MODE === Mode.CLOUD ? Mode.CLOUD : Mode.SELF_HOSTED;
|
||||
export const PROFILE = VARS.PROJECT_PROFILE === Profile.CONSOLE ? Profile.CONSOLE : Profile.STUDIO;
|
||||
export const isCloud = MODE === Mode.CLOUD;
|
||||
export const isStudio = PROFILE === Profile.STUDIO;
|
||||
export const isSelfHosted = MODE !== Mode.CLOUD;
|
||||
export const isDev = ENV.DEV;
|
||||
export const isProd = ENV.PROD;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { BillingPlan, INTERVAL } from '$lib/constants';
|
||||
import Footer from '$lib/layout/footer.svelte';
|
||||
import Shell from '$lib/layout/shell.svelte';
|
||||
import ShellStudio from '$lib/layout/shellStudio.svelte';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { newOrgModal, organization, type Organization } from '$lib/stores/organization';
|
||||
import { database, checkForDatabaseBackupPolicies } from '$lib/stores/database';
|
||||
@@ -32,7 +33,7 @@
|
||||
import { openMigrationWizard } from './(migration-wizard)';
|
||||
import { project } from './project-[project]/store';
|
||||
import { feedback } from '$lib/stores/feedback';
|
||||
import { hasStripePublicKey, isCloud, VARS } from '$lib/system';
|
||||
import { hasStripePublicKey, isCloud, isStudio, VARS } from '$lib/system';
|
||||
import { stripe } from '$lib/stores/stripe';
|
||||
import MobileSupportModal from './wizard/support/mobileSupportModal.svelte';
|
||||
import { showSupportModal } from './wizard/support/store';
|
||||
@@ -329,6 +330,9 @@
|
||||
</script>
|
||||
|
||||
<CommandCenter />
|
||||
{#if isStudio}
|
||||
<ShellStudio bind:loadedProjects><slot /></ShellStudio>
|
||||
{:else}
|
||||
<Shell
|
||||
showSideNavigation={page.url.pathname !== '/console' &&
|
||||
!page?.params.organization &&
|
||||
@@ -343,6 +347,7 @@
|
||||
<slot />
|
||||
<Footer slot="footer" />
|
||||
</Shell>
|
||||
{/if}
|
||||
|
||||
{#if $wizard.show && $wizard.component}
|
||||
<svelte:component this={$wizard.component} {...$wizard.props} />
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Container } from '$lib/layout';
|
||||
import CreateProject from './createProject.svelte';
|
||||
import CreateOrganization from '../createOrganization.svelte';
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud, isStudio } from '$lib/system';
|
||||
import { page } from '$app/state';
|
||||
import { registerCommands } from '$lib/commandCenter';
|
||||
import {
|
||||
@@ -28,7 +28,7 @@
|
||||
import { onMount, type ComponentType } from 'svelte';
|
||||
import { canWriteProjects } from '$lib/stores/roles';
|
||||
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
|
||||
import { Badge, Icon, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { Badge, Divider, Icon, Typography, Card, Layout } from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconAndroid,
|
||||
IconApple,
|
||||
@@ -48,6 +48,7 @@
|
||||
let showCreateProjectCloud = false;
|
||||
|
||||
function allServiceDisabled(project: Models.Project): boolean {
|
||||
return false;
|
||||
let disabled = true;
|
||||
services.load(project);
|
||||
$services.list.forEach((service) => {
|
||||
@@ -142,94 +143,124 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<Typography.Title>Projects</Typography.Title>
|
||||
{#if isStudio}
|
||||
<Card.Base>
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary"
|
||||
>Projects</Typography.Text>
|
||||
<Button size="s">Create project</Button>
|
||||
</Layout.Stack>
|
||||
|
||||
<DropList bind:show={showDropdown} placement="bottom-end">
|
||||
{#if $canWriteProjects}
|
||||
<Button
|
||||
on:click={handleCreateProject}
|
||||
event="create_project"
|
||||
disabled={$readOnly && !GRACE_PERIOD_OVERRIDE}>
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create project
|
||||
</Button>
|
||||
{/if}
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem on:click={() => (showCreate = true)}>Empty project</DropListItem>
|
||||
<DropListItem on:click={importProject}>
|
||||
<div class="u-flex u-gap-8 u-cross-center">
|
||||
Import project <span class="tag eyebrow-heading-3">Experimental</span>
|
||||
</div>
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
{#if data.projects.total}
|
||||
<CardContainer
|
||||
showEmpty={$canWriteProjects}
|
||||
total={data.projects.total}
|
||||
<Layout.Grid gap="l" columnsS={3} columns={4} columnsL={4} columnsXL={5}>
|
||||
<!-- TODO: this now maps over projects, but probably will be more like artifacts (and location might be different-->
|
||||
{#each data.projects.projects as project}
|
||||
<a href={`${base}/project-${project.$id}`}
|
||||
><Card.Media
|
||||
src="https://picsum.photos/260/150"
|
||||
alt=""
|
||||
description={project.description}
|
||||
title={project.name}></Card.Media
|
||||
></a>
|
||||
{/each}
|
||||
</Layout.Grid>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
{:else}
|
||||
<Container>
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<Typography.Title>Projects</Typography.Title>
|
||||
|
||||
<DropList bind:show={showDropdown} placement="bottom-end">
|
||||
{#if $canWriteProjects}
|
||||
<Button
|
||||
on:click={handleCreateProject}
|
||||
event="create_project"
|
||||
disabled={$readOnly && !GRACE_PERIOD_OVERRIDE}>
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create project
|
||||
</Button>
|
||||
{/if}
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem on:click={() => (showCreate = true)}>Empty project</DropListItem>
|
||||
<DropListItem on:click={importProject}>
|
||||
<div class="u-flex u-gap-8 u-cross-center">
|
||||
Import project <span class="tag eyebrow-heading-3">Experimental</span>
|
||||
</div>
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</div>
|
||||
|
||||
{#if data.projects.total}
|
||||
<CardContainer
|
||||
showEmpty={$canWriteProjects}
|
||||
total={data.projects.total}
|
||||
offset={data.offset}
|
||||
on:click={handleCreateProject}>
|
||||
{#each data.projects.projects as project}
|
||||
{@const platforms = filterPlatforms(
|
||||
project.platforms.map((platform) => getPlatformInfo(platform.type))
|
||||
)}
|
||||
<GridItem1 href={`${base}/project-${project.$id}`}>
|
||||
<svelte:fragment slot="eyebrow">
|
||||
{project?.platforms?.length ? project?.platforms?.length : 'No'} apps
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="title">
|
||||
{project.name}
|
||||
</svelte:fragment>
|
||||
{#if allServiceDisabled(project)}
|
||||
<p>
|
||||
<span class="icon-pause" aria-hidden="true"></span> All services are
|
||||
disabled.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{#each platforms as platform, i}
|
||||
{#if i < 3}
|
||||
{@const icon = getIconForPlatform(platform.icon)}
|
||||
<Badge variant="secondary" content={platform.name}>
|
||||
<Icon {icon} size="s" slot="start" />
|
||||
</Badge>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if platforms?.length > 3}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
content={`+${project.platforms.length - 3}`} />
|
||||
{/if}
|
||||
<svelte:fragment slot="icons">
|
||||
{#if isCloud && regions}
|
||||
{@const region = findRegion(project)}
|
||||
<span class="u-color-text-gray u-medium u-line-height-2">
|
||||
{region?.name}
|
||||
</span>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</GridItem1>
|
||||
{/each}
|
||||
<svelte:fragment slot="empty">
|
||||
<p>Create a new project</p>
|
||||
</svelte:fragment>
|
||||
</CardContainer>
|
||||
{:else}
|
||||
<Empty
|
||||
single
|
||||
allowCreate={$canWriteProjects}
|
||||
on:click={handleCreateProject}
|
||||
target="project"
|
||||
href="https://appwrite.io/docs/quick-starts"></Empty>
|
||||
{/if}
|
||||
|
||||
<PaginationWithLimit
|
||||
name="Projects"
|
||||
limit={data.limit}
|
||||
offset={data.offset}
|
||||
on:click={handleCreateProject}>
|
||||
{#each data.projects.projects as project}
|
||||
{@const platforms = filterPlatforms(
|
||||
project.platforms.map((platform) => getPlatformInfo(platform.type))
|
||||
)}
|
||||
<GridItem1 href={`${base}/project-${project.$id}`}>
|
||||
<svelte:fragment slot="eyebrow">
|
||||
{project?.platforms?.length ? project?.platforms?.length : 'No'} apps
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="title">
|
||||
{project.name}
|
||||
</svelte:fragment>
|
||||
{#if allServiceDisabled(project)}
|
||||
<p>
|
||||
<span class="icon-pause" aria-hidden="true"></span> All services are disabled.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{#each platforms as platform, i}
|
||||
{#if i < 3}
|
||||
{@const icon = getIconForPlatform(platform.icon)}
|
||||
<Badge variant="secondary" content={platform.name}>
|
||||
<Icon {icon} size="s" slot="start" />
|
||||
</Badge>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if platforms?.length > 3}
|
||||
<Badge variant="secondary" content={`+${project.platforms.length - 3}`} />
|
||||
{/if}
|
||||
<svelte:fragment slot="icons">
|
||||
{#if isCloud && regions}
|
||||
{@const region = findRegion(project)}
|
||||
<span class="u-color-text-gray u-medium u-line-height-2">
|
||||
{region?.name}
|
||||
</span>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</GridItem1>
|
||||
{/each}
|
||||
<svelte:fragment slot="empty">
|
||||
<p>Create a new project</p>
|
||||
</svelte:fragment>
|
||||
</CardContainer>
|
||||
{:else}
|
||||
<Empty
|
||||
single
|
||||
allowCreate={$canWriteProjects}
|
||||
on:click={handleCreateProject}
|
||||
target="project"
|
||||
href="https://appwrite.io/docs/quick-starts"></Empty>
|
||||
{/if}
|
||||
|
||||
<PaginationWithLimit
|
||||
name="Projects"
|
||||
limit={data.limit}
|
||||
offset={data.offset}
|
||||
total={data.projects.total} />
|
||||
</Container>
|
||||
total={data.projects.total} />
|
||||
</Container>
|
||||
{/if}
|
||||
|
||||
<CreateOrganization bind:show={addOrganization} />
|
||||
<CreateProject bind:show={showCreate} teamId={page.params.organization} />
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getLimit, getPage, pageToOffset } from '$lib/helpers/load';
|
||||
import { CARD_LIMIT, Dependencies } from '$lib/constants';
|
||||
import type { PageLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
export const load: PageLoad = async ({ params, url, route, depends, parent }) => {
|
||||
const { scopes } = await parent();
|
||||
@@ -12,7 +13,7 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
|
||||
const limit = getLimit(url, route, CARD_LIMIT);
|
||||
const offset = pageToOffset(page, limit);
|
||||
if (!scopes.includes('projects.read') && scopes.includes('billing.read')) {
|
||||
return redirect(301, `/console/organization-${params.organization}/billing`);
|
||||
return redirect(301, `${base}/organization-${params.organization}/billing`);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -5,12 +5,13 @@ import { sdk } from '$lib/stores/sdk';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
export const load: PageLoad = async ({ parent, depends }) => {
|
||||
const { organization, scopes } = await parent();
|
||||
|
||||
if (!scopes.includes('billing.read')) {
|
||||
return redirect(301, `/console/organization-${organization.$id}`);
|
||||
return redirect(301, `${base}/organization-${organization.$id}`);
|
||||
}
|
||||
depends(Dependencies.PAYMENT_METHODS);
|
||||
depends(Dependencies.ORGANIZATION);
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import type { OrganizationList } from '$lib/stores/organization';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
export let domain: Domain;
|
||||
export let organizations: OrganizationList;
|
||||
@@ -20,7 +21,7 @@
|
||||
type: 'success',
|
||||
message: 'Domain moved successfully'
|
||||
});
|
||||
await goto(`/console/organization-${selectedOrg}/domains/`);
|
||||
await goto(`${base}/organization-${selectedOrg}/domains/`);
|
||||
await invalidate(Dependencies.ORGANIZATION);
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
} catch (e) {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<script lang="ts">
|
||||
import { Avatar, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { AvatarInitials, Breadcrumbs } from '$lib/components';
|
||||
import { page } from '$app/stores';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { tierToPlan } from '$lib/stores/billing';
|
||||
import { organization, organizationList } from '$lib/stores/organization';
|
||||
import { user } from '$lib/stores/user';
|
||||
import SidebarOrganization from '$lib/components/studio/sidebarOrganization.svelte';
|
||||
import SidebarProject from '$lib/components/studio/sidebarProject.svelte';
|
||||
|
||||
$: hasProjectSidebar = $page.url.pathname.startsWith('/studio/proj');
|
||||
|
||||
$: loadedProjects = $page.data.projects.map((project) => {
|
||||
return {
|
||||
name: project?.name,
|
||||
$id: project.$id,
|
||||
isSelected: $page.data.currentProjectId === project.$id,
|
||||
platformCount: project.platforms.length,
|
||||
pingCount: project.pingCount
|
||||
};
|
||||
});
|
||||
|
||||
$: organizations = $organizationList.teams.map((org) => {
|
||||
const billingPlan = org['billingPlan'];
|
||||
return {
|
||||
name: org.name,
|
||||
$id: org.$id,
|
||||
showUpgrade: billingPlan === BillingPlan.FREE,
|
||||
tierName: isCloud ? (tierToPlan(billingPlan)?.name ?? '') : null,
|
||||
isSelected: $page.data.currentOrganization?.$id === org.$id,
|
||||
projects: loadedProjects
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<Layout.Stack>
|
||||
<header>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Breadcrumbs {organizations} />
|
||||
<AvatarInitials name={$user?.name ?? ''} size="s" />
|
||||
</Layout.Stack>
|
||||
</header>
|
||||
<div class="studio-content" class:project-sidebar={hasProjectSidebar}><slot /></div>
|
||||
{#if hasProjectSidebar}
|
||||
<SidebarProject />
|
||||
{:else}
|
||||
<SidebarOrganization organization={$page.data.currentOrganization} />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
main {
|
||||
min-height: 100vh;
|
||||
background-color: var(--bgcolor-neutral-default);
|
||||
}
|
||||
|
||||
header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
.studio-content {
|
||||
min-height: calc(100vh - 48px);
|
||||
margin-top: 48px;
|
||||
width: calc(100vw - 200px);
|
||||
margin-left: 200px;
|
||||
padding-right: var(--space-4);
|
||||
|
||||
&.project-sidebar {
|
||||
width: calc(100vw - 52px);
|
||||
margin-left: 52px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { Plan } from '$lib/sdk/billing';
|
||||
import type { Tier } from '$lib/stores/billing';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { isCloud } from '$lib/system';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
|
||||
export const load: LayoutLoad = async ({ params, fetch, depends, parent }) => {
|
||||
await parent();
|
||||
depends(Dependencies.RUNTIMES);
|
||||
depends(Dependencies.CONSOLE_VARIABLES);
|
||||
depends(Dependencies.ORGANIZATION);
|
||||
|
||||
const prefs = await sdk.forConsole.account.getPrefs();
|
||||
|
||||
const { endpoint, project } = sdk.forConsole.client.config;
|
||||
const versionPromise = fetch(`${endpoint}/health/version`, {
|
||||
headers: {
|
||||
'X-Appwrite-Project': project
|
||||
}
|
||||
}).then((response) => response.json() as { version?: string });
|
||||
|
||||
const variablesPromise = sdk.forConsole.console.variables();
|
||||
|
||||
const [data, variables] = await Promise.all([versionPromise, variablesPromise]);
|
||||
|
||||
let plansInfo = new Map<Tier, Plan>();
|
||||
if (isCloud) {
|
||||
const plansArray = await sdk.forConsole.billing.getPlansInfo();
|
||||
plansInfo = plansArray.plans.reduce((map, plan) => {
|
||||
map.set(plan.$id as Tier, plan);
|
||||
return map;
|
||||
}, new Map<Tier, Plan>());
|
||||
}
|
||||
|
||||
const organizations = !isCloud
|
||||
? await sdk.forConsole.teams.list()
|
||||
: await sdk.forConsole.billing.listOrganization();
|
||||
|
||||
let projects = [];
|
||||
let currentOrgId = params.organization ? params.organization : prefs.organization;
|
||||
|
||||
if (!currentOrgId && organizations.teams.length > 0) {
|
||||
currentOrgId = organizations.teams[0].$id;
|
||||
}
|
||||
if (currentOrgId) {
|
||||
const orgProjects = await sdk.forConsole.projects.list([
|
||||
Query.equal('teamId', currentOrgId),
|
||||
Query.limit(100),
|
||||
Query.orderDesc('$updatedAt')
|
||||
]);
|
||||
projects = orgProjects.projects.length > 0 ? orgProjects.projects : [];
|
||||
}
|
||||
|
||||
const selectedOrganizationId =
|
||||
params && params.organization ? params.organization : prefs.organization;
|
||||
|
||||
return {
|
||||
consoleVariables: variables,
|
||||
version: data?.version ?? null,
|
||||
plansInfo,
|
||||
roles: [],
|
||||
scopes: [],
|
||||
projects: projects,
|
||||
currentProjectId: params.project ?? '',
|
||||
organizations: organizations,
|
||||
currentOrganization: organizations.teams.find((org) => org.$id === selectedOrganizationId)
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { Card, Typography, Layout, Divider, Button } from '@appwrite.io/pink-svelte';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<Card.Base>
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary"
|
||||
>Projects</Typography.Text>
|
||||
<Button.Button size="s">Create project</Button.Button>
|
||||
</Layout.Stack>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Layout.Grid gap="l" columnsS={3} columns={4} columnsL={4} columnsXL={5}>
|
||||
{#each data.projects as project}
|
||||
<a href={`${base}/proj-${project.$id}`}
|
||||
><Card.Media
|
||||
src="https://picsum.photos/260/150"
|
||||
alt=""
|
||||
description={project.description}
|
||||
title={project.name}></Card.Media
|
||||
></a>
|
||||
{/each}
|
||||
</Layout.Grid>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { Layout, Card, Typography, Divider } from '@appwrite.io/pink-svelte';
|
||||
</script>
|
||||
|
||||
<Layout.Stack direction="row">
|
||||
<section class="chat">
|
||||
<Card.Base>
|
||||
<Layout.Stack height="calc(100vh - 120px)">
|
||||
<Typography.Text>Chat</Typography.Text>
|
||||
<Divider />
|
||||
<Typography.Text>
|
||||
Phasellus a ultricies nulla. Proin tristique aliquam dui. Phasellus porta
|
||||
sodales justo, et feugiat ligula pulvinar sed. Morbi et nulla hendrerit,
|
||||
pellentesque est sodales, euismod magna. Suspendisse vel massa arcu. Nulla
|
||||
ultrices sit amet metus ac euismod. Curabitur ultrices aliquet libero, a
|
||||
pharetra orci tristique in. Etiam egestas fermentum justo ut semper. Sed
|
||||
vulputate eros erat, ac sollicitudin nulla condimentum volutpat. Sed vulputate
|
||||
est ante.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
</section>
|
||||
<Card.Base>Iframe</Card.Base>
|
||||
</Layout.Stack>
|
||||
|
||||
<style>
|
||||
.chat {
|
||||
width: 600px;
|
||||
}
|
||||
</style>
|
||||
@@ -14,16 +14,23 @@
|
||||
import { user } from '$lib/stores/user';
|
||||
import { loading } from '$routes/store';
|
||||
import { Root } from '@appwrite.io/pink-svelte';
|
||||
import { ThemeDark, ThemeLight, ThemeDarkCloud, ThemeLightCloud } from '../themes';
|
||||
import {
|
||||
ThemeDark,
|
||||
ThemeLight,
|
||||
ThemeDarkCloud,
|
||||
ThemeLightCloud,
|
||||
ThemeLightStudio
|
||||
} from '../themes';
|
||||
import { isSmallViewport, updateViewport } from '$lib/stores/viewport';
|
||||
import { feedback } from '$lib/stores/feedback';
|
||||
import { isStudio } from '$lib/system.js';
|
||||
|
||||
function resolveTheme(theme: AppStore['themeInUse']) {
|
||||
switch (theme) {
|
||||
case 'dark':
|
||||
return isCloud ? ThemeDarkCloud : ThemeDark;
|
||||
case 'light':
|
||||
return isCloud ? ThemeLightCloud : ThemeLight;
|
||||
return isStudio ? ThemeLightStudio : isCloud ? ThemeLightCloud : ThemeLight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,14 +156,14 @@
|
||||
{#each preloadFonts as font}
|
||||
<link rel="preload" href={font} as="font" type="font/woff2" crossorigin="anonymous" />
|
||||
{/each}
|
||||
<link rel="preload" as="style" type="text/css" href="/console/fonts/main.css" />
|
||||
<link rel="preload" as="style" type="text/css" href={`${base}/fonts/main.css`} />
|
||||
<link rel="stylesheet" href={`${base}/fonts/main.css`} />
|
||||
|
||||
{#if isCloud}
|
||||
{#each preloadFontsCloud as font}
|
||||
<link rel="preload" href={font} as="font" type="font/woff2" crossorigin="anonymous" />
|
||||
{/each}
|
||||
<link rel="preload" as="style" type="text/css" href="/console/fonts/cloud.css" />
|
||||
<link rel="preload" as="style" type="text/css" href={`${base}/fonts/cloud.css`} />
|
||||
<link rel="stylesheet" href={`${base}/fonts/cloud.css`} />
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
+7
-2
@@ -2,7 +2,7 @@ import { redirect } from '@sveltejs/kit';
|
||||
import { base } from '$app/paths';
|
||||
import type { PageLoad } from './$types';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { VARS } from '$lib/system';
|
||||
import { isStudio, VARS } from '$lib/system';
|
||||
|
||||
const handleGithubEducationMembership = async (name: string, email: string) => {
|
||||
const result = await sdk.forConsole.billing.setMembership('github-student-developer');
|
||||
@@ -39,7 +39,12 @@ export const load: PageLoad = async ({ parent, url }) => {
|
||||
if (!teamId) {
|
||||
redirect(303, `${base}/account/organizations${url.search}`);
|
||||
} else {
|
||||
redirect(303, `${base}/organization-${teamId}${url.search}`);
|
||||
redirect(
|
||||
303,
|
||||
isStudio
|
||||
? `${base}/org-${teamId}${url.search}`
|
||||
: `${base}/organization-${teamId}${url.search}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
redirect(303, `${base}/onboarding/create-project${url.search}`);
|
||||
|
||||
@@ -3,3 +3,8 @@ export { default as ThemeLight } from './light.json';
|
||||
|
||||
export { default as ThemeDarkCloud } from './dark-cloud.json';
|
||||
export { default as ThemeLightCloud } from './light-cloud.json';
|
||||
|
||||
import { default as StudioLightOverride } from './light-studio.json';
|
||||
import { default as ThemeLightBase } from './light.json';
|
||||
|
||||
export const ThemeLightStudio = { ...ThemeLightBase, ...StudioLightOverride };
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"bgcolor-accent": "var(--neutral-1000)",
|
||||
"bgcolor-accent-secondary": "var(--neutral-900)"
|
||||
}
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
import { sveltePreprocess } from 'svelte-preprocess';
|
||||
import { preprocessMeltUI, sequence } from '@melt-ui/pp';
|
||||
import 'dotenv/config';
|
||||
|
||||
const projectProfile = process.env.PUBLIC_PROJECT_PROFILE || 'console';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
@@ -26,7 +29,7 @@ const config = {
|
||||
precompress: true
|
||||
}),
|
||||
paths: {
|
||||
base: '/console'
|
||||
base: `/${projectProfile}`
|
||||
}
|
||||
},
|
||||
vitePlugin: {
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export default defineConfig({
|
||||
project: 'console'
|
||||
}
|
||||
}),
|
||||
sveltekit()
|
||||
sveltekit(),
|
||||
],
|
||||
optimizeDeps: {
|
||||
include: ['echarts', 'prismjs']
|
||||
|
||||
Reference in New Issue
Block a user