Compare commits
8 Commits
@nhost/das
...
@nhost/apo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60bcd8f949 | ||
|
|
e28975d6a5 | ||
|
|
33284d3cf0 | ||
|
|
1dbd65eb0e | ||
|
|
6eec78f9c5 | ||
|
|
e3f0732108 | ||
|
|
807b8c049a | ||
|
|
998c0376bf |
@@ -17,5 +17,10 @@ NEXT_PUBLIC_GITHUB_APP_INSTALL_URL=<github_app_install_url>
|
||||
NEXT_PUBLIC_ANALYTICS_WRITE_KEY=<analytics_write_key>
|
||||
NEXT_PUBLIC_NHOST_BRAGI_WEBSOCKET=<nhost_bragi_websocket>
|
||||
|
||||
NEXT_PUBLIC_ZENDESK_URL=
|
||||
NEXT_PUBLIC_ZENDESK_API_KEY=
|
||||
NEXT_PUBLIC_ZENDESK_USER_EMAIL=
|
||||
|
||||
|
||||
CODEGEN_GRAPHQL_URL=https://local.graphql.nhost.run/v1
|
||||
CODEGEN_HASURA_ADMIN_SECRET=nhost-admin-secret
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @nhost/dashboard
|
||||
|
||||
## 1.23.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 33284d3: fix: don't show double scrollbar in configuration editor
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react-apollo@12.0.3
|
||||
- @nhost/nextjs@2.1.17
|
||||
|
||||
## 1.22.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 998c037: fix: align drop-down list in select component
|
||||
- 807b8c0: fix: show city name in region selection for project creation
|
||||
|
||||
## 1.21.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "1.21.0",
|
||||
"version": "1.23.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function CountrySelector({
|
||||
listbox: { className: 'min-w-0 w-full' },
|
||||
popper: {
|
||||
disablePortal: false,
|
||||
className: 'z-[10000] w-[270px] w-full',
|
||||
className: 'z-[10000] w-[270px]',
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ContactUs } from '@/components/common/ContactUs';
|
||||
import { useDialog } from '@/components/common/DialogProvider';
|
||||
import { NavLink } from '@/components/common/NavLink';
|
||||
import { AccountMenu } from '@/components/layout/AccountMenu';
|
||||
@@ -9,11 +8,9 @@ import { Logo } from '@/components/presentational/Logo';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Chip } from '@/components/ui/v2/Chip';
|
||||
import { Dropdown } from '@/components/ui/v2/Dropdown';
|
||||
import { GraphiteIcon } from '@/components/ui/v2/icons/GraphiteIcon';
|
||||
import { DevAssistant } from '@/features/ai/DevAssistant';
|
||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||
import { useIsCurrentUserOwner } from '@/features/projects/common/hooks/useIsCurrentUserOwner';
|
||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||
import { ApplicationStatus } from '@/types/application';
|
||||
import { getToastStyleProps } from '@/utils/constants/settings';
|
||||
@@ -38,8 +35,6 @@ export default function Header({ className, ...props }: HeaderProps) {
|
||||
const { currentProject, refetch: refetchProject } =
|
||||
useCurrentWorkspaceAndProject();
|
||||
|
||||
const isOwner = useIsCurrentUserOwner();
|
||||
|
||||
const isProjectUpdating =
|
||||
currentProject?.appStates[0]?.stateId === ApplicationStatus.Updating;
|
||||
|
||||
@@ -105,25 +100,19 @@ export default function Header({ className, ...props }: HeaderProps) {
|
||||
</Button>
|
||||
|
||||
{isPlatform && (
|
||||
<Dropdown.Root>
|
||||
<Dropdown.Trigger
|
||||
hideChevron
|
||||
className="rounded-md px-2.5 py-1.5 text-sm motion-safe:transition-colors"
|
||||
>
|
||||
Contact us
|
||||
</Dropdown.Trigger>
|
||||
|
||||
<Dropdown.Content
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||
>
|
||||
<ContactUs
|
||||
className="max-w-md"
|
||||
isTeam={currentProject?.plan?.name === 'Team'}
|
||||
isOwner={isOwner}
|
||||
/>
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
<NavLink
|
||||
underline="none"
|
||||
href="/support"
|
||||
className="mr-2 rounded-md px-2.5 py-1.5 text-sm motion-safe:transition-colors"
|
||||
sx={{
|
||||
color: 'text.primary',
|
||||
'&:hover': { backgroundColor: 'grey.200' },
|
||||
}}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Support
|
||||
</NavLink>
|
||||
)}
|
||||
|
||||
<NavLink
|
||||
|
||||
@@ -47,46 +47,51 @@ export default function SettingsLayout({
|
||||
sx={{ backgroundColor: 'background.default' }}
|
||||
className="flex w-full flex-auto flex-col overflow-y-auto overflow-x-hidden"
|
||||
>
|
||||
<RetryableErrorBoundary>
|
||||
<div className="flex flex-col space-y-2">
|
||||
{hasGitRepo && (
|
||||
<Alert
|
||||
severity="warning"
|
||||
className="grid grid-flow-row place-content-center gap-2"
|
||||
>
|
||||
<Text color="warning" className="text-sm ">
|
||||
As you have a connected repository, make sure to synchronize
|
||||
your changes with{' '}
|
||||
<code
|
||||
className={twMerge(
|
||||
'rounded-md px-2 py-px',
|
||||
theme.palette.mode === 'dark'
|
||||
? 'bg-brown text-copper'
|
||||
: 'bg-slate-200 text-slate-700',
|
||||
)}
|
||||
>
|
||||
nhost config pull
|
||||
</code>{' '}
|
||||
or they may be reverted with the next push.
|
||||
<br />
|
||||
If there are multiple projects linked to the same repository
|
||||
and you only want these changes to apply to a subset of them,
|
||||
please check out{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline"
|
||||
href="https://docs.nhost.io/cli/overlays"
|
||||
>
|
||||
docs.nhost.io/cli/overlays
|
||||
</a>{' '}
|
||||
for guidance.
|
||||
</Text>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
{children}
|
||||
</RetryableErrorBoundary>
|
||||
<Box
|
||||
sx={{ backgroundColor: 'background.default' }}
|
||||
className="flex h-full flex-col"
|
||||
>
|
||||
<RetryableErrorBoundary>
|
||||
<div className="flex flex-col space-y-2">
|
||||
{hasGitRepo && (
|
||||
<Alert
|
||||
severity="warning"
|
||||
className="grid grid-flow-row place-content-center gap-2"
|
||||
>
|
||||
<Text color="warning" className="text-sm ">
|
||||
As you have a connected repository, make sure to synchronize
|
||||
your changes with{' '}
|
||||
<code
|
||||
className={twMerge(
|
||||
'rounded-md px-2 py-px',
|
||||
theme.palette.mode === 'dark'
|
||||
? 'bg-brown text-copper'
|
||||
: 'bg-slate-200 text-slate-700',
|
||||
)}
|
||||
>
|
||||
nhost config pull
|
||||
</code>{' '}
|
||||
or they may be reverted with the next push.
|
||||
<br />
|
||||
If there are multiple projects linked to the same repository
|
||||
and you only want these changes to apply to a subset of
|
||||
them, please check out{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline"
|
||||
href="https://docs.nhost.io/cli/overlays"
|
||||
>
|
||||
docs.nhost.io/cli/overlays
|
||||
</a>{' '}
|
||||
for guidance.
|
||||
</Text>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
{children}
|
||||
</RetryableErrorBoundary>
|
||||
</Box>
|
||||
</Box>
|
||||
</ProjectLayout>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default function CommunityIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M5.5 10C7.29493 10 8.75 8.54493 8.75 6.75C8.75 4.95507 7.29493 3.5 5.5 3.5C3.70507 3.5 2.25 4.95507 2.25 6.75C2.25 8.54493 3.70507 10 5.5 10Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeMiterlimit="10"
|
||||
/>
|
||||
<path
|
||||
d="M9.71338 3.62107C10.1604 3.49513 10.6292 3.46644 11.0882 3.53693C11.5473 3.60743 11.9859 3.77547 12.3745 4.02975C12.7631 4.28403 13.0927 4.61863 13.3411 5.01102C13.5896 5.40342 13.751 5.84449 13.8146 6.30453C13.8782 6.76457 13.8425 7.2329 13.7098 7.67797C13.5772 8.12304 13.3507 8.53452 13.0457 8.8847C12.7406 9.23487 12.364 9.51561 11.9413 9.70799C11.5187 9.90038 11.0596 9.99996 10.5952 10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M1 12.3373C1.50758 11.6153 2.18143 11.026 2.96466 10.6192C3.74788 10.2124 4.61748 10 5.50005 10C6.38262 9.99997 7.25224 10.2123 8.0355 10.619C8.81875 11.0258 9.49264 11.615 10.0003 12.337"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.5952 10C11.4779 9.99936 12.3477 10.2114 13.131 10.6182C13.9143 11.025 14.5881 11.6146 15.0952 12.337"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as CommunityIcon } from './CommunityIcon';
|
||||
@@ -0,0 +1,29 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default function DiscordIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width={14}
|
||||
height={14}
|
||||
viewBox="0 0 14 14"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M3.40571 1.5H12.5943C13.3691 1.5 14 2.13086 14 2.91257V15.2143L12.5257 13.9114L11.696 13.1434L10.8183 12.3274L11.1817 13.596H3.40571C2.63086 13.596 2 12.9651 2 12.1834V2.91257C2 2.13086 2.63086 1.5 3.40571 1.5ZM9.49486 9.9C9.70057 10.1606 9.94743 10.4554 9.94743 10.4554C11.4629 10.4074 12.0457 9.41314 12.0457 9.41314C12.0457 7.20514 11.0583 5.41543 11.0583 5.41543C10.0709 4.67486 9.13143 4.69543 9.13143 4.69543L9.03543 4.80514C10.2011 5.16171 10.7429 5.676 10.7429 5.676C10.0297 5.28514 9.33029 5.09314 8.67886 5.01771C8.18514 4.96286 7.712 4.97657 7.29371 5.03143C7.2578 5.03143 7.2271 5.03665 7.19251 5.04254C7.18748 5.0434 7.18237 5.04427 7.17714 5.04514C6.93714 5.06571 6.35429 5.15486 5.62057 5.47714C5.36686 5.59371 5.216 5.676 5.216 5.676C5.216 5.676 5.78514 5.13429 7.01943 4.77771L6.95086 4.69543C6.95086 4.69543 6.01143 4.67486 5.024 5.41543C5.024 5.41543 4.03657 7.20514 4.03657 9.41314C4.03657 9.41314 4.61257 10.4074 6.128 10.4554C6.128 10.4554 6.38171 10.1469 6.58743 9.88628C5.71657 9.62571 5.38743 9.07714 5.38743 9.07714C5.38743 9.07714 5.456 9.12514 5.57943 9.19371C5.58629 9.20057 5.59314 9.20743 5.60686 9.21428C5.61714 9.22114 5.62743 9.22628 5.63771 9.23143C5.648 9.23657 5.65829 9.24171 5.66857 9.24857C5.84 9.34457 6.01143 9.42 6.16914 9.48171C6.45029 9.59143 6.78629 9.70114 7.17714 9.77657C7.69143 9.87257 8.29486 9.90686 8.95314 9.78343C9.27543 9.72857 9.60457 9.63257 9.94743 9.48857C10.1874 9.39943 10.4549 9.26914 10.736 9.084C10.736 9.084 10.3931 9.64628 9.49486 9.9ZM6.05942 8.01421C6.05942 7.59593 6.36799 7.25307 6.75885 7.25307C7.1497 7.25307 7.46513 7.59593 7.45827 8.01421C7.45827 8.4325 7.1497 8.77536 6.75885 8.77536C6.37485 8.77536 6.05942 8.4325 6.05942 8.01421ZM8.56227 8.01421C8.56227 7.59593 8.87084 7.25307 9.2617 7.25307C9.65256 7.25307 9.96113 7.59593 9.96113 8.01421C9.96113 8.4325 9.65256 8.77536 9.2617 8.77536C8.8777 8.77536 8.56227 8.4325 8.56227 8.01421Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as DiscordIcon } from './DiscordIcon';
|
||||
@@ -0,0 +1,37 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default function EnvelopeIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width={14}
|
||||
height={14}
|
||||
viewBox="0 0 14 14"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2 3.5H14V12C14 12.1326 13.9473 12.2598 13.8536 12.3536C13.7598 12.4473 13.6326 12.5 13.5 12.5H2.5C2.36739 12.5 2.24021 12.4473 2.14645 12.3536C2.05268 12.2598 2 12.1326 2 12V3.5Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14 3.5L8 9L2 3.5"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as EnvelopeIcon } from './EnvelopeIcon';
|
||||
@@ -117,7 +117,7 @@ export default function ArgumentsFormSection({
|
||||
listbox: { className: 'min-w-0 w-full' },
|
||||
popper: {
|
||||
disablePortal: false,
|
||||
className: 'z-[10000] w-[270px] w-full',
|
||||
className: 'z-[10000] w-[270px]',
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -143,7 +143,7 @@ export default function TOMLEditor() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box className="flex h-full flex-col">
|
||||
<>
|
||||
<Box className="flex w-full flex-col space-y-2 border-b p-4">
|
||||
<Text className="font-semibold">Configuration Editor</Text>
|
||||
</Box>
|
||||
@@ -187,6 +187,6 @@ export default function TOMLEditor() {
|
||||
Save
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ export default function PortsFormSection() {
|
||||
listbox: { className: 'min-w-0 w-full' },
|
||||
popper: {
|
||||
disablePortal: false,
|
||||
className: 'z-[10000] w-[270px] w-full',
|
||||
className: 'z-[10000] w-[270px]',
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -301,7 +301,7 @@ export function NewProjectPageContent({
|
||||
const regionInList = regions.find(({ id }) => id === value);
|
||||
setSelectedRegion({
|
||||
id: regionInList.id,
|
||||
name: regionInList.country.name,
|
||||
name: regionInList.city,
|
||||
disabled: false,
|
||||
code: regionInList.country.code,
|
||||
});
|
||||
|
||||
128
dashboard/src/pages/support/index.tsx
Normal file
128
dashboard/src/pages/support/index.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import { Logo } from '@/components/presentational/Logo';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { ArrowRightIcon } from '@/components/ui/v2/icons/ArrowRightIcon';
|
||||
import { CommunityIcon } from '@/components/ui/v2/icons/CommunityIcon';
|
||||
import { FileTextIcon } from '@/components/ui/v2/icons/FileTextIcon';
|
||||
import { GitHubIcon } from '@/components/ui/v2/icons/GitHubIcon';
|
||||
import { Link } from '@/components/ui/v2/Link';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
|
||||
function SupportPage() {
|
||||
return (
|
||||
<Box className="h-screen pb-4 overflow-auto">
|
||||
<Box className="flex justify-start w-full px-4 py-3 border-b-1">
|
||||
<Logo className="cursor-pointer" />
|
||||
</Box>
|
||||
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<Box
|
||||
sx={{ backgroundColor: 'background.default' }}
|
||||
className="flex flex-col items-center justify-center w-full h-64 gap-10 px-4 mb-10 border-b-1"
|
||||
>
|
||||
<div>
|
||||
<Text variant="h4">Nhost Support</Text>
|
||||
<Text variant="h2">How can we help?</Text>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => window.open('https://docs.nhost.io')}
|
||||
className="h-10 w-full xs+:w-98"
|
||||
startIcon={<FileTextIcon className="self-center w-4 h-4" />}
|
||||
>
|
||||
Read our docs
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Box className="flex flex-row items-center justify-center w-full gap-10">
|
||||
<div className="flex w-[900px] flex-col gap-10 p-4">
|
||||
<div className="flex flex-col w-full gap-10 md:flex-row">
|
||||
<Box
|
||||
className="flex flex-col w-full h-full gap-12 px-4 py-3 rounded-lg shadow-sm place-content-between"
|
||||
sx={{ backgroundColor: 'grey.200' }}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<GitHubIcon className="w-8 h-8" />
|
||||
<div className="grid grid-flow-row gap-1">
|
||||
<Text variant="h3" className="!font-bold">
|
||||
Issues & feature requests
|
||||
</Text>
|
||||
<Text className="!font-medium" color="secondary">
|
||||
Found a bug? We'd love to hear about it in our GitHub
|
||||
issues.
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
variant="body2"
|
||||
underline="hover"
|
||||
href="https://github.com/nhost/nhost/issues/new/choose"
|
||||
target="_blank"
|
||||
rel="dofollow"
|
||||
className="grid items-center justify-start grid-flow-col gap-1 font-medium"
|
||||
>
|
||||
Open new Issue / Feature request
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
</Link>
|
||||
</Box>
|
||||
<Box
|
||||
className="flex flex-col w-full h-full gap-12 px-4 py-3 rounded-lg shadow-sm place-content-between"
|
||||
sx={{ backgroundColor: 'grey.200' }}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<CommunityIcon className="w-8 h-8" />
|
||||
<div className="grid grid-flow-row gap-1">
|
||||
<Text variant="h3" className="!font-bold">
|
||||
Ask the Community
|
||||
</Text>
|
||||
<Text className="!font-medium" color="secondary">
|
||||
Join our Discord server to browse for help and best
|
||||
practices.
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
variant="body2"
|
||||
underline="hover"
|
||||
href="https://discord.com/invite/9V7Qb2U"
|
||||
target="_blank"
|
||||
rel="dofollow"
|
||||
className="grid items-center justify-start grid-flow-col gap-1 font-medium"
|
||||
>
|
||||
Join our Discord
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
</Link>
|
||||
</Box>
|
||||
</div>
|
||||
<Box className="flex h-full w-full flex-col place-content-between gap-4 rounded-lg border p-4 shadow-sm xs+:flex-row">
|
||||
<div className="flex flex-1">
|
||||
<Text variant="h3" className="w-full">
|
||||
Can't find what you're looking for?
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1 gap-4">
|
||||
<Text variant="h4">Our Support Team is ready to help.</Text>
|
||||
<Text>
|
||||
Response time for support tickets will vary depending on plan
|
||||
type and severity of the issue.
|
||||
</Text>
|
||||
<Link
|
||||
variant="body2"
|
||||
underline="hover"
|
||||
href="/support/ticket"
|
||||
target="_blank"
|
||||
rel="dofollow"
|
||||
className="grid items-center justify-start grid-flow-col gap-1 font-medium"
|
||||
>
|
||||
Create ticket
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default SupportPage;
|
||||
378
dashboard/src/pages/support/ticket.tsx
Normal file
378
dashboard/src/pages/support/ticket.tsx
Normal file
@@ -0,0 +1,378 @@
|
||||
import { ControlledAutocomplete } from '@/components/form/ControlledAutocomplete';
|
||||
import { ControlledSelect } from '@/components/form/ControlledSelect';
|
||||
import { Form } from '@/components/form/Form';
|
||||
import { AuthenticatedLayout } from '@/components/layout/AuthenticatedLayout';
|
||||
import { Box } from '@/components/ui/v2/Box';
|
||||
import { Button } from '@/components/ui/v2/Button';
|
||||
import { Divider } from '@/components/ui/v2/Divider';
|
||||
import { EnvelopeIcon } from '@/components/ui/v2/icons/EnvelopeIcon';
|
||||
import { Input, inputClasses } from '@/components/ui/v2/Input';
|
||||
import { Option } from '@/components/ui/v2/Option';
|
||||
import { Text } from '@/components/ui/v2/Text';
|
||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||
import {
|
||||
useGetAllWorkspacesAndProjectsQuery,
|
||||
type GetAllWorkspacesAndProjectsQuery,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { styled } from '@mui/material';
|
||||
import { useUserData } from '@nhost/nextjs';
|
||||
import { type ReactElement } from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import * as Yup from 'yup';
|
||||
|
||||
type Workspace = Omit<
|
||||
GetAllWorkspacesAndProjectsQuery['workspaces'][0],
|
||||
'__typename'
|
||||
>;
|
||||
|
||||
const validationSchema = Yup.object({
|
||||
workspace: Yup.string().label('Project').required(),
|
||||
project: Yup.string().label('Project').required(),
|
||||
services: Yup.array()
|
||||
.of(Yup.object({ label: Yup.string(), value: Yup.string() }))
|
||||
.label('Services')
|
||||
.required(),
|
||||
priority: Yup.string().label('Priority').required(),
|
||||
subject: Yup.string().label('Subject').required(),
|
||||
description: Yup.string().label('Description').required(),
|
||||
ccs: Yup.string().label('CCs').optional(),
|
||||
});
|
||||
|
||||
export type CreateTicketFormValues = Yup.InferType<typeof validationSchema>;
|
||||
|
||||
const StyledInput = styled(Input)({
|
||||
backgroundColor: 'transparent',
|
||||
[`& .${inputClasses.input}`]: {
|
||||
backgroundColor: 'transparent !important',
|
||||
},
|
||||
});
|
||||
|
||||
function TicketPage() {
|
||||
const form = useForm<CreateTicketFormValues>({
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: {
|
||||
workspace: '',
|
||||
project: '',
|
||||
services: [],
|
||||
priority: '',
|
||||
subject: '',
|
||||
description: '',
|
||||
ccs: '',
|
||||
},
|
||||
resolver: yupResolver(validationSchema),
|
||||
});
|
||||
|
||||
const {
|
||||
register,
|
||||
watch,
|
||||
formState: { errors, isSubmitting },
|
||||
} = form;
|
||||
|
||||
const selectedWorkspace = watch('workspace');
|
||||
const user = useUserData();
|
||||
|
||||
const { data } = useGetAllWorkspacesAndProjectsQuery({
|
||||
skip: !user,
|
||||
});
|
||||
|
||||
const workspaces: Workspace[] = data?.workspaces || [];
|
||||
|
||||
const handleSubmit = async (formValues: CreateTicketFormValues) => {
|
||||
const { project, services, priority, subject, description, ccs } =
|
||||
formValues;
|
||||
|
||||
const auth = btoa(
|
||||
`${process.env.NEXT_PUBLIC_ZENDESK_USER_EMAIL}/token:${process.env.NEXT_PUBLIC_ZENDESK_API_KEY}`,
|
||||
);
|
||||
const emails = ccs
|
||||
.replace(/ /g, '')
|
||||
.split(',')
|
||||
.map((email) => ({ user_email: email }));
|
||||
|
||||
await execPromiseWithErrorToast(
|
||||
async () => {
|
||||
await fetch(
|
||||
`${process.env.NEXT_PUBLIC_ZENDESK_URL}/api/v2/requests.json`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Basic ${auth}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
request: {
|
||||
subject,
|
||||
comment: {
|
||||
body: description,
|
||||
},
|
||||
priority,
|
||||
requester: {
|
||||
name: user?.displayName,
|
||||
email: user?.email,
|
||||
},
|
||||
email_ccs: emails,
|
||||
custom_fields: [
|
||||
// these custom field IDs come from zendesk
|
||||
{
|
||||
id: 19502784542098,
|
||||
value: project,
|
||||
},
|
||||
{
|
||||
id: 19922709880978,
|
||||
value: services.map((service) =>
|
||||
service.value.toLowerCase(),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
form.reset();
|
||||
},
|
||||
{
|
||||
loadingMessage: 'Creating Ticket...',
|
||||
successMessage: 'Ticket created successfully',
|
||||
errorMessage: 'Failed to create ticket',
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="flex flex-col items-center justify-center py-10"
|
||||
sx={{ backgroundColor: 'background.default' }}
|
||||
>
|
||||
<div className="flex w-full max-w-3xl flex-col">
|
||||
<div className="mb-4 flex flex-col items-center">
|
||||
<Text variant="h4" className="font-bold">
|
||||
Nhost Support
|
||||
</Text>
|
||||
<Text variant="h4">How can we help you?</Text>
|
||||
</div>
|
||||
<Box className="w-full rounded-md border p-10">
|
||||
<Box className="grid grid-flow-row gap-4">
|
||||
<Box className="flex flex-col gap-4">
|
||||
<FormProvider {...form}>
|
||||
<Form
|
||||
onSubmit={handleSubmit}
|
||||
className="grid grid-flow-row gap-4"
|
||||
>
|
||||
<Text className="font-bold">Which project is affected ?</Text>
|
||||
|
||||
<ControlledSelect
|
||||
id="workspace"
|
||||
name="workspace"
|
||||
label="Workspace"
|
||||
placeholder="Workspace"
|
||||
slotProps={{
|
||||
root: { className: 'grid grid-flow-col gap-1' },
|
||||
}}
|
||||
error={!!errors.workspace}
|
||||
helperText={errors.workspace?.message}
|
||||
renderValue={(option) => (
|
||||
<span className="inline-grid grid-flow-col items-center gap-2">
|
||||
{option?.label}
|
||||
</span>
|
||||
)}
|
||||
>
|
||||
{workspaces.map((workspace) => (
|
||||
<Option
|
||||
key={workspace.name}
|
||||
value={workspace.id}
|
||||
label={workspace.name}
|
||||
>
|
||||
{workspace.name}
|
||||
</Option>
|
||||
))}
|
||||
</ControlledSelect>
|
||||
|
||||
<ControlledSelect
|
||||
id="project"
|
||||
name="project"
|
||||
label="Project"
|
||||
placeholder="Project"
|
||||
slotProps={{
|
||||
root: { className: 'grid grid-flow-col gap-1 mb-4' },
|
||||
}}
|
||||
error={!!errors.project}
|
||||
helperText={errors.project?.message}
|
||||
renderValue={(option) => (
|
||||
<span className="inline-grid grid-flow-col items-center gap-2">
|
||||
{option?.label}
|
||||
</span>
|
||||
)}
|
||||
>
|
||||
{(
|
||||
workspaces.find((w) => w.id === selectedWorkspace)
|
||||
?.projects || []
|
||||
).map((proj) => (
|
||||
<Option
|
||||
key={proj.subdomain}
|
||||
value={proj.subdomain}
|
||||
label={proj.name}
|
||||
>
|
||||
<div className="flex flex-col">{proj.name}</div>
|
||||
</Option>
|
||||
))}
|
||||
</ControlledSelect>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Text className="mt-4 font-bold">Impact</Text>
|
||||
|
||||
<ControlledAutocomplete
|
||||
id="services"
|
||||
name="services"
|
||||
label="services"
|
||||
fullWidth
|
||||
multiple
|
||||
aria-label="Enabled APIs"
|
||||
options={[
|
||||
'Dashboard',
|
||||
'Database',
|
||||
'Authentication',
|
||||
'Storage',
|
||||
'Hasura/APIs',
|
||||
'Functions',
|
||||
'Run',
|
||||
'Graphite',
|
||||
'Other',
|
||||
].map((s) => ({ label: s, value: s }))}
|
||||
error={!!errors?.services?.message}
|
||||
helperText={errors?.services?.message}
|
||||
/>
|
||||
|
||||
<ControlledSelect
|
||||
id="priority"
|
||||
name="priority"
|
||||
label="Priority"
|
||||
placeholder="Priority"
|
||||
slotProps={{
|
||||
root: { className: 'grid grid-flow-col gap-1 mb-4' },
|
||||
}}
|
||||
renderValue={(option) => (
|
||||
<span className="inline-grid grid-flow-col items-center gap-2">
|
||||
{option?.label}
|
||||
</span>
|
||||
)}
|
||||
>
|
||||
{[
|
||||
{
|
||||
title: 'Low',
|
||||
description: 'General guidance',
|
||||
},
|
||||
{
|
||||
title: 'Normal',
|
||||
description: 'Non-production system impaired',
|
||||
},
|
||||
{
|
||||
title: 'High',
|
||||
description: 'Production System impaired',
|
||||
},
|
||||
{
|
||||
title: 'Urgent',
|
||||
description: 'Production system offline',
|
||||
},
|
||||
].map((priority) => (
|
||||
<Option
|
||||
key={priority.title}
|
||||
label={priority.title}
|
||||
value={priority.title.toLowerCase()}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<span>{priority.title}</span>
|
||||
<span className="font-mono text-xs opacity-50">
|
||||
{priority.description}
|
||||
</span>
|
||||
</div>
|
||||
</Option>
|
||||
))}
|
||||
</ControlledSelect>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Text className="mt-4 font-bold">Issue</Text>
|
||||
|
||||
<StyledInput
|
||||
{...register('subject')}
|
||||
id="subject"
|
||||
label="Subject"
|
||||
placeholder="Summary of the problem you are experiencing"
|
||||
fullWidth
|
||||
autoFocus
|
||||
inputProps={{ min: 2, max: 128 }}
|
||||
error={!!errors.subject}
|
||||
helperText={errors.subject?.message}
|
||||
/>
|
||||
|
||||
<StyledInput
|
||||
{...register('description')}
|
||||
id="description"
|
||||
label="Description"
|
||||
placeholder="Describe the issue you are experiencing in detail, along with any relevant information. Please be as detailed as possible."
|
||||
fullWidth
|
||||
multiline
|
||||
inputProps={{
|
||||
className: 'resize-y min-h-[120px]',
|
||||
}}
|
||||
error={!!errors.description}
|
||||
helperText={errors.description?.message}
|
||||
/>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Text className="mt-4 font-bold">Notifications</Text>
|
||||
|
||||
<StyledInput
|
||||
{...register('ccs')}
|
||||
id="ccs"
|
||||
label="CCs"
|
||||
placeholder="Comma separated list of emails you want to share this ticket with."
|
||||
fullWidth
|
||||
inputProps={{ min: 2, max: 128 }}
|
||||
error={!!errors.ccs}
|
||||
helperText={errors.ccs?.message}
|
||||
/>
|
||||
|
||||
<Box className="ml-auto flex w-80 flex-col gap-4">
|
||||
<Text color="secondary" className="text-right text-sm">
|
||||
We will contact you at <strong>{user?.email}</strong>
|
||||
</Text>
|
||||
<Button
|
||||
variant="outlined"
|
||||
className=" hover:!bg-white hover:!bg-opacity-10 focus:ring-0"
|
||||
size="large"
|
||||
type="submit"
|
||||
startIcon={<EnvelopeIcon />}
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
Create Support Ticket
|
||||
</Button>
|
||||
</Box>
|
||||
</Form>
|
||||
</FormProvider>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
TicketPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
title="Help & Support | Nhost"
|
||||
contentContainerProps={{
|
||||
className: 'flex w-full flex-col h-screen overflow-auto',
|
||||
}}
|
||||
>
|
||||
{page}
|
||||
</AuthenticatedLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketPage;
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/cli
|
||||
|
||||
## 0.3.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.1.6
|
||||
|
||||
## 0.3.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/cli",
|
||||
"version": "0.3.7",
|
||||
"version": "0.3.8",
|
||||
"main": "src/index.mjs",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/codegen-react-apollo
|
||||
|
||||
## 0.4.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
- @nhost/react-apollo@12.0.3
|
||||
|
||||
## 0.4.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-apollo",
|
||||
"version": "0.4.7",
|
||||
"version": "0.4.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "graphql-codegen",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/codegen-react-query
|
||||
|
||||
## 0.4.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
|
||||
## 0.4.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-query",
|
||||
"version": "0.4.7",
|
||||
"version": "0.4.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"codegen": "graphql-codegen",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/react-urql
|
||||
|
||||
## 0.3.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
- @nhost/react-urql@9.0.3
|
||||
|
||||
## 0.3.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-urql",
|
||||
"private": true,
|
||||
"version": "0.3.7",
|
||||
"version": "0.3.8",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/multi-tenant-one-to-many
|
||||
|
||||
## 2.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.1.6
|
||||
|
||||
## 2.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/multi-tenant-one-to-many",
|
||||
"private": true,
|
||||
"version": "2.2.7",
|
||||
"version": "2.2.8",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost-examples/nextjs
|
||||
|
||||
## 0.3.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
- @nhost/react-apollo@12.0.3
|
||||
- @nhost/nextjs@2.1.17
|
||||
|
||||
## 0.3.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/nextjs",
|
||||
"version": "0.3.7",
|
||||
"version": "0.3.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/node-storage
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.1.6
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/node-storage",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"private": true,
|
||||
"description": "This is an example of how to use the Storage with Node.js",
|
||||
"main": "src/index.mjs",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/nextjs-server-components
|
||||
|
||||
## 0.4.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.1.6
|
||||
|
||||
## 0.4.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/nextjs-server-components",
|
||||
"version": "0.4.8",
|
||||
"version": "0.4.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @nhost-examples/react-apollo
|
||||
|
||||
## 0.8.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
- @nhost/react-apollo@12.0.3
|
||||
|
||||
## 0.8.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e3f0732: fix: add verify email button instead of doing an auto-redirect
|
||||
|
||||
## 0.8.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -31,7 +31,8 @@ test('should be able to change email', async ({ page, browser }) => {
|
||||
page: mailhogPage,
|
||||
email: newEmail,
|
||||
context: mailhogPage.context(),
|
||||
linkText: /change email/i
|
||||
linkText: /change email/i,
|
||||
requestType: 'email-confirm-change'
|
||||
})
|
||||
|
||||
await expect(updatedEmailPage.getByText(/profile page/i)).toBeVisible()
|
||||
|
||||
@@ -158,8 +158,8 @@ export async function resetPassword({
|
||||
.click()
|
||||
|
||||
const authenticatedPage = await authenticatedPagePromise
|
||||
await authenticatedPage.getByRole('button', { name: /Verify/i }).click()
|
||||
await authenticatedPage.waitForLoadState()
|
||||
|
||||
return authenticatedPage
|
||||
}
|
||||
|
||||
@@ -177,25 +177,31 @@ export async function verifyEmail({
|
||||
email,
|
||||
page,
|
||||
context,
|
||||
linkText = /verify email/i
|
||||
linkText = /verify email/i,
|
||||
requestType
|
||||
}: {
|
||||
email: string
|
||||
page: Page
|
||||
context: BrowserContext
|
||||
linkText?: string | RegExp
|
||||
requestType?: 'email-confirm-change' | 'email-verify' | 'password-reset' | 'signin-passwordless'
|
||||
}) {
|
||||
await page.goto(mailhogURL)
|
||||
await page.locator('.messages > .msglist-message', { hasText: email }).nth(0).click()
|
||||
|
||||
// Based on: https://playwright.dev/docs/pages#handling-new-pages
|
||||
const authenticatedPagePromise = context.waitForEvent('page')
|
||||
|
||||
const verifyEmailPagePromise = context.waitForEvent('page')
|
||||
await page.frameLocator('#preview-html').getByRole('link', { name: linkText }).click()
|
||||
const verifyEmailPage = await verifyEmailPagePromise
|
||||
await verifyEmailPage.waitForLoadState()
|
||||
|
||||
const authenticatedPage = await authenticatedPagePromise
|
||||
await authenticatedPage.waitForLoadState()
|
||||
if (requestType === 'email-confirm-change') {
|
||||
return verifyEmailPage
|
||||
}
|
||||
|
||||
return authenticatedPage
|
||||
await verifyEmailPage.getByRole('button', { name: /Verify/i }).click()
|
||||
await verifyEmailPage.waitForLoadState()
|
||||
return verifyEmailPage
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[global]
|
||||
|
||||
[hasura]
|
||||
version = 'v2.33.4-ce'
|
||||
version = 'v2.38.0-ce'
|
||||
adminSecret = '{{ secrets.HASURA_GRAPHQL_ADMIN_SECRET }}'
|
||||
webhookSecret = '{{ secrets.NHOST_WEBHOOK_SECRET }}'
|
||||
|
||||
@@ -29,7 +29,7 @@ httpPoolSize = 100
|
||||
version = 18
|
||||
|
||||
[auth]
|
||||
version = '0.28.0-beta002'
|
||||
version = '0.32.0'
|
||||
|
||||
[auth.elevatedPrivileges]
|
||||
mode = 'required'
|
||||
@@ -154,7 +154,7 @@ enabled = true
|
||||
issuer = 'nhost'
|
||||
|
||||
[postgres]
|
||||
version = '14.6-20240129-1'
|
||||
version = '14.11-20240515-1'
|
||||
|
||||
[provider]
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-apollo",
|
||||
"version": "0.8.7",
|
||||
"version": "0.8.9",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.9.9",
|
||||
|
||||
@@ -1,32 +1,39 @@
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Button, Card, Container, Stack, Text } from '@mantine/core'
|
||||
import { showNotification } from '@mantine/notifications'
|
||||
import { useNhostClient } from '@nhost/react'
|
||||
import { Container } from '@mantine/core'
|
||||
import { FaEnvelope } from 'react-icons/fa'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
|
||||
const VerifyPage: React.FC = () => {
|
||||
const nhost = useNhostClient()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [searchParams] = useSearchParams()
|
||||
|
||||
useEffect(() => {
|
||||
const redirectToVerificationLink = () => {
|
||||
const ticket = searchParams.get('ticket')
|
||||
const redirectTo = searchParams.get('redirectTo')
|
||||
const type = searchParams.get('type')
|
||||
const redirectTo = searchParams.get('redirectTo')
|
||||
|
||||
if (ticket && redirectTo && type) {
|
||||
if (ticket && type && redirectTo) {
|
||||
window.location.href = `${nhost.auth.url}/verify?ticket=${ticket}&type=${type}&redirectTo=${redirectTo}`
|
||||
} else {
|
||||
showNotification({
|
||||
color: 'red',
|
||||
title: 'Error',
|
||||
message: 'An error occured while verifying your account'
|
||||
})
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}, [searchParams, nhost?.auth?.url])
|
||||
|
||||
if (loading) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<span>Failed to authenticate with magick link</span>
|
||||
<Card shadow="sm" p="lg" radius="md" withBorder>
|
||||
<Stack align="center">
|
||||
<Text>Please verify your account by clicking the link below.</Text>
|
||||
<Button leftIcon={<FaEnvelope size={14} />} onClick={redirectToVerificationLink}>
|
||||
Verify
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost-examples/react-gqty
|
||||
|
||||
## 1.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
|
||||
## 1.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-gqty",
|
||||
"private": true,
|
||||
"version": "1.2.7",
|
||||
"version": "1.2.8",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
8
examples/react_native/CHANGELOG.md
Normal file
8
examples/react_native/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# @nhost-examples/react-native
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
- @nhost/react-apollo@12.0.3
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-native",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost-examples/vue-apollo
|
||||
|
||||
## 0.6.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.1.6
|
||||
- @nhost/apollo@7.1.3
|
||||
- @nhost/vue@2.6.3
|
||||
|
||||
## 0.6.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/vue-apollo",
|
||||
"private": true,
|
||||
"version": "0.6.7",
|
||||
"version": "0.6.8",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost-examples/vue-quickstart
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/apollo@7.1.3
|
||||
- @nhost/vue@2.6.3
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/vue-quickstart",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/apollo
|
||||
|
||||
## 7.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.1.6
|
||||
|
||||
## 7.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/apollo",
|
||||
"version": "7.1.2",
|
||||
"version": "7.1.3",
|
||||
"description": "Nhost Apollo Client library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/react-apollo
|
||||
|
||||
## 12.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/apollo@7.1.3
|
||||
- @nhost/react@3.5.3
|
||||
|
||||
## 12.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-apollo",
|
||||
"version": "12.0.2",
|
||||
"version": "12.0.3",
|
||||
"description": "Nhost React Apollo client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/react-urql
|
||||
|
||||
## 9.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
|
||||
## 9.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-urql",
|
||||
"version": "9.0.2",
|
||||
"version": "9.0.3",
|
||||
"description": "Nhost React URQL client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/hasura-auth-js
|
||||
|
||||
## 2.5.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e28975d: fix: refactor refreshTimer logic to avoid an infinite loop when refreshToken has expired
|
||||
|
||||
## 2.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/hasura-auth-js",
|
||||
"version": "2.5.2",
|
||||
"version": "2.5.3",
|
||||
"description": "Hasura-auth client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -697,11 +697,6 @@ export const createAuthMachine = ({
|
||||
return false
|
||||
}
|
||||
|
||||
// This happens when either the computer goes to sleep or when Chrome descides to suspend the tab
|
||||
if (expiresAt.getTime() < Date.now()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (ctx.refreshTimer.lastAttempt) {
|
||||
// * If the refresh timer reached the maximum number of attempts, we should not try again
|
||||
if (ctx.refreshTimer.attempts > REFRESH_TOKEN_MAX_ATTEMPTS) {
|
||||
@@ -711,6 +706,12 @@ export const createAuthMachine = ({
|
||||
// * Exponential backoff
|
||||
return elapsed > Math.pow(2, ctx.refreshTimer.attempts - 1) * 5_000
|
||||
}
|
||||
|
||||
// This happens when either the computer goes to sleep or when Chrome descides to suspend the tab
|
||||
if (expiresAt.getTime() < Date.now()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (refreshIntervalTime) {
|
||||
// * If a refreshIntervalTime has been passed on as an option, it will notify
|
||||
// * the token should be refershed when this interval is overdue
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/nextjs
|
||||
|
||||
## 2.1.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/react@3.5.3
|
||||
|
||||
## 2.1.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nextjs",
|
||||
"version": "2.1.16",
|
||||
"version": "2.1.17",
|
||||
"description": "Nhost NextJS library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/nhost-js
|
||||
|
||||
## 3.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e28975d]
|
||||
- @nhost/hasura-auth-js@2.5.3
|
||||
|
||||
## 3.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nhost-js",
|
||||
"version": "3.1.5",
|
||||
"version": "3.1.6",
|
||||
"description": "Nhost JavaScript SDK",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/react
|
||||
|
||||
## 3.5.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.1.6
|
||||
|
||||
## 3.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react",
|
||||
"version": "3.5.2",
|
||||
"version": "3.5.3",
|
||||
"description": "Nhost React library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/vue
|
||||
|
||||
## 2.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@3.1.6
|
||||
|
||||
## 2.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/vue",
|
||||
"version": "2.6.2",
|
||||
"version": "2.6.3",
|
||||
"description": "Nhost Vue library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
Reference in New Issue
Block a user