Compare commits
5 Commits
@nhost/das
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3e200c55a | ||
|
|
8fb3064eea | ||
|
|
e5f1c6cb78 | ||
|
|
02994ee4e2 | ||
|
|
74a1239cd5 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "2.1.0",
|
||||
"version": "2.1.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
|
||||
@@ -24,13 +24,16 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/v3/select';
|
||||
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
||||
import { useOrgs, type Org } from '@/features/orgs/projects/hooks/useOrgs';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useBillingTransferAppMutation } from '@/utils/__generated__/graphql';
|
||||
import {
|
||||
Organization_Members_Role_Enum,
|
||||
useBillingTransferAppMutation,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useUserId } from '@nhost/nextjs';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
@@ -48,10 +51,10 @@ export default function TransferProjectDialog({
|
||||
open,
|
||||
setOpen,
|
||||
}: TransferProjectDialogProps) {
|
||||
const { orgs } = useOrgs();
|
||||
const { org: currentOrg } = useCurrentOrg();
|
||||
const { project } = useProject();
|
||||
const { push } = useRouter();
|
||||
const currentUserId = useUserId();
|
||||
const { project } = useProject();
|
||||
const { orgs, currentOrg } = useOrgs();
|
||||
const [transferProject] = useBillingTransferAppMutation();
|
||||
|
||||
const form = useForm<z.infer<typeof transferProjectFormSchema>>({
|
||||
@@ -86,16 +89,29 @@ export default function TransferProjectDialog({
|
||||
);
|
||||
};
|
||||
|
||||
const isUserAdminOfOrg = (org: Org, userId: string) =>
|
||||
org.members.some(
|
||||
(member) =>
|
||||
member.role === Organization_Members_Role_Enum.Admin &&
|
||||
member.user.id === userId,
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(value) => {
|
||||
form.reset();
|
||||
setOpen(value);
|
||||
}}
|
||||
>
|
||||
<DialogContent className="text-foreground sm:max-w-xl">
|
||||
<DialogHeader className="flex gap-2">
|
||||
<DialogTitle>
|
||||
Move the current project to a different organization.
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
To transfer a project between two organizations, you must be ADMIN
|
||||
in both.
|
||||
To transfer a project between organizations, you must be an{' '}
|
||||
<span className="font-bold">ADMIN</span> in both.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
@@ -120,7 +136,11 @@ export default function TransferProjectDialog({
|
||||
<SelectItem
|
||||
key={org.id}
|
||||
value={org.id}
|
||||
disabled={org.plan.isFree || org.id === currentOrg.id}
|
||||
disabled={
|
||||
org.plan.isFree || // disable the personal org
|
||||
org.id === currentOrg.id || // disable the current org as it can't be a destination org
|
||||
!isUserAdminOfOrg(org, currentUserId) // disable orgs that the current user is not admin of
|
||||
}
|
||||
>
|
||||
{org.name}
|
||||
<Badge
|
||||
@@ -146,11 +166,19 @@ export default function TransferProjectDialog({
|
||||
variant="secondary"
|
||||
type="button"
|
||||
disabled={form.formState.isSubmitting}
|
||||
onClick={() => setOpen(false)}
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" disabled={form.formState.isSubmitting}>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={
|
||||
form.formState.isSubmitting || !form.formState.isDirty
|
||||
}
|
||||
>
|
||||
{form.formState.isSubmitting ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
|
||||
@@ -34,9 +34,15 @@ import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function NotificationsTray() {
|
||||
const { refetch: refetchOrgs } = useOrgs();
|
||||
const { asPath, route } = useRouter();
|
||||
const userData = useUserData();
|
||||
const { asPath, route } = useRouter();
|
||||
const { refetch: refetchOrgs } = useOrgs();
|
||||
|
||||
const [stripeFormDialogOpen, setStripeFormDialogOpen] = useState(false);
|
||||
|
||||
const [pendingOrgRequest, setPendingOrgRequest] =
|
||||
useState<PostOrganizationRequestResponse | null>(null);
|
||||
|
||||
const [
|
||||
getInvites,
|
||||
{
|
||||
@@ -45,11 +51,6 @@ export default function NotificationsTray() {
|
||||
data: { organizationMemberInvites: invites = [] } = {},
|
||||
},
|
||||
] = useOrganizationMemberInvitesLazyQuery();
|
||||
|
||||
const [stripeFormDialogOpen, setStripeFormDialogOpen] = useState(false);
|
||||
|
||||
const [pendingOrgRequest, setPendingOrgRequest] =
|
||||
useState<PostOrganizationRequestResponse | null>(null);
|
||||
const [getOrganizationNewRequests] = useOrganizationNewRequestsLazyQuery();
|
||||
const [postOrganizationRequest] = usePostOrganizationRequestMutation();
|
||||
|
||||
@@ -155,12 +156,11 @@ export default function NotificationsTray() {
|
||||
<>
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="ghost" className="relative px-3 py-1 h-fit">
|
||||
<Button variant="ghost" className="relative h-fit px-3 py-1">
|
||||
<Bell className="mt-[2px] h-[1.15rem] w-[1.15rem]" />
|
||||
{invites.length > 0 ||
|
||||
(pendingOrgRequest && (
|
||||
<div className="absolute w-2 h-2 bg-red-500 rounded-full right-3 top-2" />
|
||||
))}
|
||||
{(pendingOrgRequest || Boolean(invites.length)) && (
|
||||
<div className="absolute right-3 top-2 h-2 w-2 rounded-full bg-red-500" />
|
||||
)}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="h-full w-full bg-background p-0 text-foreground sm:max-w-[310px]">
|
||||
@@ -170,14 +170,14 @@ export default function NotificationsTray() {
|
||||
List of pending invites and create organization requests
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
<div className="flex flex-col w-full h-full">
|
||||
<div className="flex items-center h-12 px-2 border-b">
|
||||
<div className="flex h-full w-full flex-col">
|
||||
<div className="flex h-12 items-center border-b px-2">
|
||||
<h3 className="font-medium">
|
||||
Notifications {invites.length > 0 && `(${invites.length})`}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="p-2">
|
||||
<div className="flex h-full flex-col gap-2 overflow-auto p-2">
|
||||
{!loading && invites.length === 0 && !pendingOrgRequest && (
|
||||
<span className="text-muted-foreground">
|
||||
No new notifications
|
||||
@@ -185,9 +185,9 @@ export default function NotificationsTray() {
|
||||
)}
|
||||
|
||||
{pendingOrgRequest && (
|
||||
<div className="flex flex-col gap-2 p-2 border rounded-md">
|
||||
<div className="flex flex-col gap-2 rounded-md border p-2">
|
||||
<div className="flex flex-col items-end gap-2">
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<Badge className="h-5 px-[6px] text-[10px]">
|
||||
New Organization pending
|
||||
</Badge>
|
||||
@@ -212,10 +212,10 @@ export default function NotificationsTray() {
|
||||
{invites.map((invite) => (
|
||||
<div
|
||||
key={invite.id}
|
||||
className="flex flex-col gap-2 p-2 border rounded-md"
|
||||
className="flex flex-col gap-2 rounded-md border p-2"
|
||||
>
|
||||
<div className="flex flex-col items-end gap-2">
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<Badge className="h-5 px-[6px] text-[10px]">
|
||||
Invitation
|
||||
</Badge>
|
||||
@@ -265,7 +265,7 @@ export default function NotificationsTray() {
|
||||
onOpenChange={setStripeFormDialogOpen}
|
||||
>
|
||||
<DialogContent
|
||||
className="text-black bg-white sm:max-w-xl"
|
||||
className="bg-white text-black sm:max-w-xl"
|
||||
onInteractOutside={(e) => e.preventDefault()}
|
||||
onEscapeKeyDown={(e) => e.preventDefault()}
|
||||
>
|
||||
|
||||
@@ -2,11 +2,9 @@ import { Button } from '@/components/ui/v2/Button';
|
||||
import { ArrowRightIcon } from '@/components/ui/v2/icons/ArrowRightIcon';
|
||||
import { Link } from '@/components/ui/v2/Link';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
useDeleteApplicationMutation,
|
||||
} from '@/generated/graphql';
|
||||
import { useBillingDeleteAppMutation } from '@/generated/graphql';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||
import { getApplicationStatusString } from '@/utils/helpers';
|
||||
@@ -14,17 +12,22 @@ import { formatDistance } from 'date-fns';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function ApplicationInfo() {
|
||||
const { project } = useProject();
|
||||
const [deleteApplication] = useDeleteApplicationMutation({
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
const router = useRouter();
|
||||
const { project } = useProject();
|
||||
const { currentOrg: org } = useOrgs();
|
||||
|
||||
const [deleteApplication] = useBillingDeleteAppMutation();
|
||||
|
||||
async function handleClickRemove() {
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await deleteApplication({ variables: { appId: project.id } });
|
||||
await router.push('/');
|
||||
await deleteApplication({
|
||||
variables: {
|
||||
appID: project?.id,
|
||||
},
|
||||
});
|
||||
|
||||
await router.push(`/orgs/${org?.slug}/projects`);
|
||||
},
|
||||
{
|
||||
loadingMessage: 'Deleting project...',
|
||||
@@ -40,7 +43,7 @@ export default function ApplicationInfo() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-flow-row gap-4 mt-4">
|
||||
<div className="mt-4 grid grid-flow-row gap-4">
|
||||
<div className="grid grid-flow-row justify-center gap-0.5">
|
||||
<Text variant="subtitle2">Application ID:</Text>
|
||||
|
||||
@@ -88,7 +91,7 @@ export default function ApplicationInfo() {
|
||||
href={`https://staging.nhost.run/console/data/default/schema/public/tables/app_state_history/browse?filter=app_id%3B%24eq%3B${project.id}`}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="grid items-center justify-center grid-flow-col gap-1 p-2"
|
||||
className="grid grid-flow-col items-center justify-center gap-1 p-2"
|
||||
underline="hover"
|
||||
>
|
||||
App State History <ArrowRightIcon />
|
||||
|
||||
@@ -42,6 +42,9 @@ export default function ApplicationPaused() {
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await unpauseApplication({ variables: { appId: project.id } });
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
await refetchProject();
|
||||
},
|
||||
{
|
||||
@@ -62,17 +65,19 @@ export default function ApplicationPaused() {
|
||||
<Modal
|
||||
showModal={showDeletingModal}
|
||||
close={() => setShowDeletingModal(false)}
|
||||
className="flex h-screen items-center justify-center"
|
||||
>
|
||||
<RemoveApplicationModal
|
||||
close={() => setShowDeletingModal(false)}
|
||||
title={`Remove project ${project.name}?`}
|
||||
description={`The project ${project.name} will be removed. All data will be lost and there will be no way to
|
||||
recover the app once it has been deleted.`}
|
||||
className="z-50"
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<Container className="grid max-w-lg grid-flow-row gap-6 mx-auto text-center">
|
||||
<div className="flex flex-col mx-auto text-center w-centImage">
|
||||
<Container className="mx-auto grid max-w-lg grid-flow-row gap-6 text-center">
|
||||
<div className="mx-auto flex w-centImage flex-col text-center">
|
||||
<ApplicationPausedSymbol isLocked={isLocked} />
|
||||
</div>
|
||||
|
||||
@@ -93,7 +98,7 @@ export default function ApplicationPaused() {
|
||||
{org && (
|
||||
<>
|
||||
<Button
|
||||
className="w-full max-w-xs mx-auto"
|
||||
className="mx-auto w-full max-w-xs"
|
||||
onClick={() => setTransferProjectDialogOpen(true)}
|
||||
>
|
||||
Transfer
|
||||
@@ -106,7 +111,7 @@ export default function ApplicationPaused() {
|
||||
)}
|
||||
<Button
|
||||
variant="borderless"
|
||||
className="w-full max-w-xs mx-auto"
|
||||
className="mx-auto w-full max-w-xs"
|
||||
loading={changingApplicationStateLoading}
|
||||
disabled={
|
||||
changingApplicationStateLoading ||
|
||||
@@ -121,7 +126,7 @@ export default function ApplicationPaused() {
|
||||
<Button
|
||||
color="error"
|
||||
variant="outlined"
|
||||
className="w-full max-w-xs mx-auto"
|
||||
className="mx-auto w-full max-w-xs"
|
||||
onClick={() => setShowDeletingModal(true)}
|
||||
>
|
||||
Delete Project
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { Container } from '@/components/layout/Container';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { ApplicationInfo } from '@/features/projects/common/components/ApplicationInfo';
|
||||
import { AppLoader } from '@/features/projects/common/components/AppLoader';
|
||||
import { StagingMetadata } from '@/features/projects/common/components/StagingMetadata';
|
||||
import { useCheckProvisioning } from '@/features/projects/common/hooks/useCheckProvisioning';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { ApplicationInfo } from '@/features/orgs/projects/common/components/ApplicationInfo';
|
||||
import { AppLoader } from '@/features/orgs/projects/common/components/AppLoader';
|
||||
import { StagingMetadata } from '@/features/orgs/projects/common/components/StagingMetadata';
|
||||
import { useCheckProvisioning } from '@/features/orgs/projects/common/hooks/useCheckProvisioning';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function ApplicationRestoring() {
|
||||
const { project } = useProject();
|
||||
const currentProjectState = useCheckProvisioning();
|
||||
const { currentProject } = useCurrentWorkspaceAndProject();
|
||||
|
||||
return (
|
||||
<Container className="mx-auto mt-8 grid max-w-sm grid-flow-row gap-4 text-center">
|
||||
@@ -26,7 +26,7 @@ export default function ApplicationRestoring() {
|
||||
{currentProjectState.state === ApplicationStatus.Empty ? (
|
||||
<div className="grid grid-flow-row gap-1">
|
||||
<Text variant="h3" component="h1">
|
||||
Setting Up {currentProject?.name}
|
||||
Setting Up {project?.name}
|
||||
</Text>
|
||||
|
||||
<Text>This normally takes around 2 minutes</Text>
|
||||
|
||||
@@ -3,13 +3,11 @@ import { Button } from '@/components/ui/v2/Button';
|
||||
import { Checkbox } from '@/components/ui/v2/Checkbox';
|
||||
import { Divider } from '@/components/ui/v2/Divider';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
useDeleteApplicationMutation,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { useBillingDeleteAppMutation } from '@/utils/__generated__/graphql';
|
||||
import router from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
@@ -44,14 +42,14 @@ export default function RemoveApplicationModal({
|
||||
description,
|
||||
className,
|
||||
}: RemoveApplicationModalProps) {
|
||||
const [deleteApplication] = useDeleteApplicationMutation({
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
const [loadingRemove, setLoadingRemove] = useState(false);
|
||||
const { project } = useProject();
|
||||
const { currentOrg: org } = useOrgs();
|
||||
const [loadingRemove, setLoadingRemove] = useState(false);
|
||||
const [deleteApplication] = useBillingDeleteAppMutation();
|
||||
|
||||
const [remove, setRemove] = useState(false);
|
||||
const [remove2, setRemove2] = useState(false);
|
||||
|
||||
const appName = project?.name;
|
||||
|
||||
async function handleClick() {
|
||||
@@ -69,14 +67,14 @@ export default function RemoveApplicationModal({
|
||||
try {
|
||||
await deleteApplication({
|
||||
variables: {
|
||||
appId: project.id,
|
||||
appID: project?.id,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
await discordAnnounce(`Error trying to delete project: ${appName}`);
|
||||
}
|
||||
close();
|
||||
await router.push('/');
|
||||
await router.push(`/orgs/${org?.slug}/projects`);
|
||||
triggerToast(`${project.name} deleted`);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApplicationStatus } from "@/types/application";
|
||||
import { getHasuraAdminSecret } from "@/utils/env";
|
||||
import { type GetProjectQuery } from "@/utils/__generated__/graphql";
|
||||
import { type Org } from "@/features/orgs/projects/hooks/useOrgs";
|
||||
import { type Org } from '@/features/orgs/projects/hooks/useOrgs';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { getHasuraAdminSecret } from '@/utils/env';
|
||||
import { type GetProjectQuery } from '@/utils/__generated__/graphql';
|
||||
|
||||
export const localApplication: GetProjectQuery['apps'][0] = {
|
||||
id: '00000000-0000-0000-0000-000000000000',
|
||||
@@ -56,4 +56,5 @@ export const localOrganization: Org = {
|
||||
featureMaxDbSize: 1,
|
||||
},
|
||||
apps: [localApplication],
|
||||
};
|
||||
members: [],
|
||||
};
|
||||
|
||||
@@ -21,5 +21,15 @@ query getOrganizations($userId: uuid!) {
|
||||
subdomain
|
||||
slug
|
||||
}
|
||||
members {
|
||||
id
|
||||
role
|
||||
user {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
dashboard/src/gql/organizations/insertOrgApp.gql
Normal file
8
dashboard/src/gql/organizations/insertOrgApp.gql
Normal file
@@ -0,0 +1,8 @@
|
||||
mutation insertOrgApplication($app: apps_insert_input!) {
|
||||
insertApp(object: $app) {
|
||||
id
|
||||
name
|
||||
slug
|
||||
subdomain
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ export default function DeploymentsPage() {
|
||||
|
||||
if (!project?.githubRepository) {
|
||||
return (
|
||||
<Container className="grid max-w-3xl grid-flow-row gap-4 mt-12 antialiased text-center">
|
||||
<div className="flex flex-col mx-auto text-center w-centImage">
|
||||
<Container className="mt-12 grid max-w-3xl grid-flow-row gap-4 text-center antialiased">
|
||||
<div className="mx-auto flex w-centImage flex-col text-center">
|
||||
<Image
|
||||
src="/assets/githubRepo.svg"
|
||||
width={72}
|
||||
@@ -38,7 +38,7 @@ export default function DeploymentsPage() {
|
||||
</div>
|
||||
|
||||
<NavLink
|
||||
href={`/orgs/${org?.slug}/projects/${project?.slug}/settings/git`}
|
||||
href={`/orgs/${org?.slug}/projects/${project?.subdomain}/settings/git`}
|
||||
passHref
|
||||
legacyBehavior
|
||||
>
|
||||
@@ -55,8 +55,8 @@ export default function DeploymentsPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Container className="flex flex-col max-w-5xl mx-auto space-y-2">
|
||||
<div className="flex flex-row mt-4 place-content-between">
|
||||
<Container className="mx-auto flex max-w-5xl flex-col space-y-2">
|
||||
<div className="mt-4 flex flex-row place-content-between">
|
||||
<Text variant="h2" component="h1">
|
||||
Deployments
|
||||
</Text>
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function SettingsGeneralPage() {
|
||||
|
||||
const isOwner = useIsCurrentUserOwner();
|
||||
const { currentOrg: org } = useOrgs();
|
||||
const { project, loading } = useProject();
|
||||
const { project, loading, refetch: refetchProject } = useProject();
|
||||
|
||||
const [updateApp] = useUpdateApplicationMutation();
|
||||
const [deleteApplication] = useBillingDeleteAppMutation();
|
||||
@@ -136,7 +136,10 @@ export default function SettingsGeneralPage() {
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await pauseApplication();
|
||||
await router.push(`/orgs/${org.slug}/projects/${project.slug}`);
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
await refetchProject();
|
||||
},
|
||||
{
|
||||
loadingMessage: `Pausing ${project.name}...`,
|
||||
|
||||
@@ -13,14 +13,11 @@ import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
||||
import { useSubmitState } from '@/hooks/useSubmitState';
|
||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||
import { getErrorMessage } from '@/utils/getErrorMessage';
|
||||
import type {
|
||||
GetOrganizationsQuery,
|
||||
PrefetchNewAppRegionsFragment,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
useInsertApplicationMutation,
|
||||
useInsertOrgApplicationMutation,
|
||||
usePrefetchNewAppQuery,
|
||||
type GetOrganizationsQuery,
|
||||
type PrefetchNewAppRegionsFragment,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -64,9 +61,7 @@ export function NewProjectPageContent({
|
||||
|
||||
const { submitState, setSubmitState } = useSubmitState();
|
||||
|
||||
const [insertApp] = useInsertApplicationMutation({
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
const [insertApp] = useInsertOrgApplicationMutation();
|
||||
|
||||
// options
|
||||
const orgOptions = orgs.map((org) => ({
|
||||
@@ -105,18 +100,21 @@ export function NewProjectPageContent({
|
||||
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await insertApp({
|
||||
variables: {
|
||||
app: {
|
||||
name,
|
||||
slug,
|
||||
organizationID: selectedOrg.id,
|
||||
regionId: selectedRegion.id,
|
||||
const { data: { insertApp: { subdomain } = {} } = {} } =
|
||||
await insertApp({
|
||||
variables: {
|
||||
app: {
|
||||
name,
|
||||
slug,
|
||||
organizationID: selectedOrg.id,
|
||||
regionId: selectedRegion.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await router.push(`/orgs/${selectedOrg.slug}/projects/${slug}`);
|
||||
if (subdomain) {
|
||||
await router.push(`/orgs/${selectedOrg.slug}/projects/${subdomain}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
loadingMessage: 'Creating the project...',
|
||||
|
||||
55
dashboard/src/utils/__generated__/graphql.ts
generated
55
dashboard/src/utils/__generated__/graphql.ts
generated
@@ -26644,7 +26644,7 @@ export type GetOrganizationsQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type GetOrganizationsQuery = { __typename?: 'query_root', organizations: Array<{ __typename?: 'organizations', id: any, name: string, slug: string, plan: { __typename?: 'plans', id: any, name: string, price: number, deprecated: boolean, individual: boolean, isFree: boolean, featureMaxDbSize: number }, apps: Array<{ __typename?: 'apps', id: any, name: string, subdomain: string, slug: string }> }> };
|
||||
export type GetOrganizationsQuery = { __typename?: 'query_root', organizations: Array<{ __typename?: 'organizations', id: any, name: string, slug: string, plan: { __typename?: 'plans', id: any, name: string, price: number, deprecated: boolean, individual: boolean, isFree: boolean, featureMaxDbSize: number }, apps: Array<{ __typename?: 'apps', id: any, name: string, subdomain: string, slug: string }>, members: Array<{ __typename?: 'organization_members', id: any, role: Organization_Members_Role_Enum, user: { __typename?: 'users', id: any, email?: any | null, displayName: string, avatarUrl: string } }> }> };
|
||||
|
||||
export type GetOrganizationPlansQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -26665,6 +26665,13 @@ export type GetProjectsQueryVariables = Exact<{
|
||||
|
||||
export type GetProjectsQuery = { __typename?: 'query_root', apps: Array<{ __typename?: 'apps', id: any, name: string, slug: string, createdAt: any, subdomain: string, deployments: Array<{ __typename?: 'deployments', id: any, commitSHA: string, commitMessage?: string | null, commitUserName?: string | null, deploymentStartedAt?: any | null, deploymentEndedAt?: any | null, commitUserAvatarUrl?: string | null, deploymentStatus?: string | null }>, creator?: { __typename?: 'users', id: any, email?: any | null, displayName: string } | null }> };
|
||||
|
||||
export type InsertOrgApplicationMutationVariables = Exact<{
|
||||
app: Apps_Insert_Input;
|
||||
}>;
|
||||
|
||||
|
||||
export type InsertOrgApplicationMutation = { __typename?: 'mutation_root', insertApp?: { __typename?: 'apps', id: any, name: string, slug: string, subdomain: string } | null };
|
||||
|
||||
export type InsertOrganizationMemberInviteMutationVariables = Exact<{
|
||||
organizationMemberInvite: Organization_Member_Invites_Insert_Input;
|
||||
}>;
|
||||
@@ -30851,6 +30858,16 @@ export const GetOrganizationsDocument = gql`
|
||||
subdomain
|
||||
slug
|
||||
}
|
||||
members {
|
||||
id
|
||||
role
|
||||
user {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -31095,6 +31112,42 @@ export type GetProjectsQueryResult = Apollo.QueryResult<GetProjectsQuery, GetPro
|
||||
export function refetchGetProjectsQuery(variables: GetProjectsQueryVariables) {
|
||||
return { query: GetProjectsDocument, variables: variables }
|
||||
}
|
||||
export const InsertOrgApplicationDocument = gql`
|
||||
mutation insertOrgApplication($app: apps_insert_input!) {
|
||||
insertApp(object: $app) {
|
||||
id
|
||||
name
|
||||
slug
|
||||
subdomain
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type InsertOrgApplicationMutationFn = Apollo.MutationFunction<InsertOrgApplicationMutation, InsertOrgApplicationMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useInsertOrgApplicationMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useInsertOrgApplicationMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useInsertOrgApplicationMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [insertOrgApplicationMutation, { data, loading, error }] = useInsertOrgApplicationMutation({
|
||||
* variables: {
|
||||
* app: // value for 'app'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useInsertOrgApplicationMutation(baseOptions?: Apollo.MutationHookOptions<InsertOrgApplicationMutation, InsertOrgApplicationMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<InsertOrgApplicationMutation, InsertOrgApplicationMutationVariables>(InsertOrgApplicationDocument, options);
|
||||
}
|
||||
export type InsertOrgApplicationMutationHookResult = ReturnType<typeof useInsertOrgApplicationMutation>;
|
||||
export type InsertOrgApplicationMutationResult = Apollo.MutationResult<InsertOrgApplicationMutation>;
|
||||
export type InsertOrgApplicationMutationOptions = Apollo.BaseMutationOptions<InsertOrgApplicationMutation, InsertOrgApplicationMutationVariables>;
|
||||
export const InsertOrganizationMemberInviteDocument = gql`
|
||||
mutation insertOrganizationMemberInvite($organizationMemberInvite: organization_member_invites_insert_input!) {
|
||||
insertOrganizationMemberInvite(object: $organizationMemberInvite) {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/docs
|
||||
|
||||
## 2.20.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- e5f1c6c: fix: copy to clipboard commands in nhost cli getting started
|
||||
|
||||
## 2.19.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -9,7 +9,7 @@ icon: square-terminal
|
||||
To install the Nhost CLI copy the following command and paste it into your terminal:
|
||||
|
||||
```bash
|
||||
> sudo curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash
|
||||
sudo curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash
|
||||
```
|
||||
|
||||
The `get.sh` script checks for both the architecture and operating system and installs the right binary.
|
||||
@@ -31,7 +31,7 @@ The `get.sh` script checks for both the architecture and operating system and in
|
||||
Update an existing installation to the latest version.
|
||||
|
||||
```bash Terminal
|
||||
> nhost sw upgrade
|
||||
nhost sw upgrade
|
||||
```
|
||||
|
||||
## Running Nhost
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/docs",
|
||||
"version": "2.19.0",
|
||||
"version": "2.20.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "mintlify dev"
|
||||
|
||||
Reference in New Issue
Block a user