Compare commits
17 Commits
@nhost/rea
...
@nhost/cor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bebf9e1f2b | ||
|
|
2413c10283 | ||
|
|
0f7fbdab97 | ||
|
|
14e5fd63a6 | ||
|
|
2446913836 | ||
|
|
1f88a9f47a | ||
|
|
261e37cda4 | ||
|
|
5ee395ea8e | ||
|
|
828633ffc9 | ||
|
|
7b7527a5e6 | ||
|
|
e0cfcafead | ||
|
|
75a1428114 | ||
|
|
d82d830849 | ||
|
|
2def59fc6c | ||
|
|
64ceb2c6bf | ||
|
|
3ee007620c | ||
|
|
b9cf8172a0 |
@@ -2,14 +2,32 @@
|
||||
|
||||
This demo is a work in progress, further improvements are to come
|
||||
|
||||
### Installation
|
||||
## Get started
|
||||
|
||||
First, clone this repo. Then run the commands:
|
||||
1. Clone the repository
|
||||
|
||||
```sh
|
||||
git clone https://github.com/nhost/nhost
|
||||
cd nhost
|
||||
```
|
||||
|
||||
2. Install dependencies
|
||||
|
||||
```sh
|
||||
cd examples/nextjs
|
||||
yarn
|
||||
yarn dev
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Terminal 1: Start Nhost
|
||||
|
||||
```sh
|
||||
nhost dev
|
||||
```
|
||||
|
||||
4. Terminal 2: Start React App
|
||||
|
||||
```sh
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
If you want to use this demo with your own cloud instance:
|
||||
@@ -18,8 +36,3 @@ If you want to use this demo with your own cloud instance:
|
||||
- don't forget to change the client URL in the Nhost console so email verification will work: `Users -> Login Settings -> Client login URLs`: `http://localhost:4000`
|
||||
|
||||
If you want to use a local Nhost instance, start the CLI in parallel to Nextjs:
|
||||
|
||||
```sh
|
||||
# Inside examples/nextjs
|
||||
nhost -d
|
||||
```
|
||||
|
||||
13
examples/nextjs/next.config.js
Normal file
13
examples/nextjs/next.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
|
||||
const pkg = require('./package.json')
|
||||
// * Only required to make it work with the monorepo. Is not required otherwise
|
||||
const withTM = require('next-transpile-modules')(
|
||||
// * All references to workspace packages are transpiled
|
||||
Object.entries(pkg.dependencies)
|
||||
.filter(([name, version]) => version.startsWith('workspace'))
|
||||
.map(([name]) => name)
|
||||
)
|
||||
|
||||
module.exports = withTM(nextConfig)
|
||||
@@ -11,4 +11,4 @@
|
||||
max_connections: 50
|
||||
retries: 20
|
||||
use_prepared_statements: true
|
||||
tables: "!include default/tables/tables.yaml"
|
||||
tables: '!include default/tables/tables.yaml'
|
||||
|
||||
@@ -16,10 +16,10 @@ configuration:
|
||||
update: updateAuthProviders
|
||||
update_by_pk: updateAuthProvider
|
||||
array_relationships:
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: provider_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: provider_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
|
||||
@@ -19,6 +19,6 @@ configuration:
|
||||
update: updateAuthRefreshTokens
|
||||
update_by_pk: updateAuthRefreshToken
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -16,17 +16,17 @@ configuration:
|
||||
update: updateAuthRoles
|
||||
update_by_pk: updateAuthRole
|
||||
array_relationships:
|
||||
- name: userRoles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: role
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: usersByDefaultRole
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: default_role
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
- name: userRoles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: role
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: usersByDefaultRole
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: default_role
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
|
||||
@@ -23,9 +23,9 @@ configuration:
|
||||
update: updateAuthUserProviders
|
||||
update_by_pk: updateAuthUserProvider
|
||||
object_relationships:
|
||||
- name: provider
|
||||
using:
|
||||
foreign_key_constraint_on: provider_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: provider
|
||||
using:
|
||||
foreign_key_constraint_on: provider_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -19,9 +19,9 @@ configuration:
|
||||
update: updateAuthUserRoles
|
||||
update_by_pk: updateAuthUserRole
|
||||
object_relationships:
|
||||
- name: roleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: role
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: roleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: role
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -38,28 +38,28 @@ configuration:
|
||||
update: updateUsers
|
||||
update_by_pk: updateUser
|
||||
object_relationships:
|
||||
- name: defaultRoleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
- name: defaultRoleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
array_relationships:
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
- name: roles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
- name: roles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
|
||||
@@ -2,9 +2,9 @@ table:
|
||||
name: books
|
||||
schema: public
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- title
|
||||
filter: {}
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- title
|
||||
filter: {}
|
||||
role: user
|
||||
|
||||
@@ -2,9 +2,9 @@ table:
|
||||
name: test
|
||||
schema: public
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- bidon
|
||||
- id
|
||||
filter: {}
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- bidon
|
||||
- id
|
||||
filter: {}
|
||||
role: user
|
||||
|
||||
@@ -23,10 +23,10 @@ configuration:
|
||||
update: updateBuckets
|
||||
update_by_pk: updateBucket
|
||||
array_relationships:
|
||||
- name: files
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bucket_id
|
||||
table:
|
||||
name: files
|
||||
schema: storage
|
||||
- name: files
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bucket_id
|
||||
table:
|
||||
name: files
|
||||
schema: storage
|
||||
|
||||
@@ -25,6 +25,6 @@ configuration:
|
||||
update: updateFiles
|
||||
update_by_pk: updateFile
|
||||
object_relationships:
|
||||
- name: bucket
|
||||
using:
|
||||
foreign_key_constraint_on: bucket_id
|
||||
- name: bucket
|
||||
using:
|
||||
foreign_key_constraint_on: bucket_id
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
- "!include auth_provider_requests.yaml"
|
||||
- "!include auth_providers.yaml"
|
||||
- "!include auth_refresh_tokens.yaml"
|
||||
- "!include auth_roles.yaml"
|
||||
- "!include auth_user_providers.yaml"
|
||||
- "!include auth_user_roles.yaml"
|
||||
- "!include auth_users.yaml"
|
||||
- "!include public_books.yaml"
|
||||
- "!include storage_buckets.yaml"
|
||||
- "!include storage_files.yaml"
|
||||
- '!include auth_provider_requests.yaml'
|
||||
- '!include auth_providers.yaml'
|
||||
- '!include auth_refresh_tokens.yaml'
|
||||
- '!include auth_roles.yaml'
|
||||
- '!include auth_user_providers.yaml'
|
||||
- '!include auth_user_roles.yaml'
|
||||
- '!include auth_users.yaml'
|
||||
- '!include public_books.yaml'
|
||||
- '!include storage_buckets.yaml'
|
||||
- '!include storage_files.yaml'
|
||||
|
||||
@@ -6,13 +6,23 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
"prettier": "prettier --check .",
|
||||
"prettier:fix": "prettier --write .",
|
||||
"lint": "eslint . --ext .ts,.tsx",
|
||||
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
||||
"verify": "run-p prettier lint",
|
||||
"verify:fix": "run-p prettier:fix lint:fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.5.10",
|
||||
"@nhost/nextjs": "^1.0.10",
|
||||
"@nhost/react": "^0.5.0",
|
||||
"@nhost/react-apollo": "^4.0.10",
|
||||
"@nhost/apollo": "workspace:*",
|
||||
"@nhost/core": "workspace:*",
|
||||
"@nhost/nextjs": "workspace:*",
|
||||
"@nhost/react": "workspace:*",
|
||||
"@nhost/react-apollo": "workspace:*",
|
||||
"@nhost/nhost-js": "workspace:*",
|
||||
"@nhost/hasura-auth-js": "workspace:*",
|
||||
"@nhost/hasura-storage-js": "workspace:*",
|
||||
"graphql": "^16.3.0",
|
||||
"next": "12.1.0",
|
||||
"react": "17.0.2",
|
||||
@@ -22,9 +32,10 @@
|
||||
"@types/node": "17.0.23",
|
||||
"@types/react": "17.0.43",
|
||||
"@xstate/inspect": "^0.6.2",
|
||||
"eslint": "8.8.0",
|
||||
"eslint-config-next": "12.0.10",
|
||||
"next-transpile-modules": "^9.0.0",
|
||||
"typescript": "4.5.5",
|
||||
"ws": "^8.5.0"
|
||||
"ws": "^8.5.0",
|
||||
"xstate": "^4.30.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,14 @@ if (typeof window !== 'undefined' && process.env.NEXT_PUBLIC_DEBUG) {
|
||||
const nhost = new NhostClient({ backendUrl: BACKEND_URL })
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
// * Monorepo-related. See: https://stackoverflow.com/questions/71843247/react-nextjs-type-error-component-cannot-be-used-as-a-jsx-component
|
||||
const AnyComponent = Component as any
|
||||
return (
|
||||
<NhostNextProvider nhost={nhost} initial={pageProps.nhostSession}>
|
||||
<NhostApolloProvider nhost={nhost}>
|
||||
<div className="App">
|
||||
<Header />
|
||||
<Component {...pageProps} />
|
||||
<AnyComponent {...pageProps} />
|
||||
</div>
|
||||
</NhostApolloProvider>
|
||||
</NhostNextProvider>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
@@ -19,11 +15,6 @@
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
REACT_APP_BACKEND_URL=http://localhost:1337
|
||||
VITE_NHOST_URL=http://localhost:1337
|
||||
@@ -72,25 +72,33 @@ This example app has some work in progress:
|
||||
|
||||
## Get started
|
||||
|
||||
1. Install dependencies
|
||||
1. Clone the repository
|
||||
|
||||
```
|
||||
npm install
|
||||
```sh
|
||||
git clone https://github.com/nhost/nhost
|
||||
cd nhost
|
||||
```
|
||||
|
||||
2. Terminal 1: Start Nhost
|
||||
2. Install dependencies
|
||||
|
||||
```sh
|
||||
cd examples/react-apollo-crm
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Terminal 1: Start Nhost
|
||||
|
||||
```sh
|
||||
nhost dev
|
||||
```
|
||||
|
||||
2. Terminal 2: Start React App
|
||||
4. Terminal 2: Start React App
|
||||
|
||||
```
|
||||
npm run start
|
||||
```sh
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
3. Terminal 3: Start GraphQL Codegens
|
||||
5. Terminal 3: Start GraphQL Codegens
|
||||
|
||||
> Make sure that the Nhost backend in step 2 has started and is available before you run this command
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
module.exports = {
|
||||
client: {
|
||||
service: {
|
||||
name: "backend",
|
||||
url: "http://localhost:1337/v1/graphql",
|
||||
name: 'backend',
|
||||
url: 'http://localhost:1337/v1/graphql',
|
||||
headers: {
|
||||
"x-hasura-admin-secret": "nhost-admin-secret",
|
||||
},
|
||||
'x-hasura-admin-secret': 'nhost-admin-secret'
|
||||
}
|
||||
},
|
||||
includes: ["src/**/*.graphql", "src/**/*.gql"],
|
||||
},
|
||||
};
|
||||
includes: ['src/**/*.graphql', 'src/**/*.gql']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ schema: http://localhost:1337/v1/graphql
|
||||
headers:
|
||||
x-hasura-admin-secret: nhost-admin-secret
|
||||
documents:
|
||||
- "src/**/*.graphql"
|
||||
- "src/**/*.gql"
|
||||
- 'src/**/*.graphql'
|
||||
- 'src/**/*.gql'
|
||||
generates:
|
||||
src/utils/__generated__/graphql.ts:
|
||||
plugins:
|
||||
- "typescript"
|
||||
- "typescript-operations"
|
||||
- "typescript-react-apollo"
|
||||
- 'typescript'
|
||||
- 'typescript-operations'
|
||||
- 'typescript-react-apollo'
|
||||
config:
|
||||
withRefetchFn: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NhostClient } from '@nhost/nhost-js'
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: process.env.NHOST_BACKEND_URL!
|
||||
backendUrl: process.env.NHOST_BACKEND_URL!
|
||||
})
|
||||
|
||||
export { nhost }
|
||||
|
||||
@@ -2,104 +2,104 @@ import { Request, Response } from 'express'
|
||||
import { nhost } from '../../_utils/nhost'
|
||||
|
||||
const handler = async (req: Request, res: Response) => {
|
||||
if (req.headers['nhsot-webhook-secret'] !== process.env.NHSOT_WEBHOOK_SECRET) {
|
||||
return res.status(401).send('Unauthorized')
|
||||
}
|
||||
if (req.headers['nhsot-webhook-secret'] !== process.env.NHSOT_WEBHOOK_SECRET) {
|
||||
return res.status(401).send('Unauthorized')
|
||||
}
|
||||
|
||||
// User who just signed up
|
||||
const user = req.body.event.data.new
|
||||
// User who just signed up
|
||||
const user = req.body.event.data.new
|
||||
|
||||
// Get the user's email domain
|
||||
const emailDomain = user.email.split('@')[1]
|
||||
// Get the user's email domain
|
||||
const emailDomain = user.email.split('@')[1]
|
||||
|
||||
// Check if a company with the user's email domain already exists.
|
||||
const GET_COMPANY_WITH_EMAIL_DOMAIN = `
|
||||
// Check if a company with the user's email domain already exists.
|
||||
const GET_COMPANY_WITH_EMAIL_DOMAIN = `
|
||||
query getCompanyWithEmailDomain($emailDomain: String!) {
|
||||
companies(where: { emailDomain: { _eq: $emailDomain } }) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
const { data, error } = await nhost.graphql.request(
|
||||
GET_COMPANY_WITH_EMAIL_DOMAIN,
|
||||
{
|
||||
emailDomain
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-admin-secret': process.env.NHOST_ADMIN_SECRET
|
||||
}
|
||||
const { data, error } = await nhost.graphql.request(
|
||||
GET_COMPANY_WITH_EMAIL_DOMAIN,
|
||||
{
|
||||
emailDomain
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-admin-secret': process.env.NHOST_ADMIN_SECRET
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (error) {
|
||||
return res.status(500).send(error)
|
||||
}
|
||||
if (error) {
|
||||
return res.status(500).send(error)
|
||||
}
|
||||
|
||||
const { companies } = data as any
|
||||
const { companies } = data as any
|
||||
|
||||
let companyId
|
||||
if (companies.length === 1) {
|
||||
// if a company already exists, use that company's id
|
||||
companyId = companies[0].id
|
||||
} else {
|
||||
// else, create a new company for the newly created user with the same email domain as the user
|
||||
const CREATE_NEW_COMPANY = `
|
||||
let companyId
|
||||
if (companies.length === 1) {
|
||||
// if a company already exists, use that company's id
|
||||
companyId = companies[0].id
|
||||
} else {
|
||||
// else, create a new company for the newly created user with the same email domain as the user
|
||||
const CREATE_NEW_COMPANY = `
|
||||
mutation insertCompany($emailDomain: String!) {
|
||||
insertCompany(object: { name: $emailDomain, emailDomain: $emailDomain }) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
const { data, error } = await nhost.graphql.request(
|
||||
CREATE_NEW_COMPANY,
|
||||
{
|
||||
emailDomain
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-admin-secret': process.env.NHOST_ADMIN_SECRET
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (error) {
|
||||
return res.status(500).send(error)
|
||||
const { data, error } = await nhost.graphql.request(
|
||||
CREATE_NEW_COMPANY,
|
||||
{
|
||||
emailDomain
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-admin-secret': process.env.NHOST_ADMIN_SECRET
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const { insertCompany } = data as any
|
||||
if (error) {
|
||||
return res.status(500).send(error)
|
||||
}
|
||||
|
||||
companyId = insertCompany.id
|
||||
}
|
||||
const { insertCompany } = data as any
|
||||
|
||||
// We now have the company id of an existing, or a newly created company.
|
||||
// Now let's add the user to the company.
|
||||
companyId = insertCompany.id
|
||||
}
|
||||
|
||||
const ADD_USER_TO_COMPANY = `
|
||||
// We now have the company id of an existing, or a newly created company.
|
||||
// Now let's add the user to the company.
|
||||
|
||||
const ADD_USER_TO_COMPANY = `
|
||||
mutation addUserToCompany($userId: uuid!, $companyId: uuid!) {
|
||||
insertCompanyUser(object: {userId: $userId, companyId: $companyId}) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
const { error: addUserToCompanyError } = await nhost.graphql.request(
|
||||
ADD_USER_TO_COMPANY,
|
||||
{
|
||||
userId: user.id,
|
||||
companyId
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-admin-secret': process.env.NHOST_ADMIN_SECRET
|
||||
}
|
||||
const { error: addUserToCompanyError } = await nhost.graphql.request(
|
||||
ADD_USER_TO_COMPANY,
|
||||
{
|
||||
userId: user.id,
|
||||
companyId
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-admin-secret': process.env.NHOST_ADMIN_SECRET
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (addUserToCompanyError) {
|
||||
return res.status(500).send(error)
|
||||
}
|
||||
if (addUserToCompanyError) {
|
||||
return res.status(500).send(error)
|
||||
}
|
||||
|
||||
res.status(200).send(`OK`)
|
||||
res.status(200).send(`OK`)
|
||||
}
|
||||
|
||||
export default handler
|
||||
|
||||
18
examples/react-apollo-crm/index.html
Normal file
18
examples/react-apollo-crm/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Web site created using create-react-app" />
|
||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>React App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,9 +7,7 @@
|
||||
<h2>Verify Email</h2>
|
||||
<p>Use this link to verify your email:</p>
|
||||
<p>
|
||||
<a
|
||||
href="${serverUrl}/verify?&ticket=${ticket}&type=emailVerify&redirectTo=${redirectTo}"
|
||||
>
|
||||
<a href="${serverUrl}/verify?&ticket=${ticket}&type=emailVerify&redirectTo=${redirectTo}">
|
||||
Verify Email
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
<h2>Reset Password</h2>
|
||||
<p>Use this link to reset your password:</p>
|
||||
<p>
|
||||
<a
|
||||
href="${serverUrl}/verify?&ticket=${ticket}&type=passwordReset&redirectTo=${redirectTo}"
|
||||
>
|
||||
<a href="${serverUrl}/verify?&ticket=${ticket}&type=passwordReset&redirectTo=${redirectTo}">
|
||||
Reset password
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
type Mutation {
|
||||
actionName(
|
||||
arg1: SampleInput!
|
||||
): SampleOutput
|
||||
actionName(arg1: SampleInput!): SampleOutput
|
||||
}
|
||||
|
||||
input SampleInput {
|
||||
@@ -12,4 +10,3 @@ input SampleInput {
|
||||
type SampleOutput {
|
||||
accessToken: String!
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
actions:
|
||||
- name: actionName
|
||||
definition:
|
||||
kind: synchronous
|
||||
handler: http://host.docker.internal:3000
|
||||
permissions:
|
||||
- role: user
|
||||
- name: actionName
|
||||
definition:
|
||||
kind: synchronous
|
||||
handler: http://host.docker.internal:3000
|
||||
permissions:
|
||||
- role: user
|
||||
custom_types:
|
||||
enums: []
|
||||
input_objects:
|
||||
- name: SampleInput
|
||||
- name: SampleInput
|
||||
objects:
|
||||
- name: SampleOutput
|
||||
- name: SampleOutput
|
||||
scalars: []
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
max_connections: 50
|
||||
retries: 20
|
||||
use_prepared_statements: true
|
||||
tables: "!include default/tables/tables.yaml"
|
||||
tables: '!include default/tables/tables.yaml'
|
||||
|
||||
@@ -16,10 +16,10 @@ configuration:
|
||||
update: updateAuthProviders
|
||||
update_by_pk: updateAuthProvider
|
||||
array_relationships:
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: provider_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: provider_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
|
||||
@@ -19,6 +19,6 @@ configuration:
|
||||
update: updateAuthRefreshTokens
|
||||
update_by_pk: updateAuthRefreshToken
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -16,17 +16,17 @@ configuration:
|
||||
update: updateAuthRoles
|
||||
update_by_pk: updateAuthRole
|
||||
array_relationships:
|
||||
- name: userRoles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: role
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: usersByDefaultRole
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: default_role
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
- name: userRoles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: role
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: usersByDefaultRole
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: default_role
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
|
||||
@@ -23,9 +23,9 @@ configuration:
|
||||
update: updateAuthUserProviders
|
||||
update_by_pk: updateAuthUserProvider
|
||||
object_relationships:
|
||||
- name: provider
|
||||
using:
|
||||
foreign_key_constraint_on: provider_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: provider
|
||||
using:
|
||||
foreign_key_constraint_on: provider_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -19,9 +19,9 @@ configuration:
|
||||
update: updateAuthUserRoles
|
||||
update_by_pk: updateAuthUserRole
|
||||
object_relationships:
|
||||
- name: roleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: role
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: roleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: role
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -38,79 +38,79 @@ configuration:
|
||||
update: updateUsers
|
||||
update_by_pk: updateUser
|
||||
object_relationships:
|
||||
- name: companyUser
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
id: user_id
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
name: company_users
|
||||
schema: public
|
||||
- name: defaultRoleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
- name: companyUser
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
id: user_id
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
name: company_users
|
||||
schema: public
|
||||
- name: defaultRoleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
array_relationships:
|
||||
- name: customer_comments
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: customer_comments
|
||||
schema: public
|
||||
- name: customers
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: customers
|
||||
schema: public
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
- name: roles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
- name: customer_comments
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: customer_comments
|
||||
schema: public
|
||||
- name: customers
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: customers
|
||||
schema: public
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
- name: roles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- avatar_url
|
||||
- display_name
|
||||
- email
|
||||
- id
|
||||
filter:
|
||||
companyUser:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- avatar_url
|
||||
- display_name
|
||||
- email
|
||||
- id
|
||||
filter:
|
||||
companyUser:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
event_triggers:
|
||||
- definition:
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: "*"
|
||||
headers:
|
||||
- name: nhost-webhook-secret
|
||||
value_from_env: NHOST_WEBHOOK_SECRET
|
||||
name: users-insert-create-company-connection
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
timeout_sec: 60
|
||||
webhook: "{{NHOST_BACKEND_URL}}/v1/functions/users/insert/create-company-connection"
|
||||
- definition:
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
headers:
|
||||
- name: nhost-webhook-secret
|
||||
value_from_env: NHOST_WEBHOOK_SECRET
|
||||
name: users-insert-create-company-connection
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
timeout_sec: 60
|
||||
webhook: '{{NHOST_BACKEND_URL}}/v1/functions/users/insert/create-company-connection'
|
||||
|
||||
@@ -18,29 +18,29 @@ configuration:
|
||||
update: updateCompanies
|
||||
update_by_pk: updateCompany
|
||||
array_relationships:
|
||||
- name: companyUsers
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: company_id
|
||||
table:
|
||||
name: company_users
|
||||
schema: public
|
||||
- name: customers
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: company_id
|
||||
table:
|
||||
name: customers
|
||||
schema: public
|
||||
- name: companyUsers
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: company_id
|
||||
table:
|
||||
name: company_users
|
||||
schema: public
|
||||
- name: customers
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: company_id
|
||||
table:
|
||||
name: customers
|
||||
schema: public
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- name
|
||||
filter:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- name
|
||||
filter:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
|
||||
@@ -20,23 +20,23 @@ configuration:
|
||||
update: updateCompanyUsers
|
||||
update_by_pk: updateCompanyUser
|
||||
object_relationships:
|
||||
- name: company
|
||||
using:
|
||||
foreign_key_constraint_on: company_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: company
|
||||
using:
|
||||
foreign_key_constraint_on: company_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- company_id
|
||||
- user_id
|
||||
filter:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- company_id
|
||||
- user_id
|
||||
filter:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
|
||||
@@ -19,65 +19,65 @@ configuration:
|
||||
update: updateCustomerComments
|
||||
update_by_pk: updateCustomerComment
|
||||
object_relationships:
|
||||
- name: customer
|
||||
using:
|
||||
foreign_key_constraint_on: customer_id
|
||||
- name: file
|
||||
using:
|
||||
foreign_key_constraint_on: file_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: customer
|
||||
using:
|
||||
foreign_key_constraint_on: customer_id
|
||||
- name: file
|
||||
using:
|
||||
foreign_key_constraint_on: file_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
insert_permissions:
|
||||
- permission:
|
||||
backend_only: false
|
||||
check:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- customer_id
|
||||
- file_id
|
||||
- text
|
||||
set:
|
||||
user_id: x-hasura-user-id
|
||||
role: user
|
||||
- permission:
|
||||
backend_only: false
|
||||
check:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- customer_id
|
||||
- file_id
|
||||
- text
|
||||
set:
|
||||
user_id: x-hasura-user-id
|
||||
role: user
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- created_at
|
||||
- customer_id
|
||||
- file_id
|
||||
- id
|
||||
- text
|
||||
- updated_at
|
||||
- user_id
|
||||
filter:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- created_at
|
||||
- customer_id
|
||||
- file_id
|
||||
- id
|
||||
- text
|
||||
- updated_at
|
||||
- user_id
|
||||
filter:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
update_permissions:
|
||||
- permission:
|
||||
check: null
|
||||
columns: []
|
||||
filter:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
- permission:
|
||||
check: null
|
||||
columns: []
|
||||
filter:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
delete_permissions:
|
||||
- permission:
|
||||
filter:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
- permission:
|
||||
filter:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
|
||||
@@ -20,48 +20,48 @@ configuration:
|
||||
update: updateCustomers
|
||||
update_by_pk: updateCustomer
|
||||
object_relationships:
|
||||
- name: company
|
||||
using:
|
||||
foreign_key_constraint_on: company_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: company
|
||||
using:
|
||||
foreign_key_constraint_on: company_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
array_relationships:
|
||||
- name: customerComments
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: customer_id
|
||||
table:
|
||||
name: customer_comments
|
||||
schema: public
|
||||
- name: customerComments
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: customer_id
|
||||
table:
|
||||
name: customer_comments
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- permission:
|
||||
backend_only: false
|
||||
check:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- address_line_1
|
||||
- company_id
|
||||
- name
|
||||
set:
|
||||
user_id: x-hasura-user-id
|
||||
role: user
|
||||
- permission:
|
||||
backend_only: false
|
||||
check:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- address_line_1
|
||||
- company_id
|
||||
- name
|
||||
set:
|
||||
user_id: x-hasura-user-id
|
||||
role: user
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- name
|
||||
- address_line_1
|
||||
- company_id
|
||||
- user_id
|
||||
filter:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- name
|
||||
- address_line_1
|
||||
- company_id
|
||||
- user_id
|
||||
filter:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
|
||||
@@ -23,10 +23,10 @@ configuration:
|
||||
update: updateBuckets
|
||||
update_by_pk: updateBucket
|
||||
array_relationships:
|
||||
- name: files
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bucket_id
|
||||
table:
|
||||
name: files
|
||||
schema: storage
|
||||
- name: files
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bucket_id
|
||||
table:
|
||||
name: files
|
||||
schema: storage
|
||||
|
||||
@@ -25,51 +25,51 @@ configuration:
|
||||
update: updateFiles
|
||||
update_by_pk: updateFile
|
||||
object_relationships:
|
||||
- name: bucket
|
||||
using:
|
||||
foreign_key_constraint_on: bucket_id
|
||||
- name: bucket
|
||||
using:
|
||||
foreign_key_constraint_on: bucket_id
|
||||
array_relationships:
|
||||
- name: customerCommentFiles
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
id: file_id
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
name: customer_comments
|
||||
schema: public
|
||||
- name: customerCommentFiles
|
||||
using:
|
||||
manual_configuration:
|
||||
column_mapping:
|
||||
id: file_id
|
||||
insertion_order: null
|
||||
remote_table:
|
||||
name: customer_comments
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- permission:
|
||||
backend_only: false
|
||||
check:
|
||||
_or:
|
||||
- bucket_id:
|
||||
_eq: customerComments
|
||||
columns:
|
||||
- bucket_id
|
||||
- id
|
||||
- mime_type
|
||||
- name
|
||||
role: user
|
||||
- permission:
|
||||
backend_only: false
|
||||
check:
|
||||
_or:
|
||||
- bucket_id:
|
||||
_eq: customerComments
|
||||
columns:
|
||||
- bucket_id
|
||||
- id
|
||||
- mime_type
|
||||
- name
|
||||
role: user
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- bucket_id
|
||||
- name
|
||||
- size
|
||||
- mime_type
|
||||
- etag
|
||||
- is_uploaded
|
||||
- uploaded_by_user_id
|
||||
filter:
|
||||
_or:
|
||||
- customerCommentFiles:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- bucket_id
|
||||
- name
|
||||
- size
|
||||
- mime_type
|
||||
- etag
|
||||
- is_uploaded
|
||||
- uploaded_by_user_id
|
||||
filter:
|
||||
_or:
|
||||
- customerCommentFiles:
|
||||
customer:
|
||||
company:
|
||||
companyUsers:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
- "!include auth_provider_requests.yaml"
|
||||
- "!include auth_providers.yaml"
|
||||
- "!include auth_refresh_tokens.yaml"
|
||||
- "!include auth_roles.yaml"
|
||||
- "!include auth_user_providers.yaml"
|
||||
- "!include auth_user_roles.yaml"
|
||||
- "!include auth_users.yaml"
|
||||
- "!include public_companies.yaml"
|
||||
- "!include public_company_users.yaml"
|
||||
- "!include public_customer_comments.yaml"
|
||||
- "!include public_customers.yaml"
|
||||
- "!include storage_buckets.yaml"
|
||||
- "!include storage_files.yaml"
|
||||
- '!include auth_provider_requests.yaml'
|
||||
- '!include auth_providers.yaml'
|
||||
- '!include auth_refresh_tokens.yaml'
|
||||
- '!include auth_roles.yaml'
|
||||
- '!include auth_user_providers.yaml'
|
||||
- '!include auth_user_roles.yaml'
|
||||
- '!include auth_users.yaml'
|
||||
- '!include public_companies.yaml'
|
||||
- '!include public_company_users.yaml'
|
||||
- '!include public_customer_comments.yaml'
|
||||
- '!include public_customers.yaml'
|
||||
- '!include storage_buckets.yaml'
|
||||
- '!include storage_files.yaml'
|
||||
|
||||
@@ -3,38 +3,34 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.4.16",
|
||||
"@headlessui/react": "^1.4.2",
|
||||
"@heroicons/react": "^1.0.5",
|
||||
"@nhost/nhost-js": "^1.0.0",
|
||||
"@nhost/react": "^0.3.0",
|
||||
"@nhost/react-apollo": "^4.0.0",
|
||||
"@tailwindcss/forms": "^0.3.4",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@apollo/client": "^3.5.10",
|
||||
"@headlessui/react": "^1.5.0",
|
||||
"@heroicons/react": "^1.0.6",
|
||||
"@nhost/nhost-js": "workspace:*",
|
||||
"@nhost/react": "workspace:*",
|
||||
"@nhost/react-apollo": "workspace:*",
|
||||
"@tailwindcss/forms": "^0.5.0",
|
||||
"classnames": "^2.3.1",
|
||||
"date-fns": "^2.28.0",
|
||||
"graphql": "^15.8.0",
|
||||
"graphql": "15.7.2",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "^6.0.2",
|
||||
"react-scripts": "^5.0.0",
|
||||
"typescript": "^4.1.2",
|
||||
"web-vitals": "^1.0.1"
|
||||
"react-router-dom": "^6.3.0",
|
||||
"tailwindcss": "^3.0.24"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"codegen": "graphql-codegen --config codegen.yaml --errors-only"
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"codegen": "graphql-codegen --config codegen.yaml --errors-only",
|
||||
"prettier": "prettier --check .",
|
||||
"prettier:fix": "prettier --write .",
|
||||
"lint": "eslint . --ext .ts,.tsx",
|
||||
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
||||
"verify": "run-p prettier lint",
|
||||
"verify:fix": "run-p prettier:fix lint:fix"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
@@ -55,15 +51,21 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "^2.2.1",
|
||||
"@graphql-codegen/introspection": "^2.1.0",
|
||||
"@graphql-codegen/typescript": "^2.2.4",
|
||||
"@graphql-codegen/typescript-operations": "^2.1.8",
|
||||
"@graphql-codegen/typescript-react-apollo": "^3.1.6",
|
||||
"@graphql-codegen/cli": "^2.6.2",
|
||||
"@graphql-codegen/introspection": "^2.1.1",
|
||||
"@graphql-codegen/typescript": "^2.4.8",
|
||||
"@graphql-codegen/typescript-operations": "^2.3.5",
|
||||
"@graphql-codegen/typescript-react-apollo": "^3.2.11",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/node": "^12.20.48",
|
||||
"@types/react": "^17.0.44",
|
||||
"@types/react-dom": "^17.0.15",
|
||||
"@types/express": "^4.17.13",
|
||||
"autoprefixer": "^9.8.8",
|
||||
"express": "^4.17.1",
|
||||
"postcss": "^7.0.39",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17"
|
||||
"@vitejs/plugin-react": "^1.3.1",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"express": "^4.17.3",
|
||||
"postcss": "^8.4.12",
|
||||
"typescript": "^4.6.3",
|
||||
"vite": "^2.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
6
examples/react-apollo-crm/postcss.config.js
Normal file
6
examples/react-apollo-crm/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
import React from 'react'
|
||||
import App from './App'
|
||||
|
||||
test('single noop test', () => {
|
||||
expect(true).toBeTruthy()
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import './App.css'
|
||||
import { NhostReactProvider } from '@nhost/react'
|
||||
import { NhostApolloProvider } from '@nhost/react-apollo'
|
||||
import { nhost } from './utils/nhost'
|
||||
import { Route, Routes } from 'react-router'
|
||||
import { Route, Routes } from 'react-router-dom'
|
||||
import { Layout } from './components/ui/Layout'
|
||||
import { Customers } from './components/Customers'
|
||||
import { Dashboard } from './components/Dashboard'
|
||||
|
||||
@@ -3,91 +3,85 @@ import { Dialog, Transition } from '@headlessui/react'
|
||||
import { nhost } from '../utils/nhost'
|
||||
|
||||
export function ChangePasswordModal() {
|
||||
const [open, setOpen] = useState(true)
|
||||
const [newPassword, setNewPassword] = useState('')
|
||||
const [open, setOpen] = useState(true)
|
||||
const [newPassword, setNewPassword] = useState('')
|
||||
|
||||
const handleSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
const handleSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
|
||||
const { error } = await nhost.auth.changePassword({ newPassword })
|
||||
const { error } = await nhost.auth.changePassword({ newPassword })
|
||||
|
||||
if (error) {
|
||||
return alert(error.message)
|
||||
}
|
||||
if (error) {
|
||||
return alert(error.message)
|
||||
}
|
||||
|
||||
setOpen(false)
|
||||
}
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition.Root show={open} as={Fragment}>
|
||||
<Dialog as="div" className="fixed z-10 inset-0 overflow-y-auto" onClose={setOpen}>
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
return (
|
||||
<Transition.Root show={open} as={Fragment}>
|
||||
<Dialog as="div" className="fixed inset-0 z-10 overflow-y-auto" onClose={setOpen}>
|
||||
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Dialog.Overlay className="fixed inset-0 transition-opacity bg-gray-500 bg-opacity-75" />
|
||||
</Transition.Child>
|
||||
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
<span
|
||||
className="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true"
|
||||
>
|
||||
​
|
||||
</span>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<div className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg leading-6 font-medium text-gray-900"
|
||||
>
|
||||
Change Password
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
className="block w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm appearance-none focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
tabIndex={2}
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-6">
|
||||
<button
|
||||
type="submit"
|
||||
className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:text-sm"
|
||||
>
|
||||
Set new password
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<div className="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transition-all transform bg-white rounded-lg shadow-xl sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
|
||||
Change Password
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
className="block w-full px-3 py-2 placeholder-gray-400 border border-gray-300 rounded-md shadow-sm appearance-none focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
tabIndex={2}
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-6">
|
||||
<button
|
||||
type="submit"
|
||||
className="inline-flex justify-center w-full px-4 py-2 text-base font-medium text-white bg-blue-600 border border-transparent rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:text-sm"
|
||||
>
|
||||
Set new password
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
)
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
import { Main } from "./ui/Main";
|
||||
import { Breadcrumbs } from "./ui/Breadcrumbs";
|
||||
import { HeaderSection } from "./ui/HeaderSection";
|
||||
import { PageHeader } from "./ui/PageHeader";
|
||||
import { Main } from './ui/Main'
|
||||
import { Breadcrumbs } from './ui/Breadcrumbs'
|
||||
import { HeaderSection } from './ui/HeaderSection'
|
||||
import { PageHeader } from './ui/PageHeader'
|
||||
|
||||
import { useParams } from "react-router";
|
||||
import { useCustomerQuery } from "../utils/__generated__/graphql";
|
||||
import { NavLink, Outlet } from "react-router-dom";
|
||||
import classNames from "classnames";
|
||||
import { CustomerActivities } from "./CustomerActivities";
|
||||
import { CustomerAddComment } from "./CustomerAddComment";
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useCustomerQuery } from '../utils/__generated__/graphql'
|
||||
import { NavLink, Outlet } from 'react-router-dom'
|
||||
import classNames from 'classnames'
|
||||
import { CustomerActivities } from './CustomerActivities'
|
||||
import { CustomerAddComment } from './CustomerAddComment'
|
||||
|
||||
const tabs = [
|
||||
{ name: "Overview", href: "" },
|
||||
{ name: "Orders", href: "orders" },
|
||||
{ name: "Files", href: "files" },
|
||||
];
|
||||
{ name: 'Overview', href: '' },
|
||||
{ name: 'Orders', href: 'orders' },
|
||||
{ name: 'Files', href: 'files' }
|
||||
]
|
||||
|
||||
export function Customer() {
|
||||
const { customerId } = useParams();
|
||||
const { customerId } = useParams()
|
||||
|
||||
const { data, loading } = useCustomerQuery({
|
||||
variables: {
|
||||
customerId,
|
||||
},
|
||||
});
|
||||
customerId
|
||||
}
|
||||
})
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading..</div>;
|
||||
return <div>Loading..</div>
|
||||
}
|
||||
|
||||
if (!data || !data.customer) {
|
||||
return <div>No customer..</div>;
|
||||
return <div>No customer..</div>
|
||||
}
|
||||
|
||||
const { customer } = data;
|
||||
const { customer } = data
|
||||
|
||||
return (
|
||||
<Main>
|
||||
<Breadcrumbs
|
||||
backLink={""}
|
||||
backLink={''}
|
||||
breadcrumbs={[
|
||||
{ link: "/customers", text: "Customers" },
|
||||
{ link: `customers/${customerId}`, text: customer.name },
|
||||
{ link: '/customers', text: 'Customers' },
|
||||
{ link: `customers/${customerId}`, text: customer.name }
|
||||
]}
|
||||
/>
|
||||
<HeaderSection>
|
||||
@@ -57,7 +57,7 @@ export function Customer() {
|
||||
id="current-tab"
|
||||
name="current-tab"
|
||||
className="block w-full py-2 pl-3 pr-10 text-base border-gray-300 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||
defaultValue={"ok"}
|
||||
defaultValue={'ok'}
|
||||
>
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
@@ -71,10 +71,10 @@ export function Customer() {
|
||||
className={({ isActive }) => {
|
||||
return classNames(
|
||||
isActive
|
||||
? "border-blue-500 text-blue-600"
|
||||
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300",
|
||||
"whitespace-nowrap pb-4 px-1 border-b-2 font-medium text-sm"
|
||||
);
|
||||
? 'border-blue-500 text-blue-600'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300',
|
||||
'whitespace-nowrap pb-4 px-1 border-b-2 font-medium text-sm'
|
||||
)
|
||||
}}
|
||||
>
|
||||
{tab.name}
|
||||
@@ -95,5 +95,5 @@ export function Customer() {
|
||||
<CustomerAddComment />
|
||||
</div>
|
||||
</Main>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import { ChatAltIcon } from "@heroicons/react/solid";
|
||||
import { useGetCustomerCommentsSubscription } from "../utils/__generated__/graphql";
|
||||
import { useParams } from "react-router";
|
||||
import { nhost } from "../utils/nhost";
|
||||
import { PhotographIcon } from "@heroicons/react/outline";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import { formatDistanceToNow, parseISO } from "date-fns";
|
||||
import { ChatAltIcon } from '@heroicons/react/solid'
|
||||
import { useGetCustomerCommentsSubscription } from '../utils/__generated__/graphql'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { nhost } from '../utils/nhost'
|
||||
import { PhotographIcon } from '@heroicons/react/outline'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
import { formatDistanceToNow, parseISO } from 'date-fns'
|
||||
|
||||
export function CustomerActivities() {
|
||||
const { customerId } = useParams();
|
||||
const { customerId } = useParams<{ customerId: string }>()
|
||||
|
||||
const { data, loading } = useGetCustomerCommentsSubscription({
|
||||
variables: {
|
||||
where: {
|
||||
customerId: {
|
||||
_eq: customerId,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
_eq: customerId
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
console.log({ data });
|
||||
console.log({ data })
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
if (!data || !data.customerComments) {
|
||||
return <div>no comments</div>;
|
||||
return <div>no comments</div>
|
||||
}
|
||||
|
||||
const { customerComments } = data;
|
||||
const { customerComments } = data
|
||||
|
||||
return (
|
||||
<div className="flow-root">
|
||||
@@ -54,10 +54,7 @@ export function CustomerActivities() {
|
||||
/>
|
||||
|
||||
<span className="absolute -bottom-0.5 -right-1 bg-white rounded-tl px-0.5 py-px">
|
||||
<ChatAltIcon
|
||||
className="w-5 h-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ChatAltIcon className="w-5 h-5 text-gray-400" aria-hidden="true" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -69,7 +66,7 @@ export function CustomerActivities() {
|
||||
</div>
|
||||
<p className="mt-0.5 text-sm text-gray-500">
|
||||
{formatDistanceToNow(parseISO(comment.createdAt), {
|
||||
addSuffix: true,
|
||||
addSuffix: true
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
@@ -80,24 +77,22 @@ export function CustomerActivities() {
|
||||
<div
|
||||
className="flex items-center mt-3 text-sm text-gray-700 cursor-pointer"
|
||||
onClick={async () => {
|
||||
const { presignedUrl, error } =
|
||||
await nhost.storage.getPresignedUrl({
|
||||
fileId: comment.file!.id,
|
||||
});
|
||||
const { presignedUrl, error } = await nhost.storage.getPresignedUrl({
|
||||
fileId: comment.file!.id
|
||||
})
|
||||
|
||||
if (error) {
|
||||
return alert(error.message);
|
||||
return alert(error.message)
|
||||
}
|
||||
|
||||
window.open(presignedUrl?.url, "_blank");
|
||||
window.open(presignedUrl?.url, '_blank')
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<PhotographIcon className="w-5 mr-1 text-gray-500" />
|
||||
</div>
|
||||
<div>
|
||||
{comment.file.name},{" "}
|
||||
{prettyBytes(comment.file.size as number)}
|
||||
{comment.file.name}, {prettyBytes(comment.file.size as number)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -106,9 +101,9 @@ export function CustomerActivities() {
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
import { useState } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import { nhost } from "../utils/nhost";
|
||||
import { useInsertCustomerCommentMutation } from "../utils/__generated__/graphql";
|
||||
import { useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { nhost } from '../utils/nhost'
|
||||
import { useInsertCustomerCommentMutation } from '../utils/__generated__/graphql'
|
||||
|
||||
export function CustomerAddComment() {
|
||||
const [text, setText] = useState("");
|
||||
const [file, setFile] = useState<null | File>(null);
|
||||
const [text, setText] = useState('')
|
||||
const [file, setFile] = useState<null | File>(null)
|
||||
|
||||
const { customerId } = useParams();
|
||||
const [insertCustomerComment, { loading }] =
|
||||
useInsertCustomerCommentMutation();
|
||||
const { customerId } = useParams<{ customerId: string }>()
|
||||
const [insertCustomerComment, { loading }] = useInsertCustomerCommentMutation()
|
||||
|
||||
const handleSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
e.preventDefault()
|
||||
|
||||
let fileMetadata;
|
||||
let fileMetadata
|
||||
if (file) {
|
||||
const fileUploadRes = await nhost.storage.upload({
|
||||
file,
|
||||
bucketId: "customerComments",
|
||||
});
|
||||
bucketId: 'customerComments'
|
||||
})
|
||||
|
||||
if (fileUploadRes.error) {
|
||||
alert(`error: ${fileUploadRes.error}`);
|
||||
return;
|
||||
alert(`error: ${fileUploadRes.error}`)
|
||||
return
|
||||
}
|
||||
|
||||
fileMetadata = fileUploadRes.fileMetadata;
|
||||
fileMetadata = fileUploadRes.fileMetadata
|
||||
}
|
||||
|
||||
await insertCustomerComment({
|
||||
@@ -34,21 +33,18 @@ export function CustomerAddComment() {
|
||||
customerComment: {
|
||||
text,
|
||||
customerId,
|
||||
fileId: fileMetadata ? fileMetadata.id : null,
|
||||
},
|
||||
},
|
||||
});
|
||||
fileId: fileMetadata ? fileMetadata.id : null
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
setText("");
|
||||
};
|
||||
setText('')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-lg mx-auto">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
|
||||
Comment
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
@@ -57,7 +53,7 @@ export function CustomerAddComment() {
|
||||
name="about"
|
||||
rows={3}
|
||||
className="block w-full max-w-lg border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
defaultValue={""}
|
||||
defaultValue={''}
|
||||
value={text}
|
||||
onChange={(e) => setText(e.target.value)}
|
||||
/>
|
||||
@@ -94,7 +90,7 @@ export function CustomerAddComment() {
|
||||
className="sr-only"
|
||||
onChange={(e) => {
|
||||
if (e.target.files && e.target.files.length > 0) {
|
||||
setFile(e.target.files[0]);
|
||||
setFile(e.target.files[0])
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -104,9 +100,7 @@ export function CustomerAddComment() {
|
||||
{file ? (
|
||||
<div>{file.name}</div>
|
||||
) : (
|
||||
<p className="text-xs text-gray-500">
|
||||
PNG, JPG, GIF up to 10MB
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">PNG, JPG, GIF up to 10MB</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,5 +118,5 @@ export function CustomerAddComment() {
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { Main } from "./ui/Main";
|
||||
import { Breadcrumbs } from "./ui/Breadcrumbs";
|
||||
import { HeaderSection } from "./ui/HeaderSection";
|
||||
import { PageHeader } from "./ui/PageHeader";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useGetCustomersSubscription } from "../utils/__generated__/graphql";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid";
|
||||
import { Main } from './ui/Main'
|
||||
import { Breadcrumbs } from './ui/Breadcrumbs'
|
||||
import { HeaderSection } from './ui/HeaderSection'
|
||||
import { PageHeader } from './ui/PageHeader'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useGetCustomersSubscription } from '../utils/__generated__/graphql'
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid'
|
||||
|
||||
export function Customers() {
|
||||
return (
|
||||
<Main>
|
||||
<Breadcrumbs
|
||||
backLink={""}
|
||||
breadcrumbs={[{ link: "/customers", text: "Customers" }]}
|
||||
/>
|
||||
<Breadcrumbs backLink={''} breadcrumbs={[{ link: '/customers', text: 'Customers' }]} />
|
||||
<HeaderSection>
|
||||
<PageHeader>Customers</PageHeader>
|
||||
<div className="flex flex-shrink-0 mt-4 md:mt-0 md:ml-4">
|
||||
@@ -31,11 +28,11 @@ export function Customers() {
|
||||
<CustomersList />
|
||||
</div>
|
||||
</Main>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function CustomersList() {
|
||||
const { data } = useGetCustomersSubscription();
|
||||
const { data } = useGetCustomersSubscription()
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -57,17 +54,14 @@ function CustomersList() {
|
||||
<tbody>
|
||||
{data?.customers.map((customer, i) => {
|
||||
return (
|
||||
<tr
|
||||
key={customer.id}
|
||||
className={i % 2 === 0 ? "bg-white" : "bg-gray-50"}
|
||||
>
|
||||
<tr key={customer.id} className={i % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
|
||||
<td className="text-sm font-medium text-gray-900 whitespace-nowrap">
|
||||
<Link to={customer.id} className="block px-6 py-4">
|
||||
{customer.name}
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -88,9 +82,9 @@ function CustomersList() {
|
||||
<div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-700">
|
||||
Showing <span className="font-medium">1</span> to{" "}
|
||||
<span className="font-medium">10</span> of{" "}
|
||||
<span className="font-medium">97</span> results
|
||||
Showing <span className="font-medium">1</span> to{' '}
|
||||
<span className="font-medium">10</span> of <span className="font-medium">97</span>{' '}
|
||||
results
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@@ -136,5 +130,5 @@ function CustomersList() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Main } from "./ui/Main";
|
||||
import { Main } from './ui/Main'
|
||||
|
||||
export function Dashboard() {
|
||||
return <Main>Dashboard</Main>;
|
||||
return <Main>Dashboard</Main>
|
||||
}
|
||||
|
||||
@@ -1,67 +1,64 @@
|
||||
import { useState } from "react";
|
||||
import { Main } from "./ui/Main";
|
||||
import { Breadcrumbs } from "./ui/Breadcrumbs";
|
||||
import { HeaderSection } from "./ui/HeaderSection";
|
||||
import { PageHeader } from "./ui/PageHeader";
|
||||
import {
|
||||
useGetCompanyWhereQuery,
|
||||
useInsertCustomerMutation,
|
||||
} from "../utils/__generated__/graphql";
|
||||
import { nhost } from "../utils/nhost";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useState } from 'react'
|
||||
import { Main } from './ui/Main'
|
||||
import { Breadcrumbs } from './ui/Breadcrumbs'
|
||||
import { HeaderSection } from './ui/HeaderSection'
|
||||
import { PageHeader } from './ui/PageHeader'
|
||||
import { useGetCompanyWhereQuery, useInsertCustomerMutation } from '../utils/__generated__/graphql'
|
||||
import { nhost } from '../utils/nhost'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
export function NewCustomer() {
|
||||
const [name, setName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [addressLine1, setAddressLine1] = useState("");
|
||||
const [name, setName] = useState('')
|
||||
const [email, setEmail] = useState('')
|
||||
const [addressLine1, setAddressLine1] = useState('')
|
||||
|
||||
const user = nhost.auth.getUser();
|
||||
let navigate = useNavigate();
|
||||
const user = nhost.auth.getUser()
|
||||
let navigate = useNavigate()
|
||||
|
||||
const { data } = useGetCompanyWhereQuery({
|
||||
variables: {
|
||||
where: {
|
||||
companyUsers: {
|
||||
userId: {
|
||||
_eq: user?.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
_eq: user?.id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const [insertCustomer, { loading }] = useInsertCustomerMutation();
|
||||
const [insertCustomer, { loading }] = useInsertCustomerMutation()
|
||||
|
||||
const handleSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
e.preventDefault()
|
||||
|
||||
console.log("handle submit");
|
||||
console.log('handle submit')
|
||||
|
||||
let res;
|
||||
let res
|
||||
try {
|
||||
res = await insertCustomer({
|
||||
variables: {
|
||||
customer: {
|
||||
name,
|
||||
addressLine1,
|
||||
companyId: data?.companies[0].id,
|
||||
},
|
||||
},
|
||||
});
|
||||
companyId: data?.companies[0].id
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
return alert(`error: ${error}`);
|
||||
return alert(`error: ${error}`)
|
||||
}
|
||||
|
||||
navigate(`/customers/${res.data?.insertCustomer?.id}`);
|
||||
};
|
||||
navigate(`/customers/${res.data?.insertCustomer?.id}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<Main>
|
||||
<Breadcrumbs
|
||||
backLink={""}
|
||||
backLink={''}
|
||||
breadcrumbs={[
|
||||
{ link: "/customers", text: "Customers" },
|
||||
{ link: "/new-customer", text: "New Customer" },
|
||||
{ link: '/customers', text: 'Customers' },
|
||||
{ link: '/new-customer', text: 'New Customer' }
|
||||
]}
|
||||
/>
|
||||
<HeaderSection>
|
||||
@@ -73,10 +70,7 @@ export function NewCustomer() {
|
||||
<div className="pt-12">
|
||||
<div className="grid grid-cols-1 mt-6 gap-y-6 gap-x-4 sm:grid-cols-6">
|
||||
<div className="sm:col-span-3">
|
||||
<label
|
||||
htmlFor="first-name"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
<label htmlFor="first-name" className="block text-sm font-medium text-gray-700">
|
||||
Name
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
@@ -93,10 +87,7 @@ export function NewCustomer() {
|
||||
</div>
|
||||
|
||||
<div className="sm:col-span-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
|
||||
Email address
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
@@ -113,10 +104,7 @@ export function NewCustomer() {
|
||||
</div>
|
||||
|
||||
<div className="sm:col-span-6">
|
||||
<label
|
||||
htmlFor="street-address"
|
||||
className="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
<label htmlFor="street-address" className="block text-sm font-medium text-gray-700">
|
||||
Street address
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
@@ -147,5 +135,5 @@ export function NewCustomer() {
|
||||
</div>
|
||||
</form>
|
||||
</Main>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useNhostAuth } from '@nhost/react'
|
||||
import React from 'react'
|
||||
import { Navigate, useLocation } from 'react-router'
|
||||
import { Navigate, useLocation } from 'react-router-dom'
|
||||
|
||||
export function RequireAuth({ children }: { children: JSX.Element }) {
|
||||
const { isAuthenticated, isLoading } = useNhostAuth()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useNhostAuth } from '@nhost/react'
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { nhost } from '../utils/nhost'
|
||||
|
||||
export function ResetPassword() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useNhostAuth } from '@nhost/react'
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { nhost } from '../utils/nhost'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useNhostAuth } from '@nhost/react'
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { nhost } from '../utils/nhost'
|
||||
|
||||
@@ -93,7 +93,7 @@ export function SignUp() {
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="text-center py-4">
|
||||
<div className="py-4 text-center">
|
||||
Already have an account?{' '}
|
||||
<Link to="/sign-in" className="text-blue-600 hover:text-blue-500">
|
||||
Sign In
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid";
|
||||
import classNames from "classnames";
|
||||
import { Link } from "react-router-dom";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid'
|
||||
import classNames from 'classnames'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
type BreadcrumbsProps = {
|
||||
backLink: string;
|
||||
breadcrumbs: Breadcrumb[];
|
||||
};
|
||||
backLink: string
|
||||
breadcrumbs: Breadcrumb[]
|
||||
}
|
||||
|
||||
type Breadcrumb = {
|
||||
link: string;
|
||||
text: string;
|
||||
};
|
||||
link: string
|
||||
text: string
|
||||
}
|
||||
|
||||
export function Breadcrumbs(props: BreadcrumbsProps) {
|
||||
const { backLink, breadcrumbs } = props;
|
||||
const { backLink, breadcrumbs } = props
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -32,13 +32,10 @@ export function Breadcrumbs(props: BreadcrumbsProps) {
|
||||
<nav className="hidden sm:flex" aria-label="Breadcrumb">
|
||||
<ol className="flex items-center space-x-4">
|
||||
{breadcrumbs.map((breadcrumb, i) => {
|
||||
const isFirstItem = i === 0;
|
||||
const classes = classNames(
|
||||
"text-sm font-medium text-gray-500 hover:text-gray-700",
|
||||
{
|
||||
"ml-4": !isFirstItem,
|
||||
}
|
||||
);
|
||||
const isFirstItem = i === 0
|
||||
const classes = classNames('text-sm font-medium text-gray-500 hover:text-gray-700', {
|
||||
'ml-4': !isFirstItem
|
||||
})
|
||||
|
||||
return (
|
||||
<li key={i}>
|
||||
@@ -54,10 +51,10 @@ export function Breadcrumbs(props: BreadcrumbsProps) {
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import React from "react";
|
||||
import React from 'react'
|
||||
|
||||
export function HeaderSection({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="mt-2 md:flex md:items-center md:justify-between">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
return <div className="mt-2 md:flex md:items-center md:justify-between">{children}</div>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React from "react";
|
||||
import React from 'react'
|
||||
|
||||
export function Main({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">{children}</div>
|
||||
);
|
||||
return <div className="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">{children}</div>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React from 'react'
|
||||
|
||||
export function PageHeader({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
@@ -7,5 +7,5 @@ export function PageHeader({ children }: { children: React.ReactNode }) {
|
||||
{children}
|
||||
</h2>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
10
examples/react-apollo-crm/src/env.d.ts
vendored
Normal file
10
examples/react-apollo-crm/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_NHOST_URL: string
|
||||
// more env variables...
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
mutation insertCustomerComment(
|
||||
$customerComment: customerComments_insert_input!
|
||||
) {
|
||||
mutation insertCustomerComment($customerComment: customerComments_insert_input!) {
|
||||
insertCustomerComment(object: $customerComment) {
|
||||
id
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
@@ -11,10 +10,5 @@ ReactDOM.render(
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
||||
document.getElementById('root')
|
||||
)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/// <reference types="react-scripts" />
|
||||
@@ -1,15 +0,0 @@
|
||||
import { ReportHandler } from 'web-vitals';
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
||||
@@ -2,4 +2,4 @@
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
5522
examples/react-apollo-crm/src/utils/__generated__/graphql.ts
generated
5522
examples/react-apollo-crm/src/utils/__generated__/graphql.ts
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import { NhostClient } from '@nhost/react'
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: process.env.REACT_APP_BACKEND_URL!
|
||||
backendUrl: import.meta.env.VITE_NHOST_URL || 'http://localhost:1337'
|
||||
})
|
||||
|
||||
export { nhost }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = {
|
||||
purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
content: ['./src/**/*.{js,jsx,ts,tsx}', './index.html'],
|
||||
darkMode: 'media',
|
||||
theme: {
|
||||
extend: {},
|
||||
extend: {}
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
extend: {}
|
||||
},
|
||||
plugins: [require("@tailwindcss/forms")],
|
||||
};
|
||||
plugins: [require('@tailwindcss/forms')]
|
||||
}
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"outDir": "dist",
|
||||
"composite": true,
|
||||
"module": "esnext"
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": ["src/**/*", "types/**/*", "../../types/**/*", "tests/**/*"]
|
||||
}
|
||||
|
||||
9
examples/react-apollo-crm/vite.config.js
Normal file
9
examples/react-apollo-crm/vite.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [tsconfigPaths(), react()]
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,29 @@
|
||||
# React-Apollo example
|
||||
|
||||
Once in the example's directory, run the two following commands in parallel:
|
||||
## Get started
|
||||
|
||||
1. Clone the repository
|
||||
|
||||
```sh
|
||||
# Start the Nhost CLI in the background
|
||||
nhost -d
|
||||
|
||||
# Start this project
|
||||
yarn run dev
|
||||
git clone https://github.com/nhost/nhost
|
||||
cd nhost
|
||||
```
|
||||
|
||||
2. Install dependencies
|
||||
|
||||
```sh
|
||||
cd examples/react-apollo
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Terminal 1: Start Nhost
|
||||
|
||||
```sh
|
||||
nhost dev
|
||||
```
|
||||
|
||||
4. Terminal 2: Start React App
|
||||
|
||||
```sh
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Confirm Email Change</h2>
|
||||
<p>Use this link to confirm changing email:</p>
|
||||
<p>
|
||||
<a href="${link}">
|
||||
Change email
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h2>Confirm Email Change</h2>
|
||||
<p>Use this link to confirm changing email:</p>
|
||||
<p>
|
||||
<a href="${link}"> Change email </a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Verify Email</h2>
|
||||
<p>Use this link to verify your email:</p>
|
||||
<p>
|
||||
<a href="${link}">
|
||||
Verify Email
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h2>Verify Email</h2>
|
||||
<p>Use this link to verify your email:</p>
|
||||
<p>
|
||||
<a href="${link}"> Verify Email </a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Reset Password</h2>
|
||||
<p>Use this link to reset your password:</p>
|
||||
<p>
|
||||
<a href="${link}">
|
||||
Reset password
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h2>Reset Password</h2>
|
||||
<p>Use this link to reset your password:</p>
|
||||
<p>
|
||||
<a href="${link}"> Reset password </a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Magic Link</h2>
|
||||
<p>Use this link to securely sign in:</p>
|
||||
<p>
|
||||
<a href="${link}">
|
||||
Sign In
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h2>Magic Link</h2>
|
||||
<p>Use this link to securely sign in:</p>
|
||||
<p>
|
||||
<a href="${link}"> Sign In </a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Confirmer changement de courriel</h2>
|
||||
<p>Utilisez ce lien pour confirmer le changement de courriel:</p>
|
||||
<p>
|
||||
<a href="${link}">
|
||||
Changer courriel
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h2>Confirmer changement de courriel</h2>
|
||||
<p>Utilisez ce lien pour confirmer le changement de courriel:</p>
|
||||
<p>
|
||||
<a href="${link}"> Changer courriel </a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Vérifiez votre courriel</h2>
|
||||
<p>Utilisez ce lien pour vérifier votre courriel:</p>
|
||||
<p>
|
||||
<a href="${link}">
|
||||
Vérifier courriel
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h2>Vérifiez votre courriel</h2>
|
||||
<p>Utilisez ce lien pour vérifier votre courriel:</p>
|
||||
<p>
|
||||
<a href="${link}"> Vérifier courriel </a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Réinitializer votre mot de passe</h2>
|
||||
<p>Utilisez ce lien pour réinitializer votre mot de passe:</p>
|
||||
<p>
|
||||
<a href="${link}">
|
||||
Réinitializer mot de passe
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h2>Réinitializer votre mot de passe</h2>
|
||||
<p>Utilisez ce lien pour réinitializer votre mot de passe:</p>
|
||||
<p>
|
||||
<a href="${link}"> Réinitializer mot de passe </a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Lien magique</h2>
|
||||
<p>Utilisez ce lien pour vous connecter de façon sécuritaire:</p>
|
||||
<p>
|
||||
<a href="${link}">
|
||||
Connexion
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<h2>Lien magique</h2>
|
||||
<p>Utilisez ce lien pour vous connecter de façon sécuritaire:</p>
|
||||
<p>
|
||||
<a href="${link}"> Connexion </a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
max_connections: 50
|
||||
retries: 20
|
||||
use_prepared_statements: true
|
||||
tables: "!include default/tables/tables.yaml"
|
||||
tables: '!include default/tables/tables.yaml'
|
||||
|
||||
@@ -15,10 +15,10 @@ configuration:
|
||||
update: updateAuthProviders
|
||||
update_by_pk: updateAuthProvider
|
||||
array_relationships:
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: provider_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: provider_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
|
||||
@@ -19,6 +19,6 @@ configuration:
|
||||
update: updateAuthRefreshTokens
|
||||
update_by_pk: updateAuthRefreshToken
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -15,17 +15,17 @@ configuration:
|
||||
update: updateAuthRoles
|
||||
update_by_pk: updateAuthRole
|
||||
array_relationships:
|
||||
- name: userRoles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: role
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: usersByDefaultRole
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: default_role
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
- name: userRoles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: role
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: usersByDefaultRole
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: default_role
|
||||
table:
|
||||
name: users
|
||||
schema: auth
|
||||
|
||||
@@ -22,9 +22,9 @@ configuration:
|
||||
update: updateAuthUserProviders
|
||||
update_by_pk: updateAuthUserProvider
|
||||
object_relationships:
|
||||
- name: provider
|
||||
using:
|
||||
foreign_key_constraint_on: provider_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: provider
|
||||
using:
|
||||
foreign_key_constraint_on: provider_id
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -17,9 +17,9 @@ configuration:
|
||||
update: updateAuthUserRoles
|
||||
update_by_pk: updateAuthUserRole
|
||||
object_relationships:
|
||||
- name: roleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: role
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
- name: roleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: role
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
|
||||
@@ -33,28 +33,28 @@ configuration:
|
||||
update: updateUsers
|
||||
update_by_pk: updateUser
|
||||
object_relationships:
|
||||
- name: defaultRoleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
- name: defaultRoleByRole
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
array_relationships:
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
- name: roles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
- name: roles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
- name: userProviders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
|
||||
@@ -2,9 +2,9 @@ table:
|
||||
name: books
|
||||
schema: public
|
||||
select_permissions:
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- title
|
||||
filter: {}
|
||||
role: user
|
||||
- permission:
|
||||
columns:
|
||||
- id
|
||||
- title
|
||||
filter: {}
|
||||
role: user
|
||||
|
||||
@@ -23,10 +23,10 @@ configuration:
|
||||
update: updateBuckets
|
||||
update_by_pk: updateBucket
|
||||
array_relationships:
|
||||
- name: files
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bucket_id
|
||||
table:
|
||||
name: files
|
||||
schema: storage
|
||||
- name: files
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bucket_id
|
||||
table:
|
||||
name: files
|
||||
schema: storage
|
||||
|
||||
@@ -25,6 +25,6 @@ configuration:
|
||||
update: updateFiles
|
||||
update_by_pk: updateFile
|
||||
object_relationships:
|
||||
- name: bucket
|
||||
using:
|
||||
foreign_key_constraint_on: bucket_id
|
||||
- name: bucket
|
||||
using:
|
||||
foreign_key_constraint_on: bucket_id
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
- "!include auth_provider_requests.yaml"
|
||||
- "!include auth_providers.yaml"
|
||||
- "!include auth_refresh_tokens.yaml"
|
||||
- "!include auth_roles.yaml"
|
||||
- "!include auth_user_providers.yaml"
|
||||
- "!include auth_user_roles.yaml"
|
||||
- "!include auth_users.yaml"
|
||||
- "!include public_books.yaml"
|
||||
- "!include storage_buckets.yaml"
|
||||
- "!include storage_files.yaml"
|
||||
- '!include auth_provider_requests.yaml'
|
||||
- '!include auth_providers.yaml'
|
||||
- '!include auth_refresh_tokens.yaml'
|
||||
- '!include auth_roles.yaml'
|
||||
- '!include auth_user_providers.yaml'
|
||||
- '!include auth_user_roles.yaml'
|
||||
- '!include auth_users.yaml'
|
||||
- '!include public_books.yaml'
|
||||
- '!include storage_buckets.yaml'
|
||||
- '!include storage_files.yaml'
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.5.10",
|
||||
"@nhost/react": "^0.5.3",
|
||||
"@nhost/react-apollo": "^4.0.13",
|
||||
"@nhost/core": "workspace:*",
|
||||
"@nhost/react": "workspace:*",
|
||||
"@nhost/react-apollo": "workspace:*",
|
||||
"@rsuite/icons": "^1.0.2",
|
||||
"graphql": "15.7.2",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"less": "^4.1.2",
|
||||
"react": "^17.0.2",
|
||||
@@ -17,10 +19,17 @@
|
||||
"react-router-dom": "^6.3.0",
|
||||
"rsuite": "^5.8.1"
|
||||
},
|
||||
"lib": "workspace:*",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"prettier": "prettier --check .",
|
||||
"prettier:fix": "prettier --write .",
|
||||
"lint": "eslint . --ext .ts,.tsx",
|
||||
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
||||
"verify": "run-p prettier lint",
|
||||
"verify:fix": "run-p prettier:fix lint:fix"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
||||
@@ -1,29 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": "./src",
|
||||
"outDir": "dist",
|
||||
"composite": true,
|
||||
"module": "esnext"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
|
||||
}
|
||||
"include": ["src/**/*", "types/**/*", "../../types/**/*", "tests/**/*"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()]
|
||||
plugins: [tsconfigPaths(), react()]
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user