Compare commits
7 Commits
release-20
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0558fcb19 | ||
|
|
5f94486faf | ||
|
|
2e58b9fd26 | ||
|
|
eb9539277b | ||
|
|
65c01c1e81 | ||
|
|
8e4282b094 | ||
|
|
81e1d78315 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "2.4.0",
|
||||
"version": "2.6.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
|
||||
@@ -5,8 +5,8 @@ import { LocalAccountMenu } from '@/components/layout/LocalAccountMenu';
|
||||
import { MobileNav } from '@/components/layout/MobileNav';
|
||||
import { Logo } from '@/components/presentational/Logo';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { GraphiteIcon } from '@/components/ui/v2/icons/GraphiteIcon';
|
||||
import { Button } from '@/components/ui/v3/button';
|
||||
import { DevAssistant as WorkspaceProjectDevAssistant } from '@/features/ai/DevAssistant';
|
||||
import { AnnouncementsTray } from '@/features/orgs/components/members/components/AnnouncementsTray';
|
||||
import { NotificationsTray } from '@/features/orgs/components/members/components/NotificationsTray';
|
||||
@@ -74,8 +74,12 @@ export default function Header({ className, ...props }: HeaderProps) {
|
||||
<BreadcrumbNav />
|
||||
|
||||
<div className="hidden grid-flow-col items-center gap-1 sm:grid">
|
||||
<Button className="rounded-full" onClick={openDevAssistant}>
|
||||
<GraphiteIcon className="h-4 w-4" />
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8"
|
||||
onClick={openDevAssistant}
|
||||
>
|
||||
<GraphiteIcon className="h-4" />
|
||||
</Button>
|
||||
|
||||
<NotificationsTray />
|
||||
|
||||
@@ -8,13 +8,20 @@ import {
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from '@/components/ui/v3/command';
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from '@/components/ui/v3/hover-card';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/v3/popover';
|
||||
import { useAppState } from '@/features/orgs/projects/common/hooks/useAppState';
|
||||
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { Box, Check, ChevronsUpDown } from 'lucide-react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -24,17 +31,76 @@ type Option = {
|
||||
label: string;
|
||||
};
|
||||
|
||||
function ProjectStatusIndicator({ status }: { status: ApplicationStatus }) {
|
||||
const indicatorStyles: Record<
|
||||
number,
|
||||
{ className: string; description: string }
|
||||
> = {
|
||||
[ApplicationStatus.Errored]: {
|
||||
className: 'bg-destructive',
|
||||
description: 'Project errored',
|
||||
},
|
||||
[ApplicationStatus.Pausing]: {
|
||||
className: 'bg-primary-main animate-blinking',
|
||||
description: 'Project is pausing',
|
||||
},
|
||||
[ApplicationStatus.Restoring]: {
|
||||
className: 'bg-primary-main animate-blinking',
|
||||
description: 'Project is restoring',
|
||||
},
|
||||
[ApplicationStatus.Paused]: {
|
||||
className: 'bg-slate-400',
|
||||
description: 'Project is paused',
|
||||
},
|
||||
[ApplicationStatus.Unpausing]: {
|
||||
className: 'bg-primary-main animate-blinking',
|
||||
description: 'Project is unpausing',
|
||||
},
|
||||
[ApplicationStatus.Live]: {
|
||||
className: 'bg-primary-main',
|
||||
description: 'Project is live',
|
||||
},
|
||||
};
|
||||
const style = indicatorStyles[status];
|
||||
|
||||
if (style) {
|
||||
return (
|
||||
<HoverCard openDelay={0}>
|
||||
<HoverCardTrigger asChild>
|
||||
<span
|
||||
className={cn('mt-[1px] h-2 w-2 rounded-full', style.className)}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent side="top" className="h-fit w-fit py-2">
|
||||
{style.description}
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function ProjectsComboBox() {
|
||||
const {
|
||||
query: { appSubdomain },
|
||||
push,
|
||||
} = useRouter();
|
||||
|
||||
const { state: appState } = useAppState();
|
||||
const { currentOrg: { slug: orgSlug, apps = [] } = {} } = useOrgs();
|
||||
const selectedProjectFromUrl = apps.find(
|
||||
(item) => item.subdomain === appSubdomain,
|
||||
);
|
||||
|
||||
const [selectedProject, setSelectedProject] = useState<Option | null>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const options: Option[] = apps.map((app) => ({
|
||||
label: app.name,
|
||||
value: app.subdomain,
|
||||
}));
|
||||
|
||||
const selectedProjectFromUrl = apps.find(
|
||||
(app) => app.subdomain === appSubdomain,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProjectFromUrl) {
|
||||
@@ -45,68 +111,64 @@ export default function ProjectsComboBox() {
|
||||
}
|
||||
}, [selectedProjectFromUrl]);
|
||||
|
||||
const options: Option[] = apps.map((app) => ({
|
||||
label: app.name,
|
||||
value: app.subdomain,
|
||||
}));
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleProjectSelect = (option: Option) => {
|
||||
setSelectedProject(option);
|
||||
setOpen(false);
|
||||
push(`/orgs/${orgSlug}/projects/${option.value}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="justify-start gap-2 bg-background text-foreground hover:bg-accent dark:hover:bg-muted"
|
||||
>
|
||||
{selectedProject ? (
|
||||
<div className="flex flex-row items-center justify-center gap-1">
|
||||
<Box className="h-4 w-4" />
|
||||
{selectedProject.label}
|
||||
<ProjectStatus />
|
||||
</div>
|
||||
) : (
|
||||
<>Select a project</>
|
||||
)}
|
||||
<ChevronsUpDown className="h-5 w-5 text-muted-foreground" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0" side="bottom" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="Select a project..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{options.map((option) => (
|
||||
<CommandItem
|
||||
keywords={[option.label]}
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
onSelect={() => {
|
||||
setSelectedProject(option);
|
||||
setOpen(false);
|
||||
push(`/orgs/${orgSlug}/projects/${option.value}`);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
'mr-2 h-4 w-4',
|
||||
selectedProject?.value === option.value
|
||||
? 'opacity-100'
|
||||
: 'opacity-0',
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
<Box className="h-4 w-4" />
|
||||
<span className="max-w-52 truncate">{option.label}</span>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<div className="flex items-center gap-1">
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="justify-start gap-2 bg-background text-foreground hover:bg-accent dark:hover:bg-muted"
|
||||
>
|
||||
{selectedProject ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<ProjectStatusIndicator status={appState} />
|
||||
{selectedProject.label}
|
||||
<ProjectStatus />
|
||||
</div>
|
||||
) : (
|
||||
<>Select a project</>
|
||||
)}
|
||||
<ChevronsUpDown className="h-5 w-5 text-muted-foreground" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0" side="bottom" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="Select a project..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{options.map((option) => (
|
||||
<CommandItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
onSelect={() => handleProjectSelect(option)}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
'mr-2 h-4 w-4',
|
||||
selectedProject?.value === option.value
|
||||
? 'opacity-100'
|
||||
: 'opacity-0',
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
<Box className="h-4 w-4" />
|
||||
<span className="max-w-52 truncate">{option.label}</span>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ const buttonVariants = cva(
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
'border bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { Badge } from '@/components/ui/v3/badge';
|
||||
import { Button } from '@/components/ui/v3/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/v3/dropdown-menu';
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
@@ -7,15 +14,64 @@ import {
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from '@/components/ui/v3/sheet';
|
||||
import { Announcements } from '@/features/projects/common/components/Announcements';
|
||||
import { Megaphone } from 'lucide-react';
|
||||
import {
|
||||
useDeleteAnnouncementReadMutation,
|
||||
useGetAnnouncementsQuery,
|
||||
useInsertAnnouncementReadMutation,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { useAuthenticationStatus } from '@nhost/nextjs';
|
||||
import { formatDistance } from 'date-fns';
|
||||
import { EllipsisVertical, Megaphone } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function AnnouncementsTray() {
|
||||
const { isAuthenticated } = useAuthenticationStatus();
|
||||
|
||||
const {
|
||||
data,
|
||||
loading,
|
||||
refetch: refetchAnnouncements,
|
||||
} = useGetAnnouncementsQuery({
|
||||
skip: !isAuthenticated,
|
||||
});
|
||||
|
||||
const [insertAnnouncementRead] = useInsertAnnouncementReadMutation();
|
||||
const [deleteAnnouncementRead] = useDeleteAnnouncementReadMutation();
|
||||
|
||||
const announcements = data?.announcements ?? [];
|
||||
const unreadAnnouncementsCount = announcements.filter(
|
||||
(ann) => ann.read.length === 0,
|
||||
).length;
|
||||
|
||||
const handleSetUnread = async (announcementReadId: string) => {
|
||||
await deleteAnnouncementRead({
|
||||
variables: {
|
||||
id: announcementReadId,
|
||||
},
|
||||
});
|
||||
|
||||
await refetchAnnouncements();
|
||||
};
|
||||
|
||||
const handleSetRead = async (announcementID: string) => {
|
||||
await insertAnnouncementRead({
|
||||
variables: { announcementID },
|
||||
});
|
||||
|
||||
await refetchAnnouncements();
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="ghost" className="relative h-fit px-3 py-1">
|
||||
<Megaphone className="h-5 w-5" />
|
||||
<Button
|
||||
variant="outline"
|
||||
className="relative flex h-8 items-center gap-2 px-2"
|
||||
>
|
||||
<Megaphone className="h-4.5 w-4.5" />
|
||||
{unreadAnnouncementsCount > 0 && (
|
||||
<Badge variant="destructive">{unreadAnnouncementsCount}</Badge>
|
||||
)}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="h-full w-full bg-background p-0 text-foreground sm:max-w-[310px]">
|
||||
@@ -25,8 +81,102 @@ export default function AnnouncementsTray() {
|
||||
Latest news and announcements.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
<div className="flex h-full w-full flex-col px-8 pt-3">
|
||||
<Announcements />
|
||||
<div className="flex h-full w-full flex-col">
|
||||
<div className="flex h-12 items-center border-b px-2">
|
||||
<h3 className="font-medium">
|
||||
Latest Announcements{' '}
|
||||
{unreadAnnouncementsCount > 0 && `(${unreadAnnouncementsCount})`}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex h-full flex-col gap-2 overflow-auto p-2">
|
||||
{!loading && announcements.length === 0 && (
|
||||
<span className="text-muted-foreground">
|
||||
No new announcements
|
||||
</span>
|
||||
)}
|
||||
{announcements.map((announcement) => (
|
||||
<Button
|
||||
key={announcement.id}
|
||||
variant="ghost"
|
||||
asChild
|
||||
className="h-fit w-full items-start gap-2 rounded-md border p-2"
|
||||
>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={announcement.href}
|
||||
shallow
|
||||
onClick={() => {
|
||||
if (announcement.read.length === 0) {
|
||||
handleSetRead(announcement.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{announcement.read.length === 0 ? (
|
||||
<span className="mt-[5px] h-2 w-2 flex-shrink-0 rounded-full bg-primary" />
|
||||
) : (
|
||||
<span className="mt-[5px] h-2 w-2 flex-shrink-0" />
|
||||
)}
|
||||
<div className="flex flex-row">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{formatDistance(
|
||||
new Date(announcement.createdAt),
|
||||
new Date(),
|
||||
{
|
||||
addSuffix: true,
|
||||
},
|
||||
)}
|
||||
</span>
|
||||
<p className="whitespace-normal">
|
||||
{announcement.content}
|
||||
</p>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-8 px-3"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div className="absolute">
|
||||
<EllipsisVertical className="h-4 w-4" />
|
||||
</div>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
side="bottom"
|
||||
align="end"
|
||||
sideOffset={-5}
|
||||
>
|
||||
<DropdownMenuItem
|
||||
disabled={announcement.read.length > 0}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleSetRead(announcement.id);
|
||||
}}
|
||||
>
|
||||
Mark as read
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
disabled={announcement.read.length === 0}
|
||||
onClick={() =>
|
||||
handleSetUnread(announcement.read.at(0).id)
|
||||
}
|
||||
>
|
||||
Mark as unread
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</Link>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
useOrganizationMemberInvitesLazyQuery,
|
||||
useOrganizationNewRequestsLazyQuery,
|
||||
usePostOrganizationRequestMutation,
|
||||
type OrganizationMemberInvitesQuery,
|
||||
type PostOrganizationRequestResponse,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { useUserData } from '@nhost/nextjs';
|
||||
@@ -33,10 +34,13 @@ import { Bell } from 'lucide-react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
type Invite = OrganizationMemberInvitesQuery['organizationMemberInvites'][0];
|
||||
|
||||
export default function NotificationsTray() {
|
||||
const userData = useUserData();
|
||||
const { asPath, route } = useRouter();
|
||||
const { asPath, route, push } = useRouter();
|
||||
const { refetch: refetchOrgs } = useOrgs();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const [stripeFormDialogOpen, setStripeFormDialogOpen] = useState(false);
|
||||
|
||||
@@ -113,17 +117,19 @@ export default function NotificationsTray() {
|
||||
const [acceptInvite] = useOrganizationMemberInviteAcceptMutation();
|
||||
const [deleteInvite] = useDeleteOrganizationMemberInviteMutation();
|
||||
|
||||
const handleAccept = async (inviteId: string) => {
|
||||
const handleAccept = async (invite: Invite) => {
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await acceptInvite({
|
||||
variables: {
|
||||
inviteId,
|
||||
inviteId: invite.id,
|
||||
},
|
||||
});
|
||||
|
||||
refetchInvites();
|
||||
refetchOrgs();
|
||||
await refetchInvites();
|
||||
await refetchOrgs();
|
||||
await push(`/orgs/${invite?.organization?.slug}/projects`);
|
||||
setOpen(false);
|
||||
},
|
||||
{
|
||||
loadingMessage: `Accepting invite...`,
|
||||
@@ -154,12 +160,18 @@ export default function NotificationsTray() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Sheet>
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="ghost" className="relative h-fit px-3 py-1">
|
||||
<Bell className="mt-[2px] h-[1.15rem] w-[1.15rem]" />
|
||||
{(pendingOrgRequest || Boolean(invites.length)) && (
|
||||
<div className="absolute right-3 top-2 h-2 w-2 rounded-full bg-red-500" />
|
||||
<Button
|
||||
variant="outline"
|
||||
className="relative flex h-8 items-center gap-2 border px-2"
|
||||
aria-label="Notifications"
|
||||
>
|
||||
<Bell className="h-4.5 w-4.5" />
|
||||
{(pendingOrgRequest || invites.length > 0) && (
|
||||
<Badge variant="destructive">
|
||||
{invites.length + (pendingOrgRequest ? 1 : 0)}
|
||||
</Badge>
|
||||
)}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
@@ -248,7 +260,7 @@ export default function NotificationsTray() {
|
||||
</Button>
|
||||
<Button
|
||||
className="h-fit"
|
||||
onClick={() => handleAccept(invite.id)}
|
||||
onClick={() => handleAccept(invite)}
|
||||
>
|
||||
Accept
|
||||
</Button>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Alert } from '@/components/ui/v2/Alert';
|
||||
import type { BoxProps } from '@/components/ui/v2/Box';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { ApplicationPaused } from '@/features/orgs/projects/common/components/ApplicationPaused';
|
||||
import { ApplicationPausedBanner } from '@/features/orgs/projects/common/components/ApplicationPausedBanner';
|
||||
import { ApplicationProvisioning } from '@/features/orgs/projects/common/components/ApplicationProvisioning';
|
||||
import { ApplicationRestoring } from '@/features/orgs/projects/common/components/ApplicationRestoring';
|
||||
import { ApplicationUnknown } from '@/features/orgs/projects/common/components/ApplicationUnknown';
|
||||
@@ -15,7 +16,7 @@ import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { NextSeo } from 'next-seo';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useMemo, type ReactNode } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export interface ProjectLayoutProps extends AuthenticatedLayoutProps {
|
||||
@@ -34,11 +35,53 @@ function ProjectLayoutContent({
|
||||
query: { appSubdomain },
|
||||
} = useRouter();
|
||||
|
||||
const isPlatform = useIsPlatform();
|
||||
const { state } = useAppState();
|
||||
const isPlatform = useIsPlatform();
|
||||
const { project, loading, error } = useProject({ poll: true });
|
||||
|
||||
const isOnOverviewPage = route === '/orgs/[orgSlug]/projects/[appSubdomain]';
|
||||
|
||||
const renderPausedProjectContent = useCallback(
|
||||
(_children: ReactNode) => {
|
||||
const baseProjectPageRoute = '/orgs/[orgSlug]/projects/[appSubdomain]/';
|
||||
const blockedPausedProjectPages = [
|
||||
'database',
|
||||
'database/browser/[dataSourceSlug]',
|
||||
'graphql',
|
||||
'hasura',
|
||||
'users',
|
||||
'storage',
|
||||
'ai/auto-embeddings',
|
||||
'ai/assistants',
|
||||
'metrics',
|
||||
].map((page) => baseProjectPageRoute.concat(page));
|
||||
|
||||
// show an alert box on top of the overview page with a wake up button
|
||||
if (isOnOverviewPage) {
|
||||
return (
|
||||
<>
|
||||
<div className="mx-auto mt-5 flex max-w-7xl p-4 pb-0">
|
||||
<ApplicationPausedBanner
|
||||
alertClassName="flex-row"
|
||||
textContainerClassName="flex flex-col items-center justify-center text-left"
|
||||
wakeUpButtonClassName="w-fit self-center"
|
||||
/>
|
||||
</div>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// block these pages when the project is paused
|
||||
if (blockedPausedProjectPages.includes(route)) {
|
||||
return <ApplicationPaused />;
|
||||
}
|
||||
|
||||
return _children;
|
||||
},
|
||||
[route, isOnOverviewPage, children],
|
||||
);
|
||||
|
||||
// Render application state based on the current state
|
||||
const projectPageContent = useMemo(() => {
|
||||
if (!appSubdomain || state === undefined) {
|
||||
@@ -67,7 +110,7 @@ function ProjectLayoutContent({
|
||||
return children;
|
||||
case ApplicationStatus.Pausing:
|
||||
case ApplicationStatus.Paused:
|
||||
return <ApplicationPaused />;
|
||||
return renderPausedProjectContent(children);
|
||||
case ApplicationStatus.Unpausing:
|
||||
return <ApplicationUnpausing />;
|
||||
case ApplicationStatus.Restoring:
|
||||
@@ -79,7 +122,13 @@ function ProjectLayoutContent({
|
||||
default:
|
||||
return <ApplicationUnknown />;
|
||||
}
|
||||
}, [state, children, appSubdomain, isOnOverviewPage]);
|
||||
}, [
|
||||
state,
|
||||
children,
|
||||
appSubdomain,
|
||||
isOnOverviewPage,
|
||||
renderPausedProjectContent,
|
||||
]);
|
||||
|
||||
// Handle loading state
|
||||
if (loading) {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { Container } from '@/components/layout/Container';
|
||||
import { Modal } from '@/components/ui/v1/Modal';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { TransferProjectDialog } from '@/features/orgs/components/common/TransferProjectDialog';
|
||||
import { ApplicationInfo } from '@/features/orgs/projects/common/components/ApplicationInfo';
|
||||
import { ApplicationLockedReason } from '@/features/orgs/projects/common/components/ApplicationLockedReason';
|
||||
import { ApplicationPausedReason } from '@/features/orgs/projects/common/components/ApplicationPausedReason';
|
||||
import { ApplicationPausedSymbol } from '@/features/orgs/projects/common/components/ApplicationPausedSymbol';
|
||||
import { ApplicationPausedBanner } from '@/features/orgs/projects/common/components/ApplicationPausedBanner';
|
||||
import { RemoveApplicationModal } from '@/features/orgs/projects/common/components/RemoveApplicationModal';
|
||||
import { StagingMetadata } from '@/features/orgs/projects/common/components/StagingMetadata';
|
||||
import { useAppPausedReason } from '@/features/orgs/projects/common/hooks/useAppPausedReason';
|
||||
import { useIsCurrentUserOwner } from '@/features/orgs/projects/common/hooks/useIsCurrentUserOwner';
|
||||
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
@@ -19,46 +13,19 @@ import {
|
||||
GetAllWorkspacesAndProjectsDocument,
|
||||
useUnpauseApplicationMutation,
|
||||
} from '@/generated/graphql';
|
||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function ApplicationPaused() {
|
||||
const { org } = useCurrentOrg();
|
||||
const { project, refetch: refetchProject } = useProject();
|
||||
const { project } = useProject();
|
||||
const isOwner = useIsCurrentUserOwner();
|
||||
const [transferProjectDialogOpen, setTransferProjectDialogOpen] =
|
||||
useState(false);
|
||||
|
||||
const [showDeletingModal, setShowDeletingModal] = useState(false);
|
||||
const [unpauseApplication, { loading: changingApplicationStateLoading }] =
|
||||
useUnpauseApplicationMutation({
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
|
||||
const { isLocked, lockedReason, freeAndLiveProjectsNumberExceeded, loading } =
|
||||
useAppPausedReason();
|
||||
|
||||
async function handleTriggerUnpausing() {
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await unpauseApplication({ variables: { appId: project.id } });
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
await refetchProject();
|
||||
},
|
||||
{
|
||||
loadingMessage: 'Starting the project...',
|
||||
successMessage: 'The project has been started successfully.',
|
||||
errorMessage:
|
||||
'An error occurred while waking up the project. Please try again.',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <ActivityIndicator label="Loading user data..." delay={1000} />;
|
||||
}
|
||||
useUnpauseApplicationMutation({
|
||||
refetchQueries: [{ query: GetAllWorkspacesAndProjectsDocument }],
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -77,65 +44,38 @@ export default function ApplicationPaused() {
|
||||
</Modal>
|
||||
|
||||
<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>
|
||||
|
||||
<Box className="grid grid-flow-row gap-6">
|
||||
<Text variant="h3" component="h1">
|
||||
{project.name} is {isLocked ? 'locked' : 'paused'}
|
||||
</Text>
|
||||
{isLocked ? (
|
||||
<ApplicationLockedReason reason={lockedReason} />
|
||||
) : (
|
||||
<div className="mx-auto flex w-full max-w-xs flex-col gap-4">
|
||||
<ApplicationPausedBanner
|
||||
alertClassName="items-center"
|
||||
textContainerClassName="items-center text-center"
|
||||
/>
|
||||
{org && (
|
||||
<>
|
||||
<ApplicationPausedReason
|
||||
freeAndLiveProjectsNumberExceeded={
|
||||
freeAndLiveProjectsNumberExceeded
|
||||
}
|
||||
/>
|
||||
<div className="grid grid-flow-row gap-4">
|
||||
{org && (
|
||||
<>
|
||||
<Button
|
||||
className="mx-auto w-full max-w-xs"
|
||||
onClick={() => setTransferProjectDialogOpen(true)}
|
||||
>
|
||||
Transfer
|
||||
</Button>
|
||||
<TransferProjectDialog
|
||||
open={transferProjectDialogOpen}
|
||||
setOpen={setTransferProjectDialogOpen}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
variant="borderless"
|
||||
className="mx-auto w-full max-w-xs"
|
||||
loading={changingApplicationStateLoading}
|
||||
disabled={
|
||||
changingApplicationStateLoading ||
|
||||
freeAndLiveProjectsNumberExceeded
|
||||
}
|
||||
onClick={handleTriggerUnpausing}
|
||||
>
|
||||
Wake Up
|
||||
</Button>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={() => setTransferProjectDialogOpen(true)}
|
||||
>
|
||||
Transfer
|
||||
</Button>
|
||||
|
||||
{isOwner && (
|
||||
<Button
|
||||
color="error"
|
||||
variant="outlined"
|
||||
className="mx-auto w-full max-w-xs"
|
||||
onClick={() => setShowDeletingModal(true)}
|
||||
>
|
||||
Delete Project
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<TransferProjectDialog
|
||||
open={transferProjectDialogOpen}
|
||||
setOpen={setTransferProjectDialogOpen}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{isOwner && (
|
||||
<Button
|
||||
color="error"
|
||||
variant="outlined"
|
||||
className="mx-auto w-full max-w-xs"
|
||||
onClick={() => setShowDeletingModal(true)}
|
||||
>
|
||||
Delete Project
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<StagingMetadata>
|
||||
<ApplicationInfo />
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import { Alert } from '@/components/ui/v2/Alert';
|
||||
import { Button } from '@/components/ui/v3/button';
|
||||
import { useAppPausedReason } from '@/features/orgs/projects/common/hooks/useAppPausedReason';
|
||||
import { useAppState } from '@/features/orgs/projects/common/hooks/useAppState';
|
||||
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { useUnpauseApplicationMutation } from '@/utils/__generated__/graphql';
|
||||
import Image from 'next/image';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export default function ApplicationPausedBanner({
|
||||
alertClassName,
|
||||
textContainerClassName,
|
||||
wakeUpButtonClassName,
|
||||
}: {
|
||||
alertClassName?: string;
|
||||
textContainerClassName?: string;
|
||||
wakeUpButtonClassName?: string;
|
||||
}) {
|
||||
const { org } = useCurrentOrg();
|
||||
const { state } = useAppState();
|
||||
const { freeAndLiveProjectsNumberExceeded } = useAppPausedReason();
|
||||
const { project, refetch: refetchProject } = useProject();
|
||||
|
||||
const [unpauseApplication, { loading: changingApplicationStateLoading }] =
|
||||
useUnpauseApplicationMutation({
|
||||
variables: {
|
||||
appId: project?.id,
|
||||
},
|
||||
});
|
||||
|
||||
const handleTriggerUnpausing = useCallback(async () => {
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await unpauseApplication({ variables: { appId: project.id } });
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
await refetchProject();
|
||||
},
|
||||
{
|
||||
loadingMessage: 'Starting the project...',
|
||||
successMessage: 'The project has been started successfully.',
|
||||
errorMessage:
|
||||
'An error occurred while waking up the project. Please try again.',
|
||||
},
|
||||
);
|
||||
}, [unpauseApplication, project?.id, refetchProject]);
|
||||
|
||||
return (
|
||||
<Alert
|
||||
severity="warning"
|
||||
className={cn(
|
||||
'flex w-full flex-col items-start justify-between gap-4 p-4',
|
||||
alertClassName,
|
||||
)}
|
||||
>
|
||||
<Image
|
||||
src="/assets/PausedApp.svg"
|
||||
className="mt-1"
|
||||
alt="Closed Eye"
|
||||
width={52}
|
||||
height={40}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-full w-full flex-col gap-2',
|
||||
textContainerClassName,
|
||||
)}
|
||||
>
|
||||
<p className="w-full">
|
||||
Project <b>{project?.name}</b> is paused.
|
||||
</p>
|
||||
<p className="w-full">
|
||||
Wake up your project to make it accessible again. Once reactivated,
|
||||
all features will be fully functional. Go to settings to manage your
|
||||
project.
|
||||
</p>
|
||||
{org?.plan?.isFree && (
|
||||
<p>
|
||||
Projects under your Personal Organization will stop responding to
|
||||
API calls after 7 days of inactivity, so consider transferring the
|
||||
project to a <b>Pro Organization</b> to avoid auto-sleep.
|
||||
</p>
|
||||
)}
|
||||
{freeAndLiveProjectsNumberExceeded && (
|
||||
<p>
|
||||
Additionally, only 1 free project can be active at any given time,
|
||||
so please pause your current active free project before unpausing
|
||||
another.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{state === ApplicationStatus.Paused && (
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn('w-full', wakeUpButtonClassName)}
|
||||
disabled={changingApplicationStateLoading}
|
||||
onClick={handleTriggerUnpausing}
|
||||
>
|
||||
{changingApplicationStateLoading ? <ActivityIndicator /> : 'Wake up'}
|
||||
</Button>
|
||||
)}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as ApplicationPausedBanner } from './ApplicationPausedBanner';
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Alert } from '@/components/ui/v2/Alert';
|
||||
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
|
||||
interface ApplicationPausedReasonProps {
|
||||
freeAndLiveProjectsNumberExceeded?: boolean;
|
||||
}
|
||||
|
||||
export default function ApplicationPausedReason({
|
||||
freeAndLiveProjectsNumberExceeded,
|
||||
}: ApplicationPausedReasonProps) {
|
||||
const { org } = useCurrentOrg();
|
||||
|
||||
return (
|
||||
<Alert
|
||||
severity="warning"
|
||||
className="flex flex-col w-full max-w-xs gap-4 p-6 mx-auto text-left"
|
||||
>
|
||||
{org?.plan?.isFree ? (
|
||||
<p>
|
||||
Projects under your Personal Organization will stop responding to API
|
||||
calls after 7 days of inactivity, so consider transferring the project
|
||||
to a <b>Pro Organization</b> to avoid auto-sleep.
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-center">Your project is Paused.</p>
|
||||
)}
|
||||
{freeAndLiveProjectsNumberExceeded && (
|
||||
<p className="text-center">
|
||||
Additionally, only 1 free project can be active at any given time, so
|
||||
please pause your current active free project before unpausing
|
||||
another.
|
||||
</p>
|
||||
)}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { default as ApplicationPausedReason } from './ApplicationPausedReason';
|
||||
@@ -48,7 +48,7 @@ export default function StagingMetadata({
|
||||
}: PropsWithChildren<unknown>) {
|
||||
return (
|
||||
isDevOrStaging() && (
|
||||
<div className="mx-auto mt-10 max-w-sm">
|
||||
<div className="mx-auto max-w-sm">
|
||||
<Box className="mx-auto grid grid-flow-row justify-items-center rounded-md border p-5 text-center">
|
||||
<Status status={StatusEnum.Deploying}>Internal info</Status>
|
||||
{children}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
import { useUI } from '@/components/common/UIProvider';
|
||||
import { StateBadge } from '@/components/presentational/StateBadge';
|
||||
import type { DeploymentStatus } from '@/components/presentational/StatusCircle';
|
||||
import { StatusCircle } from '@/components/presentational/StatusCircle';
|
||||
import type { BoxProps } from '@/components/ui/v2/Box';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import type { ButtonProps } from '@/components/ui/v2/Button';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Divider } from '@/components/ui/v2/Divider';
|
||||
import { PlusCircleIcon } from '@/components/ui/v2/icons/PlusCircleIcon';
|
||||
import { SearchIcon } from '@/components/ui/v2/icons/SearchIcon';
|
||||
import type { InputProps } from '@/components/ui/v2/Input';
|
||||
import { Input } from '@/components/ui/v2/Input';
|
||||
import { Link } from '@/components/ui/v2/Link';
|
||||
import { List } from '@/components/ui/v2/List';
|
||||
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { DeploymentStatusMessage } from '@/features/projects/deployments/components/DeploymentStatusMessage';
|
||||
import type { ApplicationState, Workspace } from '@/types/application';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { getApplicationStatusString } from '@/utils/helpers';
|
||||
import debounce from 'lodash.debounce';
|
||||
import Image from 'next/image';
|
||||
import NavLink from 'next/link';
|
||||
import type { ChangeEvent, PropsWithoutRef } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export interface WorkspaceAndProjectListProps extends BoxProps {
|
||||
/**
|
||||
* List of workspaces to be displayed.
|
||||
*/
|
||||
workspaces: Workspace[];
|
||||
/**
|
||||
* Props to be passed to individual slots.
|
||||
*/
|
||||
slotProps?: {
|
||||
root?: BoxProps;
|
||||
header?: BoxProps;
|
||||
search?: PropsWithoutRef<InputProps>;
|
||||
button?: PropsWithoutRef<ButtonProps>;
|
||||
};
|
||||
}
|
||||
|
||||
function checkStatusOfTheApplication(stateHistory: ApplicationState[] | []) {
|
||||
if (stateHistory.length === 0) {
|
||||
return ApplicationStatus.Empty;
|
||||
}
|
||||
|
||||
if (stateHistory[0].stateId === undefined) {
|
||||
return ApplicationStatus.Empty;
|
||||
}
|
||||
|
||||
return stateHistory[0].stateId;
|
||||
}
|
||||
|
||||
export default function WorkspaceAndProjectList({
|
||||
workspaces,
|
||||
className,
|
||||
slotProps = {},
|
||||
...props
|
||||
}: WorkspaceAndProjectListProps) {
|
||||
const [query, setQuery] = useState('');
|
||||
const { maintenanceActive } = useUI();
|
||||
|
||||
const handleQueryChange = debounce((event: ChangeEvent<HTMLInputElement>) => {
|
||||
slotProps?.search?.onChange?.(event);
|
||||
setQuery(event.target.value);
|
||||
}, 500);
|
||||
|
||||
const filteredWorkspaces = workspaces
|
||||
.map((workspace) => ({
|
||||
...workspace,
|
||||
projects: workspace.projects.filter((project) =>
|
||||
project.name.toLowerCase().includes(query.toLowerCase()),
|
||||
),
|
||||
}))
|
||||
.filter((workspace) => workspace.projects.length > 0);
|
||||
|
||||
return (
|
||||
<Box
|
||||
{...props}
|
||||
{...slotProps.root}
|
||||
className={twMerge(
|
||||
'grid grid-flow-row content-start gap-4',
|
||||
className,
|
||||
slotProps.root?.className,
|
||||
)}
|
||||
>
|
||||
<Box
|
||||
{...slotProps.header}
|
||||
className={twMerge(
|
||||
'grid grid-flow-col place-content-between items-center',
|
||||
slotProps.header?.className,
|
||||
)}
|
||||
>
|
||||
<Text variant="h2" component="h1" className="hidden md:block">
|
||||
My Projects
|
||||
</Text>
|
||||
|
||||
<Input
|
||||
placeholder="Find Project"
|
||||
startAdornment={
|
||||
<SearchIcon
|
||||
className="w-4 h-4 ml-2 -mr-1 shrink-0"
|
||||
sx={{ color: 'text.disabled' }}
|
||||
/>
|
||||
}
|
||||
{...slotProps.search}
|
||||
onChange={handleQueryChange}
|
||||
/>
|
||||
|
||||
<NavLink href="/new" passHref legacyBehavior>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
startIcon={<PlusCircleIcon />}
|
||||
disabled={maintenanceActive}
|
||||
{...slotProps.button}
|
||||
>
|
||||
New Project
|
||||
</Button>
|
||||
</NavLink>
|
||||
</Box>
|
||||
|
||||
<Box className="grid grid-flow-row gap-8 my-8">
|
||||
{filteredWorkspaces.map((workspace) => (
|
||||
<div key={workspace.slug}>
|
||||
<NavLink href={`/${workspace.slug}`} passHref legacyBehavior>
|
||||
<Link
|
||||
href={`${workspace.slug}`}
|
||||
className="mb-1.5 block font-medium"
|
||||
underline="none"
|
||||
sx={{ color: 'text.primary' }}
|
||||
>
|
||||
{workspace.name}
|
||||
</Link>
|
||||
</NavLink>
|
||||
|
||||
<List className="grid grid-flow-row border-y">
|
||||
{workspace.projects.map((project, index) => {
|
||||
const [latestDeployment] = project.deployments;
|
||||
|
||||
return (
|
||||
<Fragment key={project.slug}>
|
||||
<ListItem.Root
|
||||
secondaryAction={
|
||||
<div className="grid grid-flow-col gap-px">
|
||||
{latestDeployment && (
|
||||
<div className="flex self-center mr-2 align-middle">
|
||||
<StatusCircle
|
||||
status={
|
||||
latestDeployment.deploymentStatus as DeploymentStatus
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<StateBadge
|
||||
state={checkStatusOfTheApplication(
|
||||
project.appStates,
|
||||
)}
|
||||
desiredState={project.desiredState}
|
||||
title={getApplicationStatusString(
|
||||
checkStatusOfTheApplication(project.appStates),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<NavLink
|
||||
href={`${workspace?.slug}/${project.slug}`}
|
||||
passHref
|
||||
className='w-full'
|
||||
legacyBehavior>
|
||||
<ListItem.Button className="rounded-none">
|
||||
<ListItem.Avatar>
|
||||
<div className="w-10 h-10 overflow-hidden rounded-lg">
|
||||
<Image
|
||||
src="/logos/new.svg"
|
||||
alt="Nhost Logo"
|
||||
width={40}
|
||||
height={40}
|
||||
/>
|
||||
</div>
|
||||
</ListItem.Avatar>
|
||||
|
||||
<ListItem.Text
|
||||
primary={project.name}
|
||||
secondary={
|
||||
<DeploymentStatusMessage
|
||||
appCreatedAt={project.createdAt}
|
||||
deployment={latestDeployment}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</ListItem.Button>
|
||||
</NavLink>
|
||||
</ListItem.Root>
|
||||
|
||||
{index < workspace.projects.length - 1 && (
|
||||
<Divider component="li" role="listitem" />
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</div>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './WorkspaceAndProjectList';
|
||||
export { default as WorkspaceAndProjectList } from './WorkspaceAndProjectList';
|
||||
@@ -1,43 +0,0 @@
|
||||
import { ArrowSquareOutIcon } from '@/components/ui/v2/icons/ArrowSquareOutIcon';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { useTheme } from '@mui/material';
|
||||
import Image from 'next/image';
|
||||
|
||||
export interface ResourceProps {
|
||||
text: string;
|
||||
logo: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export default function Resource({ text, logo, link }: ResourceProps) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<a
|
||||
href={link}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="flex justify-between py-1 align-middle"
|
||||
>
|
||||
<div className="flex items-center align-middle">
|
||||
<Image
|
||||
src={
|
||||
theme.palette.mode === 'dark'
|
||||
? `/logos/light/${logo}.svg`
|
||||
: `/logos/${logo}.svg`
|
||||
}
|
||||
alt={text}
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
|
||||
<Text className="ml-2 inline-flex self-center align-middle font-medium">
|
||||
{text}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex self-center">
|
||||
<ArrowSquareOutIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||
import type { BoxProps } from '@/components/ui/v2/Box';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { GitHubIcon } from '@/components/ui/v2/icons/GitHubIcon';
|
||||
import { PlusCircleIcon } from '@/components/ui/v2/icons/PlusCircleIcon';
|
||||
import { List } from '@/components/ui/v2/List';
|
||||
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { Announcements } from '@/features/projects/common/components/Announcements';
|
||||
import { EditWorkspaceNameForm } from '@/features/projects/workspaces/components/EditWorkspaceNameForm';
|
||||
import type { Workspace } from '@/types/application';
|
||||
import Image from 'next/image';
|
||||
import NavLink from 'next/link';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Resource from './Resource';
|
||||
|
||||
export interface WorkspaceSidebarProps extends BoxProps {
|
||||
/**
|
||||
* List of workspaces to be displayed.
|
||||
*/
|
||||
workspaces: Workspace[];
|
||||
}
|
||||
|
||||
export default function WorkspaceSidebar({
|
||||
className,
|
||||
workspaces,
|
||||
...props
|
||||
}: WorkspaceSidebarProps) {
|
||||
const { openDialog } = useDialog();
|
||||
|
||||
return (
|
||||
<Box
|
||||
component="aside"
|
||||
className={twMerge(
|
||||
'grid w-full grid-flow-row content-start gap-8 md:grid',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<Announcements />
|
||||
|
||||
<section className="grid grid-flow-row gap-2">
|
||||
<Text color="secondary">My Workspaces</Text>
|
||||
|
||||
{workspaces.length > 0 ? (
|
||||
<List className="grid grid-flow-row gap-2">
|
||||
{workspaces.map(({ id, name, slug }) => (
|
||||
<ListItem.Root key={id}>
|
||||
<NavLink href={`/${slug}`} passHref className='w-full' legacyBehavior>
|
||||
<ListItem.Button
|
||||
dense
|
||||
aria-label={`View ${name}`}
|
||||
className="!p-1"
|
||||
>
|
||||
<ListItem.Avatar className="w-8 h-8">
|
||||
<div className="inline-block w-8 h-8 overflow-hidden rounded-lg">
|
||||
<Image
|
||||
src="/logos/new.svg"
|
||||
alt="Nhost Logo"
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
</div>
|
||||
</ListItem.Avatar>
|
||||
<ListItem.Text primary={name} />
|
||||
</ListItem.Button>
|
||||
</NavLink>
|
||||
</ListItem.Root>
|
||||
))}
|
||||
</List>
|
||||
) : (
|
||||
<ActivityIndicator
|
||||
label="Creating your first workspace..."
|
||||
className="py-1"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="borderless"
|
||||
color="secondary"
|
||||
startIcon={<PlusCircleIcon />}
|
||||
className="justify-self-start"
|
||||
onClick={() => {
|
||||
openDialog({
|
||||
title: (
|
||||
<span className="grid grid-flow-row">
|
||||
<span>New Workspace</span>
|
||||
|
||||
<Text variant="subtitle1" component="span">
|
||||
Invite team members to workspaces to work collaboratively.
|
||||
</Text>
|
||||
</span>
|
||||
),
|
||||
component: <EditWorkspaceNameForm />,
|
||||
});
|
||||
}}
|
||||
>
|
||||
New Workspace
|
||||
</Button>
|
||||
</section>
|
||||
|
||||
<section className="grid grid-flow-row gap-2">
|
||||
<Text color="secondary">Resources</Text>
|
||||
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<Resource
|
||||
text="Documentation"
|
||||
logo="Note"
|
||||
link="https://docs.nhost.io"
|
||||
/>
|
||||
<Resource
|
||||
text="JavaScript Client"
|
||||
logo="js"
|
||||
link="https://docs.nhost.io/reference/javascript/"
|
||||
/>
|
||||
<Resource
|
||||
text="Nhost CLI"
|
||||
logo="CLI"
|
||||
link="https://docs.nhost.io/platform/cli"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="grid grid-flow-row gap-2">
|
||||
<NavLink
|
||||
href="https://github.com/nhost/nhost"
|
||||
passHref
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
legacyBehavior>
|
||||
<Button
|
||||
className="grid w-full grid-flow-col gap-1"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
startIcon={<GitHubIcon />}
|
||||
>
|
||||
Star us on GitHub
|
||||
</Button>
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
href="https://discord.com/invite/9V7Qb2U"
|
||||
passHref
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
legacyBehavior>
|
||||
<Button
|
||||
className="grid w-full grid-flow-col gap-1"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
aria-labelledby="discord-button-label"
|
||||
>
|
||||
<Image
|
||||
src="/assets/brands/discord.svg"
|
||||
alt="Discord Logo"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
|
||||
<span id="discord-button-label">Join Discord</span>
|
||||
</Button>
|
||||
</NavLink>
|
||||
</section>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { default as WorkspaceSidebar } from './WorkspaceSidebar';
|
||||
@@ -25,7 +25,7 @@ export default function useAppPausedReason(): {
|
||||
});
|
||||
|
||||
const { data: isLockedData } = useGetProjectIsLockedQuery({
|
||||
variables: { appId: project.id },
|
||||
variables: { appId: project?.id },
|
||||
skip: !project,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import { Alert } from '@/components/ui/v2/Alert';
|
||||
import { XIcon } from '@/components/ui/v2/icons/XIcon';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { useAppState } from '@/features/orgs/projects/common/hooks/useAppState';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
|
||||
export default function DatabaseMigrateWarning() {
|
||||
const { state } = useAppState();
|
||||
|
||||
if (
|
||||
state === ApplicationStatus.Paused ||
|
||||
state === ApplicationStatus.Pausing
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert severity="error" className="flex flex-col gap-3 text-left">
|
||||
<Text
|
||||
|
||||
@@ -316,7 +316,7 @@ export default function DatabaseServiceVersionSettings() {
|
||||
size="medium"
|
||||
className="self-center"
|
||||
onClick={openLatestUpgradeLogsModal}
|
||||
startIcon={<RepeatIcon className="w-4 h-4" />}
|
||||
startIcon={<RepeatIcon className="h-4 w-4" />}
|
||||
>
|
||||
View latest upgrade logs
|
||||
</Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useUI } from '@/components/common/UIProvider';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { CogIcon } from '@/components/ui/v2/icons/CogIcon';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { Button } from '@/components/ui/v3/button';
|
||||
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||
@@ -94,12 +94,13 @@ export default function OverviewTopBar() {
|
||||
legacyBehavior
|
||||
>
|
||||
<Button
|
||||
endIcon={<CogIcon className="h-4 w-4" />}
|
||||
variant="outlined"
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
color="secondary"
|
||||
disabled={maintenanceActive}
|
||||
>
|
||||
Settings
|
||||
<CogIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { List } from '@/components/ui/v2/List';
|
||||
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { useGetAnnouncementsQuery } from '@/utils/__generated__/graphql';
|
||||
import formatDistance from 'date-fns/formatDistance';
|
||||
|
||||
export default function Announcements() {
|
||||
const { data, loading, error } = useGetAnnouncementsQuery({
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
|
||||
const announcements = data?.announcements || [];
|
||||
|
||||
if (loading || error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Text color="secondary" className="mb-2">
|
||||
Latest announcements
|
||||
</Text>
|
||||
|
||||
<List className="relative space-y-4 border-l border-gray-200 dark:border-gray-700">
|
||||
{announcements.map((item) => (
|
||||
<ListItem.Root key={item.id} className="ml-4">
|
||||
<div className="flex flex-col">
|
||||
<time className="mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500">
|
||||
{formatDistance(new Date(item.createdAt), new Date(), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
</time>
|
||||
<a href={item.href} target="_blank" rel="noopener noreferrer">
|
||||
<ListItem.Button
|
||||
dense
|
||||
aria-label={`View ${item.content}`}
|
||||
className="!p-1"
|
||||
>
|
||||
<p className="text-sm">{item.content}</p>
|
||||
</ListItem.Button>
|
||||
</a>
|
||||
</div>
|
||||
<div className="absolute top-[0.15rem] -ml-[1.4rem] h-3 w-3 rounded-full border border-white bg-gray-200 dark:border-gray-900 dark:bg-gray-700" />
|
||||
</ListItem.Root>
|
||||
))}
|
||||
</List>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { default as Announcements } from './Announcements';
|
||||
@@ -8,7 +8,6 @@ import { PlusCircleIcon } from '@/components/ui/v2/icons/PlusCircleIcon';
|
||||
import { List } from '@/components/ui/v2/List';
|
||||
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { Announcements } from '@/features/projects/common/components/Announcements';
|
||||
import { EditWorkspaceNameForm } from '@/features/projects/workspaces/components/EditWorkspaceNameForm';
|
||||
import type { Workspace } from '@/types/application';
|
||||
import Image from 'next/image';
|
||||
@@ -39,8 +38,6 @@ export default function WorkspaceSidebar({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<Announcements />
|
||||
|
||||
<section className="grid grid-flow-row gap-2">
|
||||
<Text color="secondary">My Workspaces</Text>
|
||||
|
||||
@@ -48,14 +45,19 @@ export default function WorkspaceSidebar({
|
||||
<List className="grid grid-flow-row gap-2">
|
||||
{workspaces.map(({ id, name, slug }) => (
|
||||
<ListItem.Root key={id}>
|
||||
<NavLink href={`/${slug}`} passHref className='w-full' legacyBehavior>
|
||||
<NavLink
|
||||
href={`/${slug}`}
|
||||
passHref
|
||||
className="w-full"
|
||||
legacyBehavior
|
||||
>
|
||||
<ListItem.Button
|
||||
dense
|
||||
aria-label={`View ${name}`}
|
||||
className="!p-1"
|
||||
>
|
||||
<ListItem.Avatar className="w-8 h-8">
|
||||
<div className="inline-block w-8 h-8 overflow-hidden rounded-lg">
|
||||
<ListItem.Avatar className="h-8 w-8">
|
||||
<div className="inline-block h-8 w-8 overflow-hidden rounded-lg">
|
||||
<Image
|
||||
src="/logos/new.svg"
|
||||
alt="Nhost Logo"
|
||||
@@ -129,7 +131,8 @@ export default function WorkspaceSidebar({
|
||||
passHref
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
legacyBehavior>
|
||||
legacyBehavior
|
||||
>
|
||||
<Button
|
||||
className="grid w-full grid-flow-col gap-1"
|
||||
variant="outlined"
|
||||
@@ -145,7 +148,8 @@ export default function WorkspaceSidebar({
|
||||
passHref
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
legacyBehavior>
|
||||
legacyBehavior
|
||||
>
|
||||
<Button
|
||||
className="grid w-full grid-flow-col gap-1"
|
||||
variant="outlined"
|
||||
|
||||
5
dashboard/src/gql/platform/deleteAnnouncementRead.gql
Normal file
5
dashboard/src/gql/platform/deleteAnnouncementRead.gql
Normal file
@@ -0,0 +1,5 @@
|
||||
mutation deleteAnnouncementRead($id: uuid!) {
|
||||
deleteAnnouncementRead(id: $id) {
|
||||
announcementID
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
query getAnnouncements($limit: Int) {
|
||||
query getAnnouncements {
|
||||
announcements(
|
||||
order_by: { createdAt: desc }
|
||||
limit: $limit
|
||||
where: {
|
||||
_or: [{ expiresAt: { _is_null: true } }, { expiresAt: { _gt: now } }]
|
||||
}
|
||||
@@ -10,5 +9,8 @@ query getAnnouncements($limit: Int) {
|
||||
href
|
||||
content
|
||||
createdAt
|
||||
read {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5
dashboard/src/gql/platform/insertAnnouncementRead.gql
Normal file
5
dashboard/src/gql/platform/insertAnnouncementRead.gql
Normal file
@@ -0,0 +1,5 @@
|
||||
mutation insertAnnouncementRead($announcementID: uuid!) {
|
||||
insertAnnouncementRead(object: { announcementID: $announcementID }) {
|
||||
id
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { TransferProject } from '@/features/orgs/components/TransferProject';
|
||||
import { ProjectLayout } from '@/features/orgs/layout/ProjectLayout';
|
||||
import { SettingsLayout } from '@/features/orgs/layout/SettingsLayout';
|
||||
import { RemoveApplicationModal } from '@/features/orgs/projects/common/components/RemoveApplicationModal';
|
||||
import { useAppState } from '@/features/orgs/projects/common/hooks/useAppState';
|
||||
import { useIsCurrentUserOwner } from '@/features/orgs/projects/common/hooks/useIsCurrentUserOwner';
|
||||
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
||||
@@ -17,8 +18,10 @@ import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWith
|
||||
import {
|
||||
useBillingDeleteAppMutation,
|
||||
usePauseApplicationMutation,
|
||||
useUnpauseApplicationMutation,
|
||||
useUpdateApplicationMutation,
|
||||
} from '@/generated/graphql';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { slugifyString } from '@/utils/helpers';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -46,12 +49,21 @@ export default function SettingsGeneralPage() {
|
||||
const isOwner = useIsCurrentUserOwner();
|
||||
const { currentOrg: org } = useOrgs();
|
||||
const { project, loading, refetch: refetchProject } = useProject();
|
||||
const { state } = useAppState();
|
||||
|
||||
const [updateApp] = useUpdateApplicationMutation();
|
||||
const [deleteApplication] = useBillingDeleteAppMutation();
|
||||
const [pauseApplication] = usePauseApplicationMutation({
|
||||
variables: { appId: project?.id },
|
||||
});
|
||||
const [pauseApplication, { loading: pauseApplicationLoading }] =
|
||||
usePauseApplicationMutation({
|
||||
variables: { appId: project?.id },
|
||||
});
|
||||
|
||||
const [unpauseApplication, { loading: unpauseApplicationLoading }] =
|
||||
useUnpauseApplicationMutation({
|
||||
variables: {
|
||||
appId: project?.id,
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<ProjectNameValidationSchema>({
|
||||
mode: 'onSubmit',
|
||||
@@ -137,6 +149,24 @@ export default function SettingsGeneralPage() {
|
||||
);
|
||||
}
|
||||
|
||||
async function handleTriggerUnpausing() {
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await unpauseApplication();
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
await refetchProject();
|
||||
},
|
||||
{
|
||||
loadingMessage: 'Starting the project...',
|
||||
successMessage: 'The project has been started successfully.',
|
||||
errorMessage:
|
||||
'An error occurred while waking up the project. Please try again.',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <ActivityIndicator label="Loading project..." />;
|
||||
}
|
||||
@@ -176,29 +206,53 @@ export default function SettingsGeneralPage() {
|
||||
</Form>
|
||||
</FormProvider>
|
||||
|
||||
<SettingsContainer
|
||||
title="Pause Project"
|
||||
description="While your project is paused, it will not be accessible. You can wake it up anytime after."
|
||||
submitButtonText="Pause"
|
||||
slotProps={{
|
||||
submitButton: {
|
||||
type: 'button',
|
||||
color: 'primary',
|
||||
variant: 'contained',
|
||||
disabled: maintenanceActive || !isPlatform,
|
||||
onClick: () => {
|
||||
openAlertDialog({
|
||||
title: 'Pause Project?',
|
||||
payload:
|
||||
'Are you sure you want to pause this project? It will not be accessible until you unpause it.',
|
||||
props: {
|
||||
onPrimaryAction: handlePauseApplication,
|
||||
},
|
||||
});
|
||||
{state === ApplicationStatus.Paused && (
|
||||
<SettingsContainer
|
||||
title="Wake up Project"
|
||||
description="Wake up your project to make it accessible again. Once reactivated, all features will be fully functional."
|
||||
submitButtonText="Wake up"
|
||||
slotProps={{
|
||||
submitButton: {
|
||||
type: 'button',
|
||||
color: 'primary',
|
||||
variant: 'contained',
|
||||
loading: unpauseApplicationLoading,
|
||||
disabled:
|
||||
maintenanceActive || !isPlatform || unpauseApplicationLoading,
|
||||
onClick: handleTriggerUnpausing,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{state !== ApplicationStatus.Paused &&
|
||||
state !== ApplicationStatus.Pausing && (
|
||||
<SettingsContainer
|
||||
title="Pause Project"
|
||||
description="While your project is paused, it will not be accessible. You can wake it up anytime after."
|
||||
submitButtonText="Pause"
|
||||
slotProps={{
|
||||
submitButton: {
|
||||
type: 'button',
|
||||
color: 'primary',
|
||||
variant: 'contained',
|
||||
loading: pauseApplicationLoading,
|
||||
disabled:
|
||||
maintenanceActive || !isPlatform || pauseApplicationLoading,
|
||||
onClick: () => {
|
||||
openAlertDialog({
|
||||
title: 'Pause Project?',
|
||||
payload:
|
||||
'Are you sure you want to pause this project? It will not be accessible until you unpause it.',
|
||||
props: {
|
||||
onPrimaryAction: handlePauseApplication,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TransferProject />
|
||||
|
||||
|
||||
447
dashboard/src/utils/__generated__/graphql.ts
generated
447
dashboard/src/utils/__generated__/graphql.ts
generated
@@ -3270,9 +3270,33 @@ export type Announcements = {
|
||||
expiresAt?: Maybe<Scalars['timestamptz']>;
|
||||
href: Scalars['String'];
|
||||
id: Scalars['uuid'];
|
||||
/** An array relationship */
|
||||
read: Array<Announcements_Read>;
|
||||
/** An aggregate relationship */
|
||||
read_aggregate: Announcements_Read_Aggregate;
|
||||
updatedAt: Scalars['timestamptz'];
|
||||
};
|
||||
|
||||
|
||||
/** columns and relationships of "announcements" */
|
||||
export type AnnouncementsReadArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Read_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<Announcements_Read_Order_By>>;
|
||||
where?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
};
|
||||
|
||||
|
||||
/** columns and relationships of "announcements" */
|
||||
export type AnnouncementsRead_AggregateArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Read_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<Announcements_Read_Order_By>>;
|
||||
where?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
};
|
||||
|
||||
/** aggregated selection of "announcements" */
|
||||
export type Announcements_Aggregate = {
|
||||
__typename?: 'announcements_aggregate';
|
||||
@@ -3305,6 +3329,8 @@ export type Announcements_Bool_Exp = {
|
||||
expiresAt?: InputMaybe<Timestamptz_Comparison_Exp>;
|
||||
href?: InputMaybe<String_Comparison_Exp>;
|
||||
id?: InputMaybe<Uuid_Comparison_Exp>;
|
||||
read?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
read_aggregate?: InputMaybe<Announcements_Read_Aggregate_Bool_Exp>;
|
||||
updatedAt?: InputMaybe<Timestamptz_Comparison_Exp>;
|
||||
};
|
||||
|
||||
@@ -3321,6 +3347,7 @@ export type Announcements_Insert_Input = {
|
||||
expiresAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
href?: InputMaybe<Scalars['String']>;
|
||||
id?: InputMaybe<Scalars['uuid']>;
|
||||
read?: InputMaybe<Announcements_Read_Arr_Rel_Insert_Input>;
|
||||
updatedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
};
|
||||
|
||||
@@ -3369,6 +3396,7 @@ export type Announcements_Order_By = {
|
||||
expiresAt?: InputMaybe<Order_By>;
|
||||
href?: InputMaybe<Order_By>;
|
||||
id?: InputMaybe<Order_By>;
|
||||
read_aggregate?: InputMaybe<Announcements_Read_Aggregate_Order_By>;
|
||||
updatedAt?: InputMaybe<Order_By>;
|
||||
};
|
||||
|
||||
@@ -3377,6 +3405,207 @@ export type Announcements_Pk_Columns_Input = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
/** columns and relationships of "announcements_read" */
|
||||
export type Announcements_Read = {
|
||||
__typename?: 'announcements_read';
|
||||
announcementID: Scalars['uuid'];
|
||||
createdAt: Scalars['timestamptz'];
|
||||
id: Scalars['uuid'];
|
||||
userID: Scalars['uuid'];
|
||||
};
|
||||
|
||||
/** aggregated selection of "announcements_read" */
|
||||
export type Announcements_Read_Aggregate = {
|
||||
__typename?: 'announcements_read_aggregate';
|
||||
aggregate?: Maybe<Announcements_Read_Aggregate_Fields>;
|
||||
nodes: Array<Announcements_Read>;
|
||||
};
|
||||
|
||||
export type Announcements_Read_Aggregate_Bool_Exp = {
|
||||
count?: InputMaybe<Announcements_Read_Aggregate_Bool_Exp_Count>;
|
||||
};
|
||||
|
||||
export type Announcements_Read_Aggregate_Bool_Exp_Count = {
|
||||
arguments?: InputMaybe<Array<Announcements_Read_Select_Column>>;
|
||||
distinct?: InputMaybe<Scalars['Boolean']>;
|
||||
filter?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
predicate: Int_Comparison_Exp;
|
||||
};
|
||||
|
||||
/** aggregate fields of "announcements_read" */
|
||||
export type Announcements_Read_Aggregate_Fields = {
|
||||
__typename?: 'announcements_read_aggregate_fields';
|
||||
count: Scalars['Int'];
|
||||
max?: Maybe<Announcements_Read_Max_Fields>;
|
||||
min?: Maybe<Announcements_Read_Min_Fields>;
|
||||
};
|
||||
|
||||
|
||||
/** aggregate fields of "announcements_read" */
|
||||
export type Announcements_Read_Aggregate_FieldsCountArgs = {
|
||||
columns?: InputMaybe<Array<Announcements_Read_Select_Column>>;
|
||||
distinct?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
/** order by aggregate values of table "announcements_read" */
|
||||
export type Announcements_Read_Aggregate_Order_By = {
|
||||
count?: InputMaybe<Order_By>;
|
||||
max?: InputMaybe<Announcements_Read_Max_Order_By>;
|
||||
min?: InputMaybe<Announcements_Read_Min_Order_By>;
|
||||
};
|
||||
|
||||
/** input type for inserting array relation for remote table "announcements_read" */
|
||||
export type Announcements_Read_Arr_Rel_Insert_Input = {
|
||||
data: Array<Announcements_Read_Insert_Input>;
|
||||
/** upsert condition */
|
||||
on_conflict?: InputMaybe<Announcements_Read_On_Conflict>;
|
||||
};
|
||||
|
||||
/** Boolean expression to filter rows from the table "announcements_read". All fields are combined with a logical 'AND'. */
|
||||
export type Announcements_Read_Bool_Exp = {
|
||||
_and?: InputMaybe<Array<Announcements_Read_Bool_Exp>>;
|
||||
_not?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
_or?: InputMaybe<Array<Announcements_Read_Bool_Exp>>;
|
||||
announcementID?: InputMaybe<Uuid_Comparison_Exp>;
|
||||
createdAt?: InputMaybe<Timestamptz_Comparison_Exp>;
|
||||
id?: InputMaybe<Uuid_Comparison_Exp>;
|
||||
userID?: InputMaybe<Uuid_Comparison_Exp>;
|
||||
};
|
||||
|
||||
/** unique or primary key constraints on table "announcements_read" */
|
||||
export enum Announcements_Read_Constraint {
|
||||
/** unique or primary key constraint on columns "user_id", "announcement_id" */
|
||||
AnnouncementsReadAnnouncementIdUserIdKey = 'announcements_read_announcement_id_user_id_key',
|
||||
/** unique or primary key constraint on columns "id" */
|
||||
AnnouncementsReadPkey = 'announcements_read_pkey'
|
||||
}
|
||||
|
||||
/** input type for inserting data into table "announcements_read" */
|
||||
export type Announcements_Read_Insert_Input = {
|
||||
announcementID?: InputMaybe<Scalars['uuid']>;
|
||||
createdAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
id?: InputMaybe<Scalars['uuid']>;
|
||||
userID?: InputMaybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** aggregate max on columns */
|
||||
export type Announcements_Read_Max_Fields = {
|
||||
__typename?: 'announcements_read_max_fields';
|
||||
announcementID?: Maybe<Scalars['uuid']>;
|
||||
createdAt?: Maybe<Scalars['timestamptz']>;
|
||||
id?: Maybe<Scalars['uuid']>;
|
||||
userID?: Maybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** order by max() on columns of table "announcements_read" */
|
||||
export type Announcements_Read_Max_Order_By = {
|
||||
announcementID?: InputMaybe<Order_By>;
|
||||
createdAt?: InputMaybe<Order_By>;
|
||||
id?: InputMaybe<Order_By>;
|
||||
userID?: InputMaybe<Order_By>;
|
||||
};
|
||||
|
||||
/** aggregate min on columns */
|
||||
export type Announcements_Read_Min_Fields = {
|
||||
__typename?: 'announcements_read_min_fields';
|
||||
announcementID?: Maybe<Scalars['uuid']>;
|
||||
createdAt?: Maybe<Scalars['timestamptz']>;
|
||||
id?: Maybe<Scalars['uuid']>;
|
||||
userID?: Maybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** order by min() on columns of table "announcements_read" */
|
||||
export type Announcements_Read_Min_Order_By = {
|
||||
announcementID?: InputMaybe<Order_By>;
|
||||
createdAt?: InputMaybe<Order_By>;
|
||||
id?: InputMaybe<Order_By>;
|
||||
userID?: InputMaybe<Order_By>;
|
||||
};
|
||||
|
||||
/** response of any mutation on the table "announcements_read" */
|
||||
export type Announcements_Read_Mutation_Response = {
|
||||
__typename?: 'announcements_read_mutation_response';
|
||||
/** number of rows affected by the mutation */
|
||||
affected_rows: Scalars['Int'];
|
||||
/** data from the rows affected by the mutation */
|
||||
returning: Array<Announcements_Read>;
|
||||
};
|
||||
|
||||
/** on_conflict condition type for table "announcements_read" */
|
||||
export type Announcements_Read_On_Conflict = {
|
||||
constraint: Announcements_Read_Constraint;
|
||||
update_columns?: Array<Announcements_Read_Update_Column>;
|
||||
where?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
};
|
||||
|
||||
/** Ordering options when selecting data from "announcements_read". */
|
||||
export type Announcements_Read_Order_By = {
|
||||
announcementID?: InputMaybe<Order_By>;
|
||||
createdAt?: InputMaybe<Order_By>;
|
||||
id?: InputMaybe<Order_By>;
|
||||
userID?: InputMaybe<Order_By>;
|
||||
};
|
||||
|
||||
/** primary key columns input for table: announcements_read */
|
||||
export type Announcements_Read_Pk_Columns_Input = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
/** select columns of table "announcements_read" */
|
||||
export enum Announcements_Read_Select_Column {
|
||||
/** column name */
|
||||
AnnouncementId = 'announcementID',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
Id = 'id',
|
||||
/** column name */
|
||||
UserId = 'userID'
|
||||
}
|
||||
|
||||
/** input type for updating data in table "announcements_read" */
|
||||
export type Announcements_Read_Set_Input = {
|
||||
announcementID?: InputMaybe<Scalars['uuid']>;
|
||||
createdAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
id?: InputMaybe<Scalars['uuid']>;
|
||||
userID?: InputMaybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** Streaming cursor of the table "announcements_read" */
|
||||
export type Announcements_Read_Stream_Cursor_Input = {
|
||||
/** Stream column input with initial value */
|
||||
initial_value: Announcements_Read_Stream_Cursor_Value_Input;
|
||||
/** cursor ordering */
|
||||
ordering?: InputMaybe<Cursor_Ordering>;
|
||||
};
|
||||
|
||||
/** Initial value of the column from where the streaming should start */
|
||||
export type Announcements_Read_Stream_Cursor_Value_Input = {
|
||||
announcementID?: InputMaybe<Scalars['uuid']>;
|
||||
createdAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
id?: InputMaybe<Scalars['uuid']>;
|
||||
userID?: InputMaybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** update columns of table "announcements_read" */
|
||||
export enum Announcements_Read_Update_Column {
|
||||
/** column name */
|
||||
AnnouncementId = 'announcementID',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
Id = 'id',
|
||||
/** column name */
|
||||
UserId = 'userID'
|
||||
}
|
||||
|
||||
export type Announcements_Read_Updates = {
|
||||
/** sets the columns of the filtered rows to the given values */
|
||||
_set?: InputMaybe<Announcements_Read_Set_Input>;
|
||||
/** filter the rows which have to be updated */
|
||||
where: Announcements_Read_Bool_Exp;
|
||||
};
|
||||
|
||||
/** select columns of table "announcements" */
|
||||
export enum Announcements_Select_Column {
|
||||
/** column name */
|
||||
@@ -12830,6 +13059,10 @@ export type Mutation_Root = {
|
||||
/** execute VOLATILE function "billing.reports_delete_older_than_days" which returns "billing.reports" */
|
||||
billing_reports_delete_older_than_days: Array<Billing_Reports>;
|
||||
changeDatabaseVersion: Scalars['Boolean'];
|
||||
/** delete single row from the table: "announcements_read" */
|
||||
deleteAnnouncementRead?: Maybe<Announcements_Read>;
|
||||
/** delete data from the table: "announcements_read" */
|
||||
deleteAnnouncementsRead?: Maybe<Announcements_Read_Mutation_Response>;
|
||||
/** delete single row from the table: "apps" */
|
||||
deleteApp?: Maybe<Apps>;
|
||||
/** delete single row from the table: "app_states" */
|
||||
@@ -13041,6 +13274,10 @@ export type Mutation_Root = {
|
||||
delete_regions?: Maybe<Regions_Mutation_Response>;
|
||||
/** delete single row from the table: "regions" */
|
||||
delete_regions_by_pk?: Maybe<Regions>;
|
||||
/** insert a single row into the table: "announcements_read" */
|
||||
insertAnnouncementRead?: Maybe<Announcements_Read>;
|
||||
/** insert data into the table: "announcements_read" */
|
||||
insertAnnouncementsRead?: Maybe<Announcements_Read_Mutation_Response>;
|
||||
/** insert a single row into the table: "apps" */
|
||||
insertApp?: Maybe<Apps>;
|
||||
/** insert a single row into the table: "app_states" */
|
||||
@@ -13265,6 +13502,12 @@ export type Mutation_Root = {
|
||||
sendEmailOrganizationStatusChange: Scalars['Boolean'];
|
||||
sendEmailOrganizationThreshold: Scalars['Boolean'];
|
||||
sendEmailTemplate: Scalars['Boolean'];
|
||||
/** update single row of the table: "announcements_read" */
|
||||
updateAnnouncementRead?: Maybe<Announcements_Read>;
|
||||
/** update data of the table: "announcements_read" */
|
||||
updateAnnouncementsRead?: Maybe<Announcements_Read_Mutation_Response>;
|
||||
/** update multiples rows of table: "announcements_read" */
|
||||
updateAnnouncementsReadMany?: Maybe<Array<Maybe<Announcements_Read_Mutation_Response>>>;
|
||||
/** update single row of the table: "apps" */
|
||||
updateApp?: Maybe<Apps>;
|
||||
/** update single row of the table: "app_states" */
|
||||
@@ -13700,6 +13943,18 @@ export type Mutation_RootChangeDatabaseVersionArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootDeleteAnnouncementReadArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootDeleteAnnouncementsReadArgs = {
|
||||
where: Announcements_Read_Bool_Exp;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootDeleteAppArgs = {
|
||||
id: Scalars['uuid'];
|
||||
@@ -14344,6 +14599,20 @@ export type Mutation_RootDelete_Regions_By_PkArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootInsertAnnouncementReadArgs = {
|
||||
object: Announcements_Read_Insert_Input;
|
||||
on_conflict?: InputMaybe<Announcements_Read_On_Conflict>;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootInsertAnnouncementsReadArgs = {
|
||||
objects: Array<Announcements_Read_Insert_Input>;
|
||||
on_conflict?: InputMaybe<Announcements_Read_On_Conflict>;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootInsertAppArgs = {
|
||||
object: Apps_Insert_Input;
|
||||
@@ -15173,6 +15442,26 @@ export type Mutation_RootSendEmailTemplateArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootUpdateAnnouncementReadArgs = {
|
||||
_set?: InputMaybe<Announcements_Read_Set_Input>;
|
||||
pk_columns: Announcements_Read_Pk_Columns_Input;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootUpdateAnnouncementsReadArgs = {
|
||||
_set?: InputMaybe<Announcements_Read_Set_Input>;
|
||||
where: Announcements_Read_Bool_Exp;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootUpdateAnnouncementsReadManyArgs = {
|
||||
updates: Array<Announcements_Read_Updates>;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type Mutation_RootUpdateAppArgs = {
|
||||
_append?: InputMaybe<Apps_Append_Input>;
|
||||
@@ -19125,8 +19414,14 @@ export type Plans_Variance_Fields = {
|
||||
|
||||
export type Query_Root = {
|
||||
__typename?: 'query_root';
|
||||
/** fetch data from the table: "announcements_read" using primary key columns */
|
||||
announcementRead?: Maybe<Announcements_Read>;
|
||||
/** fetch data from the table: "announcements" */
|
||||
announcements: Array<Announcements>;
|
||||
/** fetch data from the table: "announcements_read" */
|
||||
announcementsRead: Array<Announcements_Read>;
|
||||
/** fetch aggregated fields from the table: "announcements_read" */
|
||||
announcementsReadAggregate: Announcements_Read_Aggregate;
|
||||
/** fetch aggregated fields from the table: "announcements" */
|
||||
announcements_aggregate: Announcements_Aggregate;
|
||||
/** fetch data from the table: "announcements" using primary key columns */
|
||||
@@ -19491,6 +19786,11 @@ export type Query_Root = {
|
||||
};
|
||||
|
||||
|
||||
export type Query_RootAnnouncementReadArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
export type Query_RootAnnouncementsArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
@@ -19500,6 +19800,24 @@ export type Query_RootAnnouncementsArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type Query_RootAnnouncementsReadArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Read_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<Announcements_Read_Order_By>>;
|
||||
where?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
};
|
||||
|
||||
|
||||
export type Query_RootAnnouncementsReadAggregateArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Read_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<Announcements_Read_Order_By>>;
|
||||
where?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
};
|
||||
|
||||
|
||||
export type Query_RootAnnouncements_AggregateArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
@@ -22628,8 +22946,16 @@ export type Software_Versions_Updates = {
|
||||
|
||||
export type Subscription_Root = {
|
||||
__typename?: 'subscription_root';
|
||||
/** fetch data from the table: "announcements_read" using primary key columns */
|
||||
announcementRead?: Maybe<Announcements_Read>;
|
||||
/** fetch data from the table: "announcements" */
|
||||
announcements: Array<Announcements>;
|
||||
/** fetch data from the table: "announcements_read" */
|
||||
announcementsRead: Array<Announcements_Read>;
|
||||
/** fetch aggregated fields from the table: "announcements_read" */
|
||||
announcementsReadAggregate: Announcements_Read_Aggregate;
|
||||
/** fetch data from the table in a streaming manner: "announcements_read" */
|
||||
announcementsReadStream: Array<Announcements_Read>;
|
||||
/** fetch aggregated fields from the table: "announcements" */
|
||||
announcements_aggregate: Announcements_Aggregate;
|
||||
/** fetch data from the table: "announcements" using primary key columns */
|
||||
@@ -23052,6 +23378,11 @@ export type Subscription_Root = {
|
||||
};
|
||||
|
||||
|
||||
export type Subscription_RootAnnouncementReadArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
export type Subscription_RootAnnouncementsArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
@@ -23061,6 +23392,31 @@ export type Subscription_RootAnnouncementsArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type Subscription_RootAnnouncementsReadArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Read_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<Announcements_Read_Order_By>>;
|
||||
where?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
};
|
||||
|
||||
|
||||
export type Subscription_RootAnnouncementsReadAggregateArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Read_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<Announcements_Read_Order_By>>;
|
||||
where?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
};
|
||||
|
||||
|
||||
export type Subscription_RootAnnouncementsReadStreamArgs = {
|
||||
batch_size: Scalars['Int'];
|
||||
cursor: Array<InputMaybe<Announcements_Read_Stream_Cursor_Input>>;
|
||||
where?: InputMaybe<Announcements_Read_Bool_Exp>;
|
||||
};
|
||||
|
||||
|
||||
export type Subscription_RootAnnouncements_AggregateArgs = {
|
||||
distinct_on?: InputMaybe<Array<Announcements_Select_Column>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
@@ -27525,12 +27881,17 @@ export type SetNewDefaultPaymentMethodMutationVariables = Exact<{
|
||||
|
||||
export type SetNewDefaultPaymentMethodMutation = { __typename?: 'mutation_root', setAllPaymentMethodToDefaultFalse?: { __typename?: 'paymentMethods_mutation_response', affected_rows: number } | null, updatePaymentMethods?: { __typename?: 'paymentMethods_mutation_response', affected_rows: number } | null };
|
||||
|
||||
export type GetAnnouncementsQueryVariables = Exact<{
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
export type DeleteAnnouncementReadMutationVariables = Exact<{
|
||||
id: Scalars['uuid'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetAnnouncementsQuery = { __typename?: 'query_root', announcements: Array<{ __typename?: 'announcements', id: any, href: string, content: string, createdAt: any }> };
|
||||
export type DeleteAnnouncementReadMutation = { __typename?: 'mutation_root', deleteAnnouncementRead?: { __typename?: 'announcements_read', announcementID: any } | null };
|
||||
|
||||
export type GetAnnouncementsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetAnnouncementsQuery = { __typename?: 'query_root', announcements: Array<{ __typename?: 'announcements', id: any, href: string, content: string, createdAt: any, read: Array<{ __typename?: 'announcements_read', id: any }> }> };
|
||||
|
||||
export type GetPlansQueryVariables = Exact<{
|
||||
where?: InputMaybe<Plans_Bool_Exp>;
|
||||
@@ -27551,6 +27912,13 @@ export type GetSoftwareVersionsQueryVariables = Exact<{
|
||||
|
||||
export type GetSoftwareVersionsQuery = { __typename?: 'query_root', softwareVersions: Array<{ __typename?: 'software_versions', version: string, software: Software_Type_Enum }> };
|
||||
|
||||
export type InsertAnnouncementReadMutationVariables = Exact<{
|
||||
announcementID: Scalars['uuid'];
|
||||
}>;
|
||||
|
||||
|
||||
export type InsertAnnouncementReadMutation = { __typename?: 'mutation_root', insertAnnouncementRead?: { __typename?: 'announcements_read', id: any } | null };
|
||||
|
||||
export type RestoreApplicationDatabaseMutationVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
backupId: Scalars['String'];
|
||||
@@ -32520,17 +32888,52 @@ export function useSetNewDefaultPaymentMethodMutation(baseOptions?: Apollo.Mutat
|
||||
export type SetNewDefaultPaymentMethodMutationHookResult = ReturnType<typeof useSetNewDefaultPaymentMethodMutation>;
|
||||
export type SetNewDefaultPaymentMethodMutationResult = Apollo.MutationResult<SetNewDefaultPaymentMethodMutation>;
|
||||
export type SetNewDefaultPaymentMethodMutationOptions = Apollo.BaseMutationOptions<SetNewDefaultPaymentMethodMutation, SetNewDefaultPaymentMethodMutationVariables>;
|
||||
export const DeleteAnnouncementReadDocument = gql`
|
||||
mutation deleteAnnouncementRead($id: uuid!) {
|
||||
deleteAnnouncementRead(id: $id) {
|
||||
announcementID
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type DeleteAnnouncementReadMutationFn = Apollo.MutationFunction<DeleteAnnouncementReadMutation, DeleteAnnouncementReadMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useDeleteAnnouncementReadMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useDeleteAnnouncementReadMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useDeleteAnnouncementReadMutation` 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 [deleteAnnouncementReadMutation, { data, loading, error }] = useDeleteAnnouncementReadMutation({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDeleteAnnouncementReadMutation(baseOptions?: Apollo.MutationHookOptions<DeleteAnnouncementReadMutation, DeleteAnnouncementReadMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<DeleteAnnouncementReadMutation, DeleteAnnouncementReadMutationVariables>(DeleteAnnouncementReadDocument, options);
|
||||
}
|
||||
export type DeleteAnnouncementReadMutationHookResult = ReturnType<typeof useDeleteAnnouncementReadMutation>;
|
||||
export type DeleteAnnouncementReadMutationResult = Apollo.MutationResult<DeleteAnnouncementReadMutation>;
|
||||
export type DeleteAnnouncementReadMutationOptions = Apollo.BaseMutationOptions<DeleteAnnouncementReadMutation, DeleteAnnouncementReadMutationVariables>;
|
||||
export const GetAnnouncementsDocument = gql`
|
||||
query getAnnouncements($limit: Int) {
|
||||
query getAnnouncements {
|
||||
announcements(
|
||||
order_by: {createdAt: desc}
|
||||
limit: $limit
|
||||
where: {_or: [{expiresAt: {_is_null: true}}, {expiresAt: {_gt: now}}]}
|
||||
) {
|
||||
id
|
||||
href
|
||||
content
|
||||
createdAt
|
||||
read {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -32547,7 +32950,6 @@ export const GetAnnouncementsDocument = gql`
|
||||
* @example
|
||||
* const { data, loading, error } = useGetAnnouncementsQuery({
|
||||
* variables: {
|
||||
* limit: // value for 'limit'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
@@ -32683,6 +33085,39 @@ export type GetSoftwareVersionsQueryResult = Apollo.QueryResult<GetSoftwareVersi
|
||||
export function refetchGetSoftwareVersionsQuery(variables: GetSoftwareVersionsQueryVariables) {
|
||||
return { query: GetSoftwareVersionsDocument, variables: variables }
|
||||
}
|
||||
export const InsertAnnouncementReadDocument = gql`
|
||||
mutation insertAnnouncementRead($announcementID: uuid!) {
|
||||
insertAnnouncementRead(object: {announcementID: $announcementID}) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type InsertAnnouncementReadMutationFn = Apollo.MutationFunction<InsertAnnouncementReadMutation, InsertAnnouncementReadMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useInsertAnnouncementReadMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useInsertAnnouncementReadMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useInsertAnnouncementReadMutation` 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 [insertAnnouncementReadMutation, { data, loading, error }] = useInsertAnnouncementReadMutation({
|
||||
* variables: {
|
||||
* announcementID: // value for 'announcementID'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useInsertAnnouncementReadMutation(baseOptions?: Apollo.MutationHookOptions<InsertAnnouncementReadMutation, InsertAnnouncementReadMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<InsertAnnouncementReadMutation, InsertAnnouncementReadMutationVariables>(InsertAnnouncementReadDocument, options);
|
||||
}
|
||||
export type InsertAnnouncementReadMutationHookResult = ReturnType<typeof useInsertAnnouncementReadMutation>;
|
||||
export type InsertAnnouncementReadMutationResult = Apollo.MutationResult<InsertAnnouncementReadMutation>;
|
||||
export type InsertAnnouncementReadMutationOptions = Apollo.BaseMutationOptions<InsertAnnouncementReadMutation, InsertAnnouncementReadMutationVariables>;
|
||||
export const RestoreApplicationDatabaseDocument = gql`
|
||||
mutation RestoreApplicationDatabase($appId: String!, $backupId: String!) {
|
||||
restoreApplicationDatabase(appID: $appId, backupID: $backupId)
|
||||
|
||||
@@ -8,7 +8,7 @@ Ensuring a healthy and performant PostgreSQL service is crucial as it directly i
|
||||
|
||||
In case your Postgres service is not meeting your performance expectations, you can explore the following options:
|
||||
|
||||
1. Consider upgrading your [dedicated compute](/platform/compute) resources to provide more processing power and memory to the Postgres server.
|
||||
1. Consider upgrading your [dedicated compute](/platform/compute-resources#dedicated-compute) resources to provide more processing power and memory to the Postgres server.
|
||||
|
||||
2. Fine-tune the configuration parameters of Postgres to optimize its performance. Adjust settings such as `shared_buffers`, `work_mem`, and `effective_cache_size` to better align with your workload and server resources.
|
||||
|
||||
@@ -26,11 +26,11 @@ Before trying anything else, always upgrade to our latest postgres image first.
|
||||
|
||||
## Upgrade to dedicated compute
|
||||
|
||||
Increasing CPU and memory is the simplest way to address performance issues. You can read more about compute resources [here](/platform/compute).
|
||||
Increasing CPU and memory is the simplest way to address performance issues. You can read more about compute resources [here](/platform/compute-resources#dedicated-compute).
|
||||
|
||||
## Fine-tune configuration parameters
|
||||
|
||||
When optimizing your Postgres setup, you can consider adjusting various Postgres settings. You can find a list of these parameters [here](/database/settings). Keep in mind that the optimal values for these parameters will depend on factors such as available resources, workload, and data distribution.
|
||||
When optimizing your Postgres setup, you can consider adjusting various Postgres settings. You can find a list of these parameters [here](/guides/database/configuring-postgres). Keep in mind that the optimal values for these parameters will depend on factors such as available resources, workload, and data distribution.
|
||||
|
||||
To help you get started, you can use [pgtune](https://pgtune.leopard.in.ua) as a reference tool. Pgtune can generate recommended configuration settings based on your system specifications. By providing information about your system, it can suggest parameter values that may be a good starting point for optimization.
|
||||
|
||||
@@ -42,9 +42,9 @@ Monitoring slow queries is a highly effective method for tackling performance is
|
||||
|
||||
### pghero
|
||||
|
||||
[PgHero](https://github.com/ankane/pghero) is one of such tools you can use to idenfity and address slow queries. You can easily run pghero alongside your postgres with [Nhost Run](/run):
|
||||
[PgHero](https://github.com/ankane/pghero) is one of such tools you can use to idenfity and address slow queries. You can easily run pghero alongside your postgres with [Nhost Run](/product/run):
|
||||
|
||||
1. First, make sure the extension [pg_stat_statements](/database/extensions#pg_stat_statements) is enabled.
|
||||
1. First, make sure the extension [pg_stat_statements](/guides/database/extensions#pg-stat-statements) is enabled.
|
||||
|
||||
2. Click on this [one-click install link](https://app.nhost.io:/run-one-click-install?config=eyJuYW1lIjoicGdoZXJvIiwiaW1hZ2UiOnsiaW1hZ2UiOiJkb2NrZXIuaW8vYW5rYW5lL3BnaGVybzpsYXRlc3QifSwiY29tbWFuZCI6W10sInJlc291cmNlcyI6eyJjb21wdXRlIjp7ImNwdSI6MTI1LCJtZW1vcnkiOjI1Nn0sInN0b3JhZ2UiOltdLCJyZXBsaWNhcyI6MX0sImVudmlyb25tZW50IjpbeyJuYW1lIjoiREFUQUJBU0VfVVJMIiwidmFsdWUiOiJwb3N0Z3JlczovL3Bvc3RncmVzOltQQVNTV09SRF1AcG9zdGdyZXMtc2VydmljZTo1NDMyL1tTVUJET01BSU5dP3NzbG1vZGU9ZGlzYWJsZSJ9LHsibmFtZSI6IlBHSEVST19VU0VSTkFNRSIsInZhbHVlIjoiW1VTRVJdIn0seyJuYW1lIjoiUEdIRVJPX1BBU1NXT1JEIiwidmFsdWUiOiJbUEFTU1dPUkRdIn1dLCJwb3J0cyI6W3sicG9ydCI6ODA4MCwidHlwZSI6Imh0dHAiLCJwdWJsaXNoIjp0cnVlfV19)
|
||||
|
||||
@@ -78,7 +78,7 @@ There are tools you can use to help analyze your workload and detect missing ind
|
||||
|
||||
### pghero
|
||||
|
||||
[PgHero](https://github.com/ankane/pghero), in addition to help with slow queries, can also help finding missing and duplicate indexes. See previous section on how to deploy pghero with [Nhost Run](/run).
|
||||
[PgHero](https://github.com/ankane/pghero), in addition to help with slow queries, can also help finding missing and duplicate indexes. See previous section on how to deploy pghero with [Nhost Run](/product/run).
|
||||
|
||||
### dexter
|
||||
|
||||
|
||||
@@ -158,7 +158,8 @@
|
||||
"send": "^0.19.0",
|
||||
"dset": "^3.1.4",
|
||||
"rollup": "^4.24.0",
|
||||
"http-proxy-middleware": "^2.0.7"
|
||||
"http-proxy-middleware": "^2.0.7",
|
||||
"cross-spawn": "^7.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
242
pnpm-lock.yaml
generated
242
pnpm-lock.yaml
generated
@@ -64,6 +64,7 @@ overrides:
|
||||
dset: ^3.1.4
|
||||
rollup: ^4.24.0
|
||||
http-proxy-middleware: ^2.0.7
|
||||
cross-spawn: ^7.0.5
|
||||
|
||||
importers:
|
||||
|
||||
@@ -1161,16 +1162,16 @@ importers:
|
||||
version: 4.2.19
|
||||
svelte-check:
|
||||
specifier: ^3.6.8
|
||||
version: 3.8.6(@babel/core@7.25.2)(postcss@8.4.47)(svelte@4.2.19)
|
||||
version: 3.8.6(postcss@8.4.47)(svelte@4.2.19)
|
||||
tailwindcss:
|
||||
specifier: ^3.4.3
|
||||
version: 3.4.12(ts-node@10.9.2)
|
||||
version: 3.4.12
|
||||
typescript:
|
||||
specifier: ^5.4.3
|
||||
version: 5.5.4
|
||||
vite:
|
||||
specifier: ^5.4.6
|
||||
version: 5.4.6(@types/node@16.18.106)(sass@1.32.0)
|
||||
version: 5.4.6(@types/node@16.18.106)
|
||||
vitest:
|
||||
specifier: ^0.25.8
|
||||
version: 0.25.8
|
||||
@@ -2118,10 +2119,10 @@ importers:
|
||||
version: 9.7.0(react@18.2.0)
|
||||
'@nhost/react':
|
||||
specifier: ^3.5.4
|
||||
version: 3.6.0(@types/react@18.3.4)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: link:../../../packages/react
|
||||
'@nhost/react-apollo':
|
||||
specifier: ^12.0.4
|
||||
version: 12.0.6(@apollo/client@3.11.4)(@nhost/react@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 12.0.6(@apollo/client@3.11.4)(@nhost/react@packages+react)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-dialog':
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.2.0)(react@18.2.0)
|
||||
@@ -2244,10 +2245,10 @@ importers:
|
||||
version: 3.11.4(@types/react@18.3.4)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@nhost/react':
|
||||
specifier: ^3.5.2
|
||||
version: 3.6.0(@types/react@18.3.4)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: link:../../../packages/react
|
||||
'@nhost/react-apollo':
|
||||
specifier: ^12.0.2
|
||||
version: 12.0.6(@apollo/client@3.11.4)(@nhost/react@3.6.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 12.0.6(@apollo/client@3.11.4)(@nhost/react@packages+react)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@react-native-async-storage/async-storage':
|
||||
specifier: ^1.23.1
|
||||
version: 1.24.0(react-native@0.73.7)
|
||||
@@ -4603,7 +4604,7 @@ packages:
|
||||
autoprefixer: 10.4.20(postcss@8.4.47)
|
||||
cosmiconfig: 7.1.0
|
||||
cosmiconfig-typescript-loader: 1.0.9(@types/node@16.18.106)(typescript@4.9.5)
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
lodash: 4.17.21
|
||||
react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7)(@babel/plugin-transform-react-jsx@7.25.2)(react@18.2.0)(typescript@4.9.5)
|
||||
semver: 7.6.3
|
||||
@@ -7043,7 +7044,7 @@ packages:
|
||||
/@graphql-typed-document-node/core@3.2.0(graphql@16.8.1):
|
||||
resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
|
||||
peerDependencies:
|
||||
graphql: 16.8.1
|
||||
graphql: '>=16.8.1'
|
||||
dependencies:
|
||||
graphql: 16.8.1
|
||||
|
||||
@@ -8940,6 +8941,7 @@ packages:
|
||||
jwt-decode: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: true
|
||||
|
||||
/@nhost/hasura-auth-js@2.6.0:
|
||||
resolution: {integrity: sha512-gy2H/JlwSzpfFdpczFaVwheGq95SxmyzxJVTimBLCdVTl2yrnCZ3Zvk8gDpCcGLga/u+HjgRK+ymjH+RdNgiTg==}
|
||||
@@ -8951,6 +8953,7 @@ packages:
|
||||
xstate: 4.38.3
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: true
|
||||
|
||||
/@nhost/hasura-storage-js@2.5.1:
|
||||
resolution: {integrity: sha512-I3rOSa095lcR9BUmNw7dOoXLPWL39WOcrb0paUBFX4h3ltR92ILEHTZ38hN6bZSv157ZdqkIFNL/M2G45SSf7g==}
|
||||
@@ -8961,6 +8964,7 @@ packages:
|
||||
xstate: 4.38.3
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: true
|
||||
|
||||
/@nhost/nhost-js@3.1.10(graphql@16.8.1):
|
||||
resolution: {integrity: sha512-9KOX1krHu1UYAxTCUuRgRlaD97Nylzstck9YRSYwW27dHqDKhWUM5OWwOmOxJ2/W+Ty0V6EYbxuW2LRzrsdt1A==}
|
||||
@@ -8974,8 +8978,9 @@ packages:
|
||||
isomorphic-unfetch: 3.1.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
dev: true
|
||||
|
||||
/@nhost/react-apollo@12.0.6(@apollo/client@3.11.4)(@nhost/react@3.6.0)(react-dom@18.2.0)(react@18.2.0):
|
||||
/@nhost/react-apollo@12.0.6(@apollo/client@3.11.4)(@nhost/react@packages+react)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-6Q4uN7PvC6UqS4YPKbjv/q/9FMP4SECdEcZFrfaKfJrcWyoAA5MRwJeQwDnD3uhx+npEUNgTbBxezXHjYH3AYw==}
|
||||
peerDependencies:
|
||||
'@apollo/client': ^3.7.10
|
||||
@@ -8986,32 +8991,13 @@ packages:
|
||||
dependencies:
|
||||
'@apollo/client': 3.11.4(@types/react@18.3.4)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@nhost/apollo': 7.1.6(@apollo/client@3.11.4)
|
||||
'@nhost/react': 3.6.0(@types/react@18.3.4)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@nhost/react': link:packages/react
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@nhost/nhost-js'
|
||||
dev: false
|
||||
|
||||
/@nhost/react@3.6.0(@types/react@18.3.4)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-siJ7oHeN0xmwtuN3U6sK9tMdBB9Nr7rIQy/UvqgIuVfbqrNyFfyrzMKzY7EYb61TpBNzyiOFfJl78s6jZmIl1g==}
|
||||
peerDependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0
|
||||
dependencies:
|
||||
'@nhost/nhost-js': 3.1.10(graphql@16.8.1)
|
||||
'@xstate/react': 3.2.2(@types/react@18.3.4)(react@18.2.0)(xstate@4.38.3)
|
||||
jwt-decode: 4.0.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
xstate: 4.38.3
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- '@xstate/fsm'
|
||||
- encoding
|
||||
- graphql
|
||||
dev: false
|
||||
|
||||
/@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1:
|
||||
resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
|
||||
dependencies:
|
||||
@@ -12864,7 +12850,7 @@ packages:
|
||||
svelte: 4.2.19
|
||||
tiny-glob: 0.2.9
|
||||
undici: 5.28.4
|
||||
vite: 5.4.6(@types/node@16.18.106)(sass@1.32.0)
|
||||
vite: 5.4.6(@types/node@16.18.106)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -12880,7 +12866,7 @@ packages:
|
||||
'@sveltejs/vite-plugin-svelte': 2.5.3(svelte@4.2.19)(vite@5.4.6)
|
||||
debug: 4.3.7
|
||||
svelte: 4.2.19
|
||||
vite: 5.4.6(@types/node@16.18.106)(sass@1.32.0)
|
||||
vite: 5.4.6(@types/node@16.18.106)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -12899,7 +12885,7 @@ packages:
|
||||
magic-string: 0.30.11
|
||||
svelte: 4.2.19
|
||||
svelte-hmr: 0.15.3(svelte@4.2.19)
|
||||
vite: 5.4.6(@types/node@16.18.106)(sass@1.32.0)
|
||||
vite: 5.4.6(@types/node@16.18.106)
|
||||
vitefu: 0.2.5(vite@5.4.6)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -15982,7 +15968,7 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
JSONStream: 1.3.5
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
escape-string-regexp: 4.0.0
|
||||
event-stream: 4.0.1
|
||||
jju: 1.4.0
|
||||
@@ -17575,27 +17561,8 @@ packages:
|
||||
tslib: 2.7.0
|
||||
dev: true
|
||||
|
||||
/cross-spawn@5.1.0:
|
||||
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
|
||||
dependencies:
|
||||
lru-cache: 4.1.5
|
||||
shebang-command: 1.2.0
|
||||
which: 1.3.1
|
||||
dev: true
|
||||
|
||||
/cross-spawn@6.0.5:
|
||||
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
|
||||
engines: {node: '>=4.8'}
|
||||
dependencies:
|
||||
nice-try: 1.0.5
|
||||
path-key: 2.0.1
|
||||
semver: 7.6.3
|
||||
shebang-command: 1.2.0
|
||||
which: 1.3.1
|
||||
dev: true
|
||||
|
||||
/cross-spawn@7.0.3:
|
||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||
/cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
@@ -19955,7 +19922,7 @@ packages:
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
debug: 4.3.7
|
||||
doctrine: 3.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
@@ -20003,7 +19970,7 @@ packages:
|
||||
'@ungap/structured-clone': 1.2.0
|
||||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
debug: 4.3.7
|
||||
doctrine: 3.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
@@ -20054,7 +20021,7 @@ packages:
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
debug: 4.3.7
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 8.0.2
|
||||
@@ -20255,7 +20222,7 @@ packages:
|
||||
resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
cross-spawn: 6.0.5
|
||||
cross-spawn: 7.0.6
|
||||
get-stream: 4.1.0
|
||||
is-stream: 1.1.0
|
||||
npm-run-path: 2.0.2
|
||||
@@ -20268,7 +20235,7 @@ packages:
|
||||
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
get-stream: 6.0.1
|
||||
human-signals: 2.1.0
|
||||
is-stream: 2.0.1
|
||||
@@ -20282,7 +20249,7 @@ packages:
|
||||
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
||||
engines: {node: '>=16.17'}
|
||||
dependencies:
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
get-stream: 8.0.1
|
||||
human-signals: 5.0.0
|
||||
is-stream: 3.0.0
|
||||
@@ -20761,7 +20728,7 @@ packages:
|
||||
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
signal-exit: 4.1.0
|
||||
|
||||
/fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.0)(typescript@4.9.5)(webpack@5.94.0):
|
||||
@@ -21504,7 +21471,7 @@ packages:
|
||||
resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
graphql: 16.8.1
|
||||
graphql: '>=16.8.1'
|
||||
dependencies:
|
||||
graphql: 16.8.1
|
||||
tslib: 2.7.0
|
||||
@@ -22988,7 +22955,7 @@ packages:
|
||||
/isomorphic-unfetch@3.1.0:
|
||||
resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==}
|
||||
dependencies:
|
||||
node-fetch: 2.7.0(encoding@0.1.13)
|
||||
node-fetch: 2.7.0
|
||||
unfetch: 4.2.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
@@ -24887,13 +24854,6 @@ packages:
|
||||
/lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
|
||||
/lru-cache@4.1.5:
|
||||
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
|
||||
dependencies:
|
||||
pseudomap: 1.0.2
|
||||
yallist: 2.1.2
|
||||
dev: true
|
||||
|
||||
/lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
dependencies:
|
||||
@@ -26802,10 +26762,6 @@ packages:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
||||
/nice-try@1.0.5:
|
||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||
dev: true
|
||||
|
||||
/nlcst-to-string@3.1.1:
|
||||
resolution: {integrity: sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==}
|
||||
dependencies:
|
||||
@@ -26851,6 +26807,17 @@ packages:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
/node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
/node-fetch@2.7.0(encoding@0.1.13):
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
@@ -26960,7 +26927,7 @@ packages:
|
||||
dependencies:
|
||||
ansi-styles: 3.2.1
|
||||
chalk: 2.4.2
|
||||
cross-spawn: 6.0.5
|
||||
cross-spawn: 7.0.6
|
||||
memorystream: 0.3.1
|
||||
minimatch: 3.1.2
|
||||
pidtree: 0.3.1
|
||||
@@ -28109,6 +28076,23 @@ packages:
|
||||
yaml: 1.10.2
|
||||
dev: true
|
||||
|
||||
/postcss-load-config@4.0.2(postcss@8.4.47):
|
||||
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
|
||||
engines: {node: '>= 14'}
|
||||
peerDependencies:
|
||||
postcss: '>=8.4.31'
|
||||
ts-node: '>=9.0.0'
|
||||
peerDependenciesMeta:
|
||||
postcss:
|
||||
optional: true
|
||||
ts-node:
|
||||
optional: true
|
||||
dependencies:
|
||||
lilconfig: 3.1.2
|
||||
postcss: 8.4.47
|
||||
yaml: 2.5.0
|
||||
dev: true
|
||||
|
||||
/postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
|
||||
engines: {node: '>= 14'}
|
||||
@@ -28998,10 +28982,6 @@ packages:
|
||||
/proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
/pseudomap@1.0.2:
|
||||
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
|
||||
dev: true
|
||||
|
||||
/psl@1.9.0:
|
||||
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
|
||||
|
||||
@@ -29231,7 +29211,7 @@ packages:
|
||||
address: 1.2.2
|
||||
browserslist: 4.23.3
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.3
|
||||
cross-spawn: 7.0.6
|
||||
detect-port-alt: 1.1.6
|
||||
escape-string-regexp: 4.0.0
|
||||
filesize: 8.0.7
|
||||
@@ -31106,24 +31086,12 @@ packages:
|
||||
'@img/sharp-win32-x64': 0.33.5
|
||||
dev: true
|
||||
|
||||
/shebang-command@1.2.0:
|
||||
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
shebang-regex: 1.0.0
|
||||
dev: true
|
||||
|
||||
/shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
shebang-regex: 3.0.0
|
||||
|
||||
/shebang-regex@1.0.0:
|
||||
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/shebang-regex@3.0.0:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -31414,7 +31382,7 @@ packages:
|
||||
/spawndamnit@2.0.0:
|
||||
resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==}
|
||||
dependencies:
|
||||
cross-spawn: 5.1.0
|
||||
cross-spawn: 7.0.6
|
||||
signal-exit: 3.0.7
|
||||
dev: true
|
||||
|
||||
@@ -32031,7 +31999,7 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
/svelte-check@3.8.6(@babel/core@7.25.2)(postcss@8.4.47)(svelte@4.2.19):
|
||||
/svelte-check@3.8.6(postcss@8.4.47)(svelte@4.2.19):
|
||||
resolution: {integrity: sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -32042,7 +32010,7 @@ packages:
|
||||
picocolors: 1.1.0
|
||||
sade: 1.8.1
|
||||
svelte: 4.2.19
|
||||
svelte-preprocess: 5.1.4(@babel/core@7.25.2)(postcss@8.4.47)(svelte@4.2.19)(typescript@5.5.4)
|
||||
svelte-preprocess: 5.1.4(postcss@8.4.47)(svelte@4.2.19)(typescript@5.5.4)
|
||||
typescript: 5.5.4
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
@@ -32082,7 +32050,7 @@ packages:
|
||||
svelte: 4.2.19
|
||||
dev: true
|
||||
|
||||
/svelte-preprocess@5.1.4(@babel/core@7.25.2)(postcss@8.4.47)(svelte@4.2.19)(typescript@5.5.4):
|
||||
/svelte-preprocess@5.1.4(postcss@8.4.47)(svelte@4.2.19)(typescript@5.5.4):
|
||||
resolution: {integrity: sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==}
|
||||
engines: {node: '>= 16.0.0'}
|
||||
requiresBuild: true
|
||||
@@ -32120,7 +32088,6 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/core': 7.25.2
|
||||
'@types/pug': 2.0.10
|
||||
detect-indent: 6.1.0
|
||||
magic-string: 0.30.11
|
||||
@@ -32268,6 +32235,37 @@ packages:
|
||||
- ts-node
|
||||
dev: false
|
||||
|
||||
/tailwindcss@3.4.12:
|
||||
resolution: {integrity: sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@alloc/quick-lru': 5.2.0
|
||||
arg: 5.0.2
|
||||
chokidar: 3.6.0
|
||||
didyoumean: 1.2.2
|
||||
dlv: 1.1.3
|
||||
fast-glob: 3.3.2
|
||||
glob-parent: 6.0.2
|
||||
is-glob: 4.0.3
|
||||
jiti: 1.21.6
|
||||
lilconfig: 2.1.0
|
||||
micromatch: 4.0.8
|
||||
normalize-path: 3.0.0
|
||||
object-hash: 3.0.0
|
||||
picocolors: 1.1.0
|
||||
postcss: 8.4.47
|
||||
postcss-import: 15.1.0(postcss@8.4.47)
|
||||
postcss-js: 4.0.1(postcss@8.4.47)
|
||||
postcss-load-config: 4.0.2(postcss@8.4.47)
|
||||
postcss-nested: 6.2.0(postcss@8.4.47)
|
||||
postcss-selector-parser: 6.1.2
|
||||
resolve: 1.22.8
|
||||
sucrase: 3.35.0
|
||||
transitivePeerDependencies:
|
||||
- ts-node
|
||||
dev: true
|
||||
|
||||
/tailwindcss@3.4.12(ts-node@10.9.2):
|
||||
resolution: {integrity: sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -34059,6 +34057,45 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/vite@5.4.6(@types/node@16.18.106):
|
||||
resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^18.0.0 || >=20.0.0
|
||||
less: '*'
|
||||
lightningcss: ^1.21.0
|
||||
sass: '*'
|
||||
sass-embedded: '*'
|
||||
stylus: '*'
|
||||
sugarss: '*'
|
||||
terser: ^5.4.0
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
lightningcss:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/node': 16.18.106
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.4.47
|
||||
rollup: 4.24.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/vite@5.4.6(@types/node@16.18.106)(sass@1.32.0):
|
||||
resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@@ -34145,7 +34182,7 @@ packages:
|
||||
vite:
|
||||
optional: true
|
||||
dependencies:
|
||||
vite: 5.4.6(@types/node@16.18.106)(sass@1.32.0)
|
||||
vite: 5.4.6(@types/node@16.18.106)
|
||||
dev: true
|
||||
|
||||
/vitest@0.25.8:
|
||||
@@ -34183,7 +34220,7 @@ packages:
|
||||
tinybench: 2.9.0
|
||||
tinypool: 0.3.1
|
||||
tinyspy: 1.1.1
|
||||
vite: 5.4.6(@types/node@16.18.106)(sass@1.32.0)
|
||||
vite: 5.4.6(@types/node@16.18.106)
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
- lightningcss
|
||||
@@ -34921,6 +34958,7 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
dev: false
|
||||
|
||||
/which@2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
@@ -35279,10 +35317,6 @@ packages:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
/yallist@2.1.2:
|
||||
resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
|
||||
dev: true
|
||||
|
||||
/yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user