Compare commits
3 Commits
@nhost/das
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3e200c55a | ||
|
|
8fb3064eea | ||
|
|
e5f1c6cb78 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "2.1.1",
|
||||
"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()}
|
||||
>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
dashboard/src/utils/__generated__/graphql.ts
generated
12
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; }>;
|
||||
|
||||
@@ -30858,6 +30858,16 @@ export const GetOrganizationsDocument = gql`
|
||||
subdomain
|
||||
slug
|
||||
}
|
||||
members {
|
||||
id
|
||||
role
|
||||
user {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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