Compare commits
212 Commits
@nhost/apo
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6f9fe6304 | ||
|
|
15b652d7e0 | ||
|
|
cdc0047cb7 | ||
|
|
7c0e71e8be | ||
|
|
175d6d8cbc | ||
|
|
2ae277409a | ||
|
|
ada10170b7 | ||
|
|
0ed77cbe8b | ||
|
|
795962e3c2 | ||
|
|
ba998eb632 | ||
|
|
d3fc1bbeb9 | ||
|
|
7b9c2016d0 | ||
|
|
ebf4070be6 | ||
|
|
59e3f6abc6 | ||
|
|
0c9a03a7ff | ||
|
|
42cbe27914 | ||
|
|
d7e7b0e51b | ||
|
|
036181dd75 | ||
|
|
2828a9fe01 | ||
|
|
88bc71dffc | ||
|
|
17699870a6 | ||
|
|
5c0304ab73 | ||
|
|
637265e3d9 | ||
|
|
229604d8e1 | ||
|
|
1165a33079 | ||
|
|
82c0dd9d87 | ||
|
|
a9bfab1778 | ||
|
|
60b7a664d2 | ||
|
|
4f708d04d6 | ||
|
|
a02af56056 | ||
|
|
575136dcfb | ||
|
|
2bc6346cbc | ||
|
|
91cd494a3d | ||
|
|
d57e0a5287 | ||
|
|
dd5e7093f0 | ||
|
|
3e9bb84f07 | ||
|
|
6665b58ec8 | ||
|
|
456e893cd6 | ||
|
|
fd76170ca6 | ||
|
|
682aef2d94 | ||
|
|
cb53f71d4a | ||
|
|
4fc0b40cb4 | ||
|
|
9da2d01c55 | ||
|
|
0d5e7850f8 | ||
|
|
cb284b40b1 | ||
|
|
b8b4e36175 | ||
|
|
6e47ef68d5 | ||
|
|
2697414637 | ||
|
|
e753b2faed | ||
|
|
6f62ec7d2a | ||
|
|
d365ef1953 | ||
|
|
09f53ae43f | ||
|
|
7724ac7e06 | ||
|
|
02343fe171 | ||
|
|
98dd8d039c | ||
|
|
456e057497 | ||
|
|
93e9b58a58 | ||
|
|
019a7c2335 | ||
|
|
64882a8e16 | ||
|
|
1ed572fe39 | ||
|
|
e06271a8ae | ||
|
|
bca239ebb2 | ||
|
|
e33caa046d | ||
|
|
cbbd331341 | ||
|
|
c41bfaffdd | ||
|
|
f2596b0b14 | ||
|
|
4d0c3111d1 | ||
|
|
7ff9644ac7 | ||
|
|
87fdaa7144 | ||
|
|
bc23d051ba | ||
|
|
132a4f4be9 | ||
|
|
c330dc1c00 | ||
|
|
4bebd9842b | ||
|
|
541f2d250c | ||
|
|
6b31e31430 | ||
|
|
9ba2208dd7 | ||
|
|
538541fc79 | ||
|
|
cb0aab48b3 | ||
|
|
dcc760b6bb | ||
|
|
d2227be0d0 | ||
|
|
93e807edc4 | ||
|
|
14738d4b58 | ||
|
|
6ac4cfa80c | ||
|
|
295c6c9110 | ||
|
|
02911e4a54 | ||
|
|
2be825de08 | ||
|
|
65afad1b91 | ||
|
|
dbea13a1d2 | ||
|
|
5e2a419700 | ||
|
|
9e6660450c | ||
|
|
2db9be03ff | ||
|
|
1b8f505050 | ||
|
|
6ab6bccded | ||
|
|
b12bc1c27f | ||
|
|
04d1641cf7 | ||
|
|
e91c09aa9c | ||
|
|
7c04aad870 | ||
|
|
520044c5ab | ||
|
|
22fed668d6 | ||
|
|
ae14fb1a2f | ||
|
|
f4ca0aab42 | ||
|
|
d8a0d196af | ||
|
|
cd53718631 | ||
|
|
1fcd3f1851 | ||
|
|
67d370feaa | ||
|
|
0173ec356b | ||
|
|
2fb43d417a | ||
|
|
966b8158c2 | ||
|
|
800fac6093 | ||
|
|
20eb04cd46 | ||
|
|
5d31a88556 | ||
|
|
06019ae7ba | ||
|
|
523af2901f | ||
|
|
1e0a2c0c43 | ||
|
|
9b60f9ca48 | ||
|
|
90bc800c92 | ||
|
|
023060cee6 | ||
|
|
0bb3b127d0 | ||
|
|
cd914d30ed | ||
|
|
71dddccb9c | ||
|
|
2a846040ef | ||
|
|
8b580d05c4 | ||
|
|
24d70e8ea8 | ||
|
|
8212432983 | ||
|
|
7df00a3291 | ||
|
|
0186aa5c98 | ||
|
|
67e5cc839c | ||
|
|
25c5bd1ccd | ||
|
|
e5fa48d818 | ||
|
|
f34da3a715 | ||
|
|
fab330ce55 | ||
|
|
3a76080e40 | ||
|
|
db57572f38 | ||
|
|
b030eae999 | ||
|
|
b10f4fa631 | ||
|
|
4a6e8c9945 | ||
|
|
3a4d087e6f | ||
|
|
c408b38f28 | ||
|
|
b2336b2b33 | ||
|
|
fcf68d5c32 | ||
|
|
9e71677cd2 | ||
|
|
ce48ce0860 | ||
|
|
9b93cf95ec | ||
|
|
34d85e54d6 | ||
|
|
6ae9d6dd01 | ||
|
|
1e8d8afe70 | ||
|
|
80478e39e0 | ||
|
|
ee7338cc12 | ||
|
|
3a39970d28 | ||
|
|
b0607673ed | ||
|
|
d9f27c6c65 | ||
|
|
ea0b35fe84 | ||
|
|
4fbd22f232 | ||
|
|
c92142845f | ||
|
|
c93f818315 | ||
|
|
d20ed86219 | ||
|
|
e04390307c | ||
|
|
5c931f14ae | ||
|
|
a7ab1243a4 | ||
|
|
5638b5e770 | ||
|
|
c5ce895c97 | ||
|
|
2be99c0e55 | ||
|
|
f6299b537d | ||
|
|
11d06cce4b | ||
|
|
e9e0ad6d3c | ||
|
|
3ad2d5ced8 | ||
|
|
42bfa7c16d | ||
|
|
0dea974a2e | ||
|
|
8165b8f13e | ||
|
|
1c8864c992 | ||
|
|
dd6921676f | ||
|
|
a1193da4fb | ||
|
|
ff83a29dcc | ||
|
|
4771bff65c | ||
|
|
6106265d87 | ||
|
|
3c2455b7e1 | ||
|
|
7bc8de103b | ||
|
|
e49ba4bd02 | ||
|
|
44ac1a0095 | ||
|
|
04ce686e7c | ||
|
|
ca01224083 | ||
|
|
d489232cbc | ||
|
|
9efc0522c3 | ||
|
|
4c86ed787e | ||
|
|
9a3c05f69b | ||
|
|
2b03f80d59 | ||
|
|
baa3f0531d | ||
|
|
f9bbc78241 | ||
|
|
7f62c8b7c2 | ||
|
|
de2bcb6320 | ||
|
|
64a6d934e7 | ||
|
|
f455022e6d | ||
|
|
baac8a3605 | ||
|
|
be20cd5cdb | ||
|
|
1de4137369 | ||
|
|
18fae1a891 | ||
|
|
03c58613b8 | ||
|
|
fb23dd6b39 | ||
|
|
c12bd632fc | ||
|
|
a71eea09a9 | ||
|
|
d1f7842169 | ||
|
|
4566c2a202 | ||
|
|
c678918e73 | ||
|
|
7e113bfb1f | ||
|
|
f1d358d77c | ||
|
|
d558ef9ecf | ||
|
|
75c7ba7f12 | ||
|
|
d62dfe19a2 | ||
|
|
0dbef188b1 | ||
|
|
44f13f6240 | ||
|
|
e01cb2ed49 | ||
|
|
388eef041f |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
12
.github/actions/install-dependencies/action.yaml
vendored
12
.github/actions/install-dependencies/action.yaml
vendored
@@ -5,6 +5,10 @@ inputs:
|
||||
description: 'Turborepo token'
|
||||
TURBO_TEAM:
|
||||
description: 'Turborepo team'
|
||||
BUILD:
|
||||
description: 'Build packages'
|
||||
default: 'default'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
@@ -35,6 +39,14 @@ runs:
|
||||
# * They are reused through the Turborepo cache
|
||||
- shell: bash
|
||||
name: Build packages
|
||||
if: ${{ inputs.BUILD == 'all' }}
|
||||
run: pnpm build:all
|
||||
env:
|
||||
TURBO_TOKEN: ${{ inputs.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ inputs.TURBO_TEAM }}
|
||||
- shell: bash
|
||||
name: Build everything in the monorepo
|
||||
if: ${{ inputs.BUILD == 'default' }}
|
||||
run: pnpm build
|
||||
env:
|
||||
TURBO_TOKEN: ${{ inputs.TURBO_TOKEN }}
|
||||
|
||||
108
.github/actions/nhost-cli/README.md
vendored
Normal file
108
.github/actions/nhost-cli/README.md
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
# Nhost CLI GitHub Action
|
||||
|
||||
## Usage
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install the Nhost CLI
|
||||
uses: ./.github/actions/nhost-cli
|
||||
```
|
||||
|
||||
### Install the CLI and start the app
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Nhost CLI and start the application
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
start: true
|
||||
```
|
||||
|
||||
### Set another working directory
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Nhost CLI
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
path: examples/react-apollo
|
||||
start: true
|
||||
```
|
||||
|
||||
### Don't wait for the app to be ready
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Nhost CLI and start app
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
start: true
|
||||
wait: false
|
||||
```
|
||||
|
||||
### Stop the app
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Start app
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
start: true
|
||||
- name: Do something
|
||||
cmd: echo "do something"
|
||||
- name: Stop
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
stop: true
|
||||
```
|
||||
|
||||
### Install a given value of the CLI
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Nhost CLI
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
version: v0.8.10
|
||||
```
|
||||
|
||||
### Inject values into nhost/config.yaml
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Nhost CLI
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
config: |
|
||||
services:
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.16.1
|
||||
```
|
||||
75
.github/actions/nhost-cli/action.yaml
vendored
Normal file
75
.github/actions/nhost-cli/action.yaml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: Nhost CLI
|
||||
description: 'Action to install the Nhost CLI and to run an application'
|
||||
inputs:
|
||||
start:
|
||||
description: "Start the application. If false, the application won't be started"
|
||||
default: 'false'
|
||||
wait:
|
||||
description: 'If starting the application, wait until it is ready'
|
||||
default: 'true'
|
||||
stop:
|
||||
description: 'Stop the application'
|
||||
default: 'false'
|
||||
path:
|
||||
description: 'Path to the application'
|
||||
default: '.'
|
||||
version:
|
||||
description: 'Version of the Nhost CLI'
|
||||
default: 'latest'
|
||||
config:
|
||||
description: 'Values to be injected into nhost/config.yaml'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Check if Nhost CLI is already installed
|
||||
id: check-nhost-cli
|
||||
shell: bash
|
||||
# TODO check if the version is the same
|
||||
run: |
|
||||
if [ -z "$(which nhost)" ]
|
||||
then
|
||||
echo "installed=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "installed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Install Nhost CLI
|
||||
if: ${{ steps.check-nhost-cli.outputs.installed == 'false' }}
|
||||
shell: bash
|
||||
run: bash <(curl --silent -L https://raw.githubusercontent.com/nhost/cli/main/get.sh) ${{ inputs.version }}
|
||||
- name: Set custom configuration
|
||||
if: ${{ inputs.config }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.path }}
|
||||
run: config="${{ inputs.config }}" yq -i '. *= env(config)' nhost/config.yaml
|
||||
- name: Start the application
|
||||
if: ${{ inputs.start == 'true' }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.path }}
|
||||
run: nhost dev --no-browser &
|
||||
- name: Wait for the app to be ready
|
||||
id: wait
|
||||
if: ${{ inputs.start == 'true' && inputs.wait == 'true' }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.path }}
|
||||
continue-on-error: true
|
||||
run: |
|
||||
curl -sSf --connect-timeout 3 \
|
||||
--max-time 5 \
|
||||
--retry 300 \
|
||||
--retry-delay 1 \
|
||||
--retry-max-time 300 \
|
||||
--retry-connrefused \
|
||||
'http://localhost:9695' > /dev/null
|
||||
- name: Log on failure
|
||||
if: steps.wait.outcome == 'failure'
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.path }}
|
||||
run: |
|
||||
nhost logs
|
||||
exit 1
|
||||
- name: Stop the application
|
||||
if: ${{ inputs.stop == 'true' }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.path }}
|
||||
run: nhost down
|
||||
22
.github/labeler.yml
vendored
Normal file
22
.github/labeler.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
dashboard:
|
||||
- dashboard/**/*
|
||||
|
||||
documentation:
|
||||
- any:
|
||||
- docs/**/*
|
||||
- '!docs/docs/reference/docgen/**/*'
|
||||
|
||||
examples:
|
||||
- examples/**/*
|
||||
|
||||
sdk:
|
||||
- packages/**/*
|
||||
|
||||
react:
|
||||
- '{packages,examples}/*react*/**/*'
|
||||
|
||||
nextjs:
|
||||
- '{packages,examples}/*next*/**/*'
|
||||
|
||||
vue:
|
||||
- '{packages,examples}/*vue*/**/*'
|
||||
16
.github/renovate.json
vendored
Normal file
16
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"docker-compose": {
|
||||
"enabled": true
|
||||
},
|
||||
"ignoreDeps": [
|
||||
"pnpm",
|
||||
"node"
|
||||
],
|
||||
"labels": [
|
||||
"dependencies"
|
||||
]
|
||||
}
|
||||
30
.github/workflows/changesets.yaml
vendored
30
.github/workflows/changesets.yaml
vendored
@@ -148,3 +148,33 @@ jobs:
|
||||
vercel pull --environment=production --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
|
||||
vercel build --prod --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
|
||||
vercel deploy --prebuilt --prod --token=${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
|
||||
|
||||
bump-cli:
|
||||
name: Bump Dashboard version in the Nhost CLI
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- version
|
||||
- publish-docker
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: nhost/cli
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
fetch-depth: 0
|
||||
- name: Bump version in source code
|
||||
run: |
|
||||
IMAGE=$(echo ${{ env.DASHBOARD_PACKAGE }} | sed 's/@\(.\+\)\/\(.\+\)/\1\\\/\2/g')
|
||||
VERSION="${{ needs.version.outputs.dashboardVersion }}"
|
||||
EXPRESSION='s/"'$IMAGE':[0-9]\+\.[0-9]\+\.[0-9]\+"/"'$IMAGE':'$VERSION'"/g'
|
||||
find ./ -type f -exec sed -i -e $EXPRESSION {} \;
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
commit-message: 'chore: bump nhost/dashboard to ${{ needs.version.outputs.dashboardVersion }}'
|
||||
branch: bump-dashboard-version
|
||||
delete-branch: true
|
||||
title: 'chore: bump nhost/dashboard to ${{ needs.version.outputs.dashboardVersion }}'
|
||||
body: |
|
||||
This PR bumps the Nhost Dashboard Docker image to version ${{ needs.version.outputs.dashboardVersion }}.
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
name: Packages
|
||||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- 'dashboard/**'
|
||||
- 'docs/**'
|
||||
- 'assets/**'
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
@@ -13,15 +11,16 @@ on:
|
||||
branches: [main]
|
||||
types: [opened, synchronize]
|
||||
paths-ignore:
|
||||
- 'dashboard/**'
|
||||
- 'docs/**'
|
||||
- 'assets/**'
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: nhost
|
||||
NEXT_PUBLIC_ENV: dev
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_PUBLIC_NHOST_BACKEND_URL: http://localhost:1337
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build @nhost packages
|
||||
@@ -36,6 +35,7 @@ jobs:
|
||||
with:
|
||||
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
||||
BUILD: 'all'
|
||||
# * List packagesthat has an `e2e` script, except the root, and return an array of their name and path
|
||||
# * In a PR, only include packages that have been modified, and their dependencies
|
||||
- name: List examples with an e2e script
|
||||
@@ -43,14 +43,16 @@ jobs:
|
||||
run: |
|
||||
FILTER_MODIFIED="${{ github.event_name == 'pull_request' && format('--filter=...[origin/{0}]', github.base_ref) || '' }}"
|
||||
PACKAGES=$(pnpm recursive list --depth -1 --parseable --filter='!nhost-root' $FILTER_MODIFIED \
|
||||
| xargs -I@ realpath --relative-to=$PWD @ \
|
||||
| xargs -I@ jq "if (.scripts.e2e | length) != 0 then {name: .name, path: \"@\"} else null end" @/package.json \
|
||||
| awk "!/null/" \
|
||||
| jq -c --slurp)
|
||||
echo "matrix=$PACKAGES" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
|
||||
e2e:
|
||||
name: 'e2e: ${{ matrix.package.name }}'
|
||||
name: 'e2e (${{ matrix.package.path }})'
|
||||
needs: build
|
||||
if: ${{ needs.build.outputs.matrix != '[]' && needs.build.outputs.matrix != '' }}
|
||||
strategy:
|
||||
@@ -70,7 +72,7 @@ jobs:
|
||||
# * Install Nhost CLI if a `nhost/config.yaml` file is found
|
||||
- name: Install Nhost CLI
|
||||
if: hashFiles(format('{0}/nhost/config.yaml', matrix.package.path)) != ''
|
||||
run: curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash
|
||||
uses: ./.github/actions/nhost-cli
|
||||
# * Run the `ci` script of the current package of the matrix. Dependencies build is cached by Turborepo
|
||||
- name: Run e2e test
|
||||
run: pnpm --filter="${{ matrix.package.name }}" run e2e
|
||||
@@ -89,6 +91,7 @@ jobs:
|
||||
path: |
|
||||
${{format('{0}/cypress/screenshots/**', matrix.package.path)}}
|
||||
${{format('{0}/cypress/videos/**', matrix.package.path)}}
|
||||
|
||||
unit:
|
||||
name: Unit tests
|
||||
needs: build
|
||||
@@ -103,7 +106,7 @@ jobs:
|
||||
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
||||
# * Run every `test` script in the workspace . Dependencies build is cached by Turborepo
|
||||
- name: Run unit tests
|
||||
run: pnpm run test
|
||||
run: pnpm run test:all
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
@@ -113,6 +116,7 @@ jobs:
|
||||
run: |
|
||||
echo '### Code coverage' >> $GITHUB_STEP_SUMMARY
|
||||
echo 'Visit [codecov](https://app.codecov.io/gh/nhost/nhost/) to see the code coverage reports' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
needs: build
|
||||
@@ -127,4 +131,4 @@ jobs:
|
||||
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
||||
# * Run every `lint` script in the workspace . Dependencies build is cached by Turborepo
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
run: pnpm run lint:all
|
||||
2
.github/workflows/contributors.yaml
vendored
2
.github/workflows/contributors.yaml
vendored
@@ -10,6 +10,6 @@ jobs:
|
||||
name: A job to automate contrib in readme
|
||||
steps:
|
||||
- name: Contribute List
|
||||
uses: akhilmhdh/contributors-readme-action@v2.3.4
|
||||
uses: akhilmhdh/contributors-readme-action@v2.3.6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
21
.github/workflows/dashboard.yaml
vendored
21
.github/workflows/dashboard.yaml
vendored
@@ -2,24 +2,17 @@ name: 'Dashboard'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [opened, synchronize]
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- 'dashboard/**'
|
||||
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: nhost
|
||||
NEXT_PUBLIC_ENV: dev
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_PUBLIC_NHOST_BACKEND_URL: http://localhost:1337
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEXT_PUBLIC_ENV: dev
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_PUBLIC_NHOST_BACKEND_URL: http://localhost:1337
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Node and dependencies
|
||||
@@ -33,10 +26,6 @@ jobs:
|
||||
tests:
|
||||
name: Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEXT_PUBLIC_ENV: dev
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_PUBLIC_NHOST_BACKEND_URL: http://localhost:1337
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Node and dependencies
|
||||
@@ -50,10 +39,6 @@ jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEXT_PUBLIC_ENV: dev
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_PUBLIC_NHOST_BACKEND_URL: http://localhost:1337
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Node and dependencies
|
||||
|
||||
15
.github/workflows/labeler.yaml
vendored
Normal file
15
.github/workflows/labeler.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: 'Pull Request Labeler'
|
||||
on:
|
||||
- pull_request_target
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v4
|
||||
with:
|
||||
repo-token: '${{ secrets.GH_PAT }}'
|
||||
sync-labels: ''
|
||||
69
.github/workflows/renovate.yaml
vendored
Normal file
69
.github/workflows/renovate.yaml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: Renovate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [closed]
|
||||
paths-ignore:
|
||||
- 'assets/**'
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: nhost
|
||||
|
||||
jobs:
|
||||
renovate-changeset:
|
||||
name: Add changeset
|
||||
if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'renovate/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
# * Install Node and dependencies. Package downloads will be cached for the next jobs.
|
||||
- name: Install Node and dependencies
|
||||
uses: ./.github/actions/install-dependencies
|
||||
with:
|
||||
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
||||
BUILD: 'none'
|
||||
- name: Determine bumps
|
||||
id: bumps
|
||||
run: |
|
||||
LAST_NON_PR_SHA=$(git log --no-merges main origin/${{ github.head_ref }} --format=format:%h | head -2 | tail -1)
|
||||
echo "result<<EOF" >> $GITHUB_OUTPUT
|
||||
pnpm recursive list --depth -1 --parseable \
|
||||
--filter='!nhost-root' \
|
||||
--filter=[$LAST_NON_PR_SHA] \
|
||||
| xargs -I@ jq ".name" @/package.json \
|
||||
| sort \
|
||||
| uniq -u \
|
||||
| awk '$0=$0": patch"' \
|
||||
>> $GITHUB_OUTPUT
|
||||
echo 'EOF' >> $GITHUB_OUTPUT
|
||||
- name: Install dictionary
|
||||
if: steps.bumps.outputs.result != ''
|
||||
run: sudo apt-get install wbritish
|
||||
- name: Generate changeset file name
|
||||
id: file_name
|
||||
if: steps.bumps.outputs.result != ''
|
||||
run: |
|
||||
FILE_NAME=$(shuf -n 3 /usr/share/dict/words | tr '\n' '-' | sed 's/-$//' | sed 's/'"'"'s//g' | tr '[:upper:]' '[:lower:]')
|
||||
echo "result=./.changeset/${FILE_NAME}.md" >> $GITHUB_OUTPUT
|
||||
- name: Create changeset file
|
||||
if: steps.bumps.outputs.result != ''
|
||||
run: |
|
||||
cat <<EOF > ${{ steps.file_name.outputs.result }}
|
||||
---
|
||||
${{ steps.bumps.outputs.result }}
|
||||
---
|
||||
|
||||
${{ github.event.pull_request.title }}
|
||||
EOF
|
||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||
if: steps.bumps.outputs.result != ''
|
||||
with:
|
||||
commit_message: ${{ github.event.pull_request.title }}
|
||||
branch: main
|
||||
104
.github/workflows/test-nhost-cli-action.yaml
vendored
Normal file
104
.github/workflows/test-nhost-cli-action.yaml
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
name: Test Nhost CLI action
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [opened, synchronize]
|
||||
paths:
|
||||
- '.github/actions/nhost-cli/**'
|
||||
- '!.github/actions/nhost-cli/**/*.md'
|
||||
|
||||
jobs:
|
||||
install:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install the Nhost CLI
|
||||
uses: ./.github/actions/nhost-cli
|
||||
- name: should succeed running the nhost command
|
||||
run: nhost
|
||||
|
||||
start:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install the Nhost CLI and start the application
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
path: packages/nhost-js
|
||||
start: true
|
||||
- name: should be running
|
||||
run: curl -sSf 'http://localhost:9695' > /dev/null
|
||||
|
||||
stop:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install the Nhost CLI, start and stop the application
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
path: packages/nhost-js
|
||||
start: true
|
||||
stop: true
|
||||
- name: should have no live docker container
|
||||
run: |
|
||||
if [ -z "docker ps -q" ]; then
|
||||
echo "Some docker containers are still running"
|
||||
docker ps
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wait:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install the Nhost CLI and start the application
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
path: packages/nhost-js
|
||||
start: true
|
||||
wait: false
|
||||
- name: should not be ready
|
||||
run: curl -sSf -o /dev/null 'http://localhost:9695' > /dev/null && exit 1 || true
|
||||
- name: should eventually be ready
|
||||
run: |
|
||||
curl -sSf --connect-timeout 3 \
|
||||
--max-time 5 \
|
||||
--retry 300 \
|
||||
--retry-delay 1 \
|
||||
--retry-max-time 300 \
|
||||
--retry-connrefused \
|
||||
'http://localhost:9695' > /dev/null
|
||||
|
||||
config:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install the Nhost CLI and run the application
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
path: packages/nhost-js
|
||||
start: true
|
||||
config: |
|
||||
services:
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.15.0
|
||||
- name: should find the injected hasura-auth version
|
||||
run: |
|
||||
VERSION=$(curl -sSf 'http://localhost:1337/v1/auth/version')
|
||||
EXPECTED_VERSION='{"version":"v0.15.0"}'
|
||||
if [ "$VERSION" != "$EXPECTED_VERSION" ]; then
|
||||
echo "Expected version $EXPECTED_VERSION but got $VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install the Nhost CLI
|
||||
uses: ./.github/actions/nhost-cli
|
||||
with:
|
||||
version: v0.8.10
|
||||
- name: should find the correct version
|
||||
run: nhost version | head -n 1 | grep v0.8.10 || exit 1
|
||||
@@ -1,5 +1,45 @@
|
||||
# @nhost/dashboard
|
||||
|
||||
## 0.7.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 132a4f4b: chore(dashboard): remove unused dependencies
|
||||
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react versions
|
||||
- db57572f: fix(dashboard): correct section paddings when no env vars
|
||||
- Updated dependencies [132a4f4b]
|
||||
- @nhost/react@0.15.2
|
||||
- @nhost/react-apollo@4.9.2
|
||||
- @nhost/nextjs@1.9.3
|
||||
|
||||
## 0.7.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 34d85e54: chore(deps): update dependency critters to ^0.0.16
|
||||
- 9b93cf95: chore(deps): update dependency @netlify/functions to ^0.11.0
|
||||
- e0439030: chore(deps): update dependency @types/react-dom to v18.0.9
|
||||
- Updated dependencies [82124329]
|
||||
- @nhost/nextjs@1.9.2
|
||||
|
||||
## 0.7.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a1193da4: fix(dashboard): remove character limit from env var inputs
|
||||
|
||||
## 0.7.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 44f13f62: chore(dashboard): cleanup unused files
|
||||
|
||||
## 0.7.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e01cb2ed: chore(dashboard): change settings sidebar menu item density
|
||||
|
||||
## 0.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/dashboard",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
@@ -25,7 +25,7 @@
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@fontsource/inter": "^4.5.14",
|
||||
"@fontsource/roboto-mono": "^4.5.8",
|
||||
"@graphiql/react": "^0.14.0",
|
||||
"@graphiql/react": "^0.15.0",
|
||||
"@graphiql/toolkit": "^0.8.0",
|
||||
"@headlessui/react": "^1.6.5",
|
||||
"@heroicons/react": "^1.0.6",
|
||||
@@ -34,12 +34,14 @@
|
||||
"@mui/material": "^5.10.14",
|
||||
"@mui/system": "^5.10.14",
|
||||
"@mui/x-date-pickers": "^5.0.8",
|
||||
"@nhost/core": "^0.9.4",
|
||||
"@nhost/nextjs": "^1.9.1",
|
||||
"@nhost/nhost-js": "^1.6.2",
|
||||
"@nhost/react": "^0.15.1",
|
||||
"@nhost/react-apollo": "^4.9.1",
|
||||
"@nhost/core": "workspace:*",
|
||||
"@nhost/nextjs": "workspace:*",
|
||||
"@nhost/nhost-js": "workspace:*",
|
||||
"@nhost/react": "workspace:*",
|
||||
"@nhost/react-apollo": "workspace:*",
|
||||
"@segment/snippet": "^4.15.3",
|
||||
"@stripe/react-stripe-js": "^1.10.0",
|
||||
"@stripe/stripe-js": "^1.35.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tanstack/react-query": "^4.16.1",
|
||||
"@tanstack/react-table": "^8.5.30",
|
||||
@@ -56,16 +58,15 @@
|
||||
"graphql-request": "^4.3.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"graphql-ws": "^5.11.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"next": "^12.3.1",
|
||||
"next-seo": "^5.14.1",
|
||||
"node-pg-format": "^1.3.5",
|
||||
"pluralize": "^8.0.0",
|
||||
"prettysize": "^2.0.0",
|
||||
"randomstring": "^1.2.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
"react-hook-form": "^7.39.5",
|
||||
"react-hot-toast": "^2.4.0",
|
||||
"react-is": "17.0.2",
|
||||
@@ -75,7 +76,6 @@
|
||||
"react-table": "^7.8.0",
|
||||
"sharp": "^0.31.2",
|
||||
"slugify": "^1.6.5",
|
||||
"smartlook-client": "^6.0.0",
|
||||
"stripe": "^10.17.0",
|
||||
"tailwind-merge": "^1.8.0",
|
||||
"utility-types": "^3.10.0",
|
||||
@@ -90,7 +90,6 @@
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.5.1",
|
||||
"@graphql-codegen/typescript-operations": "^2.5.1",
|
||||
"@graphql-codegen/typescript-react-apollo": "^3.3.1",
|
||||
"@netlify/functions": "^0.7.2",
|
||||
"@next/bundle-analyzer": "^12.3.1",
|
||||
"@storybook/addon-actions": "^6.5.13",
|
||||
"@storybook/addon-essentials": "^6.5.13",
|
||||
@@ -101,18 +100,15 @@
|
||||
"@storybook/manager-webpack5": "^6.5.13",
|
||||
"@storybook/react": "^6.5.13",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@stripe/react-stripe-js": "^1.10.0",
|
||||
"@stripe/stripe-js": "^1.35.0",
|
||||
"@testing-library/dom": "^8.19.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/pluralize": "^0.0.29",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "18.0.8",
|
||||
"@types/react-dom": "18.0.9",
|
||||
"@types/react-table": "^7.7.12",
|
||||
"@types/testing-library__jest-dom": "^5.14.5",
|
||||
"@types/validator": "^13.7.10",
|
||||
@@ -123,10 +119,7 @@
|
||||
"autoprefixer": "^10.4.13",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||
"concurrently": "^6.3.0",
|
||||
"critters": "^0.0.10",
|
||||
"csstype": "^3.0.10",
|
||||
"dotenv": "^10.0.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-airbnb": "19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||
@@ -136,20 +129,17 @@
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"express": "^4.18.2",
|
||||
"express-validator": "^6.14.2",
|
||||
"jsdom": "^20.0.3",
|
||||
"lint-staged": ">=13",
|
||||
"msw": "^0.49.0",
|
||||
"postcss": "^8.4.19",
|
||||
"postmark": "^2.7.8",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-organize-imports": "^3.2.0",
|
||||
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||
"prettier-plugin-tailwindcss": "^0.2.0",
|
||||
"react-date-fns-hooks": "^0.9.4",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
"require-from-string": "^2.0.2",
|
||||
"tailwindcss": "^3.1.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
||||
"typescript": "^4.8.4",
|
||||
"vite": "^3.2.4",
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
import { useWorkspaceContext } from '@/context/workspace-context';
|
||||
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
|
||||
import { Alert } from '@/ui/Alert';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Input from '@/ui/v2/Input';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||
import { inputErrorMessages } from '@/utils/getErrorMessage';
|
||||
import { slugifyString } from '@/utils/helpers';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import { updateOwnCache } from '@/utils/updateOwnCache';
|
||||
import { useUpdateApplicationMutation } from '@/utils/__generated__/graphql';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export function ChangeApplicationName({ close }: any) {
|
||||
const [updateAppName, { client }] = useUpdateApplicationMutation({});
|
||||
const { workspaceContext } = useWorkspaceContext();
|
||||
const [name, setName] = useState(workspaceContext.appName);
|
||||
const [applicationError, setApplicationError] = useState<any>('');
|
||||
const { currentWorkspace, currentApplication } =
|
||||
useCurrentWorkspaceAndApplication();
|
||||
const router = useRouter();
|
||||
|
||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
const slug = slugifyString(name);
|
||||
if (slug.length < 4 || slug.length > 32) {
|
||||
setApplicationError('Slug should be within 4 and 32 characters.');
|
||||
return;
|
||||
}
|
||||
if (!inputErrorMessages(name, setName, setApplicationError, 'project')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateAppName({
|
||||
variables: {
|
||||
appId: currentApplication.id,
|
||||
app: {
|
||||
name,
|
||||
slug,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
triggerToast('Project name changed');
|
||||
} catch (error) {
|
||||
await discordAnnounce(
|
||||
`Error trying to delete project: ${currentApplication.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
await updateOwnCache(client);
|
||||
await router.push(`/${currentWorkspace.slug}/${slug}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-modal px-6 py-6 text-left">
|
||||
<div className="flex flex-col">
|
||||
<Text variant="h3" component="h2">
|
||||
Change Project Name
|
||||
</Text>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mt-4 grid grid-flow-row gap-2">
|
||||
<Input
|
||||
label="New Project Name"
|
||||
id="projectName"
|
||||
value={name}
|
||||
onChange={(e) => {
|
||||
setName(e.target.value);
|
||||
setApplicationError('');
|
||||
}}
|
||||
fullWidth
|
||||
autoFocus
|
||||
helperText={`https://app.nhost.io/${
|
||||
currentWorkspace.slug
|
||||
}/${slugifyString(name)}`}
|
||||
/>
|
||||
|
||||
{applicationError && (
|
||||
<Alert severity="error">{applicationError}</Alert>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 grid grid-flow-row gap-2">
|
||||
<Button type="submit" disabled={applicationError}>
|
||||
Save
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={close}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChangeApplicationName;
|
||||
@@ -1,327 +0,0 @@
|
||||
import ErrorBoundaryFallback from '@/components/common/ErrorBoundaryFallback';
|
||||
import TwilioIcon from '@/components/icons/TwilioIcon';
|
||||
import {
|
||||
useGetSmsSettingsQuery,
|
||||
useUpdateAppMutation,
|
||||
} from '@/generated/graphql';
|
||||
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
|
||||
import { Button, Input } from '@/ui';
|
||||
import { Alert } from '@/ui/Alert';
|
||||
import DelayedLoading from '@/ui/DelayedLoading';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { showLoadingToast, triggerToast } from '@/utils/toast';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useEffect } from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import {
|
||||
Controller,
|
||||
FormProvider,
|
||||
useForm,
|
||||
useFormContext,
|
||||
} from 'react-hook-form';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export function EditSMSSettingsForm({
|
||||
close,
|
||||
isAlreadyEnabled,
|
||||
}: {
|
||||
close: () => void;
|
||||
isAlreadyEnabled: boolean;
|
||||
}) {
|
||||
const { currentApplication } = useCurrentWorkspaceAndApplication();
|
||||
const {
|
||||
handleSubmit,
|
||||
watch,
|
||||
formState: { isSubmitting, errors },
|
||||
} = useFormContext<EditSMSSettingsFormData>();
|
||||
const { control } = useFormContext<EditSMSSettingsFormData>();
|
||||
|
||||
const [updateApp] = useUpdateAppMutation();
|
||||
let toastId: string;
|
||||
|
||||
const client = useApolloClient();
|
||||
const isNotCompleted =
|
||||
!watch('accountSID') ||
|
||||
!watch('authToken') ||
|
||||
!watch('messagingServiceSID');
|
||||
|
||||
const handleEditSMSSettings = async (data: EditSMSSettingsFormData) => {
|
||||
try {
|
||||
toastId = showLoadingToast('Updating SMS settings...');
|
||||
await updateApp({
|
||||
variables: {
|
||||
id: currentApplication.id,
|
||||
app: {
|
||||
authSmsTwilioAccountSid: data.accountSID,
|
||||
authSmsTwilioAuthToken: data.authToken,
|
||||
authSmsTwilioMessagingServiceId: data.messagingServiceSID,
|
||||
authSmsPasswordlessEnabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
await client.refetchQueries({ include: ['getSMSSettings'] });
|
||||
toast.remove(toastId);
|
||||
triggerToast('SMS settings updated successfully.');
|
||||
close();
|
||||
} catch (error) {
|
||||
if (toastId) {
|
||||
toast.remove(toastId);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(handleEditSMSSettings)}
|
||||
className="flex w-full flex-col pb-1"
|
||||
autoComplete="off"
|
||||
>
|
||||
{errors &&
|
||||
Object.entries(errors).map(([type, error]) => (
|
||||
<Alert key={type} className="mb-4" severity="error">
|
||||
{error.message}
|
||||
</Alert>
|
||||
))}
|
||||
|
||||
<div>
|
||||
<div className="flex flex-row place-content-between border-t border-b px-2 py-2.5">
|
||||
<div className="flex w-full flex-row">
|
||||
<Text
|
||||
color="greyscaleDark"
|
||||
className="self-center font-medium"
|
||||
size="normal"
|
||||
>
|
||||
Account SID
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex w-full">
|
||||
<Controller
|
||||
name="accountSID"
|
||||
control={control}
|
||||
rules={{
|
||||
required: true,
|
||||
pattern: {
|
||||
value: /^[a-zA-Z0-9-_]+$/,
|
||||
message:
|
||||
'The Account SID must contain only letters, hyphens, and numbers.',
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
id="accountSID"
|
||||
placeholder="Account SID"
|
||||
required
|
||||
value={field.value || ''}
|
||||
onChange={(value: string) => {
|
||||
if (value && !/^[a-zA-Z0-9-_]+$/gi.test(value)) {
|
||||
// prevent the user from entering invalid characters
|
||||
return;
|
||||
}
|
||||
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row place-content-between border-b px-2 py-2.5">
|
||||
<div className="flex w-full flex-row">
|
||||
<Text
|
||||
color="greyscaleDark"
|
||||
className="self-center font-medium"
|
||||
size="normal"
|
||||
>
|
||||
Auth Token
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex w-full">
|
||||
<Controller
|
||||
name="authToken"
|
||||
control={control}
|
||||
rules={{
|
||||
required: true,
|
||||
pattern: {
|
||||
value: /^[a-zA-Z0-9-_]+$/,
|
||||
message:
|
||||
'The Auth Token must contain only letters, hyphens, and numbers.',
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
id="authToken"
|
||||
placeholder="Auth Token"
|
||||
required
|
||||
value={field.value || ''}
|
||||
onChange={(value: string) => {
|
||||
if (value && !/^[a-zA-Z0-9-_/.]+$/gi.test(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row place-content-between border-b px-2 py-2.5">
|
||||
<div className="flex w-full flex-row">
|
||||
<Text
|
||||
color="greyscaleDark"
|
||||
className="self-center font-medium"
|
||||
size="normal"
|
||||
>
|
||||
Messaging Service SID
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex w-full">
|
||||
<Controller
|
||||
name="messagingServiceSID"
|
||||
control={control}
|
||||
rules={{
|
||||
required: true,
|
||||
pattern: {
|
||||
value: /^[+a-zA-Z0-9-_/.]+$/,
|
||||
message:
|
||||
'The Messaging Service SID must either be a valid phone number or contain only letters, hyphens, and numbers.',
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
id="messagingServiceSID"
|
||||
required
|
||||
placeholder="Messaging Service SID"
|
||||
value={field.value || ''}
|
||||
onChange={(value: string) => {
|
||||
if (value && !/^[+a-zA-Z0-9-_/.]+$/gi.test(value)) {
|
||||
return;
|
||||
}
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
className="text-grayscaleDark mt-2 border text-sm+ font-normal"
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting || isNotCompleted}
|
||||
>
|
||||
{isAlreadyEnabled ? 'Update SMS Settings' : 'Enable SMS'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export function EditSMSSettingsModal({
|
||||
close,
|
||||
isAlreadyEnabled,
|
||||
}: {
|
||||
close: () => void;
|
||||
isAlreadyEnabled: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className="w-modal px-6 py-4 text-left">
|
||||
<div className="flex flex-col">
|
||||
<div className="mx-auto mt-2.5">
|
||||
<TwilioIcon className=" text-greyscaleDark" />
|
||||
</div>
|
||||
<Text
|
||||
variant="subHeading"
|
||||
color="greyscaleDark"
|
||||
size="large"
|
||||
className="mt-3 text-center"
|
||||
>
|
||||
Set up Twilio SMS Service
|
||||
</Text>
|
||||
<Text
|
||||
variant="body"
|
||||
color="greyscaleDark"
|
||||
size="small"
|
||||
className="mt-0.5 mb-6 text-center font-normal"
|
||||
>
|
||||
SMS messages are sent through Twilio. Create an account and a
|
||||
messaging service at https://console.twilio.com.
|
||||
</Text>
|
||||
<div>
|
||||
<ErrorBoundary fallbackRender={ErrorBoundaryFallback}>
|
||||
<EditSMSSettingsForm
|
||||
close={close}
|
||||
isAlreadyEnabled={isAlreadyEnabled}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export interface EditSMSSettingsProps {
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export interface EditSMSSettingsFormData {
|
||||
accountSID: string;
|
||||
authToken: string;
|
||||
messagingServiceSID: string;
|
||||
}
|
||||
|
||||
export function EditSMSSettings({ close }: EditSMSSettingsProps) {
|
||||
const { currentApplication } = useCurrentWorkspaceAndApplication();
|
||||
|
||||
const form = useForm<EditSMSSettingsFormData>({
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: {
|
||||
accountSID: '',
|
||||
authToken: '',
|
||||
messagingServiceSID: '',
|
||||
},
|
||||
});
|
||||
|
||||
const { data, loading, error } = useGetSmsSettingsQuery({
|
||||
variables: {
|
||||
id: currentApplication.id,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
form.setValue('accountSID', data.app.authSmsTwilioAccountSid);
|
||||
form.setValue('authToken', data.app.authSmsTwilioAuthToken);
|
||||
form.setValue(
|
||||
'messagingServiceSID',
|
||||
data.app.authSmsTwilioMessagingServiceId,
|
||||
);
|
||||
}, [data, form]);
|
||||
|
||||
if (loading) {
|
||||
return <DelayedLoading delay={500} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<EditSMSSettingsModal
|
||||
close={close}
|
||||
isAlreadyEnabled={data.app.authSmsPasswordlessEnabled}
|
||||
/>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
import {
|
||||
useGetSmsSettingsQuery,
|
||||
useUpdateAppMutation,
|
||||
} from '@/generated/graphql';
|
||||
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
|
||||
import { Text, Toggle } from '@/ui';
|
||||
import DelayedLoading from '@/ui/DelayedLoading';
|
||||
import { showLoadingToast, triggerToast } from '@/utils/toast';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import clsx from 'clsx';
|
||||
import Image from 'next/image';
|
||||
import { useEffect, useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export function EnableSMSSignIn({ openSMSSettingsModal }: any) {
|
||||
const { currentApplication } = useCurrentWorkspaceAndApplication();
|
||||
const [updateApp] = useUpdateAppMutation();
|
||||
|
||||
const { data, loading, error } = useGetSmsSettingsQuery({
|
||||
variables: {
|
||||
id: currentApplication.id,
|
||||
},
|
||||
});
|
||||
|
||||
const [enableSMSLoginMethod, setEnableSMSLoginMethod] = useState(false);
|
||||
const client = useApolloClient();
|
||||
let toastId: string;
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
setEnableSMSLoginMethod(data.app.authSmsPasswordlessEnabled);
|
||||
}, [data]);
|
||||
|
||||
if (loading) {
|
||||
return <DelayedLoading delay={500} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const handleDisable = async () => {
|
||||
try {
|
||||
toastId = showLoadingToast('Disabling SMS login...');
|
||||
await updateApp({
|
||||
variables: {
|
||||
id: currentApplication.id,
|
||||
app: {
|
||||
authSmsPasswordlessEnabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
setEnableSMSLoginMethod(false);
|
||||
await client.refetchQueries({ include: ['getSMSSettings'] });
|
||||
toast.remove(toastId);
|
||||
triggerToast('Passwordless SMS disabled.');
|
||||
} catch (updateError) {
|
||||
if (toastId) {
|
||||
toast.remove(toastId);
|
||||
}
|
||||
|
||||
throw updateError;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-20">
|
||||
<div className="mx-auto font-display">
|
||||
<div className="flex flex-col place-content-between">
|
||||
<div className="">
|
||||
<div className="flex flex-row place-content-between">
|
||||
<div className="relative flex flex-row">
|
||||
<Image
|
||||
src="/assets/SMS.svg"
|
||||
width={24}
|
||||
height={24}
|
||||
alt="Phone Number (SMS)"
|
||||
/>
|
||||
<Text
|
||||
variant="body"
|
||||
size="large"
|
||||
className="ml-2 font-medium"
|
||||
color="greyscaleDark"
|
||||
>
|
||||
Phone Number (SMS)
|
||||
</Text>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className={clsx(
|
||||
'ml-2 align-bottom text-sm- font-medium text-blue transition-opacity duration-300',
|
||||
!enableSMSLoginMethod && 'invisible opacity-0',
|
||||
enableSMSLoginMethod && 'opacity-100',
|
||||
)}
|
||||
onClick={() => openSMSSettingsModal()}
|
||||
>
|
||||
Edit SMS settings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
<Toggle
|
||||
checked={enableSMSLoginMethod}
|
||||
onChange={async () => {
|
||||
if (enableSMSLoginMethod) {
|
||||
await handleDisable();
|
||||
} else {
|
||||
openSMSSettingsModal();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-row self-center align-middle">
|
||||
<Text
|
||||
variant="body"
|
||||
size="normal"
|
||||
color="greyscaleDark"
|
||||
className="self-center"
|
||||
>
|
||||
Sign in users with Phone Number (SMS).
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EnableSMSSignIn;
|
||||
@@ -1,224 +0,0 @@
|
||||
import { useCurrentWorkspaceAndApplication } from '@/hooks/useCurrentWorkspaceAndApplication';
|
||||
import { useSubmitState } from '@/hooks/useSubmitState';
|
||||
import { Alert } from '@/ui/Alert';
|
||||
import Button from '@/ui/v2/Button';
|
||||
import Input from '@/ui/v2/Input';
|
||||
import Text from '@/ui/v2/Text';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import {
|
||||
refetchGetAppInjectedVariablesQuery,
|
||||
useUpdateApplicationMutation,
|
||||
} from '@/utils/__generated__/graphql';
|
||||
import { useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
|
||||
export interface EditCustomUserJWTTokenData {
|
||||
customUserJWTToken: string;
|
||||
}
|
||||
|
||||
export type JWTSecretModalState = 'SHOW' | 'EDIT';
|
||||
|
||||
export interface JWTSecretModalProps {
|
||||
close: () => void;
|
||||
data?: any;
|
||||
jwtSecret: string;
|
||||
initialModalState?: JWTSecretModalState;
|
||||
}
|
||||
|
||||
export function EditJWTSecretModal({ close }: any) {
|
||||
const { currentApplication } = useCurrentWorkspaceAndApplication();
|
||||
const { submitState, setSubmitState } = useSubmitState();
|
||||
|
||||
const [updateApplication] = useUpdateApplicationMutation({
|
||||
refetchQueries: [
|
||||
refetchGetAppInjectedVariablesQuery({ id: currentApplication.id }),
|
||||
],
|
||||
});
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { isSubmitting },
|
||||
} = useForm<EditCustomUserJWTTokenData>({
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: {
|
||||
customUserJWTToken: '',
|
||||
},
|
||||
});
|
||||
|
||||
const handleEditJWTSecret = async (data: EditCustomUserJWTTokenData) => {
|
||||
setSubmitState({
|
||||
error: null,
|
||||
loading: false,
|
||||
fieldsWithError: [],
|
||||
});
|
||||
try {
|
||||
JSON.parse(data.customUserJWTToken);
|
||||
} catch (error) {
|
||||
setSubmitState({
|
||||
error: new Error('The custom JWT token should be valid json.'),
|
||||
loading: false,
|
||||
fieldsWithError: [],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateApplication({
|
||||
variables: {
|
||||
appId: currentApplication.id,
|
||||
app: {
|
||||
hasuraGraphqlJwtSecret: data.customUserJWTToken,
|
||||
},
|
||||
},
|
||||
});
|
||||
triggerToast(
|
||||
`Successfully added custom JWT token to ${currentApplication.name}.`,
|
||||
);
|
||||
close();
|
||||
} catch (error) {
|
||||
triggerToast(
|
||||
`Error adding custom JWT token to ${currentApplication.name}`,
|
||||
);
|
||||
setSubmitState({ error, loading: false, fieldsWithError: [] });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
className="w-modal px-6 py-4"
|
||||
onSubmit={handleSubmit(handleEditJWTSecret)}
|
||||
>
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<div className="grid grid-flow-row text-left">
|
||||
<Text variant="h3" component="h2">
|
||||
Add Custom JWT Secret
|
||||
</Text>
|
||||
|
||||
<Text variant="subtitle2">
|
||||
You can add your custom JWT key here. Hasura will use this key to
|
||||
validate the identity of your users.
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{submitState.error && (
|
||||
<Alert severity="error">{submitState.error.message}</Alert>
|
||||
)}
|
||||
|
||||
<Controller
|
||||
name="customUserJWTToken"
|
||||
control={control}
|
||||
rules={{
|
||||
required: true,
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Paste your custom JWT token here..."
|
||||
componentsProps={{
|
||||
inputRoot: {
|
||||
className: 'font-mono bg-header',
|
||||
},
|
||||
}}
|
||||
aria-label="Custom JWT token"
|
||||
type="text"
|
||||
value={field.value}
|
||||
onBlur={() =>
|
||||
setSubmitState({
|
||||
error: null,
|
||||
loading: false,
|
||||
fieldsWithError: [],
|
||||
})
|
||||
}
|
||||
multiline
|
||||
rows={6}
|
||||
fullWidth
|
||||
hideEmptyHelperText
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<Button type="submit" loading={isSubmitting}>
|
||||
Save Changes
|
||||
</Button>
|
||||
|
||||
<Button variant="outlined" color="secondary" onClick={close}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export function ShowJWTTokenModal({ JWTKey, editJWTSecret }: any) {
|
||||
return (
|
||||
<div className="w-modal px-6 py-4">
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<div className="grid grid-flow-row text-left">
|
||||
<Text variant="h3" component="h2">
|
||||
Auth JWT Secret
|
||||
</Text>
|
||||
<Text variant="subtitle2">
|
||||
This is the key used for generating JWTs. It's HMAC-SHA-based
|
||||
and the same as configured in Hasura.
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Input
|
||||
defaultValue={JWTKey}
|
||||
multiline
|
||||
disabled
|
||||
fullWidth
|
||||
hideEmptyHelperText
|
||||
rows={6}
|
||||
componentsProps={{
|
||||
inputRoot: { className: 'font-mono' },
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-sm text-center">
|
||||
<Text variant="subtitle2">
|
||||
Already using a third party auth service? <br />
|
||||
<button
|
||||
type="button"
|
||||
className="mt-0.5 ml-0.5 text-xs font-medium text-blue"
|
||||
onClick={() => {
|
||||
editJWTSecret();
|
||||
}}
|
||||
>
|
||||
Add your custom JWT token
|
||||
</button>
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function JWTSecretModal({
|
||||
close,
|
||||
data,
|
||||
jwtSecret,
|
||||
initialModalState,
|
||||
}: any) {
|
||||
const [jwtSecretModalState, setJwtSecretModalState] =
|
||||
useState<JWTSecretModalState>(initialModalState || 'SHOW');
|
||||
|
||||
const editJWTSecret = () => {
|
||||
setJwtSecretModalState('EDIT');
|
||||
};
|
||||
|
||||
if (jwtSecretModalState === 'EDIT') {
|
||||
return <EditJWTSecretModal close={close} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ShowJWTTokenModal
|
||||
JWTKey={jwtSecret || data}
|
||||
editJWTSecret={editJWTSecret}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Avatar } from '@/ui/Avatar';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { nhost } from '@/utils/nhost';
|
||||
import Image from 'next/image';
|
||||
|
||||
export function SelectedWorkspaceOnNewApp({ current }: any) {
|
||||
const user = nhost.auth.getUser();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row space-x-2 self-center">
|
||||
{current.name === 'Default Workspace' ? (
|
||||
<Avatar
|
||||
className="h-5 w-5 self-center rounded-full"
|
||||
name={user?.displayName}
|
||||
avatarUrl={user?.avatarUrl}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-5 w-5 overflow-hidden rounded-md">
|
||||
<Image src="/logos/new.svg" alt="Nhost Logo" width={20} height={20} />
|
||||
</div>
|
||||
)}
|
||||
<Text size="small" color="greyscaleDark" className="font-normal">
|
||||
{current.name}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default SelectedWorkspaceOnNewApp;
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './AppDeployments';
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface ErrorComponentProps {
|
||||
message: string;
|
||||
}
|
||||
|
||||
function ErrorComponent({ message }: ErrorComponentProps) {
|
||||
return (
|
||||
<div className="my-4 rounded-md bg-warning px-4 py-2 text-dark">
|
||||
<Text className="font-medium text-textOrange">Error</Text>
|
||||
<Text className="pt-2 font-medium text-dimBlack" size="normal">
|
||||
{message}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default ErrorComponent;
|
||||
@@ -1,22 +0,0 @@
|
||||
function Copy({ stroke = '#21324B', ...props }: any) {
|
||||
return (
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M10.5 10.5h3v-8h-8v3"
|
||||
stroke={stroke}
|
||||
strokeWidth={1.25}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.5 5.5h-8v8h8v-8z"
|
||||
stroke={stroke}
|
||||
strokeWidth={1.25}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default Copy;
|
||||
@@ -1,29 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
function Eye(props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default Eye;
|
||||
@@ -1,25 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
function EyeOff(
|
||||
props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>,
|
||||
) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default EyeOff;
|
||||
@@ -1,14 +0,0 @@
|
||||
export default function Lock(
|
||||
props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>,
|
||||
) {
|
||||
return (
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M8 1.75a1.5 1.5 0 0 0-1.5 1.5v1.5h3v-1.5A1.5 1.5 0 0 0 8 1.75Zm-3 1.5v1.5H3c-.69 0-1.25.56-1.25 1.25v7c0 .69.56 1.25 1.25 1.25h10c.69 0 1.25-.56 1.25-1.25V6c0-.69-.56-1.25-1.25-1.25h-2v-1.5a3 3 0 0 0-6 0Zm-1.75 3h9.5v6.5h-9.5v-6.5ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
|
||||
fill="#21324B"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
function Plus(props: any) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 32 32"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M16 4.75C9.787 4.75 4.75 9.787 4.75 16S9.787 27.25 16 27.25 27.25 22.213 27.25 16 22.213 4.75 16 4.75zM3.25 16C3.25 8.958 8.958 3.25 16 3.25S28.75 8.958 28.75 16 23.042 28.75 16 28.75 3.25 23.042 3.25 16zm12 .75H10v-1.5h5.25V10h1.5v5.25H22v1.5h-5.25V22h-1.5v-5.25z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default Plus;
|
||||
@@ -1,20 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
function TwilioIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width={108}
|
||||
height={32}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M16.864 0c8.844 0 15.984 7.147 15.984 16s-7.14 16-15.984 16C8.019 32 .88 24.853.88 16S8.02 0 16.864 0Zm0 4.267c-6.5 0-11.722 5.226-11.722 11.733a11.694 11.694 0 0 0 11.722 11.733c6.5 0 11.721-5.226 11.721-11.733A11.694 11.694 0 0 0 16.864 4.267Zm27.173 2.026c.213-.106.426.107.426.214v4.586h8.525c.106 0 .32.214.32.32l.639 2.667.639 2.667.107.32.106-.32 1.599-5.334c0-.213.213-.32.32-.32h4.262c.106 0 .32.214.32.32l1.704 5.654.107-.32 1.385-5.334c0-.213.213-.32.32-.32h10.869c.106 0 .213.214.213.32V25.6c0 .213-.213.32-.32.32h-5.434c-.213 0-.32-.213-.32-.32V12.16l-4.05 13.44c0 .187-.162.292-.275.315l-.044.005H60.98c-.107 0-.32-.213-.32-.32l-.853-2.88-.959-3.093L56.93 25.6c0 .213-.213.32-.32.32h-4.475c-.106 0-.32-.213-.32-.32l-4.049-13.44v3.413c0 .214-.213.32-.32.32h-3.09v3.627c0 1.067.533 1.493 1.492 1.493.426 0 .96-.106 1.492-.32.106-.106.32 0 .32.214v4.266c-.96.534-2.345.854-3.836.854-3.517 0-5.435-1.6-5.435-5.12v-5.014h-1.385c-.213 0-.32-.213-.32-.32V11.52c0-.213.213-.32.32-.32h1.385V8.32c0-.213.106-.32.32-.32l5.328-1.707Zm54.984 4.48c4.689 0 8.099 3.52 8.099 7.574 0 4.053-3.41 7.573-8.205 7.573-4.689 0-8.099-3.52-8.099-7.573 0-4.054 3.41-7.574 8.205-7.574Zm-16.197-4.48c.213 0 .32.107.32.214v18.986c0 .214-.213.32-.32.32H77.39c-.213 0-.32-.213-.32-.32V6.613c0-.213.213-.32.32-.32h5.434Zm7.14 4.8c.213 0 .32.214.32.32v13.974c0 .213-.214.32-.32.32h-5.435c-.213 0-.32-.214-.32-.32V11.413c0-.213.214-.32.32-.32h5.435ZM12.92 16.64a3.322 3.322 0 0 1 3.303 3.307 3.322 3.322 0 0 1-3.303 3.306 3.322 3.322 0 0 1-3.303-3.306 3.322 3.322 0 0 1 3.303-3.307Zm7.886 0a3.322 3.322 0 0 1 3.303 3.307 3.322 3.322 0 0 1-3.303 3.306 3.322 3.322 0 0 1-3.304-3.306 3.322 3.322 0 0 1 3.304-3.307Zm78.214-.747c-1.385 0-2.344 1.174-2.344 2.56 0 1.387 1.066 2.56 2.344 2.56 1.386 0 2.345-1.173 2.345-2.56 0-1.493-1.066-2.666-2.345-2.56ZM20.807 8.747a3.322 3.322 0 0 1 3.303 3.306 3.322 3.322 0 0 1-3.303 3.307 3.322 3.322 0 0 1-3.304-3.307 3.322 3.322 0 0 1 3.304-3.306Zm-7.886 0a3.322 3.322 0 0 1 3.303 3.306 3.322 3.322 0 0 1-3.303 3.307 3.322 3.322 0 0 1-3.303-3.307 3.322 3.322 0 0 1 3.303-3.306Zm62.87-2.454c.107 0 .213.107.32.214V9.92c0 .213-.213.32-.32.32h-5.647c-.213 0-.32-.213-.32-.32V6.613c0-.213.213-.32.32-.32h5.647Zm14.28 0c.213 0 .319.107.319.214V9.92c0 .213-.213.32-.32.32h-5.647c-.213 0-.32-.213-.32-.32V6.613c0-.213.213-.32.32-.32h5.647Z"
|
||||
fill="#F22F46"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default TwilioIcon;
|
||||
@@ -11,7 +11,7 @@ import Input from '@/ui/v2/Input';
|
||||
import InputAdornment from '@/ui/v2/InputAdornment';
|
||||
import { copy } from '@/utils/copy';
|
||||
import { discordAnnounce } from '@/utils/discordAnnounce';
|
||||
import generateRandomDatabasePassword from '@/utils/settings/generateRandomDatabasePassword/generateRandomDatabasePassword';
|
||||
import generateRandomDatabasePassword from '@/utils/settings/generateRandomDatabasePassword';
|
||||
import { resetDatabasePasswordValidationSchema } from '@/utils/settings/resetDatabasePasswordValidationSchema';
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
|
||||
@@ -48,6 +48,7 @@ function SettingsNavLink({
|
||||
return (
|
||||
<ListItem.Root>
|
||||
<ListItem.Button
|
||||
dense
|
||||
href={finalUrl}
|
||||
component={NavLink}
|
||||
selected={active}
|
||||
|
||||
@@ -130,7 +130,6 @@ export default function BaseEnvironmentVariableForm({
|
||||
}
|
||||
},
|
||||
})}
|
||||
inputProps={{ maxLength: 100 }}
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="EXAMPLE_NAME"
|
||||
@@ -145,7 +144,6 @@ export default function BaseEnvironmentVariableForm({
|
||||
|
||||
<Input
|
||||
{...register('prodValue')}
|
||||
inputProps={{ maxLength: 100 }}
|
||||
id="prodValue"
|
||||
label="Production Value"
|
||||
placeholder="Enter value"
|
||||
@@ -159,7 +157,6 @@ export default function BaseEnvironmentVariableForm({
|
||||
|
||||
<Input
|
||||
{...register('devValue')}
|
||||
inputProps={{ maxLength: 100 }}
|
||||
id="devValue"
|
||||
label="Development Value"
|
||||
placeholder="Enter value"
|
||||
|
||||
@@ -125,7 +125,10 @@ export default function EnvironmentVariableSettings() {
|
||||
docsLink="https://docs.nhost.io/platform/environment-variables"
|
||||
docsTitle="Environment Variables"
|
||||
rootClassName="gap-0"
|
||||
className="px-0 my-2"
|
||||
className={twMerge(
|
||||
'px-0 my-2',
|
||||
availableEnvironmentVariables.length === 0 && 'gap-2',
|
||||
)}
|
||||
slotProps={{ submitButton: { className: 'hidden' } }}
|
||||
>
|
||||
<div className="grid grid-cols-2 lg:grid-cols-3 gap-2 border-b-1 border-gray-200 px-4 py-3">
|
||||
@@ -134,91 +137,98 @@ export default function EnvironmentVariableSettings() {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-flow-row gap-2">
|
||||
<List>
|
||||
{availableEnvironmentVariables.map((environmentVariable, index) => {
|
||||
const timestamp = formatDistanceToNowStrict(
|
||||
parseISO(environmentVariable.updatedAt),
|
||||
{ addSuffix: true, roundingMethod: 'floor' },
|
||||
);
|
||||
{availableEnvironmentVariables.length > 0 && (
|
||||
<List>
|
||||
{availableEnvironmentVariables.map((environmentVariable, index) => {
|
||||
const timestamp = formatDistanceToNowStrict(
|
||||
parseISO(environmentVariable.updatedAt),
|
||||
{ addSuffix: true, roundingMethod: 'floor' },
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment key={environmentVariable.id}>
|
||||
<ListItem.Root
|
||||
className="px-4 grid grid-cols-2 lg:grid-cols-3 gap-2"
|
||||
secondaryAction={
|
||||
<Dropdown.Root>
|
||||
<Dropdown.Trigger
|
||||
asChild
|
||||
hideChevron
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<IconButton variant="borderless" color="secondary">
|
||||
<DotsVerticalIcon />
|
||||
</IconButton>
|
||||
</Dropdown.Trigger>
|
||||
|
||||
<Dropdown.Content
|
||||
menu
|
||||
PaperProps={{ className: 'w-32' }}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<Dropdown.Item
|
||||
onClick={() => handleOpenEditor(environmentVariable)}
|
||||
return (
|
||||
<Fragment key={environmentVariable.id}>
|
||||
<ListItem.Root
|
||||
className="px-4 grid grid-cols-2 lg:grid-cols-3 gap-2"
|
||||
secondaryAction={
|
||||
<Dropdown.Root>
|
||||
<Dropdown.Trigger
|
||||
asChild
|
||||
hideChevron
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<Text className="font-medium">Edit</Text>
|
||||
</Dropdown.Item>
|
||||
<IconButton variant="borderless" color="secondary">
|
||||
<DotsVerticalIcon />
|
||||
</IconButton>
|
||||
</Dropdown.Trigger>
|
||||
|
||||
<Divider component="li" />
|
||||
|
||||
<Dropdown.Item
|
||||
onClick={() =>
|
||||
handleConfirmDelete(environmentVariable)
|
||||
}
|
||||
<Dropdown.Content
|
||||
menu
|
||||
PaperProps={{ className: 'w-32' }}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
className="font-medium"
|
||||
sx={{
|
||||
color: (theme) => theme.palette.error.main,
|
||||
}}
|
||||
<Dropdown.Item
|
||||
onClick={() =>
|
||||
handleOpenEditor(environmentVariable)
|
||||
}
|
||||
>
|
||||
Delete
|
||||
</Text>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
}
|
||||
>
|
||||
<ListItem.Text className="truncate">
|
||||
{environmentVariable.name}
|
||||
</ListItem.Text>
|
||||
<Text className="font-medium">Edit</Text>
|
||||
</Dropdown.Item>
|
||||
|
||||
<Text variant="subtitle1" className="lg:col-span-2 truncate">
|
||||
{timestamp === '0 seconds ago' ||
|
||||
timestamp === 'in 0 seconds'
|
||||
? 'Now'
|
||||
: timestamp}
|
||||
</Text>
|
||||
</ListItem.Root>
|
||||
<Divider component="li" />
|
||||
|
||||
<Divider
|
||||
component="li"
|
||||
className={twMerge(
|
||||
index === availableEnvironmentVariables.length - 1
|
||||
? '!mt-4'
|
||||
: '!my-4',
|
||||
)}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
<Dropdown.Item
|
||||
onClick={() =>
|
||||
handleConfirmDelete(environmentVariable)
|
||||
}
|
||||
>
|
||||
<Text
|
||||
className="font-medium"
|
||||
sx={{
|
||||
color: (theme) => theme.palette.error.main,
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Text>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
}
|
||||
>
|
||||
<ListItem.Text className="truncate">
|
||||
{environmentVariable.name}
|
||||
</ListItem.Text>
|
||||
|
||||
<Text
|
||||
variant="subtitle1"
|
||||
className="lg:col-span-2 truncate"
|
||||
>
|
||||
{timestamp === '0 seconds ago' ||
|
||||
timestamp === 'in 0 seconds'
|
||||
? 'Now'
|
||||
: timestamp}
|
||||
</Text>
|
||||
</ListItem.Root>
|
||||
|
||||
<Divider
|
||||
component="li"
|
||||
className={twMerge(
|
||||
index === availableEnvironmentVariables.length - 1
|
||||
? '!mt-4'
|
||||
: '!my-4',
|
||||
)}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
|
||||
<Button
|
||||
className="justify-self-start mx-4"
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import clsx from 'clsx';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
|
||||
export interface DividerProps extends HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Determines the vertical margin of the divider.
|
||||
*
|
||||
* @default 'high'
|
||||
*/
|
||||
spacing?: 'low' | 'medium' | 'high';
|
||||
/**
|
||||
* Arbitrary classnames to be added to the divider.
|
||||
*
|
||||
*/
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Divider({ spacing = 'high', className }: DividerProps) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'order-3 mx-auto h-[0.25px] w-full self-stretch bg-verydark opacity-20',
|
||||
spacing === 'low' && 'my-12',
|
||||
spacing === 'medium' && 'my-16',
|
||||
spacing === 'high' && 'my-20',
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
import clsx from 'clsx';
|
||||
import type {
|
||||
DetailedHTMLProps,
|
||||
ForwardedRef,
|
||||
HTMLProps,
|
||||
ReactNode,
|
||||
} from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
export interface InputFieldProps
|
||||
extends DetailedHTMLProps<HTMLProps<HTMLInputElement>, HTMLInputElement> {
|
||||
/**
|
||||
* Props to be passed to the input wrapper.
|
||||
*/
|
||||
wrapperProps?: Omit<
|
||||
DetailedHTMLProps<HTMLProps<HTMLDivElement>, HTMLDivElement>,
|
||||
'children'
|
||||
>;
|
||||
/**
|
||||
* Props to be passed to the label element.
|
||||
*/
|
||||
labelProps?: Omit<
|
||||
DetailedHTMLProps<HTMLProps<HTMLLabelElement>, HTMLLabelElement>,
|
||||
'htmlFor' | 'children'
|
||||
>;
|
||||
/**
|
||||
* Input label.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* Start input adornment for this component.
|
||||
*/
|
||||
startAdornment?: ReactNode;
|
||||
/**
|
||||
* Error to be displayed.
|
||||
*/
|
||||
error?: ReactNode;
|
||||
}
|
||||
|
||||
const InlineInput = forwardRef(
|
||||
(
|
||||
{
|
||||
label,
|
||||
labelProps,
|
||||
startAdornment,
|
||||
wrapperProps,
|
||||
className,
|
||||
error,
|
||||
...props
|
||||
}: InputFieldProps,
|
||||
ref: ForwardedRef<HTMLInputElement>,
|
||||
) => {
|
||||
const { className: labelClassName, ...restLabelProps } = labelProps || {};
|
||||
const { className: wrapperClassName, ...restWrapperProps } =
|
||||
wrapperProps || {};
|
||||
|
||||
return (
|
||||
<div className="grid grid-flow-row gap-1">
|
||||
<div
|
||||
className={clsx(
|
||||
'grid grid-cols-5 items-center gap-y-1 py-1.5',
|
||||
wrapperClassName,
|
||||
)}
|
||||
{...restWrapperProps}
|
||||
>
|
||||
{label && (
|
||||
<label
|
||||
htmlFor={props.id}
|
||||
className={clsx(
|
||||
'col-span-2 text-sm+ font-medium text-greyscaleDark',
|
||||
labelClassName,
|
||||
)}
|
||||
{...restLabelProps}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'flex flex-row place-content-start items-center rounded-sm px-2 py-1',
|
||||
error
|
||||
? 'outline outline-2 outline-red'
|
||||
: 'focus-within:outline focus-within:outline-2 focus-within:outline-blue',
|
||||
label ? 'col-span-3' : 'col-span-5',
|
||||
)}
|
||||
>
|
||||
{startAdornment && (
|
||||
<label className="flex-shrink-0" htmlFor={props.id}>
|
||||
{startAdornment}
|
||||
</label>
|
||||
)}
|
||||
|
||||
<input
|
||||
className={clsx(
|
||||
'h-full w-full rounded-sm+ px-0.5 font-display text-sm+ text-greyscaleDark focus:outline-none',
|
||||
className,
|
||||
)}
|
||||
aria-invalid={Boolean(error)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div
|
||||
className="col-span-5 text-right text-xs text-red"
|
||||
role="alert"
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
InlineInput.displayName = 'NhostInlineInput';
|
||||
|
||||
export default InlineInput;
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './InlineInput';
|
||||
export { default } from './InlineInput';
|
||||
@@ -1,77 +0,0 @@
|
||||
import type { PrefetchNewAppPlansFragment } from '@/generated/graphql';
|
||||
import { Text } from '@/ui/Text';
|
||||
import Checkbox from '@/ui/v2/Checkbox';
|
||||
import { planDescriptions } from '@/utils/planDescriptions';
|
||||
import { RadioGroup } from '@headlessui/react';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
interface PlanSelectorProps {
|
||||
options: PrefetchNewAppPlansFragment[];
|
||||
value: PrefetchNewAppPlansFragment;
|
||||
onChange:
|
||||
| React.Dispatch<React.SetStateAction<PrefetchNewAppPlansFragment>>
|
||||
| any;
|
||||
}
|
||||
|
||||
export function PlanSelector({ options, value, onChange }: PlanSelectorProps) {
|
||||
return (
|
||||
<RadioGroup value={value} onChange={onChange}>
|
||||
<RadioGroup.Label className="sr-only">Pricing plans</RadioGroup.Label>
|
||||
<div className="relative divide-y-1 border-t-1 border-b-1 bg-white">
|
||||
{options.map((plan) => (
|
||||
<RadioGroup.Option key={plan.name} value={plan}>
|
||||
{({ checked }) => (
|
||||
<div className="cu flex cursor-pointer flex-row place-content-between items-center py-4 font-display">
|
||||
<RadioGroup.Label
|
||||
as="div"
|
||||
className="flex flex-row font-medium"
|
||||
>
|
||||
<Checkbox
|
||||
aria-describedby="plan"
|
||||
checked={plan.name === value.name}
|
||||
/>
|
||||
|
||||
<div className="flex w-80">
|
||||
<div className=" self-center pl-2 text-xs font-medium text-greyscaleDark">
|
||||
<span className="font-bold">{plan.name}:</span>{' '}
|
||||
<span className="leading-4">
|
||||
{planDescriptions[plan.name]}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroup.Label>
|
||||
<div className="flex">
|
||||
<span
|
||||
className={clsx(
|
||||
'self-center font-medium',
|
||||
checked ? 'text-indigo-900' : 'text-black',
|
||||
)}
|
||||
>
|
||||
<div className="mr-3 self-center text-lg text-greyscaleDark">
|
||||
{plan.isFree ? (
|
||||
'Free'
|
||||
) : (
|
||||
<div className="flex flex-row">
|
||||
$ {plan.price}{' '}
|
||||
<Text
|
||||
size="tiny"
|
||||
className="ml-1 self-center tracking-wide"
|
||||
>
|
||||
/ mo
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</RadioGroup.Option>
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlanSelector;
|
||||
@@ -1,49 +0,0 @@
|
||||
import type { ForwardedRef, PropsWithChildren } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
export interface TooltipProps extends PropsWithChildren<unknown> {
|
||||
/**
|
||||
* Title of the tooltip.
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Determine if the tooltip should be shown.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const Tooltip = forwardRef(
|
||||
(
|
||||
{ title, children, disabled }: TooltipProps,
|
||||
ref: ForwardedRef<HTMLDivElement>,
|
||||
) => (
|
||||
<div className="group relative" ref={ref}>
|
||||
{children}
|
||||
|
||||
{!disabled && (
|
||||
<div className="absolute left-2 bottom-1 z-50 hidden group-hover:block">
|
||||
<svg
|
||||
className="absolute -top-2 left-3 z-50 mr-3 h-3 w-3 -rotate-180 transform text-greyscaleDark"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 255 255"
|
||||
xmlSpace="preserve"
|
||||
>
|
||||
<polygon
|
||||
className="border border-greyscaleDark fill-current text-greyscaleDark"
|
||||
points="0,0 127.5,127.5 255,0"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<div className="text-sharp absolute left-0 z-50 mt-1 w-40 origin-top-left rounded-md bg-greyscaleDark p-2 font-display text-sm- font-medium text-white shadow-2xl">
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
Tooltip.displayName = 'NhostTooltip';
|
||||
|
||||
export default Tooltip;
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Tooltip';
|
||||
export { default } from './Tooltip';
|
||||
@@ -34,7 +34,7 @@ const StyledListItemButton = styled(MaterialListItemButton)(({ theme }) => ({
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(),
|
||||
[`&.${getListItemButtonUtilityClass('dense')}`]: {
|
||||
padding: theme.spacing(0.75, 1.25),
|
||||
padding: theme.spacing(1, 1.25),
|
||||
},
|
||||
[`&.${listItemButtonClasses.selected}`]: {
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Avatar } from '@/ui/Avatar';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { nhost } from '@/utils/nhost';
|
||||
import Image from 'next/image';
|
||||
|
||||
export function WorkspaceSelectorNewApp({ option }: any) {
|
||||
const user = nhost.auth.getUser();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center py-0.5">
|
||||
{option.name === 'Default Workspace' ? (
|
||||
<Avatar
|
||||
className="h-6 w-6 rounded-full"
|
||||
name={user?.displayName}
|
||||
avatarUrl={user?.avatarUrl}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-6 w-6 overflow-hidden rounded-md">
|
||||
<Image src="/logos/new.svg" alt="Nhost Logo" width={24} height={24} />
|
||||
</div>
|
||||
)}
|
||||
<Text className="ml-2 font-medium" color="greyscaleDark">
|
||||
{option.name}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WorkspaceSelectorNewApp;
|
||||
@@ -1,15 +0,0 @@
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export const useGetAppURL = (): {
|
||||
workspaceSlug: string;
|
||||
appSlug: string;
|
||||
} => {
|
||||
const router = useRouter();
|
||||
|
||||
const workspaceSlug = router.query.workspaceSlug as string;
|
||||
const appSlug = router.query.appSlug as string;
|
||||
|
||||
return { workspaceSlug, appSlug };
|
||||
};
|
||||
|
||||
export default useGetAppURL;
|
||||
@@ -1,5 +1,5 @@
|
||||
import Container from '@/components/layout/Container';
|
||||
import AllowedEmailDomainsSettings from '@/components/settings/authentication/AllowedEmailSettings/AllowedEmailSettings';
|
||||
import AllowedEmailDomainsSettings from '@/components/settings/authentication/AllowedEmailSettings';
|
||||
import AllowedRedirectURLsSettings from '@/components/settings/authentication/AllowedRedirectURLsSettings';
|
||||
import BlockedEmailSettings from '@/components/settings/authentication/BlockedEmailSettings';
|
||||
import ClientURLSettings from '@/components/settings/authentication/ClientURLSettings';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Container from '@/components/layout/Container';
|
||||
import PermissionVariableSettings from '@/components/settings/permissions/PermissionVariableSettings';
|
||||
import RolesSettings from '@/components/settings/roles/RoleSettings/RoleSettings';
|
||||
import RolesSettings from '@/components/settings/roles/RoleSettings';
|
||||
import SettingsLayout from '@/components/settings/SettingsLayout';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { getErrorMessage } from '@/utils/getErrorMessage';
|
||||
import { getCurrentEnvironment, slugifyString } from '@/utils/helpers';
|
||||
import { nhost } from '@/utils/nhost';
|
||||
import { planDescriptions } from '@/utils/planDescriptions';
|
||||
import generateRandomDatabasePassword from '@/utils/settings/generateRandomDatabasePassword/generateRandomDatabasePassword';
|
||||
import generateRandomDatabasePassword from '@/utils/settings/generateRandomDatabasePassword';
|
||||
import { resetDatabasePasswordValidationSchema } from '@/utils/settings/resetDatabasePasswordValidationSchema';
|
||||
|
||||
import { triggerToast } from '@/utils/toast';
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
export type Provider = {
|
||||
name: string;
|
||||
logo: string;
|
||||
active: boolean;
|
||||
docsLink: string;
|
||||
};
|
||||
|
||||
export type Providers = {
|
||||
providers: Provider[];
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
import { resolveProvider } from './resolveProvider';
|
||||
|
||||
export function getDynamicVariables(providerId, vars, prefill = false) {
|
||||
const authEnabled = `auth${resolveProvider(providerId as string)}Enabled`;
|
||||
const authClientId = `auth${resolveProvider(providerId as string)}ClientId`;
|
||||
const authClientSecret = `auth${resolveProvider(
|
||||
providerId as string,
|
||||
)}ClientSecret`;
|
||||
|
||||
// @TODO: check prefill, use only one function: there's another one with the same functionality
|
||||
// in providerId.tsx.
|
||||
if (providerId === 'twitter') {
|
||||
return {
|
||||
authEnabled: 'authTwitterEnabled',
|
||||
authClientId: 'authTwitterConsumerKey',
|
||||
authClientSecret: 'authTwitterConsumerSecret',
|
||||
};
|
||||
}
|
||||
|
||||
if (providerId === 'apple') {
|
||||
return {
|
||||
authEnabled: 'authAppleEnabled',
|
||||
authClientId: 'authAppleKeyId',
|
||||
authClientSecret: 'authApplePrivateKey',
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
authProviderEnabled,
|
||||
authProviderClientId,
|
||||
authProviderClientSecret,
|
||||
} = vars;
|
||||
|
||||
if (prefill) {
|
||||
return {
|
||||
authEnabled,
|
||||
authClientId,
|
||||
authClientSecret,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
[authEnabled]: authProviderEnabled,
|
||||
[authClientId]: authProviderClientId,
|
||||
[authClientSecret]: authProviderClientSecret,
|
||||
};
|
||||
}
|
||||
|
||||
export default getDynamicVariables;
|
||||
@@ -1,15 +0,0 @@
|
||||
import { capitalize } from './helpers';
|
||||
|
||||
export const resolveProvider = (providerId: string) => {
|
||||
if (providerId.toLowerCase() === 'microsoft') {
|
||||
return 'WindowsLive';
|
||||
}
|
||||
|
||||
if (providerId.toLowerCase() === 'workos') {
|
||||
return 'WorkOs';
|
||||
}
|
||||
|
||||
return capitalize(providerId);
|
||||
};
|
||||
|
||||
export default resolveProvider;
|
||||
@@ -1,23 +0,0 @@
|
||||
import validator from 'validator';
|
||||
|
||||
export const validateDomainsInput = (domainsFromInput: string) => {
|
||||
if (domainsFromInput.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const domains: string[] = [];
|
||||
let checkedDomains: boolean[] = [];
|
||||
|
||||
domainsFromInput.split(',').forEach((email) => {
|
||||
domains.push(email.replace(' ', ''));
|
||||
});
|
||||
checkedDomains = domains.map((domain) => {
|
||||
if (!validator.isFQDN(domain)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return checkedDomains.includes(false);
|
||||
};
|
||||
|
||||
export default validateDomainsInput;
|
||||
@@ -1,23 +0,0 @@
|
||||
import validator from 'validator';
|
||||
|
||||
export const validateEmailInputs = (emailsFromInput: string) => {
|
||||
if (emailsFromInput.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const emails: string[] = [];
|
||||
let checkEmails: boolean[] = [];
|
||||
|
||||
emailsFromInput.split(',').forEach((email) => {
|
||||
emails.push(email.replace(' ', ''));
|
||||
});
|
||||
checkEmails = emails.map((email) => {
|
||||
if (!validator.isEmail(email)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return checkEmails.includes(false);
|
||||
};
|
||||
|
||||
export default validateEmailInputs;
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/docs
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1b8f5050: fix(deps): update docusaurus monorepo to v2.2.0
|
||||
|
||||
## 0.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/docs",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
@@ -16,9 +16,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@algolia/client-search": "^4.9.1",
|
||||
"@docusaurus/core": "2.1.0",
|
||||
"@docusaurus/plugin-sitemap": "2.1.0",
|
||||
"@docusaurus/preset-classic": "2.1.0",
|
||||
"@docusaurus/core": "2.2.0",
|
||||
"@docusaurus/plugin-sitemap": "2.2.0",
|
||||
"@docusaurus/preset-classic": "2.2.0",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"docusaurus-plugin-image-zoom": "^0.1.1",
|
||||
@@ -30,7 +30,7 @@
|
||||
"unist-util-visit": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.1.0",
|
||||
"@docusaurus/module-type-aliases": "2.2.0",
|
||||
"@tsconfig/docusaurus": "^1.0.6",
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @nhost-examples/codegen-react-apollo
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react versions
|
||||
- Updated dependencies [132a4f4b]
|
||||
- @nhost/react@0.15.2
|
||||
- @nhost/react-apollo@4.9.2
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-apollo",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.6.9",
|
||||
@@ -39,8 +39,8 @@
|
||||
"@graphql-codegen/typescript-operations": "^2.5.3",
|
||||
"@graphql-codegen/typescript-react-apollo": "^3.3.3",
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"typescript": "^4.8.2",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost-examples/codegen-react-query
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react versions
|
||||
- Updated dependencies [132a4f4b]
|
||||
- @nhost/react@0.15.2
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/codegen-react-query",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@nhost/react": "*",
|
||||
@@ -39,8 +39,8 @@
|
||||
"@graphql-codegen/typescript-operations": "^2.5.3",
|
||||
"@graphql-codegen/typescript-react-query": "^4.0.1",
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"eslint": "^8.23.0",
|
||||
"typescript": "^4.8.2",
|
||||
"vite": "^3.2.2"
|
||||
|
||||
11
examples/docker-compose/CHANGELOG.md
Normal file
11
examples/docker-compose/CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# @nhost-examples/docker-compose
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 26974146: Improve QA
|
||||
|
||||
- Add end-to-end tests
|
||||
- Use the default health checks from the services
|
||||
- Do not use `latest` tags so we are sure the docker-compose example works for the given versions of the services
|
||||
@@ -42,7 +42,7 @@ services:
|
||||
- "traefik.http.routers.hasura.rule=Host(`localhost`) && PathPrefix(`/`)"
|
||||
- "traefik.http.routers.hasura.entrypoints=web"
|
||||
auth:
|
||||
image: nhost/hasura-auth:latest
|
||||
image: nhost/hasura-auth:0.16.2
|
||||
depends_on:
|
||||
- postgres
|
||||
- graphql-engine
|
||||
@@ -63,8 +63,6 @@ services:
|
||||
AUTH_SMTP_SENDER: mail@example.com
|
||||
expose:
|
||||
- 4000
|
||||
healthcheck:
|
||||
disable: true
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.middlewares.strip-auth.stripprefix.prefixes=/v1/auth"
|
||||
@@ -80,8 +78,6 @@ services:
|
||||
restart: always
|
||||
expose:
|
||||
- 8000
|
||||
healthcheck:
|
||||
disable: true
|
||||
environment:
|
||||
PUBLIC_URL: http://localhost:${PROXY_PORT:-1337}
|
||||
HASURA_METADATA: 1
|
||||
@@ -103,7 +99,7 @@ services:
|
||||
- "traefik.http.routers.storage.middlewares=strip-suffix@docker"
|
||||
command: serve
|
||||
functions:
|
||||
image: nhost/functions:latest
|
||||
image: nhost/functions:0.1.8
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.middlewares.strip-functions.stripprefix.prefixes=/v1/functions"
|
||||
@@ -113,8 +109,6 @@ services:
|
||||
restart: always
|
||||
expose:
|
||||
- 3000
|
||||
healthcheck:
|
||||
disable: true
|
||||
volumes:
|
||||
- .:/opt/project
|
||||
- functions_node_modules:/opt/project/node_modules
|
||||
@@ -146,7 +140,7 @@ services:
|
||||
volumes:
|
||||
- ./data/mailhog:/maildir
|
||||
dashboard:
|
||||
image: nhost/dashboard:latest
|
||||
image: nhost/dashboard:0.7.4
|
||||
ports:
|
||||
- "3030:3000"
|
||||
volumes:
|
||||
|
||||
11
examples/docker-compose/package.json
Normal file
11
examples/docker-compose/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@nhost-examples/docker-compose",
|
||||
"version": "0.0.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"e2e": "vitest run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-fetch": "^3.1.5"
|
||||
}
|
||||
}
|
||||
72
examples/docker-compose/test/docker-compose.test.ts
Normal file
72
examples/docker-compose/test/docker-compose.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { exec } from 'child_process'
|
||||
import fetch from 'cross-fetch'
|
||||
import { promisify } from 'util'
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||
|
||||
const promisifiedExec = promisify(exec)
|
||||
|
||||
describe(
|
||||
'docker-compose should start, work and stop',
|
||||
() => {
|
||||
beforeAll(async () => {
|
||||
// * Start docker compose
|
||||
await promisifiedExec(
|
||||
'docker compose -f docker-compose.yaml --env-file .env.example up --wait --quiet-pull'
|
||||
)
|
||||
}, 5 * 60 * 1000)
|
||||
|
||||
afterAll(async () => {
|
||||
// * Stop docker compose
|
||||
await promisifiedExec('docker compose -f docker-compose.yaml --env-file .env.example down')
|
||||
}, 5 * 60 * 1000)
|
||||
|
||||
it(
|
||||
'Hasura',
|
||||
async () => {
|
||||
await expect(
|
||||
fetch('http://localhost:1337/healthz').then((res) => res.status)
|
||||
).resolves.toEqual(200)
|
||||
},
|
||||
{ retry: 10, timeout: 1000 }
|
||||
)
|
||||
|
||||
it(
|
||||
'Hasura auth',
|
||||
async () => {
|
||||
await expect(
|
||||
fetch('http://localhost:1337/v1/auth/healthz').then((res) => res.status)
|
||||
).resolves.toEqual(200)
|
||||
},
|
||||
{ retry: 10, timeout: 1000 }
|
||||
)
|
||||
|
||||
it(
|
||||
'Hasura storage',
|
||||
async () => {
|
||||
await expect(
|
||||
fetch('http://localhost:1337/v1/storage/version').then((res) => res.status)
|
||||
).resolves.toEqual(200)
|
||||
},
|
||||
{ retry: 10, timeout: 1000 }
|
||||
)
|
||||
|
||||
it(
|
||||
'Serverless functions',
|
||||
async () => {
|
||||
await expect(
|
||||
fetch('http://localhost:1337/v1/functions/hello').then((res) => res.status)
|
||||
).resolves.toEqual(200)
|
||||
},
|
||||
{ retry: 10, timeout: 1000 }
|
||||
)
|
||||
|
||||
it(
|
||||
'Dashboard',
|
||||
async () => {
|
||||
await expect(fetch('http://localhost:3030').then((res) => res.status)).resolves.toEqual(200)
|
||||
},
|
||||
{ retry: 10, timeout: 1000 }
|
||||
)
|
||||
},
|
||||
{ timeout: 5 * 60 * 1000 }
|
||||
)
|
||||
@@ -1,5 +1,15 @@
|
||||
# @nhost-examples/nextjs
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react versions
|
||||
- Updated dependencies [132a4f4b]
|
||||
- @nhost/react@0.15.2
|
||||
- @nhost/react-apollo@4.9.2
|
||||
- @nhost/nextjs@1.9.3
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/nextjs",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -32,7 +32,7 @@
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^12.2.5",
|
||||
"@types/node": "17.0.23",
|
||||
"@types/react": "18.0.8",
|
||||
"@types/react": "18.0.25",
|
||||
"@xstate/inspect": "^0.6.2",
|
||||
"eslint-config-next": "12.0.10",
|
||||
"typescript": "4.5.5",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @nhost-examples/react-apollo
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react versions
|
||||
- Updated dependencies [132a4f4b]
|
||||
- @nhost/react@0.15.2
|
||||
- @nhost/react-apollo@4.9.2
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-apollo",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.6.9",
|
||||
@@ -23,8 +23,8 @@
|
||||
"dev": "vite --host localhost --port 3000",
|
||||
"generate": "graphql-codegen --config graphql.config.yaml",
|
||||
"cypress": "cypress open",
|
||||
"test": "cypress run",
|
||||
"e2e": "start-test e2e:backend http-get://localhost:9695 e2e:frontend 3000 test",
|
||||
"e2e": "start-test e2e:backend http-get://localhost:9695 e2e:frontend 3000 e2e:test",
|
||||
"e2e:test": "cypress run",
|
||||
"e2e:backend": "nhost dev --no-browser",
|
||||
"e2e:frontend": "run-s build preview",
|
||||
"build": "vite build",
|
||||
@@ -53,8 +53,8 @@
|
||||
"@graphql-codegen/cli": "^2.12.0",
|
||||
"@nuintun/qrcode": "^3.3.0",
|
||||
"@testing-library/cypress": "^8.0.3",
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/totp-generator": "^0.0.4",
|
||||
"@vitejs/plugin-react": "^1.3.2",
|
||||
"@xstate/inspect": "^0.6.2",
|
||||
@@ -67,4 +67,4 @@
|
||||
"ws": "^8.8.1",
|
||||
"xstate": "^4.33.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
examples/react-gqty/CHANGELOG.md
Normal file
9
examples/react-gqty/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# @nhost-examples/react-gqty
|
||||
|
||||
## 0.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react versions
|
||||
- Updated dependencies [132a4f4b]
|
||||
- @nhost/react@0.15.2
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/react-gqty",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -20,8 +20,8 @@
|
||||
"devDependencies": {
|
||||
"@gqty/cli": "3.3.0-alpha-d8cdbf6.0",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@vitejs/plugin-react": "^2.1.0",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"postcss": "^8.4.18",
|
||||
|
||||
5
examples/react-gqty/src/react-app-env.d.ts
vendored
Normal file
5
examples/react-gqty/src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
declare module 'react/jsx-runtime' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost-examples/vue-apollo
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b030eae9: chore(deps): update dependency @xstate/inspect to ^0.7.0
|
||||
- Updated dependencies [b030eae9]
|
||||
- @nhost/vue@0.6.2
|
||||
|
||||
## 0.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@nhost-examples/vue-apollo",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
curly: 'off',
|
||||
'quote-props': 'off',
|
||||
'vue/html-self-closing': 'off',
|
||||
'vue/singleline-html-element-content-newline': 'off'
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
'eol-last': 'off'
|
||||
}
|
||||
}
|
||||
|
||||
9
examples/vue-quickstart/CHANGELOG.md
Normal file
9
examples/vue-quickstart/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# @nhost-examples/vue-quickstart
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b030eae9: chore(deps): update dependency @xstate/inspect to ^0.7.0
|
||||
- Updated dependencies [b030eae9]
|
||||
- @nhost/vue@0.6.2
|
||||
7
examples/vue-quickstart/auto-imports.d.ts
vendored
7
examples/vue-quickstart/auto-imports.d.ts
vendored
@@ -1,13 +1,6 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
// We suggest you to commit this file into source control
|
||||
declare global {
|
||||
const $: typeof import('vue/macros')['$']
|
||||
const $$: typeof import('vue/macros')['$$']
|
||||
const $computed: typeof import('vue/macros')['$computed']
|
||||
const $customRef: typeof import('vue/macros')['$customRef']
|
||||
const $ref: typeof import('vue/macros')['$ref']
|
||||
const $shallowRef: typeof import('vue/macros')['$shallowRef']
|
||||
const $toRef: typeof import('vue/macros')['$toRef']
|
||||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@nhost-examples/vue-quickstart",
|
||||
"version": "0.0.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
@@ -39,4 +40,4 @@
|
||||
"vitest": "^0.12.4",
|
||||
"vue-tsc": "^0.38.9"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`Counter.vue > should render 1`] = `"<div>10 <button class=\\"inc\\"> + </button><button class=\\"dec\\"> - </button></div>"`;
|
||||
@@ -1,22 +0,0 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import Counter from '../src/components/Counter.vue'
|
||||
|
||||
describe('Counter.vue', () => {
|
||||
it('should render', () => {
|
||||
const wrapper = mount(Counter, { props: { initial: 10 } })
|
||||
expect(wrapper.text()).toContain('10')
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should be interactive', async () => {
|
||||
const wrapper = mount(Counter, { props: { initial: 0 } })
|
||||
expect(wrapper.text()).toContain('0')
|
||||
|
||||
expect(wrapper.find('.inc').exists()).toBe(true)
|
||||
|
||||
await wrapper.get('button').trigger('click')
|
||||
|
||||
expect(wrapper.text()).toContain('1')
|
||||
})
|
||||
})
|
||||
@@ -16,7 +16,7 @@ export default defineConfig({
|
||||
},
|
||||
plugins: [
|
||||
Vue({
|
||||
reactivityTransform: true
|
||||
reactivityTransform: path.resolve(__dirname, 'src'),
|
||||
}),
|
||||
|
||||
// https://github.com/hannoeru/vite-plugin-pages
|
||||
@@ -24,7 +24,7 @@ export default defineConfig({
|
||||
|
||||
// https://github.com/antfu/unplugin-auto-import
|
||||
AutoImport({
|
||||
imports: ['vue/macros', 'vue-router', '@vueuse/core'],
|
||||
imports: ['vue-router', '@vueuse/core'],
|
||||
dts: true
|
||||
}),
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"release": "pnpm run prerelease && changeset publish",
|
||||
"snapshot": "pnpm prerelease && changeset version --snapshot preview && pnpm install && changeset publish --tag preview",
|
||||
"test": "turbo run test --filter=!@nhost/dashboard --filter=!@nhost/docs --filter=!@nhost-examples/* --no-deps --include-dependencies",
|
||||
"test:all": "turbo run test",
|
||||
"test:dashboard": "turbo run test --filter=@nhost/dashboard",
|
||||
"e2e": "turbo run e2e --concurrency=1",
|
||||
"changeset": "changeset",
|
||||
@@ -70,7 +71,7 @@
|
||||
"eslint-plugin-react": "^7.31.10",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-vue": "^8.7.1",
|
||||
"eslint-plugin-vue": "^9.0.0",
|
||||
"husky": "^8.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.7.1",
|
||||
@@ -80,7 +81,7 @@
|
||||
"vite": "^3.2.2",
|
||||
"vite-plugin-dts": "^1.6.6",
|
||||
"vite-tsconfig-paths": "^3.5.2",
|
||||
"vitest": "^0.24.5"
|
||||
"vitest": "^0.25.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"graphql": "15.7.2"
|
||||
|
||||
@@ -66,6 +66,6 @@
|
||||
"devDependencies": {
|
||||
"@simplewebauthn/typescript-types": "^6.0.0",
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"msw": "^0.47.4"
|
||||
"msw": "^0.49.0"
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,6 @@
|
||||
"devDependencies": {
|
||||
"@nhost/core": "workspace:*",
|
||||
"@nhost/docgen": "workspace:*",
|
||||
"@types/faker": "5",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"mailhog": "^4.16.0",
|
||||
"start-server-and-test": "^1.14.0"
|
||||
|
||||
@@ -71,6 +71,6 @@
|
||||
"@nhost/docgen": "workspace:*",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"uuid": "^8.3.2"
|
||||
"uuid": "^9.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,18 @@
|
||||
# @nhost/nextjs
|
||||
|
||||
## 1.9.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [132a4f4b]
|
||||
- @nhost/react@0.15.2
|
||||
|
||||
## 1.9.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 82124329: chore(deps): update dependency next to v12.3.4
|
||||
|
||||
## 1.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nextjs",
|
||||
"version": "1.9.1",
|
||||
"version": "1.9.3",
|
||||
"description": "Nhost NextJS library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -79,7 +79,7 @@
|
||||
"@nhost/docgen": "workspace:*",
|
||||
"@nhost/react": "workspace:*",
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"next": "12.0.10",
|
||||
"next": "12.3.4",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @nhost/react-apollo
|
||||
|
||||
## 4.9.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react versions
|
||||
- Updated dependencies [132a4f4b]
|
||||
- @nhost/react@0.15.2
|
||||
|
||||
## 4.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-apollo",
|
||||
"version": "4.9.1",
|
||||
"version": "4.9.2",
|
||||
"description": "Nhost React Apollo client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -72,7 +72,7 @@
|
||||
"devDependencies": {
|
||||
"@apollo/client": "^3.7.1",
|
||||
"@nhost/react": "workspace:*",
|
||||
"@types/react": "^18.0.24",
|
||||
"@types/react": "^18.0.25",
|
||||
"graphql": "15.7.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/react
|
||||
|
||||
## 0.15.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 132a4f4b: chore(deps): synchronize @types/react-dom and @types/react versions
|
||||
|
||||
## 0.15.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react",
|
||||
"version": "0.15.1",
|
||||
"version": "0.15.2",
|
||||
"description": "Nhost React library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -71,7 +71,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nhost/docgen": "workspace:*",
|
||||
"@types/react": "^18.0.24",
|
||||
"@types/react": "^18.0.25",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/sync-versions
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3a76080e: chore(deps): update dependency @types/glob to v8
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@nhost/sync-versions",
|
||||
"description": "Sync the versions of Nhost services in each of the packages of a pnpm workspace",
|
||||
"private": true,
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -27,7 +27,7 @@
|
||||
"devDependencies": {
|
||||
"@swc-node/register": "^1.5.4",
|
||||
"@swc/core": "^1.3.11",
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/object-path": "^0.11.1",
|
||||
"typescript": "^4.8.4"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/vue
|
||||
|
||||
## 0.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b030eae9: chore(deps): update dependency @xstate/inspect to ^0.7.0
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/vue",
|
||||
"version": "0.6.1",
|
||||
"version": "0.6.2",
|
||||
"description": "Nhost Vue library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -76,7 +76,7 @@
|
||||
"@nhost/docgen": "workspace:*",
|
||||
"@nhost/hasura-auth-js": "workspace:*",
|
||||
"@vitejs/plugin-vue": "^3.2.0",
|
||||
"@xstate/inspect": "^0.6.2",
|
||||
"@xstate/inspect": "^0.7.0",
|
||||
"vue": "^3.2.41",
|
||||
"vue-router": "^4.1.6",
|
||||
"ws": "^8.10.0",
|
||||
|
||||
3666
pnpm-lock.yaml
generated
3666
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user