Compare commits
2 Commits
release-20
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e4282b094 | ||
|
|
81e1d78315 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "2.4.0",
|
||||
"version": "2.5.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 />
|
||||
|
||||
@@ -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,99 @@ 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={() => 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>
|
||||
|
||||
@@ -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';
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user