Compare commits

...

76 Commits

Author SHA1 Message Date
Szilárd Dóró
43705b992d Merge pull request #1509 from nhost/changeset-release/main
chore: update versions
2023-01-12 12:41:41 +01:00
github-actions[bot]
2e999e8715 chore: update versions 2023-01-12 10:14:41 +00:00
Pilou
0370696d5c Merge pull request #1511 from nhost/chore/unlink-packages
chore(changeset): stop linking packages
2023-01-12 11:12:44 +01:00
Pierre-Louis Mercereau
f62131d55a chore(changeset): stop linking packages 2023-01-12 10:59:57 +01:00
Szilárd Dóró
86d077ac00 Merge pull request #1508 from nhost/renovate-changesets
chore: create changesest from Renovate bumps
2023-01-12 10:10:35 +01:00
szilarddoro
200e9f774c chore(deps): update dependency @types/react-dom to v18.0.10 2023-01-12 08:49:58 +00:00
Szilárd Dóró
bc1235de3b Merge pull request #1433 from nhost/renovate/react-dom-18.x
chore(deps): update dependency @types/react-dom to v18.0.10
2023-01-12 09:48:10 +01:00
Szilárd Dóró
fce58ebaea remove changeset, CI generates it 2023-01-12 09:47:53 +01:00
Szilárd Dóró
452e281120 chore(dashboard): add changeset 2023-01-12 09:47:04 +01:00
Szilárd Dóró
9a338e54c9 Merge pull request #1492 from nhost/renovate/vitest-monorepo
chore(deps): update vitest monorepo to ^0.27.0
2023-01-12 09:45:14 +01:00
Szilárd Dóró
baeebf980d Merge pull request #1507 from nhost/changeset-release/main
chore: update versions
2023-01-12 09:27:25 +01:00
github-actions[bot]
ac92c6ee61 chore: update versions 2023-01-12 01:38:20 +00:00
Guido Curcio
1ddaf680c0 Merge pull request #1471 from nhost/fix(dashboard)/workspace-creation-redirection-delete 2023-01-11 22:36:50 -03:00
Guido Curcio
c6e6194d8e mutating -> updating for signaling changes in course. 2023-01-11 09:05:43 -03:00
Pilou
83deea8b45 Merge pull request #1501 from nhost/chore/exclude-functions-from-workspace
chore: exclude functions from workspace
2023-01-11 11:42:07 +01:00
Pierre-Louis Mercereau
acbaabcf85 chore: update lockfile 2023-01-11 10:44:36 +01:00
Pierre-Louis Mercereau
3534501f37 chore: force using turborepo v1 2023-01-11 10:31:10 +01:00
Pierre-Louis Mercereau
27bc23cbbc chore: exclude functions from workspace 2023-01-11 10:15:47 +01:00
Szilárd Dóró
6450223558 Merge pull request #1498 from nhost/changeset-release/main
chore: update versions
2023-01-11 08:48:36 +01:00
Guido Curcio
a62a85a777 add comments to effects and router changes. 2023-01-11 02:07:15 -03:00
Guido Curcio
ae24f83953 fix changing application name redirect to 404, fix 404 flash when changing workspace name. 2023-01-11 01:33:28 -03:00
Guido Curcio
fc60d7a782 2023-01-10 19:14:00 -03:00
Guido Curcio
6be8a998df 2023-01-10 19:13:09 -03:00
Guido Curcio
ea091f6251 2023-01-10 19:11:02 -03:00
Guido Curcio
8175c052f7 2023-01-10 18:50:45 -03:00
github-actions[bot]
e6605a6ed0 chore: update versions 2023-01-10 16:43:20 +00:00
Szilárd Dóró
1cba0e6492 Merge pull request #1497 from nhost/fix/database-ui-hasura-metadata
fix(dashboard): don't break the table creation process
2023-01-10 17:41:38 +01:00
Szilárd Dóró
179c90fcdb fix(dashboard): update inline snapshot 2023-01-10 17:13:29 +01:00
Szilárd Dóró
85f0f943a1 chore(dashboard): add changeset 2023-01-10 16:04:15 +01:00
Szilárd Dóró
c4c23fde31 fix(dashboard): don't break table creation
don't break table creation when referencing a table that is not in the `public` schema
2023-01-10 15:39:05 +01:00
Szilárd Dóró
e0b94c3e90 Merge pull request #1493 from nhost/changeset-release/main
chore: update versions
2023-01-10 09:33:58 +01:00
github-actions[bot]
113d638532 chore: update versions 2023-01-09 17:20:13 +00:00
Pilou
d87448916f Merge pull request #1486 from nhost/chore/bump-start-server-and-test
chore(deps): bump start-server-and-test
2023-01-09 18:17:47 +01:00
Pilou
af4292658c Merge pull request #1495 from nhost/fix/export-types
Export commonly used types
2023-01-09 18:17:01 +01:00
Pilou
f735bcd2ea Merge pull request #1485 from nhost/fix/explicit-types
fix: 🐛 add explicit types to React hooks and Vue composables
2023-01-09 18:00:48 +01:00
Pierre-Louis Mercereau
66fb74af86 Merge branch 'main' into fix/explicit-types 2023-01-09 16:30:20 +01:00
Pierre-Louis Mercereau
791eac30bb Merge branch 'main' into chore/bump-start-server-and-test 2023-01-09 16:29:57 +01:00
Pierre-Louis Mercereau
da4ad889d7 Merge branch 'main' into fix/export-types 2023-01-09 16:27:31 +01:00
Pilou
9ef111760c Merge pull request #1490 from nhost/test/correct-forgot-password
test: correct forgot-password test
2023-01-09 16:26:57 +01:00
Pierre-Louis Mercereau
c2706c7d97 chore: export commonly used types 2023-01-09 16:26:01 +01:00
Pilou
683b8768c4 Merge pull request #1482 from nhost/chore/access-token-cookie
chore: store the session in a cookie to avoid refetching the jwt on every ssr call
2023-01-09 16:11:39 +01:00
renovate[bot]
6d9df237a8 chore(deps): update vitest monorepo to ^0.27.0 2023-01-09 13:34:33 +00:00
Szilárd Dóró
220ae37aa7 Merge pull request #1491 from nhost/changeset-release/main
chore: update versions
2023-01-09 14:26:56 +01:00
Pierre-Louis Mercereau
d0d94d9239 chore: use email+password sign-up 2023-01-09 13:27:28 +01:00
Pierre-Louis Mercereau
aed3d1f147 chore: wrap 2023-01-09 11:59:49 +01:00
github-actions[bot]
d07bf08e45 chore: update versions 2023-01-09 10:46:27 +00:00
Szilárd Dóró
f2183250d2 Merge pull request #1470 from nhost/fix(dashboard)/sign-out
fix(dashboard): Resetting the cache when signing out.
2023-01-09 11:44:48 +01:00
Pierre-Louis Mercereau
d2bb5ecfae refactor: unnest code blocks 2023-01-09 11:39:33 +01:00
Pierre-Louis Mercereau
02d0db0cf0 revert: remove line 2023-01-09 11:20:28 +01:00
Pierre-Louis Mercereau
441005d5c3 chore: another attempt 2023-01-09 10:59:38 +01:00
Pierre-Louis Mercereau
eea8708549 chore: visit 2023-01-09 10:31:44 +01:00
Szilárd Dóró
5f3f9390aa chore(dashboard): updated changeset 2023-01-09 09:42:36 +01:00
Pierre-Louis Mercereau
1c5b0560ed chore: 10 attempts 2023-01-09 09:38:19 +01:00
Pierre-Louis Mercereau
1bfdf21b99 test: correct forgot-password test 2023-01-09 09:33:40 +01:00
Pierre-Louis Mercereau
efd522a38a chore: update changesets 2023-01-06 16:53:21 +01:00
Pierre-Louis Mercereau
55c35fa9c5 chore(deps): bump start-server-and-test 2023-01-06 16:45:06 +01:00
Pierre-Louis Mercereau
d42c27ae99 fix: 🐛 add explicit types to React hooks and Vue composables 2023-01-06 13:56:19 +01:00
Pierre-Louis Mercereau
927be4a2c9 chore: store the session in a cookie 2023-01-06 10:47:09 +01:00
Guido Curcio
e44352abbd typo in CreateWorkspaceFormProps Save -> Create 2023-01-05 23:48:35 -03:00
Guido Curcio
f9289f3c32 Merge branch 'fix(dashboard)/workspace-creation-redirection-delete' of https://github.com/nhost/nhost into fix(dashboard)/workspace-creation-redirection-delete 2023-01-05 23:47:01 -03:00
Guido Curcio
8ff06e5637 disable create workspace button if error on input. 2023-01-05 23:45:32 -03:00
Guido Curcio
49e4633bca handle when workspace name is already taken. 2023-01-05 23:41:23 -03:00
Guido Curcio
7ae7a7206c Update dashboard/src/components/home/CreateWorkspaceForm/CreateWorkspaceForm.tsx
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2023-01-05 23:31:02 -03:00
Guido Curcio
43d7e7babf Update dashboard/src/components/workspace/WorkspaceSection.tsx
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2023-01-05 23:30:54 -03:00
Guido Curcio
463a51ce7c Update dashboard/src/components/home/CreateWorkspaceForm/CreateWorkspaceForm.tsx
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2023-01-05 23:30:47 -03:00
Guido Curcio
86e9d9d47f Update dashboard/src/components/home/CreateWorkspaceForm/CreateWorkspaceForm.tsx
Co-authored-by: Szilárd Dóró <doroszilard@icloud.com>
2023-01-05 23:30:42 -03:00
Guido Curcio
f99b72cd7c useUserData instead of nhost.auth.getUser() 2023-01-05 23:29:42 -03:00
Guido Curcio
0dc2f3ff29 remove unused file 2023-01-05 23:24:11 -03:00
Guido Curcio
d0f8081101 new changeset 2023-01-05 23:16:09 -03:00
Guido Curcio
84ebfb79d0 reorder calls when signing out 2023-01-05 23:14:35 -03:00
Pilou
3c78d0ef46 Merge pull request #1476 from nhost/test/reset-password
test: add forgot password test
2023-01-05 17:07:47 +01:00
Pierre-Louis Mercereau
ad28bf2166 test: add forgot password test 2023-01-05 10:59:52 +01:00
Guido Curcio
dbd3ded515 add patch changeset for dashboard. 2023-01-04 17:41:20 -03:00
Guido Curcio
5399fac211 remove AddWorkspace.tsx file and imports. 2023-01-04 17:39:01 -03:00
Guido Curcio
52e3127a34 fix(dashboard): workspaces creation, new form, correct redirects. 2023-01-04 17:34:35 -03:00
renovate[bot]
a529b654bc chore(deps): update dependency @types/react-dom to v18.0.10 2022-12-26 17:31:33 +00:00
122 changed files with 967 additions and 672 deletions

View File

@@ -2,20 +2,7 @@
"$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"linked": [
[
"@nhost/nextjs",
"@nhost/react",
"@nhost/vue",
"@nhost/nhost-js",
"@nhost/hasura-auth-js",
"@nhost/hasura-storage-js"
],
[
"@nhost/react-apollo",
"@nhost/apollo"
]
],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",

View File

@@ -38,7 +38,7 @@ runs:
uses: nick-fields/retry@v2
with:
timeout_minutes: 3
max_attempts: 3
max_attempts: 10
command: bash <(curl --silent -L https://raw.githubusercontent.com/nhost/cli/main/get.sh) ${{ inputs.version }}
- name: Set custom configuration
if: ${{ inputs.config }}

View File

@@ -1,5 +1,41 @@
# @nhost/dashboard
## 0.9.5
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/nextjs@1.13.2
- @nhost/react-apollo@4.13.2
## 0.9.4
### Patch Changes
- dbd3ded5: fix(dashboard): workspaces creation, new form, correct redirects.
## 0.9.3
### Patch Changes
- 85f0f943: fix(dashboard): don't break the table creation process
## 0.9.2
### Patch Changes
- Updated dependencies [d42c27ae]
- Updated dependencies [927be4a2]
- @nhost/nextjs@1.13.1
- @nhost/react-apollo@4.13.1
## 0.9.1
### Patch Changes
- d0f80811: fix(dashboard): don't show error when signing out the user
## 0.9.0
### Minor Changes

View File

@@ -3,7 +3,7 @@ RUN apk add --no-cache libc6-compat
RUN apk update
WORKDIR /app
RUN yarn global add turbo
RUN yarn global add turbo@1
COPY . .
RUN turbo prune --scope="@nhost/dashboard" --docker

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/dashboard",
"version": "0.9.0",
"version": "0.9.5",
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -105,14 +105,14 @@
"@types/node": "^16.11.7",
"@types/pluralize": "^0.0.29",
"@types/react": "18.0.25",
"@types/react-dom": "18.0.9",
"@types/react-dom": "18.0.10",
"@types/react-table": "^7.7.12",
"@types/testing-library__jest-dom": "^5.14.5",
"@types/validator": "^13.7.10",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"@vitejs/plugin-react": "^3.0.0",
"@vitest/coverage-c8": "^0.26.0",
"@vitest/coverage-c8": "^0.27.0",
"autoprefixer": "^10.4.13",
"babel-loader": "^8.3.0",
"babel-plugin-transform-remove-console": "^6.9.4",
@@ -143,7 +143,7 @@
"typescript": "^4.8.4",
"vite": "^4.0.2",
"vite-tsconfig-paths": "^4.0.3",
"vitest": "^0.26.2",
"vitest": "^0.27.0",
"webpack": "^5.75.0"
},
"browserslist": {

View File

@@ -1,119 +0,0 @@
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
import { Alert } from '@/ui/Alert';
import Button from '@/ui/v2/Button';
import Input from '@/ui/v2/Input';
import Text from '@/ui/v2/Text';
import { discordAnnounce } from '@/utils/discordAnnounce';
import { inputErrorMessages } from '@/utils/getErrorMessage';
import { slugifyString } from '@/utils/helpers';
import { triggerToast } from '@/utils/toast';
import { useUpdateWorkspaceMutation } from '@/utils/__generated__/graphql';
import router from 'next/router';
import type { ChangeEvent } from 'react';
import React, { useState } from 'react';
type ChangeWorkspaceNameProps = {
close: VoidFunction;
};
export default function ChangeWorkspaceName({
close,
}: ChangeWorkspaceNameProps) {
const { currentWorkspace } = useCurrentWorkspaceAndApplication();
const [newWorkspaceName, setNewWorkspaceName] = useState(
currentWorkspace.name,
);
const [workspaceError, setWorkspaceError] = useState<string>('');
const [updateWorkspace, { loading: mutationLoading, error: mutationError }] =
useUpdateWorkspaceMutation({
refetchQueries: [],
});
function handleChange(event: ChangeEvent<HTMLInputElement>) {
inputErrorMessages(
event.target.value,
setNewWorkspaceName,
setWorkspaceError,
'Workspace',
);
}
async function handleSubmit(e: React.SyntheticEvent<HTMLFormElement>) {
e.preventDefault();
const name = newWorkspaceName;
const slug = slugifyString(name);
if (slug.length < 4 || slug.length > 32) {
setWorkspaceError('Slug should be within 4 and 32 characters.');
return;
}
try {
await updateWorkspace({
variables: {
id: currentWorkspace.id,
workspace: {
name,
slug,
},
},
});
close();
triggerToast('Workspace name changed');
} catch (error) {
await discordAnnounce(
`Error trying to remove workspace: ${currentWorkspace.id} - ${error.message}`,
);
}
await router.push(slug);
}
return (
<div className="w-modal px-6 py-6 text-left">
<div className="flex flex-col">
<Text variant="h3" component="h2">
Change Workspace Name
</Text>
<form onSubmit={handleSubmit}>
<div className="mt-4 grid grid-flow-row gap-2">
<Input
id="workspaceName"
label="New Workspace Name"
onChange={handleChange}
value={newWorkspaceName}
placeholder="New workspace name"
fullWidth
autoFocus
autoComplete="off"
helperText={`https://app.nhost.io/${slugifyString(
newWorkspaceName || '',
)}`}
/>
{workspaceError && <Alert severity="error">{workspaceError}</Alert>}
{mutationError && (
<Alert severity="error">{mutationError.toString()}</Alert>
)}
</div>
<div className="mt-6 grid grid-flow-row gap-2">
<Button
type="submit"
disabled={mutationLoading || !!workspaceError}
>
Save Changes
</Button>
<Button variant="outlined" color="secondary" onClick={close}>
Close
</Button>
</div>
</form>
</div>
</div>
);
}

View File

@@ -52,9 +52,9 @@ function ControlledAutocomplete(
return (
<Autocomplete
inputValue={typeof field.value === 'string' ? field.value : undefined}
{...props}
{...field}
inputValue={typeof field.value === 'string' ? field.value : undefined}
ref={mergeRefs([field.ref, ref])}
onChange={(event, options, reason, details) => {
setValue?.(controllerProps?.name || name, options, {

View File

@@ -6,6 +6,7 @@ import { createContext } from 'react';
* Available dialog types.
*/
export type DialogType =
| 'EDIT_WORKSPACE_NAME'
| 'CREATE_RECORD'
| 'CREATE_COLUMN'
| 'EDIT_COLUMN'

View File

@@ -1,6 +1,7 @@
import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
import CreateForeignKeyForm from '@/components/dataBrowser/CreateForeignKeyForm';
import EditForeignKeyForm from '@/components/dataBrowser/EditForeignKeyForm';
import EditWorkspaceNameForm from '@/components/home/EditWorkspaceNameForm';
import CreateEnvironmentVariableForm from '@/components/settings/environmentVariables/CreateEnvironmentVariableForm';
import EditEnvironmentVariableForm from '@/components/settings/environmentVariables/EditEnvironmentVariableForm';
import EditJwtSecretForm from '@/components/settings/environmentVariables/EditJwtSecretForm';
@@ -366,6 +367,10 @@ function DialogProvider({ children }: PropsWithChildren<unknown>) {
<RetryableErrorBoundary
errorMessageProps={{ className: 'pt-0 pb-5 px-6' }}
>
{activeDialogType === 'EDIT_WORKSPACE_NAME' && (
<EditWorkspaceNameForm {...sharedDialogProps} />
)}
{activeDialogType === 'CREATE_FOREIGN_KEY' && (
<CreateForeignKeyForm {...sharedDialogProps} />
)}

View File

@@ -1,13 +1,11 @@
import { ChangePasswordModal } from '@/components/applications/ChangePasswordModal';
import { useWorkspaceContext } from '@/context/workspace-context';
import { useUserDataContext } from '@/context/workspace1-context';
import { Avatar } from '@/ui/Avatar';
import { Modal } from '@/ui/Modal';
import Button from '@/ui/v2/Button';
import { Dropdown, useDropdown } from '@/ui/v2/Dropdown';
import Text from '@/ui/v2/Text';
import { emptyWorkspace } from '@/utils/helpers';
import { nhost } from '@/utils/nhost';
import { useApolloClient } from '@apollo/client';
import { useUserData } from '@nhost/nextjs';
import Image from 'next/image';
import { useRouter } from 'next/router';
@@ -22,9 +20,8 @@ function AccountMenuContent({
}: AccountMenuContentProps) {
const user = useUserData();
const router = useRouter();
const client = useApolloClient();
const [clicked, setClicked] = useState(false);
const { setWorkspaceContext } = useWorkspaceContext();
const { setUserContext } = useUserDataContext();
const { handleClose } = useDropdown();
return (
@@ -34,10 +31,9 @@ function AccountMenuContent({
color="secondary"
className="absolute top-6 right-4 grid grid-flow-col items-center gap-1 self-start font-medium"
onClick={async () => {
setWorkspaceContext(emptyWorkspace());
setUserContext({ workspaces: [] });
nhost.auth.signOut();
router.push('/signin');
await nhost.auth.signOut();
await client.resetStore();
}}
aria-label="Sign Out"
>

View File

@@ -118,6 +118,7 @@ export default function BaseColumnForm({
variant="inline"
className="col-span-8 py-3"
autoFocus
autoComplete="off"
/>
<ControlledAutocomplete
@@ -272,6 +273,7 @@ export default function BaseColumnForm({
error={Boolean(errors.comment)}
variant="inline"
className="col-span-8 py-3"
autoComplete="off"
/>
</section>
</div>

View File

@@ -88,6 +88,7 @@ function NameInput() {
error={Boolean(errors.name)}
variant="inline"
className="col-span-8 py-3"
autoComplete="off"
autoFocus
/>
);

View File

@@ -70,6 +70,7 @@ function NameInput({ index }: FieldArrayInputProps) {
}
},
})}
autoComplete="off"
aria-label="Name"
placeholder="Enter name"
hideEmptyHelperText

View File

@@ -13,6 +13,7 @@ import Option from '@/ui/v2/Option';
import Text from '@/ui/v2/Text';
import getPermissionVariablesArray from '@/utils/settings/getPermissionVariablesArray';
import { useGetAppCustomClaimsQuery } from '@/utils/__generated__/graphql';
import { useTheme } from '@mui/material';
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';
import PermissionSettingsSection from './PermissionSettingsSection';
@@ -41,6 +42,7 @@ export default function ColumnPresetsSection({
table,
disabled,
}: ColumnPresetSectionProps) {
const theme = useTheme();
const {
data: tableData,
status: tableStatus,
@@ -131,7 +133,12 @@ export default function ColumnPresetsSection({
freeSolo
fullWidth
disableClearable={false}
clearIcon={<XIcon />}
clearIcon={
<XIcon
className="w-4 h-4 mt-px"
sx={{ color: theme.palette.text.primary }}
/>
}
autoSelect
autoHighlight={false}
error={Boolean(

View File

@@ -0,0 +1,239 @@
import Form from '@/components/common/Form';
import Button from '@/ui/v2/Button';
import Input from '@/ui/v2/Input';
import {
refetchGetOneUserQuery,
useInsertWorkspaceMutation,
useUpdateWorkspaceMutation,
} from '@/utils/__generated__/graphql';
import { slugifyString } from '@/utils/helpers';
import { toastStyleProps } from '@/utils/settings/settingsConstants';
import { yupResolver } from '@hookform/resolvers/yup';
import { useUserData } from '@nhost/nextjs';
import { useRouter } from 'next/router';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import * as Yup from 'yup';
export interface EditWorkspaceNameFormProps {
/**
* The current workspace name if this is an edit operation.
*/
currentWorkspaceName?: string;
/**
* The current workspace name id if this is an edit operation.
*/
currentWorkspaceId?: string;
/**
* Determines whether the form is disabled.
*/
disabled?: boolean;
/**
* Submit button text.
*
* @default 'Create'
*/
submitButtonText?: string;
/**
* Function to be called when the form is submitted.
*/
onSubmit?: () => void;
/**
* Function to be called when the operation is cancelled.
*/
onCancel?: VoidFunction;
}
export interface EditWorkspaceNameFormValues {
/**
* New workspace name.
*/
newWorkspaceName: string;
}
const validationSchema = Yup.object().shape({
newWorkspaceName: Yup.string()
.required('Workspace name is required.')
.min(4, 'The new Workspace name must be at least 4 characters.')
.max(32, "The new Workspace name can't be longer than 32 characters.")
.test(
'canBeSlugified',
`This field should be at least 4 characters and can't be longer than 32 characters.`,
(value) => {
const slug = slugifyString(value);
if (slug.length < 4 || slug.length > 32) {
return false;
}
return true;
},
),
});
export default function EditWorkspaceName({
disabled,
onSubmit,
onCancel,
currentWorkspaceName,
currentWorkspaceId,
submitButtonText = 'Create',
}: EditWorkspaceNameFormProps) {
const currentUser = useUserData();
const [insertWorkspace, { client }] = useInsertWorkspaceMutation();
const [updateWorkspaceName] = useUpdateWorkspaceMutation({
refetchQueries: [
refetchGetOneUserQuery({
userId: currentUser.id,
}),
],
awaitRefetchQueries: true,
ignoreResults: true,
});
const router = useRouter();
const form = useForm<EditWorkspaceNameFormValues>({
defaultValues: {
newWorkspaceName: currentWorkspaceName || '',
},
resolver: yupResolver(validationSchema),
});
const {
register,
formState: { dirtyFields, isSubmitting, errors },
} = form;
const isDirty = Object.keys(dirtyFields).length > 0;
async function handleSubmit({
newWorkspaceName,
}: EditWorkspaceNameFormValues) {
const slug = slugifyString(newWorkspaceName);
try {
if (currentWorkspaceId) {
// In this bit of code we spread the props of the current path (e.g. /workspace/...) and add one key-value pair: `mutating: true`.
// We want to indicate that the currently we're in the process of running a mutation state that will affect the routing behaviour of the website
// i.e. redirecting to 404 if there's no workspace/project with that slug.
await router.replace({
pathname: router.pathname,
query: { ...router.query, updating: true },
});
await toast.promise(
updateWorkspaceName({
variables: {
id: currentWorkspaceId,
workspace: {
name: newWorkspaceName,
slug,
},
},
}),
{
loading: 'Updating workspace name...',
success: 'Workspace name has been updated successfully.',
error: 'An error occurred while updating the workspace name.',
},
toastStyleProps,
);
} else {
await toast.promise(
insertWorkspace({
variables: {
workspace: {
name: newWorkspaceName,
companyName: newWorkspaceName,
email: currentUser.email,
slug,
workspaceMembers: {
data: [
{
userId: currentUser.id,
type: 'owner',
},
],
},
},
},
}),
{
loading: 'Creating new workspace...',
success: 'The new workspace has been created successfully.',
error: 'An error occurred while creating the new workspace.',
},
toastStyleProps,
);
}
} catch (error) {
if (error.message?.includes('duplicate key value')) {
form.setError(
'newWorkspaceName',
{
type: 'manual',
message: 'This workspace name is already taken.',
},
{
shouldFocus: false,
},
);
}
return;
}
await client.refetchQueries({
include: ['getOneUser'],
});
await router.push(slug);
onSubmit?.();
}
return (
<FormProvider {...form}>
<Form
onSubmit={handleSubmit}
className="flex flex-col content-between flex-auto pt-2 pb-6 overflow-hidden"
>
<div className="flex-auto px-6 overflow-y-auto">
<Input
{...register('newWorkspaceName')}
error={Boolean(errors.newWorkspaceName?.message)}
label="Name"
helperText={errors.newWorkspaceName?.message}
autoFocus={!disabled}
disabled={disabled}
fullWidth
hideEmptyHelperText
placeholder='e.g. "My Workspace"'
/>
</div>
<div className="grid flex-shrink-0 grid-flow-row gap-2 px-6 pt-4">
{!disabled && (
<Button
loading={isSubmitting}
disabled={
isSubmitting || Boolean(errors.newWorkspaceName?.message)
}
type="submit"
>
{currentWorkspaceName ? 'Save' : submitButtonText}
</Button>
)}
<Button
variant="outlined"
color="secondary"
onClick={onCancel}
tabIndex={isDirty ? -1 : 0}
autoFocus={disabled}
>
{disabled ? 'Close' : 'Cancel'}
</Button>
</div>
</Form>
</FormProvider>
);
}

View File

@@ -0,0 +1,3 @@
export * from './EditWorkspaceNameForm';
export { default } from './EditWorkspaceNameForm';

View File

@@ -4,11 +4,8 @@ import { InviteAnnounce } from '@/components/home/InviteAnnounce';
import type { BaseLayoutProps } from '@/components/layout/BaseLayout';
import BaseLayout from '@/components/layout/BaseLayout';
import Container from '@/components/layout/Container';
import AddWorkspace from '@/components/workspace/AddWorkspace';
import { useUI } from '@/context/UIContext';
import useIsHealthy from '@/hooks/common/useIsHealthy';
import useIsPlatform from '@/hooks/common/useIsPlatform';
import { Modal } from '@/ui';
import ActivityIndicator from '@/ui/v2/ActivityIndicator';
import Link from '@/ui/v2/Link';
import Text from '@/ui/v2/Text';
@@ -39,7 +36,6 @@ export default function AuthenticatedLayout({
}: AuthenticatedLayoutProps) {
const router = useRouter();
const isPlatform = useIsPlatform();
const { newWorkspace, closeSection } = useUI();
const { isAuthenticated, isLoading } = useAuthenticationStatus();
const isHealthy = useIsHealthy();
@@ -85,7 +81,7 @@ export default function AuthenticatedLayout({
<BaseLayout {...props}>
<Header className="flex max-h-[59px] flex-auto" />
<Container className="my-12 grid max-w-md grid-flow-row justify-center gap-2 text-center">
<Container className="grid justify-center max-w-md grid-flow-row gap-2 my-12 text-center">
<div className="mx-auto">
<Image
src="/terminal-text.svg"
@@ -123,13 +119,7 @@ export default function AuthenticatedLayout({
}
return (
<BaseLayout className="flex h-full flex-col" {...props}>
<Modal
showModal={newWorkspace}
close={closeSection}
Component={AddWorkspace}
/>
<BaseLayout className="flex flex-col h-full" {...props}>
<Header className="flex max-h-[59px] flex-auto" />
<InviteAnnounce />

View File

@@ -1,159 +0,0 @@
import { useUI } from '@/context/UIContext';
import { useInsertWorkspaceMutation } from '@/generated/graphql';
import { Alert } from '@/ui/Alert';
import Button from '@/ui/v2/Button';
import Input from '@/ui/v2/Input';
import Text from '@/ui/v2/Text';
import { getErrorMessage, inputErrorMessages } from '@/utils/getErrorMessage';
import { slugifyString } from '@/utils/helpers';
import { nhost } from '@/utils/nhost';
import { triggerToast } from '@/utils/toast';
import router from 'next/router';
import React, { useState } from 'react';
import slugify from 'slugify';
function AddNewWorkspaceForm({
closeSection: externalCloseSection,
}: {
closeSection: VoidFunction;
}) {
const [workspace, setWorkspace] = useState('');
const { closeSection } = useUI();
const [workspaceError, setWorkspaceError] = useState<string>('');
const [loadingAddWorkspace, setLoadingAddWorkspace] = useState(false);
const [insertWorkspace, { client }] = useInsertWorkspaceMutation();
const slug = slugify(workspace, { lower: true, strict: true });
const user = nhost.auth.getUser();
if (!user) {
return <div>No user..</div>;
}
const userId = user.id;
async function handleSubmit(e: React.SyntheticEvent<HTMLFormElement>) {
e.preventDefault();
setWorkspaceError('');
setLoadingAddWorkspace(true);
if (
!inputErrorMessages(
workspace,
setWorkspace,
setWorkspaceError,
'Workspace',
)
) {
return;
}
if (slug.length < 4 || slug.length > 32) {
setWorkspaceError('Slug should be within 4 and 32 characters.');
setLoadingAddWorkspace(false);
return;
}
const currentUser = nhost.auth.getUser();
if (!currentUser) {
triggerToast('User is not signed in');
setLoadingAddWorkspace(false);
return;
}
try {
await insertWorkspace({
variables: {
workspace: {
name: workspace,
companyName: workspace,
email: user.email,
slug,
workspaceMembers: {
data: [
{
userId,
type: 'owner',
},
],
},
},
},
});
await client.refetchQueries({ include: ['getOneUser'] });
router.push(`/${slug}`);
setLoadingAddWorkspace(false);
closeSection();
} catch (error: any) {
setWorkspaceError(getErrorMessage(error, 'workspace'));
setLoadingAddWorkspace(false);
}
}
return (
<form onSubmit={handleSubmit} className="grid grid-flow-row gap-4">
<Input
type="text"
placeholder="Your new workspace"
name="workspace"
id="workspace"
label="Workspace"
fullWidth
autoFocus
helperText={`https://app.nhost.io/${slugifyString(workspace)}`}
onChange={(event) => {
setWorkspace(event.target.value);
setWorkspaceError('');
}}
/>
{workspaceError && <Alert severity="error">{workspaceError}</Alert>}
<div className="grid grid-flow-col justify-between gap-2">
<Button
variant="outlined"
color="secondary"
onClick={(e) => {
e.preventDefault();
externalCloseSection();
}}
>
Cancel
</Button>
<Button
type="submit"
disabled={!!workspaceError}
loading={loadingAddWorkspace}
>
Create Workspace
</Button>
</div>
</form>
);
}
export default function AddWorkspace() {
const { closeSection } = useUI();
const user = nhost.auth.getUser();
if (!user) {
return <div>No user..</div>;
}
return (
<div className="grid w-modal grid-flow-row gap-2 px-6 py-6 text-left">
<div className="grid w-full grid-flow-row gap-1">
<Text variant="h3" component="h2">
New Workspace
</Text>
<Text variant="subtitle2">
Invite team members to workspaces to work collaboratively.
</Text>
</div>
<AddNewWorkspaceForm closeSection={closeSection} />
</div>
);
}

View File

@@ -1,14 +1,16 @@
import { Avatar } from '@/ui/Avatar';
import Text from '@/ui/v2/Text';
import { nhost } from '@/utils/nhost';
import { useGetWorkspacesQuery } from '@/utils/__generated__/graphql';
import { nhost } from '@/utils/nhost';
import Image from 'next/image';
import Link from 'next/link';
import { useEffect } from 'react';
export default function SidebarWorkspaces() {
const user = nhost.auth.getUser();
const { data, loading, startPolling, stopPolling } = useGetWorkspacesQuery();
const { data, loading, startPolling, stopPolling } = useGetWorkspacesQuery({
fetchPolicy: 'cache-and-network',
});
useEffect(() => {
startPolling(1000);
@@ -28,7 +30,7 @@ export default function SidebarWorkspaces() {
<div className="mt-3 mb-4 space-y-2">
<div className="flex flex-row">
<svg
className="ml-1 h-4 w-4 animate-spin self-center text-dark"
className="self-center w-4 h-4 ml-1 animate-spin text-dark"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
@@ -47,7 +49,7 @@ export default function SidebarWorkspaces() {
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
<Text size="tiny" className="ml-2 self-center" color="greyscaleGrey">
<Text size="tiny" className="self-center ml-2" color="greyscaleGrey">
Creating first workspace...
</Text>
</div>
@@ -66,12 +68,12 @@ export default function SidebarWorkspaces() {
>
{name === 'Default Workspace' && creatorUserId === user.id ? (
<Avatar
className="h-8 w-8 self-center rounded-full"
className="self-center w-8 h-8 rounded-full"
name={user?.displayName}
avatarUrl={user?.avatarUrl}
/>
) : (
<div className="inline-block h-8 w-8 overflow-hidden rounded-lg">
<div className="inline-block w-8 h-8 overflow-hidden rounded-lg">
<Image
src="/logos/new.svg"
alt="Nhost Logo"

View File

@@ -1,4 +1,4 @@
import ChangeWorkspaceName from '@/components/applications/ChangeWorkspaceName';
import { useDialog } from '@/components/common/DialogProvider';
import RemoveWorkspaceModal from '@/components/workspace/RemoveWorkspaceModal';
import { useUI } from '@/context/UIContext';
import { useGetWorkspace } from '@/hooks/use-GetWorkspace';
@@ -13,7 +13,6 @@ import { copy } from '@/utils/copy';
import { nhost } from '@/utils/nhost';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useState } from 'react';
export default function WorkspaceHeader() {
const { currentWorkspace } = useCurrentWorkspaceAndApplication();
@@ -21,14 +20,14 @@ export default function WorkspaceHeader() {
query: { workspaceSlug },
} = useRouter();
const [changeWorkspaceNameModal, setChangeWorkspaceNameModal] =
useState(false);
const {
openDeleteWorkspaceModal,
closeDeleteWorkspaceModal,
deleteWorkspaceModal,
} = useUI();
const { openDialog } = useDialog();
const { data } = useGetWorkspace(workspaceSlug);
const workspace = data?.workspaces[0];
@@ -45,11 +44,6 @@ export default function WorkspaceHeader() {
return (
<div className="mx-auto flex max-w-3xl flex-col">
<Modal
showModal={changeWorkspaceNameModal}
close={() => setChangeWorkspaceNameModal(!changeWorkspaceNameModal)}
Component={ChangeWorkspaceName}
/>
<Modal
showModal={deleteWorkspaceModal}
close={closeDeleteWorkspaceModal}
@@ -112,9 +106,23 @@ export default function WorkspaceHeader() {
>
<Dropdown.Item
className="py-2"
onClick={() =>
setChangeWorkspaceNameModal(!changeWorkspaceNameModal)
}
onClick={() => {
openDialog('EDIT_WORKSPACE_NAME', {
title: (
<span className="grid grid-flow-row">
<span>Change Workspace Name</span>
<Text variant="subtitle1" component="span">
Changing the workspace name will also affect the URL
of the workspace.
</Text>
</span>
),
payload: {
currentWorkspaceName: currentWorkspace.name,
currentWorkspaceId: currentWorkspace.id,
},
});
}}
>
Change workspace name
</Dropdown.Item>

View File

@@ -1,11 +1,12 @@
import { useDialog } from '@/components/common/DialogProvider';
import { SidebarTitle } from '@/components/home/SidebarTitle';
import { useUI } from '@/context/UIContext';
import Button from '@/ui/v2/Button';
import Text from '@/ui/v2/Text';
import PlusCircleIcon from '@/ui/v2/icons/PlusCircleIcon';
import SidebarWorkspaces from './SidebarWorkspaces';
export function WorkspaceSection() {
const { openSection } = useUI();
const { openDialog } = useDialog();
return (
<>
@@ -15,7 +16,19 @@ export function WorkspaceSection() {
<Button
variant="borderless"
color="secondary"
onClick={openSection}
onClick={() => {
openDialog('EDIT_WORKSPACE_NAME', {
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>
),
});
}}
startIcon={<PlusCircleIcon />}
>
New Workspace

View File

@@ -237,7 +237,10 @@ test('should drop existing relationships and prepare a new one-to-many relations
"cascade": false,
"relationship": "books",
"source": "default",
"table": "authors",
"table": {
"name": "authors",
"schema": "public",
},
},
"type": "pg_drop_relationship",
}

View File

@@ -152,7 +152,12 @@ export default async function prepareTrackForeignKeyRelationsMetadata({
type: 'pg_drop_relationship',
args: {
source: dataSource,
table: foreignKeyRelation.referencedTable,
table: foreignKeyRelation.referencedSchema
? {
name: foreignKeyRelation.referencedTable,
schema: foreignKeyRelation.referencedSchema,
}
: foreignKeyRelation.referencedTable,
relationship: oneToManyRelationshipName,
cascade: false,
},

View File

@@ -184,7 +184,7 @@ export default function prepareUpdateTableQuery({
);
return [
...args,
...updatedArgs,
...prepareUpdateForeignKeyConstraintQuery({
...baseVariables,
originalForeignKeyRelation,

View File

@@ -10,7 +10,7 @@ export default function useNotFoundRedirect() {
useCurrentWorkspaceAndApplication();
const router = useRouter();
const {
query: { workspaceSlug, appSlug },
query: { workspaceSlug, appSlug, updating },
} = useRouter();
const notIn404Already = router.pathname !== '/404';
@@ -25,6 +25,15 @@ export default function useNotFoundRedirect() {
const inSettingsDatabasePage = router.pathname.includes('/settings/database');
useEffect(() => {
// This code is checking if the URL has a query of the form `?updating=true`
// If it does (`updating` is true) this useEffect will immediately exit without executing
// any further statements (e.g. the page will show a loader until `updating` is false).
// This is to prevent the user from being redirected to the 404 page while we are updating
// either the workspace slug or application slug.
if (updating) {
return;
}
if (noResolvedWorkspace && notIn404Already) {
router.push('/404');
}
@@ -37,6 +46,7 @@ export default function useNotFoundRedirect() {
router.push('/404');
}
}, [
updating,
currentApplication,
currentWorkspace,
noResolvedApplication,

View File

@@ -9,8 +9,8 @@ import {
useUpdateAppMutation,
} from '@/generated/graphql';
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
import CheckIcon from '@/ui/v2/icons/CheckIcon';
import Input from '@/ui/v2/Input';
import CheckIcon from '@/ui/v2/icons/CheckIcon';
import { discordAnnounce } from '@/utils/discordAnnounce';
import { slugifyString } from '@/utils/helpers';
import { updateOwnCache } from '@/utils/updateOwnCache';
@@ -53,6 +53,7 @@ export default function SettingsGeneralPage() {
const [deleteApplication] = useDeleteApplicationMutation({
variables: { appId: currentApplication?.id },
});
const { currentWorkspace } = useCurrentWorkspaceAndApplication();
const router = useRouter();
const form = useForm<ProjectNameValidationSchema>({
@@ -69,6 +70,14 @@ export default function SettingsGeneralPage() {
const { register, formState } = form;
const handleProjectNameChange = async (data: ProjectNameValidationSchema) => {
// In this bit of code we spread the props of the current path (e.g. /workspace/...) and add one key-value pair: `updating: true`.
// We want to indicate that the currently we're in the process of running a mutation state that will affect the routing behaviour of the website
// i.e. redirecting to 404 if there's no workspace/project with that slug.
await router.replace({
pathname: router.pathname,
query: { ...router.query, updating: true },
});
const newProjectSlug = slugifyString(data.name);
if (newProjectSlug.length < 1 || newProjectSlug.length > 32) {
@@ -100,8 +109,13 @@ export default function SettingsGeneralPage() {
toastStyleProps,
);
try {
await client.refetchQueries({ include: ['getOneUser'] });
await client.refetchQueries({
include: ['getOneUser'],
});
form.reset(undefined, { keepValues: true, keepDirty: false });
await router.push(
`/${currentWorkspace.slug}/${newProjectSlug}/settings/general`,
);
} catch (error) {
await discordAnnounce(
error.message || 'Error while trying to update application cache',

View File

@@ -1,5 +1,11 @@
# @nhost/docs
## 0.0.10
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
## 0.0.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/docs",
"version": "0.0.9",
"version": "0.0.10",
"private": true,
"scripts": {
"docusaurus": "docusaurus",

View File

@@ -1,5 +1,14 @@
# @nhost-examples/codegen-react-apollo
## 0.1.5
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/react@1.13.2
- @nhost/react-apollo@4.13.2
## 0.1.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/codegen-react-apollo",
"version": "0.1.4",
"version": "0.1.5",
"private": true,
"dependencies": {
"@apollo/client": "^3.6.9",

View File

@@ -1,5 +1,13 @@
# @nhost-examples/codegen-react-query
## 0.1.5
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/react@1.13.2
## 0.1.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/codegen-react-query",
"version": "0.1.4",
"version": "0.1.5",
"private": true,
"dependencies": {
"@nhost/react": "*",

View File

@@ -1,5 +1,11 @@
# @nhost-examples/docker-compose
## 0.0.4
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
## 0.0.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/docker-compose",
"version": "0.0.3",
"version": "0.0.4",
"private": true,
"scripts": {
"e2e": "vitest run"

View File

@@ -1,5 +1,15 @@
# @nhost-examples/nextjs
## 0.1.5
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/nextjs@1.13.2
- @nhost/react@1.13.2
- @nhost/react-apollo@4.13.2
## 0.1.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/nextjs",
"version": "0.1.4",
"version": "0.1.5",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -1,5 +1,27 @@
# @nhost-examples/react-apollo
## 0.1.7
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/react@1.13.2
- @nhost/react-apollo@4.13.2
## 0.1.6
### Patch Changes
- c2706c7d: Export commonly used types
`BackendUrl`, `ErrorPayload`, `NhostSession`, `Subdomain`, and `User` are now exported in all our SDKs
- Updated dependencies [c2706c7d]
- Updated dependencies [d42c27ae]
- @nhost/react@1.13.1
- @nhost/react-apollo@4.13.1
## 0.1.5
### Patch Changes

View File

@@ -0,0 +1,21 @@
import { faker } from '@faker-js/faker'
context('Forgot password', () => {
it('should reset password', () => {
const email = faker.internet.email()
const password = faker.internet.password(8)
cy.signUpEmailPassword(email, password)
cy.contains('Verification email sent').should('be.visible')
cy.visit('/sign-in')
cy.findByRole('button', { name: /Continue with email \+ password/i }).click()
cy.findByRole('button', { name: /Forgot Password?/i }).click()
cy.findByPlaceholderText('Email Address').type(email)
cy.findByRole('button', { name: /Reset your password/i }).click()
cy.confirmEmail(email)
cy.contains('Profile page')
})
})

View File

@@ -1,5 +1,5 @@
import { faker } from '@faker-js/faker'
import { User } from '@nhost/hasura-auth-js'
import { User } from '@nhost/react'
import '@testing-library/cypress/add-commands'
import 'cypress-mailhog'
@@ -83,7 +83,7 @@ Cypress.Commands.add('signOut', () => {
})
Cypress.Commands.add('confirmEmail', (email) => {
cy.mhGetMailsByRecipient(email)
cy.mhGetMailsByRecipient(email, 1)
.should('have.length', 1)
.then(([message]) => {
cy.visit(message.Content.Headers['X-Link'][0])

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/react-apollo",
"version": "0.1.5",
"version": "0.1.7",
"private": true,
"dependencies": {
"@apollo/client": "^3.6.9",
@@ -60,7 +60,7 @@
"@xstate/inspect": "^0.6.2",
"cypress": "^10.7.0",
"cypress-mailhog": "^1.6.0",
"start-server-and-test": "^1.14.0",
"start-server-and-test": "^1.15.2",
"totp-generator": "^0.0.13",
"typescript": "^4.8.2",
"vite": "^4.0.2",

View File

@@ -1,5 +1,13 @@
# @nhost-examples/react-gqty
## 0.0.4
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/react@1.13.2
## 0.0.3
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/react-gqty",
"private": true,
"version": "0.0.3",
"version": "0.0.4",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,14 @@
# @nhost-examples/react-urql
## 0.0.2
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/react@1.13.2
- @nhost/react-urql@1.0.2
## 0.0.1
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/react-urql",
"private": true,
"version": "0.0.1",
"version": "0.0.2",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",

View File

@@ -1,5 +1,13 @@
# @nhost-examples/serverless-functions
## 0.0.4
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/stripe-graphql-js@0.0.8
## 0.0.3
### Patch Changes

View File

@@ -1,13 +1,13 @@
{
"name": "@nhost-examples/serverless-functions",
"private": true,
"version": "0.0.3",
"version": "0.0.4",
"devDependencies": {
"@types/express": "^4.17.13"
},
"dependencies": {
"@graphql-yoga/node": "^2.13.13",
"@nhost/stripe-graphql-js": "^0.0.7",
"@nhost/stripe-graphql-js": "^0.0.8",
"@pothos/core": "^3.21.0",
"cross-fetch": "^3.1.5",
"graphql": "15.7.2",

View File

@@ -1,5 +1,26 @@
# @nhost-examples/vue-apollo
## 0.0.5
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/apollo@4.13.1
- @nhost/vue@1.13.2
## 0.0.4
### Patch Changes
- c2706c7d: Export commonly used types
`BackendUrl`, `ErrorPayload`, `NhostSession`, `Subdomain`, and `User` are now exported in all our SDKs
- Updated dependencies [c2706c7d]
- Updated dependencies [d42c27ae]
- @nhost/vue@1.13.1
## 0.0.3
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@nhost-examples/vue-apollo",
"private": true,
"version": "0.0.3",
"version": "0.0.5",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",

View File

@@ -10,7 +10,7 @@
<script lang="ts" setup>
import { PropType, ref, watchEffect } from 'vue'
import { ErrorPayload } from '@nhost/hasura-auth-js'
import { ErrorPayload } from '@nhost/vue'
const props = defineProps({
error: Object as PropType<ErrorPayload | null>

View File

@@ -1,5 +1,14 @@
# @nhost-examples/vue-quickstart
## 0.0.4
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/apollo@4.13.1
- @nhost/vue@1.13.2
## 0.0.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost-examples/vue-quickstart",
"version": "0.0.3",
"version": "0.0.4",
"private": true,
"scripts": {
"build": "vite build",

View File

@@ -1,5 +1,13 @@
# @nhost/apollo
## 4.13.1
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/nhost-js@1.13.1
## 4.13.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/apollo",
"version": "4.13.0",
"version": "4.13.1",
"description": "Nhost Apollo Client library",
"license": "MIT",
"keywords": [

View File

@@ -0,0 +1,7 @@
# @nhost/google-translation
## 0.0.2
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/google-translation",
"version": "0.0.1",
"version": "0.0.2",
"description": "Google Translation GraphQL API",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,22 @@
# @nhost/react-apollo
## 4.13.2
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/apollo@4.13.1
- @nhost/react@1.13.2
## 4.13.1
### Patch Changes
- Updated dependencies [c2706c7d]
- Updated dependencies [d42c27ae]
- @nhost/react@1.13.1
## 4.13.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react-apollo",
"version": "4.13.0",
"version": "4.13.2",
"description": "Nhost React Apollo client",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,21 @@
# @nhost/react-urql
## 1.0.2
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/react@1.13.2
## 1.0.1
### Patch Changes
- Updated dependencies [c2706c7d]
- Updated dependencies [d42c27ae]
- @nhost/react@1.13.1
## 1.0.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react-urql",
"version": "1.0.0",
"version": "1.0.2",
"description": "Nhost React URQL client",
"license": "MIT",
"keywords": [

View File

@@ -1,5 +1,11 @@
# @nhost/stripe-graphql-js
## 0.0.8
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
## 0.0.7
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/stripe-graphql-js",
"version": "0.0.7",
"version": "0.0.8",
"description": "Stripe GraphQL API",
"license": "MIT",
"keywords": [

View File

@@ -61,7 +61,7 @@
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^5.42.1",
"@vitejs/plugin-react": "^3.0.0",
"@vitest/coverage-c8": "^0.26.0",
"@vitest/coverage-c8": "^0.27.0",
"eslint": "^8.26.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-cypress": "^2.12.1",
@@ -82,7 +82,7 @@
"vite": "^4.0.2",
"vite-plugin-dts": "^1.7.1",
"vite-tsconfig-paths": "^4.0.3",
"vitest": "^0.26.2"
"vitest": "^0.27.0"
},
"resolutions": {
"graphql": "16.6.0"

View File

@@ -1,5 +1,11 @@
# @nhost/docgen
## 0.1.5
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
## 0.1.4
### Patch Changes

View File

@@ -2,7 +2,7 @@
"name": "@nhost/docgen",
"description": "Documentation generator for classes and functions",
"private": true,
"version": "0.1.4",
"version": "0.1.5",
"license": "MIT",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",

View File

@@ -1,5 +1,11 @@
# @nhost/hasura-auth-js
## 1.12.1
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
## 1.12.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/hasura-auth-js",
"version": "1.12.0",
"version": "1.12.1",
"description": "Hasura-auth client",
"license": "MIT",
"keywords": [
@@ -75,6 +75,6 @@
"cheerio": "1.0.0-rc.12",
"mailhog": "^4.16.0",
"msw": "^0.47.4",
"start-server-and-test": "^1.14.0"
"start-server-and-test": "^1.15.2"
}
}

View File

@@ -1,5 +1,11 @@
# @nhost/hasura-storage-js
## 1.13.1
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
## 1.13.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/hasura-storage-js",
"version": "1.13.0",
"version": "1.13.1",
"description": "Hasura-storage client",
"license": "MIT",
"keywords": [
@@ -68,7 +68,7 @@
"cross-fetch": "^3.1.5",
"jpeg-js": "^0.4.4",
"pixelmatch": "^5.3.0",
"start-server-and-test": "^1.14.0",
"start-server-and-test": "^1.15.2",
"uuid": "^9.0.0"
}
}

View File

@@ -1,5 +1,23 @@
# @nhost/nextjs
## 1.13.2
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/react@1.13.2
## 1.13.1
### Patch Changes
- d42c27ae: Add explicit return types to React hooks
- 927be4a2: Store the session in a cookie so there is no need for an SSR page to ask for a new access token
- Updated dependencies [c2706c7d]
- Updated dependencies [d42c27ae]
- @nhost/react@1.13.1
## 1.13.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/nextjs",
"version": "1.13.0",
"version": "1.13.2",
"description": "Nhost NextJS library",
"license": "MIT",
"keywords": [

View File

@@ -1,8 +1,14 @@
import { AuthMachine, NHOST_REFRESH_TOKEN_KEY, VanillaNhostClient } from '@nhost/react'
import {
AuthMachine,
NhostSession,
NHOST_REFRESH_TOKEN_KEY,
VanillaNhostClient
} from '@nhost/react'
import Cookies from 'js-cookie'
import { GetServerSidePropsContext } from 'next'
import { StateFrom } from 'xstate'
import { waitFor } from 'xstate/lib/waitFor'
import { NHOST_SESSION_KEY } from './utils'
/**
* Creates an Nhost client that runs on the server side.
@@ -42,11 +48,17 @@ export const createServerSideClient = async (
Cookies.remove(key)
}
},
start: true,
start: false,
autoRefreshToken: false,
autoSignIn: true
})
const strSession = context.req.cookies[NHOST_SESSION_KEY]
const refreshToken = context.req.cookies[NHOST_REFRESH_TOKEN_KEY]
const initialSession: NhostSession = strSession &&
refreshToken && { ...JSON.parse(strSession), refreshToken }
nhost.auth.client.start({ initialSession })
await waitFor(
nhost.auth.client.interpreter!,
(state: StateFrom<AuthMachine>) => !state.hasTag('loading')

View File

@@ -5,6 +5,7 @@ import {
NhostProvider,
Subdomain
} from '@nhost/react'
import { setNhostSessionInCookie } from './utils'
export * from '@nhost/react'
export * from './create-server-side-client'
@@ -38,5 +39,10 @@ export class NhostClient extends ReactNhostClient {
autoRefreshToken: isBrowser && params.autoRefreshToken,
clientStorageType: 'cookie'
})
this.auth.onAuthStateChanged(() => {
setNhostSessionInCookie(this)
})
this.auth.onTokenChanged(setNhostSessionInCookie)
}
}

View File

@@ -1,5 +1,6 @@
import { NhostSession } from '@nhost/react'
import { NhostClient, NhostSession } from '@nhost/react'
import fetch from 'cross-fetch'
import Cookies from 'js-cookie'
export const refresh = async (nhostUrl: string, refreshToken: string): Promise<NhostSession> => {
const result = await fetch(`${nhostUrl}/v1/auth/token`, {
@@ -9,6 +10,26 @@ export const refresh = async (nhostUrl: string, refreshToken: string): Promise<N
},
body: JSON.stringify({ refreshToken })
})
if (result.ok) return result.json()
else return Promise.reject(result.statusText)
if (result.ok) {
return result.json()
}
return Promise.reject(result.statusText)
}
export const NHOST_SESSION_KEY = 'nhostSession'
export const setNhostSessionInCookie = (param: NhostClient | NhostSession | null) => {
const session = param && 'auth' in param ? param.auth.getSession() : param
if (!session) {
Cookies.remove(NHOST_SESSION_KEY)
return
}
const { refreshToken, ...rest } = session
const expires = new Date()
// * Expire the cookie 60 seconds before the token expires
expires.setSeconds(expires.getSeconds() + session.accessTokenExpiresIn - 60)
Cookies.set(NHOST_SESSION_KEY, JSON.stringify(rest), {
sameSite: 'strict',
expires
})
}

View File

@@ -1,5 +1,14 @@
# @nhost/nhost-js
## 1.13.1
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/hasura-auth-js@1.12.1
- @nhost/hasura-storage-js@1.13.1
## 1.13.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/nhost-js",
"version": "1.13.0",
"version": "1.13.1",
"description": "Nhost JavaScript SDK",
"license": "MIT",
"keywords": [
@@ -72,7 +72,7 @@
"@faker-js/faker": "^7.6.0",
"@nhost/docgen": "workspace:*",
"graphql": "16.6.0",
"start-server-and-test": "^1.14.0"
"start-server-and-test": "^1.15.2"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"

View File

@@ -1,5 +1,23 @@
# @nhost/react
## 1.13.2
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
- Updated dependencies [200e9f77]
- @nhost/nhost-js@1.13.1
## 1.13.1
### Patch Changes
- c2706c7d: Export commonly used types
`BackendUrl`, `ErrorPayload`, `NhostSession`, `Subdomain`, and `User` are now exported in all our SDKs
- d42c27ae: Add explicit return types to React hooks
## 1.13.0
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/react",
"version": "1.13.0",
"version": "1.13.2",
"description": "Nhost React library",
"license": "MIT",
"keywords": [

View File

@@ -3,12 +3,12 @@ import {
BackendUrl,
NhostAuthConstructorParams,
NhostClient as _VanillaNhostClient,
NhostSession,
NHOST_REFRESH_TOKEN_KEY,
Subdomain
} from '@nhost/nhost-js'
export type { NhostSession, NhostAuthConstructorParams, AuthMachine, Subdomain, BackendUrl }
// * Required for @nhost/nextjs
export type { NhostAuthConstructorParams, AuthMachine }
export { NHOST_REFRESH_TOKEN_KEY }
/** @internal */

View File

@@ -1,3 +1,4 @@
export type { BackendUrl, ErrorPayload, NhostSession, Subdomain, User } from '@nhost/nhost-js'
export * from './client'
export * from './components'
export * from './provider'

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-access-token
*/
export const useAccessToken = () => {
export const useAccessToken = (): string | null => {
const service = useAuthInterpreter()
return useSelector(service, (state) => state.context.accessToken.value)
}

View File

@@ -11,7 +11,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* Use {@link useAuthenticationStatus} instead.
*/
export const useAuthLoading = () => {
export const useAuthLoading = (): boolean => {
const service = useAuthInterpreter()
return useSelector(service, (state) => state.hasTag('loading'))
}

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-access-token
*/
export const useAuthenticated = () => {
export const useAuthenticated = (): boolean => {
const service = useAuthInterpreter()
const [isAuthenticated, setIsAuthenticated] = useState(
!!service.status && service.getSnapshot().matches({ authentication: 'signedIn' })

View File

@@ -1,3 +1,4 @@
import { ErrorPayload } from '@nhost/nhost-js'
import { useSelector } from '@xstate/react'
import { useAuthInterpreter } from './useAuthInterpreter'
@@ -10,7 +11,13 @@ import { useAuthInterpreter } from './useAuthInterpreter'
* const { isAuthenticated, isLoading } = useAuthenticationStatus();
* ```
*/
export const useAuthenticationStatus = () => {
export const useAuthenticationStatus = (): {
isAuthenticated: boolean
isLoading: boolean
error: ErrorPayload | null
isError: boolean
connectionAttempts: number
} => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -12,7 +12,7 @@ import { useAccessToken } from './useAccessToken'
*
* @docs https://docs.nhost.io/reference/react/use-decoded-access-token
*/
export const useDecodedAccessToken = () => {
export const useDecodedAccessToken = (): JWTClaims | null => {
const jwt = useAccessToken()
return jwt ? jwt_decode<JWTClaims>(jwt) : null
}

View File

@@ -11,7 +11,7 @@ import { useHasuraClaims } from './useHasuraClaims'
*
* @docs https://docs.nhost.io/reference/react/use-hasura-claim
*/
export const useHasuraClaim = (name: string) => {
export const useHasuraClaim = (name: string): string | string[] | null => {
const hasuraClaims = useHasuraClaims()
return hasuraClaims?.[name.startsWith('x-hasura-') ? name : `x-hasura-${name}`] || null
}

View File

@@ -1,4 +1,4 @@
import { JWTHasuraClaims } from '@nhost/nhost-js'
import { useDecodedAccessToken } from './useDecodedAccessToken'
/**
@@ -11,7 +11,7 @@ import { useDecodedAccessToken } from './useDecodedAccessToken'
*
* @docs https://docs.nhost.io/reference/react/use-hasura-claims
*/
export const useHasuraClaims = () => {
export const useHasuraClaims = (): JWTHasuraClaims | null => {
const claims = useDecodedAccessToken()
return claims?.['https://hasura.io/jwt/claims'] || null
}

View File

@@ -1,3 +1,4 @@
import { User } from '@nhost/nhost-js'
import { useMemo } from 'react'
import { useAuthenticationStatus } from './useAuthenticationStatus'
@@ -7,7 +8,11 @@ import { useUserData } from './useUserData'
* @deprecated
* This hook ensures backward compatibility with `@nhost/react-auth`, which is deprecated.
*/
export const useNhostAuth = () => {
export const useNhostAuth = (): {
isLoading: boolean
isAuthenticated: boolean
user: User | null
} => {
const { isLoading, isAuthenticated } = useAuthenticationStatus()
const user = useUserData()
return useMemo(() => ({ isLoading, isAuthenticated, user }), [isLoading, isAuthenticated, user])

View File

@@ -13,7 +13,7 @@ import { NhostReactContext } from './provider'
*
* @docs https://docs.nhost.io/reference/react/use-nhost-backend-url
*/
export const useNhostBackendUrl = () => {
export const useNhostBackendUrl = (): string => {
const nhost = useContext(NhostReactContext)
return nhost.auth.client.backendUrl.replace('/v1/auth', '')
}

View File

@@ -31,7 +31,7 @@ import { NhostReactContext } from './provider'
* };
* ```
*/
export const useProviderLink = (options?: ProviderOptions) => {
export const useProviderLink = (options?: ProviderOptions): Record<Provider, string> => {
/**
* @internal When using Nextjs or any SSR framework, nhost.auth.client.clientUrl will be set to `undefined`
* as its value is set to window.location.origin.

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-avatar-url
*/
export const useUserAvatarUrl = () => {
export const useUserAvatarUrl = (): string | undefined => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -1,3 +1,4 @@
import { User } from '@nhost/nhost-js'
import { useSelector } from '@xstate/react'
import { useAuthInterpreter } from './useAuthInterpreter'
@@ -31,7 +32,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-data
*/
export const useUserData = () => {
export const useUserData = (): User | null => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-default-role
*/
export const useUserDefaultRole = () => {
export const useUserDefaultRole = (): string | undefined => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-display-name
*/
export const useUserDisplayName = () => {
export const useUserDisplayName = (): string | undefined => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-email
*/
export const useUserEmail = () => {
export const useUserEmail = (): string | undefined => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-id
*/
export const useUserId = () => {
export const useUserId = (): string | undefined => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-is-anonymous
*/
export const useUserIsAnonymous = () => {
export const useUserIsAnonymous = (): boolean | undefined => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-locale
*/
export const useUserLocale = () => {
export const useUserLocale = (): string | undefined => {
const service = useAuthInterpreter()
return useSelector(
service,

View File

@@ -12,7 +12,7 @@ import { useAuthInterpreter } from './useAuthInterpreter'
*
* @docs https://docs.nhost.io/reference/react/use-user-roles
*/
export const useUserRoles = () => {
export const useUserRoles = (): string[] => {
const service = useAuthInterpreter()
return useSelector(service, (state) => {
if (!state.matches('authentication.signedIn')) {

View File

@@ -1,5 +1,11 @@
# @nhost/sync-versions
## 0.0.4
### Patch Changes
- 200e9f77: chore(deps): update dependency @types/react-dom to v18.0.10
## 0.0.3
### Patch Changes

View File

@@ -2,7 +2,7 @@
"name": "@nhost/sync-versions",
"description": "Sync the versions of Nhost services in each of the packages of a pnpm workspace",
"private": true,
"version": "0.0.3",
"version": "0.0.4",
"license": "MIT",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",

Some files were not shown because too many files have changed in this diff Show More