Compare commits
56 Commits
@nhost/rea
...
@nhost/rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b6a4adf40 | ||
|
|
6cc8f954e1 | ||
|
|
1821df7a96 | ||
|
|
ab8a55ede4 | ||
|
|
39eb70678b | ||
|
|
e3cd5f858f | ||
|
|
69d9ab60c8 | ||
|
|
a8961c0ab0 | ||
|
|
6b8163d21f | ||
|
|
a21553c774 | ||
|
|
2dd4df5170 | ||
|
|
403a45d2cf | ||
|
|
05f063b8e2 | ||
|
|
09f5bed1e8 | ||
|
|
91d5fbba42 | ||
|
|
498363db25 | ||
|
|
8c2779930b | ||
|
|
0aa27a2fd1 | ||
|
|
8656749e5a | ||
|
|
d097eb8feb | ||
|
|
dd04c3df43 | ||
|
|
70b2d5a3ec | ||
|
|
3b12dc425e | ||
|
|
e3146a30af | ||
|
|
eb7f09e485 | ||
|
|
9084222a1c | ||
|
|
4815c7c580 | ||
|
|
3c3fa8953a | ||
|
|
cb632337f9 | ||
|
|
d8747139ab | ||
|
|
aecbec643b | ||
|
|
196cd38018 | ||
|
|
fd5991845b | ||
|
|
2e3357b7a3 | ||
|
|
4385524311 | ||
|
|
9e404c8fc9 | ||
|
|
f8e6b615dd | ||
|
|
ac4aa01ec9 | ||
|
|
e515e71c8b | ||
|
|
1246e0024a | ||
|
|
81cc9b3810 | ||
|
|
5c6ff6efc8 | ||
|
|
1956ed23f8 | ||
|
|
af34015dbe | ||
|
|
88919a3d99 | ||
|
|
ab26a57d05 | ||
|
|
f1052a8826 | ||
|
|
30daa4146e | ||
|
|
7537237465 | ||
|
|
76e77da5de | ||
|
|
04d2ce110a | ||
|
|
b2755045c9 | ||
|
|
d43931e761 | ||
|
|
44c1e17fd5 | ||
|
|
5df6fa2d0b | ||
|
|
1fa6cc47ec |
10
.github/actions/install-dependencies/action.yaml
vendored
10
.github/actions/install-dependencies/action.yaml
vendored
@@ -14,7 +14,7 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 9.15.0
|
version: 10.1.0
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm cache directory
|
- name: Get pnpm cache directory
|
||||||
id: pnpm-cache-dir
|
id: pnpm-cache-dir
|
||||||
@@ -30,6 +30,14 @@ runs:
|
|||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
- shell: bash
|
||||||
|
name: Use Latest Corepack
|
||||||
|
run: |
|
||||||
|
echo "Before: corepack version => $(corepack --version || echo 'not installed')"
|
||||||
|
npm install -g corepack@latest
|
||||||
|
echo "After : corepack version => $(corepack --version)"
|
||||||
|
corepack enable
|
||||||
|
pnpm --version
|
||||||
- shell: bash
|
- shell: bash
|
||||||
name: Install packages
|
name: Install packages
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|||||||
5
.github/workflows/ci.yaml
vendored
5
.github/workflows/ci.yaml
vendored
@@ -107,6 +107,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
|
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
|
||||||
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
||||||
|
- name: Enforce Prettier formatting in dashboard
|
||||||
|
working-directory: ./dashboard
|
||||||
|
run: pnpm prettier --check "./**/*.tsx" --config prettier.config.js
|
||||||
# * Run every `lint` script in the workspace . Dependencies build is cached by Turborepo
|
# * Run every `lint` script in the workspace . Dependencies build is cached by Turborepo
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm run lint:all
|
run: pnpm run lint:all
|
||||||
@@ -163,7 +166,7 @@ jobs:
|
|||||||
# * Run this step only if the previous step failed, and Playwright generated a report
|
# * Run this step only if the previous step failed, and Playwright generated a report
|
||||||
- name: Upload Playwright Report
|
- name: Upload Playwright Report
|
||||||
if: ${{ failure() && hashFiles(format('{0}/playwright-report/**', matrix.package.path)) != ''}}
|
if: ${{ failure() && hashFiles(format('{0}/playwright-report/**', matrix.package.path)) != ''}}
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: playwright-${{ steps.file-name.outputs.fileName }}
|
name: playwright-${{ steps.file-name.outputs.fileName }}
|
||||||
path: ${{format('{0}/playwright-report/**', matrix.package.path)}}
|
path: ${{format('{0}/playwright-report/**', matrix.package.path)}}
|
||||||
|
|||||||
4
.github/workflows/test-nhost-cli-action.yaml
vendored
4
.github/workflows/test-nhost-cli-action.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
path: packages/nhost-js
|
path: packages/nhost-js
|
||||||
start: true
|
start: true
|
||||||
- name: should be running
|
- name: should be running
|
||||||
run: curl -sSf 'https://local.hasura.nhost.run' > /dev/null
|
run: curl -sSf 'https://local.hasura.local.nhost.run' > /dev/null
|
||||||
|
|
||||||
stop:
|
stop:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
start: true
|
start: true
|
||||||
- name: should find the injected hasura-auth version
|
- name: should find the injected hasura-auth version
|
||||||
run: |
|
run: |
|
||||||
VERSION=$(curl -sSf 'https://local.auth.nhost.run/v1/version')
|
VERSION=$(curl -sSf 'https://local.auth.local.nhost.run/v1/version')
|
||||||
EXPECTED_VERSION='{"version":"v0.20.1"}'
|
EXPECTED_VERSION='{"version":"v0.20.1"}'
|
||||||
if [ "$VERSION" != "$EXPECTED_VERSION" ]; then
|
if [ "$VERSION" != "$EXPECTED_VERSION" ]; then
|
||||||
echo "Expected version $EXPECTED_VERSION but got $VERSION"
|
echo "Expected version $EXPECTED_VERSION but got $VERSION"
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -63,3 +63,5 @@ out/
|
|||||||
# Nix
|
# Nix
|
||||||
.envrc
|
.envrc
|
||||||
.direnv/
|
.direnv/
|
||||||
|
|
||||||
|
/.vscode/
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# Nhost
|
# Nhost
|
||||||
|
|
||||||
<a href="https://docs.nhost.io/#quickstart">Quickstart</a>
|
<a href="https://docs.nhost.io/introduction#quick-start-guides">Quickstart</a>
|
||||||
<span> • </span>
|
<span> • </span>
|
||||||
<a href="http://nhost.io/">Website</a>
|
<a href="http://nhost.io/">Website</a>
|
||||||
<span> • </span>
|
<span> • </span>
|
||||||
@@ -36,7 +36,7 @@ Nhost consists of open source software:
|
|||||||
- Authentication: [Hasura Auth](https://github.com/nhost/hasura-auth/)
|
- Authentication: [Hasura Auth](https://github.com/nhost/hasura-auth/)
|
||||||
- Storage: [Hasura Storage](https://github.com/nhost/hasura-storage)
|
- Storage: [Hasura Storage](https://github.com/nhost/hasura-storage)
|
||||||
- Serverless Functions: Node.js (JavaScript and TypeScript)
|
- Serverless Functions: Node.js (JavaScript and TypeScript)
|
||||||
- [Nhost CLI](https://docs.nhost.io/cli) for local development
|
- [Nhost CLI](https://docs.nhost.io/development/cli/overview) for local development
|
||||||
|
|
||||||
## Architecture of Nhost
|
## Architecture of Nhost
|
||||||
|
|
||||||
@@ -89,12 +89,12 @@ await nhost.graphql.request(`{
|
|||||||
Nhost is frontend agnostic, which means Nhost works with all frontend frameworks.
|
Nhost is frontend agnostic, which means Nhost works with all frontend frameworks.
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://docs.nhost.io/platform/quickstarts/nextjs"><img src="assets/nextjs.svg"/></a>
|
<a href="https://docs.nhost.io/guides/quickstarts/nextjs"><img src="assets/nextjs.svg"/></a>
|
||||||
<a href="https://docs.nhost.io/reference/javascript"><img src="assets/nuxtjs.svg"/></a>
|
<a href="https://docs.nhost.io/reference/javascript"><img src="assets/nuxtjs.svg"/></a>
|
||||||
<a href="https://docs.nhost.io/platform/quickstarts/react"><img src="assets/react.svg"/></a>
|
<a href="https://docs.nhost.io/guides/quickstarts/react"><img src="assets/react.svg"/></a>
|
||||||
<a href="https://docs.nhost.io/reference/javascript"><img src="assets/react-native.svg"/></a>
|
<a href="https://docs.nhost.io/reference/javascript"><img src="assets/react-native.svg"/></a>
|
||||||
<a href="https://docs.nhost.io/reference/javascript"><img src="assets/svelte.svg"/></a>
|
<a href="https://docs.nhost.io/reference/javascript"><img src="assets/svelte.svg"/></a>
|
||||||
<a href="https://docs.nhost.io/platform/quickstarts/vue"><img src="assets/vuejs.svg"/></a>
|
<a href="https://docs.nhost.io/guides/quickstarts/vue"><img src="assets/vuejs.svg"/></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Resources
|
# Resources
|
||||||
@@ -140,7 +140,7 @@ This repository, and most of our other open source projects, are licensed under
|
|||||||
|
|
||||||
Here are some ways of contributing to making Nhost better:
|
Here are some ways of contributing to making Nhost better:
|
||||||
|
|
||||||
- **[Try out Nhost](https://docs.nhost.io/get-started/quick-start)**, and think of ways to make the service better. Let us know here on GitHub.
|
- **[Try out Nhost](https://docs.nhost.io/introduction)**, and think of ways to make the service better. Let us know here on GitHub.
|
||||||
- Join our [Discord](https://discord.com/invite/9V7Qb2U) and connect with other members to share and learn from.
|
- Join our [Discord](https://discord.com/invite/9V7Qb2U) and connect with other members to share and learn from.
|
||||||
- Send a pull request to any of our [open source repositories](https://github.com/nhost) on Github. Check our [contribution guide](https://github.com/nhost/nhost/blob/main/CONTRIBUTING.md) and our [developers guide](https://github.com/nhost/nhost/blob/main/DEVELOPERS.md) for more details about how to contribute. We're looking forward to your contribution!
|
- Send a pull request to any of our [open source repositories](https://github.com/nhost) on Github. Check our [contribution guide](https://github.com/nhost/nhost/blob/main/CONTRIBUTING.md) and our [developers guide](https://github.com/nhost/nhost/blob/main/DEVELOPERS.md) for more details about how to contribute. We're looking forward to your contribution!
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
// $schema provides code completion hints to IDEs.
|
// $schema provides code completion hints to IDEs.
|
||||||
"$schema": "https://github.com/IBM/audit-ci/raw/main/docs/schema.json",
|
"$schema": "https://github.com/IBM/audit-ci/raw/main/docs/schema.json",
|
||||||
"moderate": true,
|
"moderate": true,
|
||||||
"allowlist": ["vue-template-compiler", "micromatch", "path-to-regexp"]
|
"allowlist": ["vue-template-compiler"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,19 @@ NEXT_PUBLIC_ENV=dev
|
|||||||
NEXT_PUBLIC_NHOST_PLATFORM=false
|
NEXT_PUBLIC_NHOST_PLATFORM=false
|
||||||
|
|
||||||
# Environment Variables for Self Hosting and Local Development
|
# Environment Variables for Self Hosting and Local Development
|
||||||
NEXT_PUBLIC_NHOST_AUTH_URL=https://local.auth.nhost.run/v1
|
NEXT_PUBLIC_NHOST_AUTH_URL=https://local.auth.nhost.local.run/v1
|
||||||
NEXT_PUBLIC_NHOST_FUNCTIONS_URL=https://local.functions.nhost.run/v1
|
NEXT_PUBLIC_NHOST_FUNCTIONS_URL=https://local.functions.local.nhost.run/v1
|
||||||
NEXT_PUBLIC_NHOST_GRAPHQL_URL=https://local.graphql.nhost.run/v1
|
NEXT_PUBLIC_NHOST_GRAPHQL_URL=https://local.graphql.local.nhost.run/v1
|
||||||
NEXT_PUBLIC_NHOST_STORAGE_URL=https://local.storage.nhost.run/v1
|
NEXT_PUBLIC_NHOST_STORAGE_URL=https://local.storage.local.nhost.run/v1
|
||||||
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL=https://local.hasura.nhost.run
|
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL=https://local.hasura.local.nhost.run
|
||||||
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL=https://local.hasura.nhost.run/v1/migrations
|
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL=https://local.hasura.local.nhost.run/v1/migrations
|
||||||
NEXT_PUBLIC_NHOST_HASURA_API_URL=https://local.hasura.nhost.run
|
NEXT_PUBLIC_NHOST_HASURA_API_URL=https://local.hasura.local.nhost.run
|
||||||
|
|
||||||
# Environment Variables when running the Nhost Dashboard against the Nhost Backend
|
# Environment Variables when running the Nhost Dashboard against the Nhost Backend
|
||||||
NEXT_PUBLIC_STRIPE_PK=<nhost_stripe_public_key>
|
NEXT_PUBLIC_STRIPE_PK=<nhost_stripe_public_key>
|
||||||
NEXT_PUBLIC_GITHUB_APP_INSTALL_URL=<github_app_install_url>
|
NEXT_PUBLIC_GITHUB_APP_INSTALL_URL=<github_app_install_url>
|
||||||
NEXT_PUBLIC_ANALYTICS_WRITE_KEY=<analytics_write_key>
|
NEXT_PUBLIC_ANALYTICS_WRITE_KEY=<analytics_write_key>
|
||||||
|
NEXT_PUBLIC_SEGMENT_CDN_URL=<segment_cdn_url>
|
||||||
NEXT_PUBLIC_NHOST_BRAGI_WEBSOCKET=<nhost_bragi_websocket>
|
NEXT_PUBLIC_NHOST_BRAGI_WEBSOCKET=<nhost_bragi_websocket>
|
||||||
|
|
||||||
NEXT_PUBLIC_ZENDESK_URL=
|
NEXT_PUBLIC_ZENDESK_URL=
|
||||||
@@ -22,6 +23,6 @@ NEXT_PUBLIC_ZENDESK_API_KEY=
|
|||||||
NEXT_PUBLIC_ZENDESK_USER_EMAIL=
|
NEXT_PUBLIC_ZENDESK_USER_EMAIL=
|
||||||
|
|
||||||
|
|
||||||
CODEGEN_GRAPHQL_URL=https://local.graphql.nhost.run/v1
|
CODEGEN_GRAPHQL_URL=https://local.graphql.local.nhost.run/v1
|
||||||
CODEGEN_HASURA_ADMIN_SECRET=nhost-admin-secret
|
CODEGEN_HASURA_ADMIN_SECRET=nhost-admin-secret
|
||||||
NEXT_PUBLIC_TURNSTILE_SITE_KEY=FIXME
|
NEXT_PUBLIC_TURNSTILE_SITE_KEY=FIXME
|
||||||
2
dashboard/.vscode/settings.json
vendored
2
dashboard/.vscode/settings.json
vendored
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.organizeImports": true
|
"source.organizeImports": "explicit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,46 @@
|
|||||||
# @nhost/dashboard
|
# @nhost/dashboard
|
||||||
|
|
||||||
|
## 2.17.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- fd59918: fix: redirect to 404 with nhost cli dashboard
|
||||||
|
|
||||||
|
## 2.16.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- f8e6b61: fix: can add rule groups in table permissions
|
||||||
|
- 9e404c8: fix: not redirect to 404 page if using local Nhost backend
|
||||||
|
- ac4aa01: fix: can delete column in database page
|
||||||
|
- 4385524: fix: update url to check service health in local dashboard
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- @nhost/react-apollo@16.0.1
|
||||||
|
- @nhost/nextjs@2.2.2
|
||||||
|
|
||||||
|
## 2.15.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- f1052a8: fix: improve stability of the dashboard when pausing projects
|
||||||
|
- 30daa41: fix: update links to docs in overview page
|
||||||
|
- 7537237: feat: add image preview toggle in storage
|
||||||
|
|
||||||
|
## 2.14.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- d43931e: fix: invalid organization slug/project subdomain doesn't open 404 page
|
||||||
|
- 5df6fa2: feat: add unencrypted disk warning in storage capacity settings
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 44c1e17: chore: update `msw` to v1.3.5 to fix vulnerabilities
|
||||||
|
- @nhost/react-apollo@16.0.0
|
||||||
|
- @nhost/nextjs@2.2.1
|
||||||
|
|
||||||
## 2.13.0
|
## 2.13.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ ENV NEXT_PUBLIC_NHOST_CONFIGSERVER_URL=__NEXT_PUBLIC_NHOST_CONFIGSERVER_URL__
|
|||||||
RUN yarn global add pnpm@9.15.0
|
RUN yarn global add pnpm@9.15.0
|
||||||
COPY .gitignore .gitignore
|
COPY .gitignore .gitignore
|
||||||
COPY --from=pruner /app/out/json/ .
|
COPY --from=pruner /app/out/json/ .
|
||||||
COPY --from=pruner /app/out/pnpm-*.yaml .
|
COPY --from=pruner /app/out/pnpm-*.yaml ./
|
||||||
RUN pnpm install --frozen-lockfile
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
COPY --from=pruner /app/out/full/ .
|
COPY --from=pruner /app/out/full/ .
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ You can connect the Nhost Dashboard to your locally running backend by setting t
|
|||||||
```bash
|
```bash
|
||||||
NEXT_PUBLIC_ENV=dev
|
NEXT_PUBLIC_ENV=dev
|
||||||
NEXT_PUBLIC_NHOST_PLATFORM=false
|
NEXT_PUBLIC_NHOST_PLATFORM=false
|
||||||
NEXT_PUBLIC_NHOST_AUTH_URL=https://local.auth.nhost.run/v1
|
NEXT_PUBLIC_NHOST_AUTH_URL=https://local.auth.local.nhost.run/v1
|
||||||
NEXT_PUBLIC_NHOST_FUNCTIONS_URL=https://local.functions.nhost.run/v1
|
NEXT_PUBLIC_NHOST_FUNCTIONS_URL=https://local.functions.local.nhost.run/v1
|
||||||
NEXT_PUBLIC_NHOST_GRAPHQL_URL=https://local.graphql.nhost.run/v1
|
NEXT_PUBLIC_NHOST_GRAPHQL_URL=https://local.graphql.local.nhost.run/v1
|
||||||
NEXT_PUBLIC_NHOST_STORAGE_URL=https://local.storage.nhost.run/v1
|
NEXT_PUBLIC_NHOST_STORAGE_URL=https://local.storage.local.nhost.run/v1
|
||||||
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL=https://local.hasura.nhost.run
|
NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL=https://local.hasura.local.nhost.run
|
||||||
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL=https://local.hasura.nhost.run/v1/migrations
|
NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL=https://local.hasura.local.nhost.run/v1/migrations
|
||||||
NEXT_PUBLIC_NHOST_HASURA_API_URL=https://local.hasura.nhost.run
|
NEXT_PUBLIC_NHOST_HASURA_API_URL=https://local.hasura.local.nhost.run
|
||||||
```
|
```
|
||||||
|
|
||||||
This will connect the Nhost Dashboard to your locally running Nhost backend.
|
This will connect the Nhost Dashboard to your locally running Nhost backend.
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ test('should delete a table', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should not be able to delete a table if other tables have foreign keys referencing it', async () => {
|
test('should not be able to delete a table if other tables have foreign keys referencing it', async () => {
|
||||||
|
test.setTimeout(60000);
|
||||||
await page.getByRole('button', { name: /new table/i }).click();
|
await page.getByRole('button', { name: /new table/i }).click();
|
||||||
await expect(page.getByText(/create a new table/i)).toBeVisible();
|
await expect(page.getByText(/create a new table/i)).toBeVisible();
|
||||||
|
|
||||||
|
|||||||
149
dashboard/e2e/database/permissions-table.test.ts
Normal file
149
dashboard/e2e/database/permissions-table.test.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import { TEST_ORGANIZATION_SLUG, TEST_PROJECT_SUBDOMAIN } from '@/e2e/env';
|
||||||
|
import {
|
||||||
|
clickPermissionButton,
|
||||||
|
navigateToProject,
|
||||||
|
prepareTable,
|
||||||
|
} from '@/e2e/utils';
|
||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import type { Page } from '@playwright/test';
|
||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
import { snakeCase } from 'snake-case';
|
||||||
|
|
||||||
|
let page: Page;
|
||||||
|
|
||||||
|
test.beforeAll(async ({ browser }) => {
|
||||||
|
page = await browser.newPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async () => {
|
||||||
|
await page.goto('/');
|
||||||
|
|
||||||
|
await navigateToProject({
|
||||||
|
page,
|
||||||
|
orgSlug: TEST_ORGANIZATION_SLUG,
|
||||||
|
projectSubdomain: TEST_PROJECT_SUBDOMAIN,
|
||||||
|
});
|
||||||
|
|
||||||
|
const databaseRoute = `/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default`;
|
||||||
|
await page.goto(databaseRoute);
|
||||||
|
await page.waitForURL(databaseRoute);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterAll(async () => {
|
||||||
|
await page.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create a table with role permissions to select row', async () => {
|
||||||
|
await page.getByRole('button', { name: /new table/i }).click();
|
||||||
|
await expect(page.getByText(/create a new table/i)).toBeVisible();
|
||||||
|
|
||||||
|
const tableName = snakeCase(faker.lorem.words(3));
|
||||||
|
|
||||||
|
await prepareTable({
|
||||||
|
page,
|
||||||
|
name: tableName,
|
||||||
|
primaryKey: 'id',
|
||||||
|
columns: [
|
||||||
|
{ name: 'id', type: 'uuid', defaultValue: 'gen_random_uuid()' },
|
||||||
|
{ name: 'title', type: 'text' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// create table
|
||||||
|
await page.getByRole('button', { name: /create/i }).click();
|
||||||
|
|
||||||
|
await page.waitForURL(
|
||||||
|
`/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default/public/${tableName}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('link', { name: tableName, exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Press three horizontal dots more options button next to the table name
|
||||||
|
await page
|
||||||
|
.locator(`li:has-text("${tableName}") #table-management-menu button`)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page.getByRole('menuitem', { name: /edit permissions/i }).click();
|
||||||
|
|
||||||
|
await clickPermissionButton({ page, role: 'user', permission: 'Select' });
|
||||||
|
|
||||||
|
await page.getByLabel('Without any checks').click();
|
||||||
|
await page.getByRole('button', { name: /select all/i }).click();
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: /save/i }).click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByText(/permission has been saved successfully/i),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create a table with role permissions and a custom check to select rows', async () => {
|
||||||
|
await page.getByRole('button', { name: /new table/i }).click();
|
||||||
|
await expect(page.getByText(/create a new table/i)).toBeVisible();
|
||||||
|
|
||||||
|
const tableName = snakeCase(faker.lorem.words(3));
|
||||||
|
|
||||||
|
await prepareTable({
|
||||||
|
page,
|
||||||
|
name: tableName,
|
||||||
|
primaryKey: 'id',
|
||||||
|
columns: [
|
||||||
|
{ name: 'id', type: 'uuid', defaultValue: 'gen_random_uuid()' },
|
||||||
|
{ name: 'title', type: 'text' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// create table
|
||||||
|
await page.getByRole('button', { name: /create/i }).click();
|
||||||
|
|
||||||
|
await page.waitForURL(
|
||||||
|
`/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default/public/${tableName}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('link', { name: tableName, exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Press three horizontal dots more options button next to the table name
|
||||||
|
await page
|
||||||
|
.locator(`li:has-text("${tableName}") #table-management-menu button`)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page.getByRole('menuitem', { name: /edit permissions/i }).click();
|
||||||
|
|
||||||
|
await clickPermissionButton({ page, role: 'user', permission: 'Select' });
|
||||||
|
|
||||||
|
await page.getByLabel('With custom check').click();
|
||||||
|
|
||||||
|
// await page.getByRole('combobox', { name: /select a column/i }).click();
|
||||||
|
await page.getByText('Select a column', { exact: true }).click();
|
||||||
|
|
||||||
|
const columnSelector = page.locator('input[role="combobox"]');
|
||||||
|
|
||||||
|
await columnSelector.fill('id');
|
||||||
|
|
||||||
|
await columnSelector.press('Enter');
|
||||||
|
|
||||||
|
await expect(page.getByText(/_eq/i)).toBeVisible();
|
||||||
|
|
||||||
|
// limit on number of rows fetched per request.
|
||||||
|
await page.locator('#limit').fill('100');
|
||||||
|
|
||||||
|
await page.getByText('Select variable...', { exact: true }).click();
|
||||||
|
|
||||||
|
const variableSelector = await page.locator('input[role="combobox"]');
|
||||||
|
|
||||||
|
await variableSelector.fill('X-Hasura-User-Id');
|
||||||
|
|
||||||
|
await variableSelector.press('Enter');
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: /select all/i }).click();
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: /save/i }).click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByText(/permission has been saved successfully/i),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
61
dashboard/e2e/teardown/database.teardown.ts
Normal file
61
dashboard/e2e/teardown/database.teardown.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import {
|
||||||
|
TEST_DASHBOARD_URL,
|
||||||
|
TEST_ORGANIZATION_SLUG,
|
||||||
|
TEST_PROJECT_SUBDOMAIN,
|
||||||
|
} from '@/e2e/env';
|
||||||
|
import { navigateToProject } from '@/e2e/utils';
|
||||||
|
import { type Page, expect, test as teardown } from '@playwright/test';
|
||||||
|
|
||||||
|
let page: Page;
|
||||||
|
|
||||||
|
teardown.beforeAll(async ({ browser }) => {
|
||||||
|
const context = await browser.newContext({
|
||||||
|
baseURL: TEST_DASHBOARD_URL,
|
||||||
|
storageState: 'e2e/.auth/user.json',
|
||||||
|
});
|
||||||
|
|
||||||
|
page = await context.newPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown.beforeEach(async () => {
|
||||||
|
await page.goto('/');
|
||||||
|
|
||||||
|
await navigateToProject({
|
||||||
|
page,
|
||||||
|
orgSlug: TEST_ORGANIZATION_SLUG,
|
||||||
|
projectSubdomain: TEST_PROJECT_SUBDOMAIN,
|
||||||
|
});
|
||||||
|
|
||||||
|
const databaseRoute = `/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default`;
|
||||||
|
await page.goto(databaseRoute);
|
||||||
|
await page.waitForURL(databaseRoute);
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown.afterAll(async () => {
|
||||||
|
await page.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown('clean up database tables', async () => {
|
||||||
|
await page.getByRole('link', { name: /sql editor/i }).click();
|
||||||
|
|
||||||
|
await page.waitForURL(
|
||||||
|
`/orgs/${TEST_ORGANIZATION_SLUG}/projects/${TEST_PROJECT_SUBDOMAIN}/database/browser/default/editor`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const inputField = page.locator('[contenteditable]');
|
||||||
|
await inputField.fill(`
|
||||||
|
DO $$ DECLARE
|
||||||
|
tablename text;
|
||||||
|
BEGIN
|
||||||
|
FOR tablename IN
|
||||||
|
SELECT table_name FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
LOOP
|
||||||
|
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(tablename) || ' CASCADE';
|
||||||
|
END LOOP;
|
||||||
|
END $$;
|
||||||
|
`);
|
||||||
|
|
||||||
|
await page.locator('button[type="button"]', { hasText: /run/i }).click();
|
||||||
|
await expect(page.getByText(/success/i)).toBeVisible();
|
||||||
|
});
|
||||||
@@ -191,3 +191,23 @@ export function generateTestEmail(prefix: string = 'Nhost_Test_') {
|
|||||||
|
|
||||||
return [prefix, email].join('');
|
return [prefix, email].join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function clickPermissionButton({
|
||||||
|
page,
|
||||||
|
role,
|
||||||
|
permission,
|
||||||
|
}: {
|
||||||
|
page: Page;
|
||||||
|
role: string;
|
||||||
|
permission: 'Insert' | 'Select' | 'Update' | 'Delete';
|
||||||
|
}) {
|
||||||
|
const permissionIndex =
|
||||||
|
['Insert', 'Select', 'Update', 'Delete'].indexOf(permission) + 1;
|
||||||
|
|
||||||
|
await page
|
||||||
|
.locator('tr', { hasText: role })
|
||||||
|
.locator('td')
|
||||||
|
.nth(permissionIndex)
|
||||||
|
.locator('button')
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import {
|
|
||||||
TEST_DASHBOARD_URL,
|
|
||||||
TEST_ORGANIZATION_SLUG,
|
|
||||||
TEST_PROJECT_ADMIN_SECRET,
|
|
||||||
TEST_PROJECT_SUBDOMAIN,
|
|
||||||
} from '@/e2e/env';
|
|
||||||
import { navigateToProject } from '@/e2e/utils';
|
|
||||||
import { chromium } from '@playwright/test';
|
|
||||||
|
|
||||||
async function globalTeardown() {
|
|
||||||
const browser = await chromium.launch({ slowMo: 1000 });
|
|
||||||
|
|
||||||
const context = await browser.newContext({
|
|
||||||
baseURL: TEST_DASHBOARD_URL,
|
|
||||||
storageState: 'e2e/.auth/user.json',
|
|
||||||
});
|
|
||||||
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
await navigateToProject({
|
|
||||||
page,
|
|
||||||
orgSlug: TEST_ORGANIZATION_SLUG,
|
|
||||||
projectSubdomain: TEST_PROJECT_SUBDOMAIN,
|
|
||||||
});
|
|
||||||
|
|
||||||
const pagePromise = context.waitForEvent('page');
|
|
||||||
|
|
||||||
await page.getByRole('link', { name: /hasura/i }).click();
|
|
||||||
await page.getByRole('link', { name: /open hasura/i }).click();
|
|
||||||
|
|
||||||
const hasuraPage = await pagePromise;
|
|
||||||
await hasuraPage.waitForLoadState();
|
|
||||||
|
|
||||||
const adminSecretInput = hasuraPage.getByPlaceholder(/enter admin-secret/i);
|
|
||||||
|
|
||||||
// note: a more ideal way would be to paste from clipboard, but Playwright
|
|
||||||
// doesn't support that yet
|
|
||||||
await adminSecretInput.fill(TEST_PROJECT_ADMIN_SECRET);
|
|
||||||
await adminSecretInput.press('Enter');
|
|
||||||
|
|
||||||
// note: getByRole doesn't work here
|
|
||||||
await hasuraPage.locator('a', { hasText: /data/i }).nth(0).click();
|
|
||||||
await hasuraPage.locator('[data-test="sql-link"]').click();
|
|
||||||
|
|
||||||
// Set the value of the Ace code editor using JavaScript evaluation in the browser context
|
|
||||||
await hasuraPage.evaluate(() => {
|
|
||||||
const editor = ace.edit('raw_sql');
|
|
||||||
|
|
||||||
editor.setValue(`
|
|
||||||
DO $$ DECLARE
|
|
||||||
tablename text;
|
|
||||||
BEGIN
|
|
||||||
FOR tablename IN
|
|
||||||
SELECT table_name FROM information_schema.tables
|
|
||||||
WHERE table_schema = 'public'
|
|
||||||
LOOP
|
|
||||||
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(tablename) || ' CASCADE';
|
|
||||||
END LOOP;
|
|
||||||
END $$;
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
await hasuraPage.getByRole('button', { name: /run!/i }).click();
|
|
||||||
await hasuraPage.getByText(/sql executed!/i).waitFor();
|
|
||||||
}
|
|
||||||
|
|
||||||
export default globalTeardown;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
schema:
|
schema:
|
||||||
- https://local.graphql.nhost.run/v1:
|
- https://local.graphql.local.nhost.run/v1:
|
||||||
headers:
|
headers:
|
||||||
x-hasura-admin-secret: nhost-admin-secret
|
x-hasura-admin-secret: nhost-admin-secret
|
||||||
generates:
|
generates:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nhost/dashboard",
|
"name": "@nhost/dashboard",
|
||||||
"version": "2.13.0",
|
"version": "2.21.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"@radix-ui/react-separator": "^1.1.0",
|
"@radix-ui/react-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
"@segment/snippet": "^4.16.2",
|
"@segment/analytics-next": "^1.77.0",
|
||||||
"@stripe/react-stripe-js": "^2.6.2",
|
"@stripe/react-stripe-js": "^2.6.2",
|
||||||
"@stripe/stripe-js": "^1.54.2",
|
"@stripe/stripe-js": "^1.54.2",
|
||||||
"@tailwindcss/forms": "^0.5.7",
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
@@ -67,7 +67,6 @@
|
|||||||
"@uiw/codemirror-theme-bbedit": "^4.22.2",
|
"@uiw/codemirror-theme-bbedit": "^4.22.2",
|
||||||
"@uiw/codemirror-theme-github": "^4.21.25",
|
"@uiw/codemirror-theme-github": "^4.21.25",
|
||||||
"@uiw/react-codemirror": "^4.21.25",
|
"@uiw/react-codemirror": "^4.21.25",
|
||||||
"analytics-node": "^6.2.0",
|
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
@@ -177,13 +176,13 @@
|
|||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"jsdom": "^22.1.0",
|
"jsdom": "^22.1.0",
|
||||||
"lint-staged": "^15.2.2",
|
"lint-staged": "^15.2.2",
|
||||||
"msw": "^1.3.3",
|
"msw": "^1.3.5",
|
||||||
"msw-storybook-addon": "^1.10.0",
|
"msw-storybook-addon": "^1.10.0",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.4.2",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"prettier-plugin-organize-imports": "^4.1.0",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.6",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
"react-date-fns-hooks": "^0.9.4",
|
"react-date-fns-hooks": "^0.9.4",
|
||||||
"require-from-string": "^2.0.2",
|
"require-from-string": "^2.0.2",
|
||||||
"snake-case": "^3.0.4",
|
"snake-case": "^3.0.4",
|
||||||
@@ -191,7 +190,7 @@
|
|||||||
"tailwindcss": "^3.4.12",
|
"tailwindcss": "^3.4.12",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsconfig-paths-webpack-plugin": "^4.1.0",
|
"tsconfig-paths-webpack-plugin": "^4.1.0",
|
||||||
"vite": "^5.4.6",
|
"vite": "^5.4.12",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vitest": "^0.32.4"
|
"vitest": "^0.32.4"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,16 +6,15 @@ dotenv.config({ path: path.resolve(__dirname, '.env.test') });
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './e2e',
|
testDir: './e2e',
|
||||||
timeout: 40 * 1000,
|
timeout: 60 * 1000,
|
||||||
expect: {
|
expect: {
|
||||||
timeout: 5000,
|
timeout: 10000,
|
||||||
},
|
},
|
||||||
fullyParallel: false,
|
fullyParallel: false,
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
retries: process.env.CI ? 2 : 0,
|
retries: process.env.CI ? 2 : 0,
|
||||||
workers: 1,
|
workers: 1,
|
||||||
reporter: 'html',
|
reporter: 'html',
|
||||||
globalTeardown: require.resolve('./global-teardown'),
|
|
||||||
use: {
|
use: {
|
||||||
actionTimeout: 0,
|
actionTimeout: 0,
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
@@ -28,6 +27,11 @@ export default defineConfig({
|
|||||||
{
|
{
|
||||||
name: 'setup',
|
name: 'setup',
|
||||||
testMatch: ['**/setup/*.setup.ts'],
|
testMatch: ['**/setup/*.setup.ts'],
|
||||||
|
teardown: 'teardown',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'teardown',
|
||||||
|
testMatch: ['**/teardown/*.teardown.ts'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'chromium',
|
name: 'chromium',
|
||||||
|
|||||||
30
dashboard/src/components/analytics/analytics.tsx
Normal file
30
dashboard/src/components/analytics/analytics.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { useCurrentOrg } from '@/features/orgs/projects/hooks/useCurrentOrg';
|
||||||
|
import { useProject } from '@/features/orgs/projects/hooks/useProject';
|
||||||
|
import { analytics } from '@/lib/segment';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
export default function Analytics() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { org } = useCurrentOrg();
|
||||||
|
const { project } = useProject();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const customProperties = {
|
||||||
|
organizationSlug: org?.slug || '',
|
||||||
|
projectSubdomain: project?.subdomain || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
analytics.page(customProperties);
|
||||||
|
|
||||||
|
const handleRouteChange = () => analytics.page(customProperties);
|
||||||
|
|
||||||
|
router.events.on('routeChangeComplete', handleRouteChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
router.events.off('routeChangeComplete', handleRouteChange);
|
||||||
|
};
|
||||||
|
}, [router.events, org?.slug, project?.subdomain]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ export default function ApplyLocalSettingsDialog() {
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Text color="secondary">
|
<Text color="secondary">
|
||||||
Run{' '}
|
Run{' '}
|
||||||
<code className="px-1 py-px mx-1 rounded-md bg-slate-500 text-slate-100">
|
<code className="mx-1 rounded-md bg-slate-500 px-1 py-px text-slate-100">
|
||||||
$ nhost up
|
$ nhost up
|
||||||
</code>{' '}
|
</code>{' '}
|
||||||
using the cli to apply your changes
|
using the cli to apply your changes
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default function FeedbackForm({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'grid max-w-md grid-flow-row gap-2 py-4 px-5',
|
'grid max-w-md grid-flow-row gap-2 px-5 py-4',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function IconLink(
|
|||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'grid cursor-default grid-flow-row justify-items-center gap-1 rounded-md py-2.5 px-0.5 text-center text-[10px] font-medium opacity-40',
|
'grid cursor-default grid-flow-row justify-items-center gap-1 rounded-md px-0.5 py-2.5 text-center text-[10px] font-medium opacity-40',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -68,7 +68,7 @@ function IconLink(
|
|||||||
href={href}
|
href={href}
|
||||||
underline="none"
|
underline="none"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'grid grid-flow-row justify-items-center gap-1 rounded-md py-2.5 px-0.5 text-center font-medium motion-safe:transition-colors',
|
'grid grid-flow-row justify-items-center gap-1 rounded-md px-0.5 py-2.5 text-center font-medium motion-safe:transition-colors',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export default function InviteNotification() {
|
|||||||
|
|
||||||
const handleInviteAccept = async (
|
const handleInviteAccept = async (
|
||||||
_event: React.SyntheticEvent<HTMLButtonElement>,
|
_event: React.SyntheticEvent<HTMLButtonElement>,
|
||||||
invite: typeof data.workspaceMemberInvites[number],
|
invite: (typeof data.workspaceMemberInvites)[number],
|
||||||
) => {
|
) => {
|
||||||
setSubmitState({
|
setSubmitState({
|
||||||
error: null,
|
error: null,
|
||||||
@@ -99,7 +99,7 @@ export default function InviteNotification() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function handleIgnoreInvitation(
|
async function handleIgnoreInvitation(
|
||||||
inviteId: typeof data.workspaceMemberInvites[number]['id'],
|
inviteId: (typeof data.workspaceMemberInvites)[number]['id'],
|
||||||
) {
|
) {
|
||||||
setIgnoreState({
|
setIgnoreState({
|
||||||
loading: true,
|
loading: true,
|
||||||
@@ -151,7 +151,7 @@ export default function InviteNotification() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{data?.workspaceMemberInvites?.map(
|
{data?.workspaceMemberInvites?.map(
|
||||||
(invite: typeof data.workspaceMemberInvites[number]) => (
|
(invite: (typeof data.workspaceMemberInvites)[number]) => (
|
||||||
<div key={invite.id} className="grid grid-flow-row gap-4 text-center">
|
<div key={invite.id} className="grid grid-flow-row gap-4 text-center">
|
||||||
<div className="grid grid-flow-row gap-1">
|
<div className="grid grid-flow-row gap-1">
|
||||||
<Text variant="h3" component="h2" sx={{ color: 'common.white' }}>
|
<Text variant="h3" component="h2" sx={{ color: 'common.white' }}>
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { useDialog } from '@/components/common/DialogProvider';
|
||||||
|
import { Button } from '@/components/ui/v2/Button';
|
||||||
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
|
import { useIsCurrentUserOwner } from '@/features/orgs/projects/common/hooks/useIsCurrentUserOwner';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
buttonText?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpenTransferDialogButton({ buttonText, onClick }: Props) {
|
||||||
|
const text = buttonText ?? 'Transfer Project';
|
||||||
|
const isOwner = useIsCurrentUserOwner();
|
||||||
|
const { openAlertDialog } = useDialog();
|
||||||
|
const handleClick = () => {
|
||||||
|
if (isOwner) {
|
||||||
|
onClick();
|
||||||
|
} else {
|
||||||
|
openAlertDialog({
|
||||||
|
title: "You can't migrate this project",
|
||||||
|
payload: (
|
||||||
|
<Text variant="subtitle1" component="span">
|
||||||
|
Ask an owner of this organization to migrate the project.
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
props: {
|
||||||
|
secondaryButtonText: 'I understand',
|
||||||
|
hidePrimaryAction: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Button className="max-w-xs lg:w-auto" onClick={handleClick}>
|
||||||
|
{text}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OpenTransferDialogButton;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as OpenTransferDialogButton } from './OpenTransferDialogButton';
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
|
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
|
||||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||||
import { Box } from '@/components/ui/v2/Box';
|
import { Box } from '@/components/ui/v2/Box';
|
||||||
@@ -8,7 +7,7 @@ import { List } from '@/components/ui/v2/List';
|
|||||||
import { ListItem } from '@/components/ui/v2/ListItem';
|
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
||||||
import { } from '@/utils/__generated__/graphql';
|
import {} from '@/utils/__generated__/graphql';
|
||||||
import { Divider } from '@mui/material';
|
import { Divider } from '@mui/material';
|
||||||
import debounce from 'lodash.debounce';
|
import debounce from 'lodash.debounce';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
@@ -37,20 +36,15 @@ export default function SelectOrganizationAndProject() {
|
|||||||
|
|
||||||
useEffect(() => () => handleFilterChange.cancel(), [handleFilterChange]);
|
useEffect(() => () => handleFilterChange.cancel(), [handleFilterChange]);
|
||||||
|
|
||||||
const goToOrgPage = async (org: {
|
const goToOrgPage = async (org: { name: string; value: string }) => {
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
}) => {
|
|
||||||
const { slug } = router.query;
|
const { slug } = router.query;
|
||||||
await router.push({
|
await router.push({
|
||||||
pathname: `${org.value}/${
|
pathname: `${org.value}/${(() => {
|
||||||
(() => {
|
if (!slug) {
|
||||||
if (!slug) {
|
return '';
|
||||||
return '';
|
}
|
||||||
}
|
return Array.isArray(slug) ? slug.join('/') : slug;
|
||||||
return Array.isArray(slug) ? slug.join('/') : slug;
|
})()}`,
|
||||||
})()
|
|
||||||
}`,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,16 +57,13 @@ export default function SelectOrganizationAndProject() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full justify-center">
|
<div className="flex w-full justify-center">
|
||||||
<ActivityIndicator
|
<ActivityIndicator delay={500} label="Loading organizations..." />
|
||||||
delay={500}
|
|
||||||
label="Loading organizations..."
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-start w-full h-full px-5 py-4 mx-auto bg-background">
|
<div className="mx-auto flex h-full w-full flex-col items-start bg-background px-5 py-4">
|
||||||
<div className="mx-auto flex h-full w-full max-w-[760px] flex-col gap-4 py-6 sm:py-14">
|
<div className="mx-auto flex h-full w-full max-w-[760px] flex-col gap-4 py-6 sm:py-14">
|
||||||
<Text variant="h2" component="h1" className="">
|
<Text variant="h2" component="h1" className="">
|
||||||
Select an Organization
|
Select an Organization
|
||||||
@@ -97,7 +88,7 @@ export default function SelectOrganizationAndProject() {
|
|||||||
{orgsToDisplay.map((org, index) => (
|
{orgsToDisplay.map((org, index) => (
|
||||||
<Fragment key={org.value}>
|
<Fragment key={org.value}>
|
||||||
<ListItem.Root
|
<ListItem.Root
|
||||||
className="grid grid-flow-col justify-start gap-2 py-2.5"
|
className="grid grid-flow-col justify-start gap-2 py-2.5"
|
||||||
secondaryAction={
|
secondaryAction={
|
||||||
<Button
|
<Button
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
|
import { RetryableErrorBoundary } from '@/components/presentational/RetryableErrorBoundary';
|
||||||
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
||||||
import { Box } from '@/components/ui/v2/Box';
|
import { Box } from '@/components/ui/v2/Box';
|
||||||
@@ -8,7 +7,7 @@ import { List } from '@/components/ui/v2/List';
|
|||||||
import { ListItem } from '@/components/ui/v2/ListItem';
|
import { ListItem } from '@/components/ui/v2/ListItem';
|
||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
import { useOrgs } from '@/features/orgs/projects/hooks/useOrgs';
|
||||||
import { } from '@/utils/__generated__/graphql';
|
import {} from '@/utils/__generated__/graphql';
|
||||||
import { Divider } from '@mui/material';
|
import { Divider } from '@mui/material';
|
||||||
import debounce from 'lodash.debounce';
|
import debounce from 'lodash.debounce';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
@@ -47,14 +46,12 @@ export default function SelectOrganizationAndProject() {
|
|||||||
}) => {
|
}) => {
|
||||||
const { slug } = router.query;
|
const { slug } = router.query;
|
||||||
await router.push({
|
await router.push({
|
||||||
pathname: `${project.value}/${
|
pathname: `${project.value}/${(() => {
|
||||||
(() => {
|
if (!slug) {
|
||||||
if (!slug) {
|
return '';
|
||||||
return '';
|
}
|
||||||
}
|
return Array.isArray(slug) ? slug.join('/') : slug;
|
||||||
return Array.isArray(slug) ? slug.join('/') : slug;
|
})()}`,
|
||||||
})()
|
|
||||||
}`,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,7 +73,7 @@ export default function SelectOrganizationAndProject() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-start w-full h-full px-5 py-4 mx-auto bg-background">
|
<div className="mx-auto flex h-full w-full flex-col items-start bg-background px-5 py-4">
|
||||||
<div className="mx-auto flex h-full w-full max-w-[760px] flex-col gap-4 py-6 sm:py-14">
|
<div className="mx-auto flex h-full w-full max-w-[760px] flex-col gap-4 py-6 sm:py-14">
|
||||||
<Text variant="h2" component="h1" className="">
|
<Text variant="h2" component="h1" className="">
|
||||||
Select a Project
|
Select a Project
|
||||||
@@ -101,7 +98,7 @@ export default function SelectOrganizationAndProject() {
|
|||||||
{projectsToDisplay.map((project, index) => (
|
{projectsToDisplay.map((project, index) => (
|
||||||
<Fragment key={project.value}>
|
<Fragment key={project.value}>
|
||||||
<ListItem.Root
|
<ListItem.Root
|
||||||
className="grid grid-flow-col justify-start gap-2 py-2.5"
|
className="grid grid-flow-col justify-start gap-2 py-2.5"
|
||||||
secondaryAction={
|
secondaryAction={
|
||||||
<Button
|
<Button
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
@@ -138,4 +135,4 @@ export default function SelectOrganizationAndProject() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { useDialog } from '@/components/common/DialogProvider';
|
|
||||||
import { NhostIcon } from '@/components/presentational/NhostIcon';
|
import { NhostIcon } from '@/components/presentational/NhostIcon';
|
||||||
import { Box } from '@/components/ui/v2/Box';
|
import { Box } from '@/components/ui/v2/Box';
|
||||||
import { Button } from '@/components/ui/v2/Button';
|
|
||||||
import { ArrowSquareOutIcon } from '@/components/ui/v2/icons/ArrowSquareOutIcon';
|
import { ArrowSquareOutIcon } from '@/components/ui/v2/icons/ArrowSquareOutIcon';
|
||||||
import { Link } from '@/components/ui/v2/Link';
|
import { Link } from '@/components/ui/v2/Link';
|
||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import { TransferProjectDialog } from '@/features/orgs/components/common/TransferProjectDialog';
|
import { TransferProjectDialog } from '@/features/orgs/components/common/TransferProjectDialog';
|
||||||
import { useIsCurrentUserOwner } from '@/features/orgs/projects/common/hooks/useIsCurrentUserOwner';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { OpenTransferDialogButton } from '@/components/common/OpenTransferDialogButton';
|
||||||
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
|
|
||||||
@@ -21,11 +20,11 @@ export default function UpgradeToProBanner({
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
}: UpgradeToProBannerProps) {
|
}: UpgradeToProBannerProps) {
|
||||||
const isOwner = useIsCurrentUserOwner();
|
|
||||||
const { openAlertDialog } = useDialog();
|
|
||||||
const [transferProjectDialogOpen, setTransferProjectDialogOpen] =
|
const [transferProjectDialogOpen, setTransferProjectDialogOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
|
const handleTransferDialogOpen = () => setTransferProjectDialogOpen(true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{ backgroundColor: 'primary.light' }}
|
sx={{ backgroundColor: 'primary.light' }}
|
||||||
@@ -51,29 +50,7 @@ export default function UpgradeToProBanner({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2 space-y-2 lg:flex-row lg:items-center lg:space-x-2 lg:space-y-0">
|
<div className="flex flex-col gap-2 space-y-2 lg:flex-row lg:items-center lg:space-x-2 lg:space-y-0">
|
||||||
<Button
|
<OpenTransferDialogButton onClick={handleTransferDialogOpen} />
|
||||||
className="max-w-xs lg:w-auto"
|
|
||||||
onClick={() => {
|
|
||||||
if (isOwner) {
|
|
||||||
setTransferProjectDialogOpen(true);
|
|
||||||
} else {
|
|
||||||
openAlertDialog({
|
|
||||||
title: "You can't migrate this project",
|
|
||||||
payload: (
|
|
||||||
<Text variant="subtitle1" component="span">
|
|
||||||
Ask an owner of this organization to migrate the project.
|
|
||||||
</Text>
|
|
||||||
),
|
|
||||||
props: {
|
|
||||||
secondaryButtonText: 'I understand',
|
|
||||||
hidePrimaryAction: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Transfer Project
|
|
||||||
</Button>
|
|
||||||
<TransferProjectDialog
|
<TransferProjectDialog
|
||||||
open={transferProjectDialogOpen}
|
open={transferProjectDialogOpen}
|
||||||
setOpen={setTransferProjectDialogOpen}
|
setOpen={setTransferProjectDialogOpen}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { CommonDataGridCellProps } from '@/components/dataGrid/DataGridCell
|
|||||||
import { useDataGridCell } from '@/components/dataGrid/DataGridCell';
|
import { useDataGridCell } from '@/components/dataGrid/DataGridCell';
|
||||||
import { ReadOnlyToggle } from '@/components/presentational/ReadOnlyToggle';
|
import { ReadOnlyToggle } from '@/components/presentational/ReadOnlyToggle';
|
||||||
import { Dropdown } from '@/components/ui/v2/Dropdown';
|
import { Dropdown } from '@/components/ui/v2/Dropdown';
|
||||||
import type { KeyboardEvent as ReactKeyboardEvent, MouseEvent } from 'react';
|
import type { MouseEvent, KeyboardEvent as ReactKeyboardEvent } from 'react';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
export type DataGridBooleanCellProps<TData extends object> =
|
export type DataGridBooleanCellProps<TData extends object> =
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ export default function DataGridPreviewCell<TData extends object>({
|
|||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
className="absolute top-2 right-2 z-50 p-2"
|
className="absolute right-2 top-2 z-50 p-2"
|
||||||
sx={{
|
sx={{
|
||||||
[`&:hover, &:active, &:focus`]: {
|
[`&:hover, &:active, &:focus`]: {
|
||||||
backgroundColor: (theme) => {
|
backgroundColor: (theme) => {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/ui/v3/popover';
|
} from '@/components/ui/v3/popover';
|
||||||
|
|
||||||
|
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||||
import { Check, ChevronsUpDown } from 'lucide-react';
|
import { Check, ChevronsUpDown } from 'lucide-react';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
@@ -40,6 +41,8 @@ export default function OrgPagesComboBox() {
|
|||||||
asPath,
|
asPath,
|
||||||
} = useRouter();
|
} = useRouter();
|
||||||
|
|
||||||
|
const isPlatform = useIsPlatform();
|
||||||
|
|
||||||
const pathSegments = useMemo(() => asPath.split('/'), [asPath]);
|
const pathSegments = useMemo(() => asPath.split('/'), [asPath]);
|
||||||
const orgPageFromUrl = pathSegments[3] || null;
|
const orgPageFromUrl = pathSegments[3] || null;
|
||||||
|
|
||||||
@@ -64,7 +67,7 @@ export default function OrgPagesComboBox() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger disabled={!isPlatform} asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -75,7 +78,7 @@ export default function OrgPagesComboBox() {
|
|||||||
) : (
|
) : (
|
||||||
<>Select a page</>
|
<>Select a page</>
|
||||||
)}
|
)}
|
||||||
<ChevronsUpDown className="w-5 h-5 text-muted-foreground" />
|
<ChevronsUpDown className="h-5 w-5 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="p-0" side="bottom" align="start">
|
<PopoverContent className="p-0" side="bottom" align="start">
|
||||||
@@ -103,7 +106,7 @@ export default function OrgPagesComboBox() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
<span className="truncate max-w-52">{option.label}</span>
|
<span className="max-w-52 truncate">{option.label}</span>
|
||||||
</div>
|
</div>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export default function OrgsComboBox() {
|
|||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="justify-between w-full gap-2 bg-background text-foreground hover:bg-accent dark:hover:bg-muted"
|
className="w-full justify-between gap-2 bg-background text-foreground hover:bg-accent dark:hover:bg-muted"
|
||||||
>
|
>
|
||||||
{selectedItem ? (
|
{selectedItem ? (
|
||||||
<div className="flex flex-row items-center justify-center">
|
<div className="flex flex-row items-center justify-center">
|
||||||
@@ -115,7 +115,7 @@ export default function OrgsComboBox() {
|
|||||||
) : (
|
) : (
|
||||||
'Select organization / workspace'
|
'Select organization / workspace'
|
||||||
)}
|
)}
|
||||||
<ChevronsUpDown className="w-5 h-5 text-muted-foreground" />
|
<ChevronsUpDown className="h-5 w-5 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="p-0" side="bottom" align="start">
|
<PopoverContent className="p-0" side="bottom" align="start">
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/ui/v3/popover';
|
} from '@/components/ui/v3/popover';
|
||||||
|
import { useIsPlatform } from '@/features/orgs/projects/common/hooks/useIsPlatform';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect, useMemo, useState, type ReactElement } from 'react';
|
import { useEffect, useMemo, useState, type ReactElement } from 'react';
|
||||||
@@ -40,88 +41,10 @@ type Option = {
|
|||||||
value: string;
|
value: string;
|
||||||
label: string;
|
label: string;
|
||||||
icon: ReactElement;
|
icon: ReactElement;
|
||||||
|
disabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const projectPages = [
|
type SelectedOption = Omit<Option, 'disabled'>;
|
||||||
{
|
|
||||||
label: 'Overview',
|
|
||||||
value: 'overview',
|
|
||||||
icon: <HomeIcon className="w-4 h-4" />,
|
|
||||||
slug: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Database',
|
|
||||||
value: 'database',
|
|
||||||
icon: <DatabaseIcon className="w-4 h-4" />,
|
|
||||||
slug: '/database/browser/default',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'GraphQL',
|
|
||||||
value: 'graphql',
|
|
||||||
icon: <GraphQLIcon className="w-4 h-4" />,
|
|
||||||
slug: 'graphql',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Hasura',
|
|
||||||
value: 'hasura',
|
|
||||||
icon: <HasuraIcon className="w-4 h-4" />,
|
|
||||||
slug: 'hasura',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Auth',
|
|
||||||
value: 'users',
|
|
||||||
icon: <UserIcon className="w-4 h-4" />,
|
|
||||||
slug: 'users',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Storage',
|
|
||||||
value: 'storage',
|
|
||||||
icon: <StorageIcon className="w-4 h-4" />,
|
|
||||||
slug: 'storage',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Run',
|
|
||||||
value: 'run',
|
|
||||||
icon: <ServicesIcon className="w-4 h-4" />,
|
|
||||||
slug: 'run',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'AI',
|
|
||||||
value: 'ai',
|
|
||||||
icon: <AIIcon className="w-4 h-4" />,
|
|
||||||
slug: 'ai/auto-embeddings',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Deployments',
|
|
||||||
value: 'deployments',
|
|
||||||
icon: <RocketIcon className="w-4 h-4" />,
|
|
||||||
slug: 'deployments',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Backups',
|
|
||||||
value: 'backups',
|
|
||||||
icon: <CloudIcon className="w-4 h-4" />,
|
|
||||||
slug: 'backups',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Logs',
|
|
||||||
value: 'logs',
|
|
||||||
icon: <FileTextIcon className="w-4 h-4" />,
|
|
||||||
slug: 'logs',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Metrics',
|
|
||||||
value: 'metrics',
|
|
||||||
icon: <GaugeIcon className="w-4 h-4" />,
|
|
||||||
slug: 'metrics',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Settings',
|
|
||||||
value: 'settings',
|
|
||||||
icon: <CogIcon className="w-4 h-4" />,
|
|
||||||
slug: 'settings',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function ProjectPagesComboBox() {
|
export default function ProjectPagesComboBox() {
|
||||||
const {
|
const {
|
||||||
@@ -130,6 +53,105 @@ export default function ProjectPagesComboBox() {
|
|||||||
asPath,
|
asPath,
|
||||||
} = useRouter();
|
} = useRouter();
|
||||||
|
|
||||||
|
const isPlatform = useIsPlatform();
|
||||||
|
|
||||||
|
const projectPages = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
label: 'Overview',
|
||||||
|
value: 'overview',
|
||||||
|
icon: <HomeIcon className="h-4 w-4" />,
|
||||||
|
slug: '',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Database',
|
||||||
|
value: 'database',
|
||||||
|
icon: <DatabaseIcon className="h-4 w-4" />,
|
||||||
|
slug: '/database/browser/default',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'GraphQL',
|
||||||
|
value: 'graphql',
|
||||||
|
icon: <GraphQLIcon className="h-4 w-4" />,
|
||||||
|
slug: 'graphql',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hasura',
|
||||||
|
value: 'hasura',
|
||||||
|
icon: <HasuraIcon className="h-4 w-4" />,
|
||||||
|
slug: 'hasura',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Auth',
|
||||||
|
value: 'users',
|
||||||
|
icon: <UserIcon className="h-4 w-4" />,
|
||||||
|
slug: 'users',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Storage',
|
||||||
|
value: 'storage',
|
||||||
|
icon: <StorageIcon className="h-4 w-4" />,
|
||||||
|
slug: 'storage',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Run',
|
||||||
|
value: 'run',
|
||||||
|
icon: <ServicesIcon className="h-4 w-4" />,
|
||||||
|
slug: 'run',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'AI',
|
||||||
|
value: 'ai',
|
||||||
|
icon: <AIIcon className="h-4 w-4" />,
|
||||||
|
slug: 'ai/auto-embeddings',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Deployments',
|
||||||
|
value: 'deployments',
|
||||||
|
icon: <RocketIcon className="h-4 w-4" />,
|
||||||
|
slug: 'deployments',
|
||||||
|
disabled: !isPlatform,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Backups',
|
||||||
|
value: 'backups',
|
||||||
|
icon: <CloudIcon className="h-4 w-4" />,
|
||||||
|
slug: 'backups',
|
||||||
|
disabled: !isPlatform,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Logs',
|
||||||
|
value: 'logs',
|
||||||
|
icon: <FileTextIcon className="h-4 w-4" />,
|
||||||
|
slug: 'logs',
|
||||||
|
disabled: !isPlatform,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Metrics',
|
||||||
|
value: 'metrics',
|
||||||
|
icon: <GaugeIcon className="h-4 w-4" />,
|
||||||
|
slug: 'metrics',
|
||||||
|
disabled: !isPlatform,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Settings',
|
||||||
|
value: 'settings',
|
||||||
|
icon: <CogIcon className="h-4 w-4" />,
|
||||||
|
slug: 'settings',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[isPlatform],
|
||||||
|
);
|
||||||
|
|
||||||
const pathSegments = useMemo(() => asPath.split('/'), [asPath]);
|
const pathSegments = useMemo(() => asPath.split('/'), [asPath]);
|
||||||
const projectPageFromUrl = appSubdomain
|
const projectPageFromUrl = appSubdomain
|
||||||
? pathSegments[5] || 'overview'
|
? pathSegments[5] || 'overview'
|
||||||
@@ -137,9 +159,8 @@ export default function ProjectPagesComboBox() {
|
|||||||
const selectedProjectPageFromUrl = projectPages.find(
|
const selectedProjectPageFromUrl = projectPages.find(
|
||||||
(item) => item.value === projectPageFromUrl,
|
(item) => item.value === projectPageFromUrl,
|
||||||
);
|
);
|
||||||
const [selectedProjectPage, setSelectedProjectPage] = useState<Option | null>(
|
const [selectedProjectPage, setSelectedProjectPage] =
|
||||||
null,
|
useState<SelectedOption | null>(null);
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedProjectPageFromUrl) {
|
if (selectedProjectPageFromUrl) {
|
||||||
@@ -155,6 +176,7 @@ export default function ProjectPagesComboBox() {
|
|||||||
label: app.label,
|
label: app.label,
|
||||||
value: app.slug,
|
value: app.slug,
|
||||||
icon: app.icon,
|
icon: app.icon,
|
||||||
|
disabled: app.disabled,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -175,7 +197,7 @@ export default function ProjectPagesComboBox() {
|
|||||||
) : (
|
) : (
|
||||||
<>Select a page</>
|
<>Select a page</>
|
||||||
)}
|
)}
|
||||||
<ChevronsUpDown className="w-5 h-5 text-muted-foreground" />
|
<ChevronsUpDown className="h-5 w-5 text-muted-foreground" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="p-0" side="bottom" align="start">
|
<PopoverContent className="p-0" side="bottom" align="start">
|
||||||
@@ -188,6 +210,7 @@ export default function ProjectPagesComboBox() {
|
|||||||
<CommandItem
|
<CommandItem
|
||||||
key={option.value}
|
key={option.value}
|
||||||
value={option.label}
|
value={option.label}
|
||||||
|
disabled={option.disabled}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
setSelectedProjectPage(option);
|
setSelectedProjectPage(option);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
@@ -206,7 +229,7 @@ export default function ProjectPagesComboBox() {
|
|||||||
/>
|
/>
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
{option.icon}
|
{option.icon}
|
||||||
<span className="truncate max-w-52">{option.label}</span>
|
<span className="max-w-52 truncate">{option.label}</span>
|
||||||
</div>
|
</div>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default function MainNav({ container }: MainNavProps) {
|
|||||||
className="min- absolute left-0 z-50 flex h-full w-6 justify-center border-r-[1px] bg-background pt-1 hover:bg-accent"
|
className="min- absolute left-0 z-50 flex h-full w-6 justify-center border-r-[1px] bg-background pt-1 hover:bg-accent"
|
||||||
onMouseEnter={() => setOpen(true)}
|
onMouseEnter={() => setOpen(true)}
|
||||||
>
|
>
|
||||||
<Menu className="w-4 h-4" />
|
<Menu className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SheetContent
|
<SheetContent
|
||||||
@@ -65,16 +65,16 @@ export default function MainNav({ container }: MainNavProps) {
|
|||||||
</SheetDescription>
|
</SheetDescription>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
|
|
||||||
<div className="flex flex-row items-center justify-end w-full h-12 px-1 border-b bg-background">
|
<div className="flex h-12 w-full flex-row items-center justify-end border-b bg-background px-1">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="hidden sm:flex"
|
className="hidden sm:flex"
|
||||||
onClick={() => setMainNavPinned(!mainNavPinned)}
|
onClick={() => setMainNavPinned(!mainNavPinned)}
|
||||||
>
|
>
|
||||||
{mainNavPinned ? (
|
{mainNavPinned ? (
|
||||||
<PinOff className="w-5 h-5" />
|
<PinOff className="h-5 w-5" />
|
||||||
) : (
|
) : (
|
||||||
<Pin className="w-5 h-5" />
|
<Pin className="h-5 w-5" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ export default function MainNav({ container }: MainNavProps) {
|
|||||||
className="flex sm:hidden"
|
className="flex sm:hidden"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
<X className="w-5 h-5" />
|
<X className="h-5 w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -54,15 +54,15 @@ export default function PinnedMainNav() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full flex-shrink-0 flex-col border-r p-0 sm:max-w-[310px]">
|
<div className="flex h-full w-full flex-shrink-0 flex-col border-r p-0 sm:max-w-[310px]">
|
||||||
<div className="flex justify-end w-full h-12 p-1 border-b bg-background">
|
<div className="flex h-12 w-full justify-end border-b bg-background p-1">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => setMainNavPinned(!mainNavPinned)}
|
onClick={() => setMainNavPinned(!mainNavPinned)}
|
||||||
>
|
>
|
||||||
{mainNavPinned ? (
|
{mainNavPinned ? (
|
||||||
<PinOff className="w-5 h-5" />
|
<PinOff className="h-5 w-5" />
|
||||||
) : (
|
) : (
|
||||||
<Pin className="w-5 h-5" />
|
<Pin className="h-5 w-5" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,18 +17,18 @@ export default function SettingsLayout({ children }: SettingsLayoutProps) {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{ backgroundColor: 'background.default' }}
|
sx={{ backgroundColor: 'background.default' }}
|
||||||
className="flex flex-col flex-auto w-full overflow-x-hidden overflow-y-auto"
|
className="flex w-full flex-auto flex-col overflow-y-auto overflow-x-hidden"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{ backgroundColor: 'background.default' }}
|
sx={{ backgroundColor: 'background.default' }}
|
||||||
className="flex flex-col h-full"
|
className="flex h-full flex-col"
|
||||||
>
|
>
|
||||||
<RetryableErrorBoundary>
|
<RetryableErrorBoundary>
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex flex-col space-y-2">
|
||||||
{hasGitRepo && (
|
{hasGitRepo && (
|
||||||
<Alert
|
<Alert
|
||||||
severity="warning"
|
severity="warning"
|
||||||
className="grid grid-flow-row gap-2 place-content-center"
|
className="grid grid-flow-row place-content-center gap-2"
|
||||||
>
|
>
|
||||||
<Text color="warning" className="text-sm">
|
<Text color="warning" className="text-sm">
|
||||||
As you have a connected repository, make sure to synchronize
|
As you have a connected repository, make sure to synchronize
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default function LoadingScreen({
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'absolute top-0 left-0 bottom-0 right-0 z-50 flex h-full w-full items-center justify-center',
|
'absolute bottom-0 left-0 right-0 top-0 z-50 flex h-full w-full items-center justify-center',
|
||||||
className,
|
className,
|
||||||
slotProps?.root?.className,
|
slotProps?.root?.className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export default function Modal({
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'flex min-h-screen items-center justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0',
|
'flex min-h-screen items-center justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0',
|
||||||
wrapperClassName,
|
wrapperClassName,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import { Input, inputClasses } from '@/components/ui/v2/Input';
|
|||||||
import { OptionBase } from '@/components/ui/v2/Option';
|
import { OptionBase } from '@/components/ui/v2/Option';
|
||||||
import { OptionGroupBase } from '@/components/ui/v2/OptionGroup';
|
import { OptionGroupBase } from '@/components/ui/v2/OptionGroup';
|
||||||
import type { StyledComponent } from '@emotion/styled';
|
import type { StyledComponent } from '@emotion/styled';
|
||||||
|
import { Popper } from '@mui/base';
|
||||||
import type { UseAutocompleteProps } from '@mui/base/useAutocomplete';
|
import type { UseAutocompleteProps } from '@mui/base/useAutocomplete';
|
||||||
import { createFilterOptions } from '@mui/base/useAutocomplete';
|
import { createFilterOptions } from '@mui/base/useAutocomplete';
|
||||||
import { Popper } from '@mui/base'
|
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import type { AutocompleteProps as MaterialAutocompleteProps } from '@mui/material/Autocomplete';
|
import type { AutocompleteProps as MaterialAutocompleteProps } from '@mui/material/Autocomplete';
|
||||||
import MaterialAutocomplete, {
|
import MaterialAutocomplete, {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
|||||||
import type { SxProps, Theme } from '@mui/material';
|
import type { SxProps, Theme } from '@mui/material';
|
||||||
import { alpha, styled } from '@mui/material';
|
import { alpha, styled } from '@mui/material';
|
||||||
import type {
|
import type {
|
||||||
ButtonProps as MaterialButtonProps,
|
|
||||||
ButtonTypeMap,
|
ButtonTypeMap,
|
||||||
|
ButtonProps as MaterialButtonProps,
|
||||||
} from '@mui/material/Button';
|
} from '@mui/material/Button';
|
||||||
import MaterialButton, { buttonClasses } from '@mui/material/Button';
|
import MaterialButton, { buttonClasses } from '@mui/material/Button';
|
||||||
import type { ForwardedRef } from 'react';
|
import type { ForwardedRef } from 'react';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import type {
|
import type {
|
||||||
DividerProps as MaterialDividerProps,
|
|
||||||
DividerTypeMap,
|
DividerTypeMap,
|
||||||
|
DividerProps as MaterialDividerProps,
|
||||||
} from '@mui/material/Divider';
|
} from '@mui/material/Divider';
|
||||||
import MaterialDivider from '@mui/material/Divider';
|
import MaterialDivider from '@mui/material/Divider';
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const AccordionContent = React.forwardRef<
|
|||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, children, ...props }, ref) => (
|
||||||
<AccordionPrimitive.Content
|
<AccordionPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm transition-all"
|
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div className={cn('pb-4 pt-0', className)}>{children}</div>
|
<div className={cn('pb-4 pt-0', className)}>{children}</div>
|
||||||
@@ -55,4 +55,4 @@ const AccordionContent = React.forwardRef<
|
|||||||
|
|
||||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
||||||
|
|||||||
@@ -126,14 +126,14 @@ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogPortal,
|
|
||||||
AlertDialogOverlay,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogOverlay,
|
||||||
|
AlertDialogPortal,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogTrigger,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,4 +58,4 @@ const AlertDescription = React.forwardRef<
|
|||||||
));
|
));
|
||||||
AlertDescription.displayName = 'AlertDescription';
|
AlertDescription.displayName = 'AlertDescription';
|
||||||
|
|
||||||
export { Alert, AlertTitle, AlertDescription };
|
export { Alert, AlertDescription, AlertTitle };
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ const BreadcrumbEllipsis = ({
|
|||||||
className={cn('flex h-9 w-9 items-center justify-center', className)}
|
className={cn('flex h-9 w-9 items-center justify-center', className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<MoreHorizontal className="w-4 h-4" />
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
<span className="sr-only">More</span>
|
<span className="sr-only">More</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -106,10 +106,10 @@ BreadcrumbEllipsis.displayName = 'BreadcrumbElipssis';
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbList,
|
BreadcrumbEllipsis,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
BreadcrumbLink,
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
BreadcrumbSeparator,
|
BreadcrumbSeparator,
|
||||||
BreadcrumbEllipsis,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as React from "react"
|
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
import { Check } from 'lucide-react';
|
||||||
import { Check } from "lucide-react"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const Checkbox = React.forwardRef<
|
const Checkbox = React.forwardRef<
|
||||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||||
@@ -11,18 +11,18 @@ const Checkbox = React.forwardRef<
|
|||||||
<CheckboxPrimitive.Root
|
<CheckboxPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
'peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<CheckboxPrimitive.Indicator
|
<CheckboxPrimitive.Indicator
|
||||||
className={cn("flex items-center justify-center text-current")}
|
className={cn('flex items-center justify-center text-current')}
|
||||||
>
|
>
|
||||||
<Check className="h-4 w-4" />
|
<Check className="h-4 w-4" />
|
||||||
</CheckboxPrimitive.Indicator>
|
</CheckboxPrimitive.Indicator>
|
||||||
</CheckboxPrimitive.Root>
|
</CheckboxPrimitive.Root>
|
||||||
))
|
));
|
||||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName
|
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
||||||
|
|
||||||
export { Checkbox }
|
export { Checkbox };
|
||||||
|
|||||||
@@ -169,13 +169,13 @@ CommandCreateItem.displayName = 'CommandCreateItem';
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Command,
|
Command,
|
||||||
|
CommandCreateItem,
|
||||||
CommandDialog,
|
CommandDialog,
|
||||||
CommandInput,
|
|
||||||
CommandList,
|
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
CommandGroup,
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandShortcut,
|
CommandList,
|
||||||
CommandSeparator,
|
CommandSeparator,
|
||||||
CommandCreateItem,
|
CommandShortcut,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -109,13 +109,13 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogPortal,
|
|
||||||
DialogOverlay,
|
|
||||||
DialogClose,
|
DialogClose,
|
||||||
DialogTrigger,
|
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
|
||||||
DialogFooter,
|
|
||||||
DialogTitle,
|
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogPortal,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,42 +1,42 @@
|
|||||||
import * as React from "react"
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
import { Check, ChevronRight, Circle } from 'lucide-react';
|
||||||
import { Check, ChevronRight, Circle } from "lucide-react"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||||
|
|
||||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||||
|
|
||||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
||||||
|
|
||||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
||||||
|
|
||||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||||
|
|
||||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||||
|
|
||||||
const DropdownMenuSubTrigger = React.forwardRef<
|
const DropdownMenuSubTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||||
inset?: boolean
|
inset?: boolean;
|
||||||
}
|
}
|
||||||
>(({ className, inset, children, ...props }, ref) => (
|
>(({ className, inset, children, ...props }, ref) => (
|
||||||
<DropdownMenuPrimitive.SubTrigger
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
|
||||||
inset && "pl-8",
|
inset && 'pl-8',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<ChevronRight className="ml-auto h-4 w-4" />
|
<ChevronRight className="ml-auto h-4 w-4" />
|
||||||
</DropdownMenuPrimitive.SubTrigger>
|
</DropdownMenuPrimitive.SubTrigger>
|
||||||
))
|
));
|
||||||
DropdownMenuSubTrigger.displayName =
|
DropdownMenuSubTrigger.displayName =
|
||||||
DropdownMenuPrimitive.SubTrigger.displayName
|
DropdownMenuPrimitive.SubTrigger.displayName;
|
||||||
|
|
||||||
const DropdownMenuSubContent = React.forwardRef<
|
const DropdownMenuSubContent = React.forwardRef<
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||||
@@ -45,14 +45,14 @@ const DropdownMenuSubContent = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.SubContent
|
<DropdownMenuPrimitive.SubContent
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
DropdownMenuSubContent.displayName =
|
DropdownMenuSubContent.displayName =
|
||||||
DropdownMenuPrimitive.SubContent.displayName
|
DropdownMenuPrimitive.SubContent.displayName;
|
||||||
|
|
||||||
const DropdownMenuContent = React.forwardRef<
|
const DropdownMenuContent = React.forwardRef<
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||||
@@ -63,32 +63,32 @@ const DropdownMenuContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</DropdownMenuPrimitive.Portal>
|
</DropdownMenuPrimitive.Portal>
|
||||||
))
|
));
|
||||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
||||||
|
|
||||||
const DropdownMenuItem = React.forwardRef<
|
const DropdownMenuItem = React.forwardRef<
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||||
inset?: boolean
|
inset?: boolean;
|
||||||
}
|
}
|
||||||
>(({ className, inset, ...props }, ref) => (
|
>(({ className, inset, ...props }, ref) => (
|
||||||
<DropdownMenuPrimitive.Item
|
<DropdownMenuPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
inset && "pl-8",
|
inset && 'pl-8',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
||||||
|
|
||||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||||
@@ -97,8 +97,8 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.CheckboxItem
|
<DropdownMenuPrimitive.CheckboxItem
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -110,9 +110,9 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|||||||
</span>
|
</span>
|
||||||
{children}
|
{children}
|
||||||
</DropdownMenuPrimitive.CheckboxItem>
|
</DropdownMenuPrimitive.CheckboxItem>
|
||||||
))
|
));
|
||||||
DropdownMenuCheckboxItem.displayName =
|
DropdownMenuCheckboxItem.displayName =
|
||||||
DropdownMenuPrimitive.CheckboxItem.displayName
|
DropdownMenuPrimitive.CheckboxItem.displayName;
|
||||||
|
|
||||||
const DropdownMenuRadioItem = React.forwardRef<
|
const DropdownMenuRadioItem = React.forwardRef<
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||||
@@ -121,8 +121,8 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.RadioItem
|
<DropdownMenuPrimitive.RadioItem
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@@ -133,26 +133,26 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|||||||
</span>
|
</span>
|
||||||
{children}
|
{children}
|
||||||
</DropdownMenuPrimitive.RadioItem>
|
</DropdownMenuPrimitive.RadioItem>
|
||||||
))
|
));
|
||||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
||||||
|
|
||||||
const DropdownMenuLabel = React.forwardRef<
|
const DropdownMenuLabel = React.forwardRef<
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||||
inset?: boolean
|
inset?: boolean;
|
||||||
}
|
}
|
||||||
>(({ className, inset, ...props }, ref) => (
|
>(({ className, inset, ...props }, ref) => (
|
||||||
<DropdownMenuPrimitive.Label
|
<DropdownMenuPrimitive.Label
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"px-2 py-1.5 text-sm font-semibold",
|
'px-2 py-1.5 text-sm font-semibold',
|
||||||
inset && "pl-8",
|
inset && 'pl-8',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
||||||
|
|
||||||
const DropdownMenuSeparator = React.forwardRef<
|
const DropdownMenuSeparator = React.forwardRef<
|
||||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||||
@@ -160,11 +160,11 @@ const DropdownMenuSeparator = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<DropdownMenuPrimitive.Separator
|
<DropdownMenuPrimitive.Separator
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
||||||
|
|
||||||
const DropdownMenuShortcut = ({
|
const DropdownMenuShortcut = ({
|
||||||
className,
|
className,
|
||||||
@@ -172,27 +172,27 @@ const DropdownMenuShortcut = ({
|
|||||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuTrigger,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuRadioItem,
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuPortal,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuShortcut,
|
DropdownMenuShortcut,
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuPortal,
|
|
||||||
DropdownMenuSub,
|
DropdownMenuSub,
|
||||||
DropdownMenuSubContent,
|
DropdownMenuSubContent,
|
||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
DropdownMenuRadioGroup,
|
DropdownMenuTrigger,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -164,12 +164,12 @@ const FormMessage = React.forwardRef<
|
|||||||
FormMessage.displayName = 'FormMessage';
|
FormMessage.displayName = 'FormMessage';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useFormField,
|
|
||||||
Form,
|
Form,
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormControl,
|
FormControl,
|
||||||
FormDescription,
|
FormDescription,
|
||||||
FormMessage,
|
|
||||||
FormField,
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
useFormField,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import * as React from "react"
|
import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
|
||||||
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const HoverCard = HoverCardPrimitive.Root
|
const HoverCard = HoverCardPrimitive.Root;
|
||||||
|
|
||||||
const HoverCardTrigger = HoverCardPrimitive.Trigger
|
const HoverCardTrigger = HoverCardPrimitive.Trigger;
|
||||||
|
|
||||||
const HoverCardContent = React.forwardRef<
|
const HoverCardContent = React.forwardRef<
|
||||||
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
||||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
|
||||||
<HoverCardPrimitive.Content
|
<HoverCardPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
align={align}
|
align={align}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
|
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
export { HoverCard, HoverCardContent, HoverCardTrigger };
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import * as React from "react"
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const labelVariants = cva(
|
const labelVariants = cva(
|
||||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||||
)
|
);
|
||||||
|
|
||||||
const Label = React.forwardRef<
|
const Label = React.forwardRef<
|
||||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
@@ -18,7 +18,7 @@ const Label = React.forwardRef<
|
|||||||
className={cn(labelVariants(), className)}
|
className={cn(labelVariants(), className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
Label.displayName = LabelPrimitive.Root.displayName
|
Label.displayName = LabelPrimitive.Root.displayName;
|
||||||
|
|
||||||
export { Label }
|
export { Label };
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import * as React from "react"
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
||||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const Popover = PopoverPrimitive.Root
|
const Popover = PopoverPrimitive.Root;
|
||||||
|
|
||||||
const PopoverTrigger = PopoverPrimitive.Trigger
|
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||||
|
|
||||||
const PopoverContent = React.forwardRef<
|
const PopoverContent = React.forwardRef<
|
||||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
|
||||||
<PopoverPrimitive.Portal>
|
<PopoverPrimitive.Portal>
|
||||||
<PopoverPrimitive.Content
|
<PopoverPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
align={align}
|
align={align}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</PopoverPrimitive.Portal>
|
</PopoverPrimitive.Portal>
|
||||||
))
|
));
|
||||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Popover, PopoverTrigger, PopoverContent }
|
export { Popover, PopoverContent, PopoverTrigger };
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const Progress = React.forwardRef<
|
|||||||
<ProgressPrimitive.Indicator
|
<ProgressPrimitive.Indicator
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-full w-full flex-1 rounded-full bg-primary transition-all',
|
'h-full w-full flex-1 rounded-full bg-primary transition-all',
|
||||||
indeterminate && 'animate-progress origin-left',
|
indeterminate && 'origin-left animate-progress',
|
||||||
)}
|
)}
|
||||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as React from "react"
|
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
||||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
import { Circle } from 'lucide-react';
|
||||||
import { Circle } from "lucide-react"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const RadioGroup = React.forwardRef<
|
const RadioGroup = React.forwardRef<
|
||||||
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||||
@@ -10,13 +10,13 @@ const RadioGroup = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => {
|
>(({ className, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<RadioGroupPrimitive.Root
|
<RadioGroupPrimitive.Root
|
||||||
className={cn("grid gap-2", className)}
|
className={cn('grid gap-2', className)}
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
|
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
||||||
|
|
||||||
const RadioGroupItem = React.forwardRef<
|
const RadioGroupItem = React.forwardRef<
|
||||||
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||||
@@ -26,8 +26,8 @@ const RadioGroupItem = React.forwardRef<
|
|||||||
<RadioGroupPrimitive.Item
|
<RadioGroupPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
'aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@@ -35,8 +35,8 @@ const RadioGroupItem = React.forwardRef<
|
|||||||
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
||||||
</RadioGroupPrimitive.Indicator>
|
</RadioGroupPrimitive.Indicator>
|
||||||
</RadioGroupPrimitive.Item>
|
</RadioGroupPrimitive.Item>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
|
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
||||||
|
|
||||||
export { RadioGroup, RadioGroupItem }
|
export { RadioGroup, RadioGroupItem };
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const SelectTrigger = React.forwardRef<
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<SelectPrimitive.Icon asChild>
|
<SelectPrimitive.Icon asChild>
|
||||||
<ChevronDown className="w-4 h-4 opacity-50" />
|
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||||
</SelectPrimitive.Icon>
|
</SelectPrimitive.Icon>
|
||||||
</SelectPrimitive.Trigger>
|
</SelectPrimitive.Trigger>
|
||||||
));
|
));
|
||||||
@@ -42,7 +42,7 @@ const SelectScrollUpButton = React.forwardRef<
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ChevronUp className="w-4 h-4" />
|
<ChevronUp className="h-4 w-4" />
|
||||||
</SelectPrimitive.ScrollUpButton>
|
</SelectPrimitive.ScrollUpButton>
|
||||||
));
|
));
|
||||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
||||||
@@ -59,7 +59,7 @@ const SelectScrollDownButton = React.forwardRef<
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ChevronDown className="w-4 h-4" />
|
<ChevronDown className="h-4 w-4" />
|
||||||
</SelectPrimitive.ScrollDownButton>
|
</SelectPrimitive.ScrollDownButton>
|
||||||
));
|
));
|
||||||
SelectScrollDownButton.displayName =
|
SelectScrollDownButton.displayName =
|
||||||
@@ -123,7 +123,7 @@ const SelectItem = React.forwardRef<
|
|||||||
>
|
>
|
||||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
<SelectPrimitive.ItemIndicator>
|
<SelectPrimitive.ItemIndicator>
|
||||||
<Check className="w-4 h-4" />
|
<Check className="h-4 w-4" />
|
||||||
</SelectPrimitive.ItemIndicator>
|
</SelectPrimitive.ItemIndicator>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -146,13 +146,13 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Select,
|
Select,
|
||||||
SelectGroup,
|
|
||||||
SelectValue,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectLabel,
|
SelectGroup,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectSeparator,
|
SelectLabel,
|
||||||
SelectScrollUpButton,
|
|
||||||
SelectScrollDownButton,
|
SelectScrollDownButton,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import * as React from "react"
|
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
||||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const Separator = React.forwardRef<
|
const Separator = React.forwardRef<
|
||||||
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
{ className, orientation = "horizontal", decorative = true, ...props },
|
{ className, orientation = 'horizontal', decorative = true, ...props },
|
||||||
ref
|
ref,
|
||||||
) => (
|
) => (
|
||||||
<SeparatorPrimitive.Root
|
<SeparatorPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
decorative={decorative}
|
decorative={decorative}
|
||||||
orientation={orientation}
|
orientation={orientation}
|
||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 bg-border",
|
'shrink-0 bg-border',
|
||||||
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
)
|
);
|
||||||
Separator.displayName = SeparatorPrimitive.Root.displayName
|
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
||||||
|
|
||||||
export { Separator }
|
export { Separator };
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const SheetOverlay = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<SheetPrimitive.Overlay
|
<SheetPrimitive.Overlay
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -80,7 +80,7 @@ const SheetContent = React.forwardRef<
|
|||||||
{children}
|
{children}
|
||||||
{!hideCloseButton && (
|
{!hideCloseButton && (
|
||||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||||
<X className="w-4 h-4" />
|
<X className="h-4 w-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</SheetPrimitive.Close>
|
</SheetPrimitive.Close>
|
||||||
)}
|
)}
|
||||||
@@ -150,13 +150,13 @@ SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Sheet,
|
Sheet,
|
||||||
SheetPortal,
|
|
||||||
SheetOverlay,
|
|
||||||
SheetTrigger,
|
|
||||||
SheetClose,
|
SheetClose,
|
||||||
SheetContent,
|
SheetContent,
|
||||||
SheetHeader,
|
|
||||||
SheetFooter,
|
|
||||||
SheetTitle,
|
|
||||||
SheetDescription,
|
SheetDescription,
|
||||||
|
SheetFooter,
|
||||||
|
SheetHeader,
|
||||||
|
SheetOverlay,
|
||||||
|
SheetPortal,
|
||||||
|
SheetTitle,
|
||||||
|
SheetTrigger,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "react"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const Table = React.forwardRef<
|
const Table = React.forwardRef<
|
||||||
HTMLTableElement,
|
HTMLTableElement,
|
||||||
@@ -9,20 +9,20 @@ const Table = React.forwardRef<
|
|||||||
<div className="relative w-full overflow-auto">
|
<div className="relative w-full overflow-auto">
|
||||||
<table
|
<table
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("w-full caption-bottom text-sm", className)}
|
className={cn('w-full caption-bottom text-sm', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))
|
));
|
||||||
Table.displayName = "Table"
|
Table.displayName = 'Table';
|
||||||
|
|
||||||
const TableHeader = React.forwardRef<
|
const TableHeader = React.forwardRef<
|
||||||
HTMLTableSectionElement,
|
HTMLTableSectionElement,
|
||||||
React.HTMLAttributes<HTMLTableSectionElement>
|
React.HTMLAttributes<HTMLTableSectionElement>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
||||||
))
|
));
|
||||||
TableHeader.displayName = "TableHeader"
|
TableHeader.displayName = 'TableHeader';
|
||||||
|
|
||||||
const TableBody = React.forwardRef<
|
const TableBody = React.forwardRef<
|
||||||
HTMLTableSectionElement,
|
HTMLTableSectionElement,
|
||||||
@@ -30,11 +30,11 @@ const TableBody = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<tbody
|
<tbody
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("[&_tr:last-child]:border-0", className)}
|
className={cn('[&_tr:last-child]:border-0', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TableBody.displayName = "TableBody"
|
TableBody.displayName = 'TableBody';
|
||||||
|
|
||||||
const TableFooter = React.forwardRef<
|
const TableFooter = React.forwardRef<
|
||||||
HTMLTableSectionElement,
|
HTMLTableSectionElement,
|
||||||
@@ -43,13 +43,13 @@ const TableFooter = React.forwardRef<
|
|||||||
<tfoot
|
<tfoot
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TableFooter.displayName = "TableFooter"
|
TableFooter.displayName = 'TableFooter';
|
||||||
|
|
||||||
const TableRow = React.forwardRef<
|
const TableRow = React.forwardRef<
|
||||||
HTMLTableRowElement,
|
HTMLTableRowElement,
|
||||||
@@ -58,13 +58,13 @@ const TableRow = React.forwardRef<
|
|||||||
<tr
|
<tr
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TableRow.displayName = "TableRow"
|
TableRow.displayName = 'TableRow';
|
||||||
|
|
||||||
const TableHead = React.forwardRef<
|
const TableHead = React.forwardRef<
|
||||||
HTMLTableCellElement,
|
HTMLTableCellElement,
|
||||||
@@ -73,13 +73,13 @@ const TableHead = React.forwardRef<
|
|||||||
<th
|
<th
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TableHead.displayName = "TableHead"
|
TableHead.displayName = 'TableHead';
|
||||||
|
|
||||||
const TableCell = React.forwardRef<
|
const TableCell = React.forwardRef<
|
||||||
HTMLTableCellElement,
|
HTMLTableCellElement,
|
||||||
@@ -87,11 +87,11 @@ const TableCell = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<td
|
<td
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TableCell.displayName = "TableCell"
|
TableCell.displayName = 'TableCell';
|
||||||
|
|
||||||
const TableCaption = React.forwardRef<
|
const TableCaption = React.forwardRef<
|
||||||
HTMLTableCaptionElement,
|
HTMLTableCaptionElement,
|
||||||
@@ -99,19 +99,19 @@ const TableCaption = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<caption
|
<caption
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
className={cn('mt-4 text-sm text-muted-foreground', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TableCaption.displayName = "TableCaption"
|
TableCaption.displayName = 'TableCaption';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Table,
|
Table,
|
||||||
TableHeader,
|
|
||||||
TableBody,
|
TableBody,
|
||||||
|
TableCaption,
|
||||||
|
TableCell,
|
||||||
TableFooter,
|
TableFooter,
|
||||||
TableHead,
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
TableCell,
|
};
|
||||||
TableCaption,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as React from "react"
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const TooltipProvider = TooltipPrimitive.Provider
|
const TooltipProvider = TooltipPrimitive.Provider;
|
||||||
|
|
||||||
const Tooltip = TooltipPrimitive.Root
|
const Tooltip = TooltipPrimitive.Root;
|
||||||
|
|
||||||
const TooltipTrigger = TooltipPrimitive.Trigger
|
const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||||
|
|
||||||
const TooltipContent = React.forwardRef<
|
const TooltipContent = React.forwardRef<
|
||||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
@@ -17,12 +17,12 @@ const TooltipContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import { Input } from '@/components/ui/v2/Input';
|
|||||||
import { Option } from '@/components/ui/v2/Option';
|
import { Option } from '@/components/ui/v2/Option';
|
||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import type { DialogFormProps } from '@/types/common';
|
import type { DialogFormProps } from '@/types/common';
|
||||||
|
import { GetPersonalAccessTokensDocument } from '@/utils/__generated__/graphql';
|
||||||
import { getToastStyleProps } from '@/utils/constants/settings';
|
import { getToastStyleProps } from '@/utils/constants/settings';
|
||||||
import { copy } from '@/utils/copy';
|
import { copy } from '@/utils/copy';
|
||||||
import { getDateComponents } from '@/utils/getDateComponents';
|
import { getDateComponents } from '@/utils/getDateComponents';
|
||||||
import { GetPersonalAccessTokensDocument } from '@/utils/__generated__/graphql';
|
|
||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { useNhostClient } from '@nhost/nextjs';
|
import { useNhostClient } from '@nhost/nextjs';
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { Box } from '@/components/ui/v2/Box';
|
|||||||
import { Button } from '@/components/ui/v2/Button';
|
import { Button } from '@/components/ui/v2/Button';
|
||||||
import { Checkbox } from '@/components/ui/v2/Checkbox';
|
import { Checkbox } from '@/components/ui/v2/Checkbox';
|
||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
useDeleteUserAccountMutation,
|
useDeleteUserAccountMutation,
|
||||||
useGetAllWorkspacesAndProjectsQuery,
|
useGetAllWorkspacesAndProjectsQuery,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { useSignOut, useUserData } from '@nhost/nextjs';
|
import { useSignOut, useUserData } from '@nhost/nextjs';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Form } from '@/components/form/Form';
|
import { Form } from '@/components/form/Form';
|
||||||
import { SettingsContainer } from '@/components/layout/SettingsContainer';
|
import { SettingsContainer } from '@/components/layout/SettingsContainer';
|
||||||
import { Input } from '@/components/ui/v2/Input';
|
import { Input } from '@/components/ui/v2/Input';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import { useUpdateUserDisplayNameMutation } from '@/utils/__generated__/graphql';
|
import { useUpdateUserDisplayNameMutation } from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { useUserData } from '@nhost/nextjs';
|
import { useUserData } from '@nhost/nextjs';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import { ListItem } from '@/components/ui/v2/ListItem';
|
|||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
||||||
import { CreatePATForm } from '@/features/account/settings/components/CreatePATForm';
|
import { CreatePATForm } from '@/features/account/settings/components/CreatePATForm';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
GetPersonalAccessTokensDocument,
|
GetPersonalAccessTokensDocument,
|
||||||
useDeletePersonalAccessTokenMutation,
|
useDeletePersonalAccessTokenMutation,
|
||||||
useGetPersonalAccessTokensQuery,
|
useGetPersonalAccessTokensQuery,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default function SocialProvidersSettings() {
|
|||||||
className="flex flex-row items-center justify-start space-x-2 rounded-md p-2"
|
className="flex flex-row items-center justify-start space-x-2 rounded-md p-2"
|
||||||
>
|
>
|
||||||
<GitHubIcon />
|
<GitHubIcon />
|
||||||
<Text className="font-medium ">Connected</Text>
|
<Text className="font-medium">Connected</Text>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import { Text } from '@/components/ui/v2/Text';
|
|||||||
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
||||||
import { GraphqlDataSourcesFormSection } from '@/features/ai/AssistantForm/components/GraphqlDataSourcesFormSection';
|
import { GraphqlDataSourcesFormSection } from '@/features/ai/AssistantForm/components/GraphqlDataSourcesFormSection';
|
||||||
import { WebhooksDataSourcesFormSection } from '@/features/ai/AssistantForm/components/WebhooksDataSourcesFormSection';
|
import { WebhooksDataSourcesFormSection } from '@/features/ai/AssistantForm/components/WebhooksDataSourcesFormSection';
|
||||||
import { useAdminApolloClient } from '@/features/orgs/projects/hooks/useAdminApolloClient'
|
import { useAdminApolloClient } from '@/features/orgs/projects/hooks/useAdminApolloClient';
|
||||||
|
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
|
||||||
import type { DialogFormProps } from '@/types/common';
|
import type { DialogFormProps } from '@/types/common';
|
||||||
import {
|
import {
|
||||||
useInsertAssistantMutation,
|
useInsertAssistantMutation,
|
||||||
useUpdateAssistantMutation,
|
useUpdateAssistantMutation,
|
||||||
} from '@/utils/__generated__/graphite.graphql';
|
} from '@/utils/__generated__/graphite.graphql';
|
||||||
import { execPromiseWithErrorToast } from '@/features/orgs/utils/execPromiseWithErrorToast';
|
|
||||||
import { removeTypename, type DeepRequired } from '@/utils/helpers';
|
import { removeTypename, type DeepRequired } from '@/utils/helpers';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
@@ -73,7 +73,7 @@ export interface AssistantFormProps extends DialogFormProps {
|
|||||||
/**
|
/**
|
||||||
* if there is initialData then it's an update operation
|
* if there is initialData then it's an update operation
|
||||||
*/
|
*/
|
||||||
initialData?: AssistantFormValues
|
initialData?: AssistantFormValues;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to be called when the operation is cancelled.
|
* Function to be called when the operation is cancelled.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default function ArgumentsFormSection({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="space-y-4">
|
<Box className="space-y-4">
|
||||||
<div className="flex flex-row items-center justify-between ">
|
<div className="flex flex-row items-center justify-between">
|
||||||
<div className="flex flex-row items-center space-x-2">
|
<div className="flex flex-row items-center space-x-2">
|
||||||
<Text variant="h4" className="font-semibold">
|
<Text variant="h4" className="font-semibold">
|
||||||
Arguments
|
Arguments
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import { Text } from '@/components/ui/v2/Text';
|
|||||||
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
||||||
import { useAdminApolloClient } from '@/features/projects/common/hooks/useAdminApolloClient';
|
import { useAdminApolloClient } from '@/features/projects/common/hooks/useAdminApolloClient';
|
||||||
import type { DialogFormProps } from '@/types/common';
|
import type { DialogFormProps } from '@/types/common';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
useInsertGraphiteAutoEmbeddingsConfigurationMutation,
|
useInsertGraphiteAutoEmbeddingsConfigurationMutation,
|
||||||
useUpdateGraphiteAutoEmbeddingsConfigurationMutation,
|
useUpdateGraphiteAutoEmbeddingsConfigurationMutation,
|
||||||
} from '@/utils/__generated__/graphite.graphql';
|
} from '@/utils/__generated__/graphite.graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
@@ -143,9 +143,9 @@ export default function AutoEmbeddingsForm({
|
|||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<Form
|
<Form
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className="flex flex-col h-full gap-4 overflow-hidden"
|
className="flex h-full flex-col gap-4 overflow-hidden"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col flex-1 px-6 space-y-6 overflow-auto">
|
<div className="flex flex-1 flex-col space-y-6 overflow-auto px-6">
|
||||||
<Input
|
<Input
|
||||||
{...register('name')}
|
{...register('name')}
|
||||||
id="name"
|
id="name"
|
||||||
@@ -155,7 +155,7 @@ export default function AutoEmbeddingsForm({
|
|||||||
<Tooltip title="Name of the Auto-Embeddings">
|
<Tooltip title="Name of the Auto-Embeddings">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -182,7 +182,7 @@ export default function AutoEmbeddingsForm({
|
|||||||
<Tooltip title="Auto-Embeddings Model">
|
<Tooltip title="Auto-Embeddings Model">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -208,7 +208,7 @@ export default function AutoEmbeddingsForm({
|
|||||||
<Tooltip title={<span>Schema where the table belongs to</span>}>
|
<Tooltip title={<span>Schema where the table belongs to</span>}>
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -230,7 +230,7 @@ export default function AutoEmbeddingsForm({
|
|||||||
<Tooltip title="Table Name">
|
<Tooltip title="Table Name">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -252,7 +252,7 @@ export default function AutoEmbeddingsForm({
|
|||||||
<Tooltip title="Column name">
|
<Tooltip title="Column name">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -274,7 +274,7 @@ export default function AutoEmbeddingsForm({
|
|||||||
<Tooltip title="Query">
|
<Tooltip title="Query">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -298,7 +298,7 @@ export default function AutoEmbeddingsForm({
|
|||||||
<Tooltip title="Mutation">
|
<Tooltip title="Mutation">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -315,7 +315,7 @@ export default function AutoEmbeddingsForm({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Box className="flex flex-row justify-between w-full px-6 py-4 border-t rounded">
|
<Box className="flex w-full flex-row justify-between rounded border-t px-6 py-4">
|
||||||
<Button variant="outlined" color="secondary" onClick={onCancel}>
|
<Button variant="outlined" color="secondary" onClick={onCancel}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ export default function DevAssistant() {
|
|||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<Box className="p-4">
|
<Box className="p-4">
|
||||||
<Alert className="grid items-center w-full grid-flow-col gap-2 place-content-between">
|
<Alert className="grid w-full grid-flow-col place-content-between items-center gap-2">
|
||||||
<Text className="grid grid-flow-row justify-items-start gap-0.5">
|
<Text className="grid grid-flow-row justify-items-start gap-0.5">
|
||||||
<Text component="span">
|
<Text component="span">
|
||||||
To enable graphite, configure the service first in{' '}
|
To enable graphite, configure the service first in{' '}
|
||||||
@@ -197,11 +197,11 @@ export default function DevAssistant() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full overflow-auto">
|
<div className="flex h-full flex-col overflow-auto">
|
||||||
<MessagesList loading={loading} />
|
<MessagesList loading={loading} />
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<Box className="relative flex flex-row justify-between w-full p-2">
|
<Box className="relative flex w-full flex-row justify-between p-2">
|
||||||
<Input
|
<Input
|
||||||
value={userInput}
|
value={userInput}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
@@ -224,7 +224,7 @@ export default function DevAssistant() {
|
|||||||
color="primary"
|
color="primary"
|
||||||
aria-label="Send"
|
aria-label="Send"
|
||||||
type="submit"
|
type="submit"
|
||||||
className="absolute self-end w-12 h-10 right-2 rounded-xl"
|
className="absolute right-2 h-10 w-12 self-end rounded-xl"
|
||||||
>
|
>
|
||||||
{loading ? <ActivityIndicator /> : <ArrowUpIcon />}
|
{loading ? <ActivityIndicator /> : <ArrowUpIcon />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function PreComponent(
|
|||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative group">
|
<div className="group relative">
|
||||||
<pre>{children}</pre>
|
<pre>{children}</pre>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
@@ -34,13 +34,13 @@ function PreComponent(
|
|||||||
}}
|
}}
|
||||||
color="warning"
|
color="warning"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
className="absolute hidden top-2 right-2 group-hover:flex"
|
className="absolute right-2 top-2 hidden group-hover:flex"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
copy(onlyText(children), 'Snippet');
|
copy(onlyText(children), 'Snippet');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-5 h-5" />
|
<CopyIcon className="h-5 w-5" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -53,7 +53,7 @@ export default function MessageBox({ message }: { message: Message }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className="flex flex-col p-4 space-y-4 border-t first:border-t-0"
|
className="flex flex-col space-y-4 border-t p-4 first:border-t-0"
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: isUserMessage && 'background.default',
|
backgroundColor: isUserMessage && 'background.default',
|
||||||
}}
|
}}
|
||||||
@@ -67,7 +67,7 @@ export default function MessageBox({ message }: { message: Message }) {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Avatar
|
<Avatar
|
||||||
className="rounded-full h-7 w-7"
|
className="h-7 w-7 rounded-full"
|
||||||
alt={user?.displayName}
|
alt={user?.displayName}
|
||||||
src={user?.avatarUrl}
|
src={user?.avatarUrl}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Input } from '@/components/ui/v2/Input';
|
|||||||
import { Switch } from '@/components/ui/v2/Switch';
|
import { Switch } from '@/components/ui/v2/Switch';
|
||||||
import { Text } from '@/components/ui/v2/Text';
|
import { Text } from '@/components/ui/v2/Text';
|
||||||
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
import { Tooltip } from '@/components/ui/v2/Tooltip';
|
||||||
|
import { isPostgresVersionValidForAI } from '@/features/ai/settings/utils/isPostgresVersionValidForAI';
|
||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||||
import { COST_PER_VCPU } from '@/features/projects/resources/settings/utils/resourceSettingsValidationSchema';
|
import { COST_PER_VCPU } from '@/features/projects/resources/settings/utils/resourceSettingsValidationSchema';
|
||||||
@@ -32,7 +33,6 @@ import { FormProvider, useForm } from 'react-hook-form';
|
|||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { DisableAIServiceConfirmationDialog } from './DisableAIServiceConfirmationDialog';
|
import { DisableAIServiceConfirmationDialog } from './DisableAIServiceConfirmationDialog';
|
||||||
import { isPostgresVersionValidForAI } from '@/features/ai/settings/utils/isPostgresVersionValidForAI';
|
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
version: Yup.object({
|
version: Yup.object({
|
||||||
@@ -267,7 +267,7 @@ export default function AISettings() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="space-y-4" sx={{ backgroundColor: 'background.default' }}>
|
<Box className="space-y-4" sx={{ backgroundColor: 'background.default' }}>
|
||||||
<Box className="flex flex-row items-center justify-between p-4 rounded-lg border-1">
|
<Box className="flex flex-row items-center justify-between rounded-lg border-1 p-4">
|
||||||
<Text className="text-lg font-semibold">Enable AI service</Text>
|
<Text className="text-lg font-semibold">Enable AI service</Text>
|
||||||
<Switch
|
<Switch
|
||||||
checked={aiServiceEnabled}
|
checked={aiServiceEnabled}
|
||||||
@@ -296,7 +296,7 @@ export default function AISettings() {
|
|||||||
<Tooltip title="Version of the service to use.">
|
<Tooltip title="Version of the service to use.">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -351,7 +351,7 @@ export default function AISettings() {
|
|||||||
<Tooltip title="Used to validate requests between postgres and the AI service. The AI service will also include the header X-Graphite-Webhook-Secret with this value set when calling external webhooks so the source of the request can be validated.">
|
<Tooltip title="Used to validate requests between postgres and the AI service. The AI service will also include the header X-Graphite-Webhook-Secret with this value set when calling external webhooks so the source of the request can be validated.">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -375,7 +375,7 @@ export default function AISettings() {
|
|||||||
<Tooltip title="Dedicated resources allocated for the service.">
|
<Tooltip title="Dedicated resources allocated for the service.">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -415,7 +415,7 @@ export default function AISettings() {
|
|||||||
<Tooltip title="Key to use for authenticating API requests to OpenAI">
|
<Tooltip title="Key to use for authenticating API requests to OpenAI">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -438,7 +438,7 @@ export default function AISettings() {
|
|||||||
<Tooltip title="Optional. OpenAI organization to use.">
|
<Tooltip title="Optional. OpenAI organization to use.">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -466,7 +466,7 @@ export default function AISettings() {
|
|||||||
<Tooltip title="How often to run the job that keeps embeddings up to date.">
|
<Tooltip title="How often to run the job that keeps embeddings up to date.">
|
||||||
<InfoIcon
|
<InfoIcon
|
||||||
aria-label="Info"
|
aria-label="Info"
|
||||||
className="w-4 h-4"
|
className="h-4 w-4"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -494,4 +494,3 @@ export default function AISettings() {
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { Text } from '@/components/ui/v2/Text';
|
|||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||||
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import { useUpdateConfigMutation } from '@/utils/__generated__/graphql';
|
import { useUpdateConfigMutation } from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ export default function AppleProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export default function AuthServiceVersionSettings() {
|
|||||||
}}
|
}}
|
||||||
docsLink="https://github.com/nhost/hasura-auth/releases"
|
docsLink="https://github.com/nhost/hasura-auth/releases"
|
||||||
docsTitle="the latest releases"
|
docsTitle="the latest releases"
|
||||||
className="grid grid-flow-row px-4 gap-x-4 gap-y-2 lg:grid-cols-5"
|
className="grid grid-flow-row gap-x-4 gap-y-2 px-4 lg:grid-cols-5"
|
||||||
>
|
>
|
||||||
<ControlledAutocomplete
|
<ControlledAutocomplete
|
||||||
id="version"
|
id="version"
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ export default function AzureADProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
|||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||||
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
GetAuthenticationSettingsDocument,
|
GetAuthenticationSettingsDocument,
|
||||||
useGetAuthenticationSettingsQuery,
|
useGetAuthenticationSettingsQuery,
|
||||||
useUpdateConfigMutation,
|
useUpdateConfigMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import { Text } from '@/components/ui/v2/Text';
|
|||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||||
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
GetSmtpSettingsDocument,
|
GetSmtpSettingsDocument,
|
||||||
useUpdateConfigMutation,
|
useUpdateConfigMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator';
|
|||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||||
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
useGetAuthenticationSettingsQuery,
|
useGetAuthenticationSettingsQuery,
|
||||||
useUpdateConfigMutation,
|
useUpdateConfigMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export default function DiscordProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export default function FacebookProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ export default function GitHubProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export default function GoogleProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export default function LinkedInProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { Input } from '@/components/ui/v2/Input';
|
|||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||||
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
GetSmtpSettingsDocument,
|
GetSmtpSettingsDocument,
|
||||||
useGetSmtpSettingsQuery,
|
useGetSmtpSettingsQuery,
|
||||||
useUpdateConfigMutation,
|
useUpdateConfigMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import { Input } from '@/components/ui/v2/Input';
|
|||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
import { useIsPlatform } from '@/features/projects/common/hooks/useIsPlatform';
|
||||||
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
import { useLocalMimirClient } from '@/hooks/useLocalMimirClient';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
GetSmtpSettingsDocument,
|
GetSmtpSettingsDocument,
|
||||||
useGetSmtpSettingsQuery,
|
useGetSmtpSettingsQuery,
|
||||||
useUpdateConfigMutation,
|
useUpdateConfigMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
import { type Optional } from 'utility-types';
|
import { type Optional } from 'utility-types';
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export default function SpotifyProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ export default function TwitchProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ export default function TwitterProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ export default function WindowsLiveProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ export default function WorkOsProviderSettings() {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CopyIcon className="w-4 h-4" />
|
<CopyIcon className="h-4 w-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/
|
|||||||
import { getUserRoles } from '@/features/projects/roles/settings/utils/getUserRoles';
|
import { getUserRoles } from '@/features/projects/roles/settings/utils/getUserRoles';
|
||||||
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
|
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
|
||||||
import type { DialogFormProps } from '@/types/common';
|
import type { DialogFormProps } from '@/types/common';
|
||||||
import { copy } from '@/utils/copy';
|
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
RemoteAppGetUsersDocument,
|
RemoteAppGetUsersAndAuthRolesDocument,
|
||||||
useGetProjectLocalesQuery,
|
useGetProjectLocalesQuery,
|
||||||
useGetRolesPermissionsQuery,
|
useGetRolesPermissionsQuery,
|
||||||
useUpdateRemoteAppUserMutation,
|
useUpdateRemoteAppUserMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { copy } from '@/utils/copy';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { useTheme } from '@mui/material';
|
import { useTheme } from '@mui/material';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
@@ -116,7 +116,7 @@ export default function EditUserForm({
|
|||||||
|
|
||||||
const [updateUser] = useUpdateRemoteAppUserMutation({
|
const [updateUser] = useUpdateRemoteAppUserMutation({
|
||||||
client: remoteProjectGQLClient,
|
client: remoteProjectGQLClient,
|
||||||
refetchQueries: [RemoteAppGetUsersDocument],
|
refetchQueries: [RemoteAppGetUsersAndAuthRolesDocument],
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useForm<EditUserFormValues>({
|
const form = useForm<EditUserFormValues>({
|
||||||
@@ -320,14 +320,14 @@ export default function EditUserForm({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<InputLabel as="h3" className="col-span-1 self-center ">
|
<InputLabel as="h3" className="col-span-1 self-center">
|
||||||
Created At
|
Created At
|
||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Text className="col-span-3 font-medium">
|
<Text className="col-span-3 font-medium">
|
||||||
{format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm:ss')}
|
{format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm:ss')}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<InputLabel as="h3" className="col-span-1 self-center ">
|
<InputLabel as="h3" className="col-span-1 self-center">
|
||||||
Last Seen
|
Last Seen
|
||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Text className="col-span-3 font-medium">
|
<Text className="col-span-3 font-medium">
|
||||||
@@ -386,7 +386,7 @@ export default function EditUserForm({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="col-span-1 my-1 grid grid-flow-col grid-cols-8 items-center">
|
<div className="col-span-1 my-1 grid grid-flow-col grid-cols-8 items-center">
|
||||||
<div className="col-span-2 ">
|
<div className="col-span-2">
|
||||||
<InputLabel as="h3">Password</InputLabel>
|
<InputLabel as="h3">Password</InputLabel>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import { Input } from '@/components/ui/v2/Input';
|
|||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
|
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
|
||||||
import type { DialogFormProps } from '@/types/common';
|
import type { DialogFormProps } from '@/types/common';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
import type { RemoteAppGetUsersAndAuthRolesQuery } from '@/utils/__generated__/graphql';
|
||||||
import type { RemoteAppGetUsersQuery } from '@/utils/__generated__/graphql';
|
|
||||||
import {
|
import {
|
||||||
useGetSignInMethodsQuery,
|
useGetSignInMethodsQuery,
|
||||||
useUpdateRemoteAppUserMutation,
|
useUpdateRemoteAppUserMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -26,7 +26,7 @@ export interface EditUserPasswordFormProps extends DialogFormProps {
|
|||||||
/**
|
/**
|
||||||
* The selected user.
|
* The selected user.
|
||||||
*/
|
*/
|
||||||
user: RemoteAppGetUsersQuery['users'][0];
|
user: RemoteAppGetUsersAndAuthRolesQuery['users'][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EditUserPasswordForm({
|
export default function EditUserPasswordForm({
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import { getReadableProviderName } from '@/features/authentication/users/utils/g
|
|||||||
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
import { useCurrentWorkspaceAndProject } from '@/features/projects/common/hooks/useCurrentWorkspaceAndProject';
|
||||||
import { getUserRoles } from '@/features/projects/roles/settings/utils/getUserRoles';
|
import { getUserRoles } from '@/features/projects/roles/settings/utils/getUserRoles';
|
||||||
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
|
import { useRemoteApplicationGQLClient } from '@/hooks/useRemoteApplicationGQLClient';
|
||||||
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
|
||||||
import {
|
import {
|
||||||
useDeleteRemoteAppUserRolesMutation,
|
useDeleteRemoteAppUserRolesMutation,
|
||||||
useGetRolesPermissionsQuery,
|
useGetRolesPermissionsQuery,
|
||||||
@@ -25,6 +24,7 @@ import {
|
|||||||
useRemoteAppDeleteUserMutation,
|
useRemoteAppDeleteUserMutation,
|
||||||
useUpdateRemoteAppUserMutation,
|
useUpdateRemoteAppUserMutation,
|
||||||
} from '@/utils/__generated__/graphql';
|
} from '@/utils/__generated__/graphql';
|
||||||
|
import { execPromiseWithErrorToast } from '@/utils/execPromiseWithErrorToast';
|
||||||
import { useTheme } from '@mui/material';
|
import { useTheme } from '@mui/material';
|
||||||
import { formatDistance } from 'date-fns';
|
import { formatDistance } from 'date-fns';
|
||||||
import kebabCase from 'just-kebab-case';
|
import kebabCase from 'just-kebab-case';
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default function BaseColumnForm({
|
|||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
onSubmit={handleExternalSubmit}
|
onSubmit={handleExternalSubmit}
|
||||||
className="flex flex-col content-between flex-auto overflow-hidden border-t-1"
|
className="flex flex-auto flex-col content-between overflow-hidden border-t-1"
|
||||||
>
|
>
|
||||||
<div className="flex-auto overflow-y-auto">
|
<div className="flex-auto overflow-y-auto">
|
||||||
<section className="grid grid-cols-8 px-6 py-3">
|
<section className="grid grid-cols-8 px-6 py-3">
|
||||||
@@ -184,7 +184,7 @@ export default function BaseColumnForm({
|
|||||||
</Text>
|
</Text>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
className="w-full col-span-8 py-3 m-0 sm:col-span-6 sm:col-start-3 sm:ml-1"
|
className="col-span-8 m-0 w-full py-3 sm:col-span-6 sm:col-start-3 sm:ml-1"
|
||||||
onChange={(_event, checked) => {
|
onChange={(_event, checked) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
setDefaultValueInputText('');
|
setDefaultValueInputText('');
|
||||||
@@ -197,7 +197,7 @@ export default function BaseColumnForm({
|
|||||||
|
|
||||||
<Box
|
<Box
|
||||||
component="section"
|
component="section"
|
||||||
className="grid grid-cols-8 px-6 py-3 border-t-1"
|
className="grid grid-cols-8 border-t-1 px-6 py-3"
|
||||||
>
|
>
|
||||||
<ControlledAutocomplete
|
<ControlledAutocomplete
|
||||||
id="defaultValue"
|
id="defaultValue"
|
||||||
@@ -249,7 +249,7 @@ export default function BaseColumnForm({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<ControlledCheckbox
|
<ControlledCheckbox
|
||||||
className="w-full col-span-8 py-3 m-0 sm:col-span-6 sm:col-start-3 sm:ml-1"
|
className="col-span-8 m-0 w-full py-3 sm:col-span-6 sm:col-start-3 sm:ml-1"
|
||||||
name="isNullable"
|
name="isNullable"
|
||||||
label={
|
label={
|
||||||
<span className="inline-grid grid-flow-row">
|
<span className="inline-grid grid-flow-row">
|
||||||
@@ -269,7 +269,7 @@ export default function BaseColumnForm({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<ControlledCheckbox
|
<ControlledCheckbox
|
||||||
className="w-full col-span-8 py-3 m-0 sm:col-span-6 sm:col-start-3 sm:ml-1"
|
className="col-span-8 m-0 w-full py-3 sm:col-span-6 sm:col-start-3 sm:ml-1"
|
||||||
name="isUnique"
|
name="isUnique"
|
||||||
label={
|
label={
|
||||||
<span className="inline-grid grid-flow-row">
|
<span className="inline-grid grid-flow-row">
|
||||||
@@ -306,7 +306,7 @@ export default function BaseColumnForm({
|
|||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Box className="grid justify-between flex-shrink-0 grid-flow-col gap-3 p-2 border-t-1">
|
<Box className="grid flex-shrink-0 grid-flow-col justify-between gap-3 border-t-1 p-2">
|
||||||
<Button
|
<Button
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ const ForeignKeyEditorInput = forwardRef(
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
className="min-w-[initial] py-1 px-2"
|
className="min-w-[initial] px-2 py-1"
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -114,7 +114,7 @@ const ForeignKeyEditorInput = forwardRef(
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => setValue('foreignKeyRelation', null)}
|
onClick={() => setValue('foreignKeyRelation', null)}
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
className="min-w-[initial] py-1 px-2"
|
className="min-w-[initial] px-2 py-1"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -107,9 +107,9 @@ export default function BaseForeignKeyForm({
|
|||||||
selectedColumn?.isPrimary || selectedColumn?.isUnique || false,
|
selectedColumn?.isPrimary || selectedColumn?.isUnique || false,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="flex flex-col content-between flex-auto pb-4 overflow-hidden"
|
className="flex flex-auto flex-col content-between overflow-hidden pb-4"
|
||||||
>
|
>
|
||||||
<Box className="grid flex-auto grid-flow-row gap-4 py-4 overflow-y-auto border-t-1">
|
<Box className="grid flex-auto grid-flow-row gap-4 overflow-y-auto border-t-1 py-4">
|
||||||
<Box component="section" className="grid grid-flow-row gap-4 px-6">
|
<Box component="section" className="grid grid-flow-row gap-4 px-6">
|
||||||
<Text variant="h3">From</Text>
|
<Text variant="h3">From</Text>
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ export default function BaseForeignKeyForm({
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box className="grid flex-shrink-0 grid-flow-row gap-2 px-6 pt-4 border-t-1">
|
<Box className="grid flex-shrink-0 grid-flow-row gap-2 border-t-1 px-6 pt-4">
|
||||||
<Button loading={isSubmitting} disabled={isSubmitting} type="submit">
|
<Button loading={isSubmitting} disabled={isSubmitting} type="submit">
|
||||||
{submitButtonText}
|
{submitButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -152,17 +152,17 @@ export default function BaseTableForm({
|
|||||||
className="flex flex-auto flex-col content-between overflow-hidden border-t-1"
|
className="flex flex-auto flex-col content-between overflow-hidden border-t-1"
|
||||||
>
|
>
|
||||||
<div className="flex-auto overflow-y-auto pb-4">
|
<div className="flex-auto overflow-y-auto pb-4">
|
||||||
<Box component="section" className="grid grid-cols-8 py-3 px-6">
|
<Box component="section" className="grid grid-cols-8 px-6 py-3">
|
||||||
<NameInput />
|
<NameInput />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
component="section"
|
component="section"
|
||||||
className="grid grid-cols-8 border-t-1 py-3 px-6"
|
className="grid grid-cols-8 border-t-1 px-6 py-3"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
variant="h2"
|
variant="h2"
|
||||||
className="col-span-8 mt-3 mb-1.5 text-sm+ font-bold"
|
className="col-span-8 mb-1.5 mt-3 text-sm+ font-bold"
|
||||||
>
|
>
|
||||||
Columns
|
Columns
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function ColumnEditorTable() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div role="table" className="col-span-8">
|
<div role="table" className="col-span-8">
|
||||||
<div className="sticky top-0 z-10 grid w-full grid-cols-12 gap-1 pt-1 pb-2">
|
<div className="sticky top-0 z-10 grid w-full grid-cols-12 gap-1 pb-2 pt-1">
|
||||||
<div role="columnheader" className="col-span-3">
|
<div role="columnheader" className="col-span-3">
|
||||||
<InputLabel as="span">
|
<InputLabel as="span">
|
||||||
Name
|
Name
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default function ForeignKeyEditorRow({
|
|||||||
<Button
|
<Button
|
||||||
onClick={onEdit}
|
onClick={onEdit}
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
className="min-w-[initial] py-1 px-2"
|
className="min-w-[initial] px-2 py-1"
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -58,7 +58,7 @@ export default function ForeignKeyEditorRow({
|
|||||||
<Button
|
<Button
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
className="min-w-[initial] py-1 px-2"
|
className="min-w-[initial] px-2 py-1"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user